diff --git a/chris_backend/core/celery.py b/chris_backend/core/celery.py index 23fbd439..504c4089 100755 --- a/chris_backend/core/celery.py +++ b/chris_backend/core/celery.py @@ -33,6 +33,7 @@ {'queue': 'periodic'}, 'plugininstances.tasks.cancel_waiting_plugin_instances': {'queue': 'periodic'}, + 'pacsfiles.tasks.send_pacs_query': {'queue': 'main2'}, 'pacsfiles.tasks.register_pacs_series': {'queue': 'main2'} } app.conf.update(task_routes=task_routes) diff --git a/chris_backend/pacsfiles/migrations/0005_pacsquery_status_pacsretrieve_status.py b/chris_backend/pacsfiles/migrations/0005_pacsquery_status_pacsretrieve_status.py new file mode 100644 index 00000000..e0f38fc3 --- /dev/null +++ b/chris_backend/pacsfiles/migrations/0005_pacsquery_status_pacsretrieve_status.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.5 on 2024-12-07 00:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pacsfiles', '0004_pacsretrieve'), + ] + + operations = [ + migrations.AddField( + model_name='pacsquery', + name='status', + field=models.CharField(choices=[('created', 'Default initial'), ('sent', 'Sent to PACS'), ('succeeded', 'Finished successfully'), ('errored', 'Finished with error')], default='created', max_length=10), + ), + migrations.AddField( + model_name='pacsretrieve', + name='status', + field=models.CharField(choices=[('created', 'Default initial'), ('sent', 'Sent to PACS'), ('succeeded', 'Finished successfully'), ('errored', 'Finished with error')], default='created', max_length=10), + ), + ] diff --git a/chris_backend/pacsfiles/models.py b/chris_backend/pacsfiles/models.py index 64db4611..27af53d7 100755 --- a/chris_backend/pacsfiles/models.py +++ b/chris_backend/pacsfiles/models.py @@ -10,8 +10,9 @@ from django_filters.rest_framework import FilterSet from core.models import ChrisFolder, ChrisFile -from core.utils import filter_files_by_n_slashes +from core.utils import filter_files_by_n_slashes, json_zip2str from core.storage import connect_storage +from .services import PfdcmClient logger = logging.getLogger(__name__) @@ -43,12 +44,20 @@ class Meta: fields = ['id', 'identifier', 'active'] +PACS_QUERY_STATUS_CHOICES = [("created", "Default initial"), + ("sent", "Sent to PACS"), + ("succeeded", "Finished successfully"), + ("errored", "Finished with error")] + + class PACSQuery(models.Model): creation_date = models.DateTimeField(auto_now_add=True) title = models.CharField(max_length=300, db_index=True) query = models.JSONField() description = models.CharField(max_length=700, blank=True) result = models.TextField(blank=True) + status = models.CharField(max_length=10, choices=PACS_QUERY_STATUS_CHOICES, + default='created') pacs = models.ForeignKey(PACS, on_delete=models.CASCADE, related_name='query_list') owner = models.ForeignKey('auth.User', on_delete=models.CASCADE) @@ -57,7 +66,28 @@ class Meta: unique_together = ('pacs', 'owner', 'title',) def __str__(self): - return self.query + return self.title + + def send(self): + """ + Custom method to send the query request to pfdcm service. + """ + pacs_name = str(self.pacs.identifier) + query = self.query + pfdcm_cl = PfdcmClient() + + self.status = 'sent' + self.save() + try: + result = pfdcm_cl.query(pacs_name, query) + except Exception: + self.status = 'errored' + self.save() + else: + if result: + self.result = json_zip2str(result) + self.status = 'succeeded' + self.save() class PACSQueryFilter(FilterSet): @@ -78,12 +108,18 @@ class PACSQueryFilter(FilterSet): class Meta: model = PACSQuery fields = ['id', 'min_creation_date', 'max_creation_date', 'title_exact', - 'title', 'description', 'pacs_id', 'pacs_identifier', 'owner_username'] + 'title', 'status', 'description', 'pacs_id', 'pacs_identifier', + 'owner_username'] + + +PACS_RETRIEVE_STATUS_CHOICES = PACS_QUERY_STATUS_CHOICES class PACSRetrieve(models.Model): creation_date = models.DateTimeField(auto_now_add=True) result = models.TextField(blank=True) + status = models.CharField(max_length=10, choices=PACS_RETRIEVE_STATUS_CHOICES, + default='created') pacs_query = models.ForeignKey(PACSQuery, on_delete=models.CASCADE, related_name='retrieve_list') owner = models.ForeignKey('auth.User', on_delete=models.CASCADE) @@ -92,7 +128,28 @@ class Meta: ordering = ('pacs_query', '-creation_date',) def __str__(self): - return self.pacs_query.query + return self.pacs_query.title + + def send(self): + """ + Custom method to send the retrieve request to pfdcm service. + """ + pacs_query = self.pacs_query + pacs_name = str(pacs_query.pacs.identifier) + query = pacs_query.query + pfdcm_cl = PfdcmClient() + + self.status = 'sent' + self.save() + try: + result = pfdcm_cl.retrieve(pacs_name, query) + except Exception: + self.status = 'errored' + self.save() + else: + if result: + self.result = json_zip2str(result) + self.save() class PACSRetrieveFilter(FilterSet): @@ -105,7 +162,8 @@ class PACSRetrieveFilter(FilterSet): class Meta: model = PACSRetrieve - fields = ['id', 'min_creation_date', 'max_creation_date', 'owner_username'] + fields = ['id', 'min_creation_date', 'max_creation_date', 'status', + 'owner_username'] class PACSSeries(models.Model): diff --git a/chris_backend/pacsfiles/serializers.py b/chris_backend/pacsfiles/serializers.py index b3434b3b..abdb28f0 100755 --- a/chris_backend/pacsfiles/serializers.py +++ b/chris_backend/pacsfiles/serializers.py @@ -11,10 +11,7 @@ from core.models import ChrisFolder from core.storage import connect_storage from core.serializers import ChrisFileSerializer -from core.utils import json_zip2str - from .models import PACS, PACSQuery, PACSRetrieve, PACSSeries, PACSFile -from .services import PfdcmClient logger = logging.getLogger(__name__) @@ -39,38 +36,30 @@ class PACSQuerySerializer(serializers.HyperlinkedModelSerializer): pacs_identifier = serializers.ReadOnlyField(source='pacs.identifier') owner_username = serializers.ReadOnlyField(source='owner.username') result = serializers.ReadOnlyField() + status = serializers.ReadOnlyField() retrieve_list = serializers.HyperlinkedIdentityField(view_name='pacsretrieve-list') class Meta: model = PACSQuery fields = ('url', 'id', 'creation_date', 'title', 'query', 'description', - 'pacs_identifier', 'owner_username', 'result', 'retrieve_list') + 'status', 'pacs_identifier', 'owner_username', 'result', + 'retrieve_list') def create(self, validated_data): """ Overriden to rise a serializer error when attempting to create a PACSQuery - object that results in a DB conflict. Then a PACS query operation is requested - to the PFDCM service. + object that results in a DB conflict. """ title = validated_data['title'] - query = validated_data['query'] pacs_name = validated_data['pacs'].identifier try: - pacs_query = super(PACSQuerySerializer, self).create(validated_data) + return super(PACSQuerySerializer, self).create(validated_data) except IntegrityError: error_msg = (f'You have already registered a PACS query with title={title} ' f'for pacs {pacs_name}') raise serializers.ValidationError([error_msg]) - pfdcm_cl = PfdcmClient() - result = pfdcm_cl.query(pacs_name, query) - - if result: - pacs_query.result = json_zip2str(result) - pacs_query.save() - return pacs_query - def update(self, instance, validated_data): """ Overriden to rise a serializer error when attempting to update a PACSQuery @@ -106,31 +95,15 @@ class PACSRetrieveSerializer(serializers.HyperlinkedModelSerializer): pacs_identifier = serializers.ReadOnlyField(source='pacs_query.pacs.identifier') owner_username = serializers.ReadOnlyField(source='owner.username') result = serializers.ReadOnlyField() + status = serializers.ReadOnlyField() pacs_query = serializers.HyperlinkedRelatedField(view_name='pacsquery-detail', read_only=True) class Meta: model = PACSRetrieve fields = ('url', 'id', 'creation_date', 'pacs_query_id', 'pacs_query_title', - 'query', 'pacs_identifier', 'owner_username', 'result', 'pacs_query') - - def create(self, validated_data): - """ - Overriden to request a PACS retrieve operation to the PFDCM service. - """ - pacs_query = validated_data['pacs_query'] - query = pacs_query.query - pacs_name = pacs_query.pacs.identifier - - pacs_retrieve = super(PACSRetrieveSerializer, self).create(validated_data) - - pfdcm_cl = PfdcmClient() - result = pfdcm_cl.retrieve(pacs_name, query) - - if result: - pacs_retrieve.result = json_zip2str(result) - pacs_retrieve.save() - return pacs_retrieve + 'query', 'pacs_identifier', 'status', 'owner_username', 'result', + 'pacs_query') class PACSSeriesSerializer(serializers.HyperlinkedModelSerializer): diff --git a/chris_backend/pacsfiles/tasks.py b/chris_backend/pacsfiles/tasks.py index 8f09fbbc..c4e3b3f0 100755 --- a/chris_backend/pacsfiles/tasks.py +++ b/chris_backend/pacsfiles/tasks.py @@ -1,10 +1,23 @@ + from typing import Optional -from celery import shared_task + from django.contrib.auth.models import User +from celery import shared_task +from .models import PACSQuery from .serializers import PACSSeriesSerializer +@shared_task +def send_pacs_query(pacs_query_id): + """ + Send PACS query. + """ + #from celery.contrib import rdb;rdb.set_trace() + pacs_query = PACSQuery.objects.get(pk=pacs_query_id) + pacs_query.send() + + @shared_task def register_pacs_series( PatientID: str, diff --git a/chris_backend/pacsfiles/tests/test_models.py b/chris_backend/pacsfiles/tests/test_models.py new file mode 100644 index 00000000..41e5227e --- /dev/null +++ b/chris_backend/pacsfiles/tests/test_models.py @@ -0,0 +1,135 @@ + +import logging +import os +from unittest import mock + +from django.test import TestCase, tag +from django.contrib.auth.models import User +from django.conf import settings + +from core.models import ChrisFolder +from core.utils import json_zip2str +from pacsfiles.models import PACS, PACSQuery, PACSRetrieve + + +CHRIS_SUPERUSER_PASSWORD = settings.CHRIS_SUPERUSER_PASSWORD + + +class ModelTests(TestCase): + + def setUp(self): + # avoid cluttered console output (for instance logging all the http requests) + logging.disable(logging.WARNING) + + # superuser chris (owner of root folders) + self.chris_username = 'chris' + chris_user = User.objects.get(username=self.chris_username) + + # create normal user + self.username = 'foo' + self.password = 'bar' + User.objects.create_user(username=self.username, password=self.password) + + # create a PACS + self.pacs_name = 'myPACS' + folder_path = f'SERVICES/PACS/{self.pacs_name}' + (pacs_folder, tf) = ChrisFolder.objects.get_or_create(path=folder_path, + owner=chris_user) + PACS.objects.get_or_create(folder=pacs_folder, identifier=self.pacs_name) + + def tearDown(self): + # re-enable logging + logging.disable(logging.NOTSET) + + +class PACSQueryModelTests(ModelTests): + + def setUp(self): + super(PACSQueryModelTests, self).setUp() + + # create a PACSQuery instance + user = User.objects.get(username=self.username) + pacs = PACS.objects.get(identifier=self.pacs_name) + self.query = {'SeriesInstanceUID': '2.3.15.2.1057'} + + pacs_query, _ = PACSQuery.objects.get_or_create(title='query1', query=self.query, + owner=user, pacs=pacs) + + def test_send_success(self): + """ + Test whether overriden send method successfully completes a PACS query request + to pfdcm service. + """ + pacs_query = PACSQuery.objects.get(title='query1') + + with mock.patch('pacsfiles.models.PfdcmClient.query') as pfdcm_query_mock: + result = {'mock': 'mock'} + pfdcm_query_mock.return_value = result + + pacs_query.send() + + pfdcm_query_mock.assert_called_with(self.pacs_name, self.query) + self.assertEqual(pacs_query.status, 'succeeded') + + def test_send_failure(self): + """ + Test whether overriden send method fails to make a PACS query request + to pfdcm service. + """ + pacs_query = PACSQuery.objects.get(title='query1') + + with mock.patch('pacsfiles.models.PfdcmClient.query') as pfdcm_query_mock: + pfdcm_query_mock.side_effect=Exception + + pacs_query.send() + + pfdcm_query_mock.assert_called_with(self.pacs_name, self.query) + self.assertEqual(pacs_query.status, 'errored') + + +class PACSRetrieveModelTests(ModelTests): + + def setUp(self): + super(PACSRetrieveModelTests, self).setUp() + + # create a PACSQuery instance + user = User.objects.get(username=self.username) + pacs = PACS.objects.get(identifier=self.pacs_name) + self.query = {'SeriesInstanceUID': '2.3.15.2.1057'} + + pacs_query, _ = PACSQuery.objects.get_or_create(title='query1', query=self.query, + owner=user, pacs=pacs) + + self.pacs_retrieve, _ = PACSRetrieve.objects.get_or_create(pacs_query=pacs_query, + owner=user) + + def test_send_success(self): + """ + Test whether overriden send method successfully sends a PACS retrieve request + to pfdcm service. + """ + pacs_retrieve = self.pacs_retrieve + + with mock.patch('pacsfiles.models.PfdcmClient.retrieve') as pfdcm_retrieve_mock: + result = {'mock': 'mock'} + pfdcm_retrieve_mock.return_value = result + + pacs_retrieve.send() + + pfdcm_retrieve_mock.assert_called_with(self.pacs_name, self.query) + self.assertEqual(pacs_retrieve.status, 'sent') + + def test_send_failure(self): + """ + Test whether overriden send method fails to make a PACS retrieve request + to pfdcm service. + """ + pacs_retrieve = self.pacs_retrieve + + with mock.patch('pacsfiles.models.PfdcmClient.retrieve') as pfdcm_retrieve_mock: + pfdcm_retrieve_mock.side_effect = Exception + + pacs_retrieve.send() + + pfdcm_retrieve_mock.assert_called_with(self.pacs_name, self.query) + self.assertEqual(pacs_retrieve.status, 'errored') diff --git a/chris_backend/pacsfiles/tests/test_serializers.py b/chris_backend/pacsfiles/tests/test_serializers.py index 54a65440..6cc2c4f4 100755 --- a/chris_backend/pacsfiles/tests/test_serializers.py +++ b/chris_backend/pacsfiles/tests/test_serializers.py @@ -8,7 +8,6 @@ from rest_framework import serializers from core.models import ChrisFolder -from core.utils import json_zip2str from pacsfiles.models import PACS, PACSQuery from pacsfiles.serializers import (PACSQuerySerializer, PACSRetrieveSerializer, PACSSeriesSerializer) @@ -54,13 +53,9 @@ def test_create_success(self): query = {'SeriesInstanceUID': '2.3.15.2.1057'} data = {'title': 'query1', 'query': query, 'owner': user, 'pacs': pacs} - with mock.patch('pacsfiles.serializers.PfdcmClient.query') as pfdcm_query_mock: - result = {'mock': 'mock'} - pfdcm_query_mock.return_value = result - pacs_query_serializer = PACSQuerySerializer(data=data) - pacs_query = pacs_query_serializer.create(data) - pfdcm_query_mock.assert_called_with(self.pacs_name, query) - self.assertEqual(pacs_query.result, json_zip2str(result)) + pacs_query_serializer = PACSQuerySerializer(data=data) + pacs_query = pacs_query_serializer.create(data) + self.assertEqual(pacs_query.status, 'created') def test_create_failure_pacs_user_title_combination_already_exists(self): @@ -71,10 +66,10 @@ def test_create_failure_pacs_user_title_combination_already_exists(self): user = User.objects.get(username=self.username) pacs = PACS.objects.get(identifier=self.pacs_name) query = {'SeriesInstanceUID': '1.3.12.2.1107'} + data = {'title': 'query2', 'query': query, 'owner': user, 'pacs': pacs} PACSQuery.objects.get_or_create(title='query2', query=query, owner=user, pacs=pacs) - data = {'title': 'query2', 'query': query, 'owner': user, 'pacs': pacs} pacs_query_serializer = PACSQuerySerializer(data=data) with self.assertRaises(serializers.ValidationError): pacs_query_serializer.create(data) @@ -114,29 +109,6 @@ def test_update_failure_pacs_user_title_combination_already_exists(self): pacs_query_serializer.update(pacs_query, data) -class PACSRetrieveSerializerTests(SerializerTests): - - def test_create_success(self): - """ - Test whether overriden 'create' method successfully creates a new PACS retrieve. - """ - user = User.objects.get(username=self.username) - pacs = PACS.objects.get(identifier=self.pacs_name) - query = {'SeriesInstanceUID': '2.3.15.2.1057'} - - pacs_query, _ = PACSQuery.objects.get_or_create(title='query2', query=query, - owner=user, pacs=pacs) - data = {'pacs_query': pacs_query, 'owner': user} - - with mock.patch('pacsfiles.serializers.PfdcmClient.retrieve') as pfdcm_retrieve_mock: - result = {'mock': 'mock'} - pfdcm_retrieve_mock.return_value = result - pacs_retrieve_serializer = PACSRetrieveSerializer(data=data) - pacs_retrieve = pacs_retrieve_serializer.create(data) - pfdcm_retrieve_mock.assert_called_with(self.pacs_name, query) - self.assertEqual(pacs_retrieve.result, json_zip2str(result)) - - class PACSSeriesSerializerTests(SerializerTests): def test_validate_ndicom_failure_not_positive(self): diff --git a/chris_backend/pacsfiles/tests/test_views.py b/chris_backend/pacsfiles/tests/test_views.py index 82c275de..19e934ed 100755 --- a/chris_backend/pacsfiles/tests/test_views.py +++ b/chris_backend/pacsfiles/tests/test_views.py @@ -4,11 +4,10 @@ import io from unittest import mock -from django.test import TestCase, tag +from django.test import TestCase, TransactionTestCase, tag from django.conf import settings from django.contrib.auth.models import User, Group from django.urls import reverse - from rest_framework import status from core.models import ChrisFolder @@ -190,7 +189,7 @@ def setUp(self): self.create_read_url = reverse("pacsquery-list", kwargs={"pk": pacs.id}) query = {'SeriesInstanceUID': '2.3.15.2.1057'} - pacs_query, _ = PACSQuery.objects.get_or_create(title='query1', query=query, + pacs_query, _ = PACSQuery.objects.get_or_create(title='query10', query=query, owner=user, pacs=pacs) self.post = json.dumps( @@ -200,7 +199,7 @@ def setUp(self): def test_pacs_query_list_success(self): self.client.login(username=self.username, password=self.password) response = self.client.get(self.create_read_url) - self.assertContains(response, 'query1') + self.assertContains(response, 'query10') def test_pacs_query_list_success_readonly(self): pacs = PACS.objects.get(identifier=self.pacs_name) @@ -212,17 +211,24 @@ def test_pacs_query_list_success_readonly(self): self.client.login(username=self.other_username, password=self.other_password) # not a member of pacs_users response = self.client.get(self.create_read_url) self.assertContains(response, 'query2') # can see its own queries - self.assertNotContains(response, 'query1') # cannot see other users' queries + self.assertNotContains(response, 'query10') # cannot see other users' queries def test_pacs_query_list_failure_unauthenticated(self): response = self.client.get(self.create_read_url) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_pacs_query_create_success(self): - self.client.login(username=self.username, password=self.password) - response = self.client.post(self.create_read_url, data=self.post, - content_type=self.content_type) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + with mock.patch.object(views.send_pacs_query, 'delay', + return_value=None) as delay_mock: + # make API request + self.client.login(username=self.username, password=self.password) + response = self.client.post(self.create_read_url, data=self.post, + content_type=self.content_type) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + # check that the send_pacs_query task was called with appropriate args + delay_mock.assert_called_with(response.data['id']) + self.assertEqual(response.data['status'], 'created') def test_pacs_query_create_failure_unauthenticated(self): response = self.client.post(self.create_read_url, data=self.post, @@ -236,7 +242,7 @@ def test_pacs_query_create_failure_forbidden(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) -class AllPACSQueryListViewTests(PACSQueryListViewTests): +class AllPACSQueryListViewTests(PACSViewTests): """ Test the allpacsquery-list view. """ @@ -244,6 +250,12 @@ class AllPACSQueryListViewTests(PACSQueryListViewTests): def setUp(self): super(AllPACSQueryListViewTests, self).setUp() + pacs = PACS.objects.get(identifier=self.pacs_name) + user = User.objects.get(username=self.username) + query = {'SeriesInstanceUID': '2.3.15.2.1057'} + pacs_query, _ = PACSQuery.objects.get_or_create(title='query1', query=query, + owner=user, pacs=pacs) + self.read_url = reverse("allpacsquery-list") def test_all_pacs_query_list_success(self): @@ -268,7 +280,7 @@ def test_all_pacs_query_list_failure_unauthenticated(self): self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) -class AllPACSQueryListQuerySearchViewTests(PACSQueryListViewTests): +class AllPACSQueryListQuerySearchViewTests(PACSViewTests): """ Test the allpacsquery-list-query-search view. """ @@ -276,6 +288,12 @@ class AllPACSQueryListQuerySearchViewTests(PACSQueryListViewTests): def setUp(self): super(AllPACSQueryListQuerySearchViewTests, self).setUp() + pacs = PACS.objects.get(identifier=self.pacs_name) + user = User.objects.get(username=self.username) + query = {'SeriesInstanceUID': '2.3.15.2.1057'} + pacs_query, _ = PACSQuery.objects.get_or_create(title='query1', query=query, + owner=user, pacs=pacs) + self.read_url = reverse("allpacsquery-list-query-search") + '?name=query1' def test_all_pacs_query_list_query_search_success(self): @@ -432,7 +450,7 @@ def test_pacs_retrieve_create_failure_forbidden(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) -class PACSRetrieveListQuerySearchViewTests(PACSQueryListViewTests): +class PACSRetrieveListQuerySearchViewTests(PACSViewTests): """ Test the pacsretrieve-list-query-search view. """ @@ -605,7 +623,7 @@ def test_pacs_series_create_failure_forbidden(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) -class PACSSeriesListQuerySearchViewTests(PACSQueryListViewTests): +class PACSSeriesListQuerySearchViewTests(PACSViewTests): """ Test the pacsseries-list-query-search view. """ diff --git a/chris_backend/pacsfiles/views.py b/chris_backend/pacsfiles/views.py index 7392d14b..2a1591d9 100755 --- a/chris_backend/pacsfiles/views.py +++ b/chris_backend/pacsfiles/views.py @@ -14,6 +14,7 @@ from .models import (PACS, PACSFilter, PACSQuery, PACSQueryFilter, PACSRetrieve, PACSRetrieveFilter, PACSSeries, PACSSeriesFilter, PACSFile, PACSFileFilter) +from .tasks import send_pacs_query from .serializers import (PACSSerializer, PACSQuerySerializer, PACSRetrieveSerializer, PACSSeriesSerializer, PACSFileSerializer) from .services import PfdcmClient @@ -41,9 +42,9 @@ def list(self, request, *args, **kwargs): def get_queryset(self): """ - Overriden to contact pfdcm for new PACS available. New PACS might be created - with a POST request in the future but this is an initial implementation so no - changes are required to other backend services (oxidicom for instance). + Overriden to contact pfdcm service for new PACS available. New PACS might be + created with a POST request in the future but this is an initial implementation + so no changes are required to other backend services (oxidicom for instance). """ queryset = PACS.objects.all() existing_pacs_names_set = {pacs.identifier for pacs in queryset} @@ -172,10 +173,11 @@ def get_pacs_queries_queryset(self): def perform_create(self, serializer): """ Overriden to associate the owner and the pacs with the PACS query before first - saving to the DB. + saving to the DB. Then the PACS query operation is sent to the remote PACS. """ pacs = self.get_object() - serializer.save(owner=self.request.user, pacs=pacs) + pacs_query = serializer.save(owner=self.request.user, pacs=pacs) + send_pacs_query.delay(pacs_query.id) # call async task class AllPACSQueryList(generics.ListAPIView): @@ -297,10 +299,12 @@ def get_pacs_retrieves_queryset(self): def perform_create(self, serializer): """ Overriden to associate the owner and the pacs query with the retrieve - before first saving to the DB. + before first saving to the DB. Then the PACS retrieve operation is sent to + the remote PACS. """ pacs_query = self.get_object() - serializer.save(owner=self.request.user, pacs_query=pacs_query) + pacs_retrieve = serializer.save(owner=self.request.user, pacs_query=pacs_query) + pacs_retrieve.send() class PACSRetrieveListQuerySearch(generics.ListAPIView):