diff --git a/code.json b/code.json index 81acba6..73a8d91 100644 --- a/code.json +++ b/code.json @@ -3,7 +3,7 @@ "name": "lide", "organization": "U.S. Geological Survey", "description": "Web services for LILI (LIDE (Laboratory for Infectious Disease and the Environment) Information Management System)", - "version": "v0.119.2", + "version": "v0.120.0", "status": "Release Candidate", "permissions": { @@ -47,7 +47,7 @@ }, "date": { - "metadataLastUpdated": "2019-10-09" + "metadataLastUpdated": "2019-10-11" } } ] diff --git a/lideservices/migrations/0011_historicalreporttype_historicalstatus_reporttype_status.py b/lideservices/migrations/0011_historicalreporttype_historicalstatus_reporttype_status.py new file mode 100644 index 0000000..482ec1c --- /dev/null +++ b/lideservices/migrations/0011_historicalreporttype_historicalstatus_reporttype_status.py @@ -0,0 +1,90 @@ +# Generated by Django 2.2 on 2019-10-11 11:26 + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import simple_history.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('lideservices', '0010_auto_20191008_2214'), + ] + + operations = [ + migrations.CreateModel( + name='Status', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_date', models.DateField(blank=True, db_index=True, default=datetime.date.today, null=True)), + ('modified_date', models.DateField(auto_now=True, null=True)), + ('name', models.CharField(max_length=128, unique=True)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='status_creator', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='status_modifier', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'lide_status', + }, + ), + migrations.CreateModel( + name='ReportType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_date', models.DateField(blank=True, db_index=True, default=datetime.date.today, null=True)), + ('modified_date', models.DateField(auto_now=True, null=True)), + ('name', models.CharField(max_length=128, unique=True)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reporttype_creator', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reporttype_modifier', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'lide_reporttype', + }, + ), + migrations.CreateModel( + name='HistoricalStatus', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('created_date', models.DateField(blank=True, db_index=True, default=datetime.date.today, null=True)), + ('modified_date', models.DateField(blank=True, editable=False, null=True)), + ('name', models.CharField(db_index=True, max_length=128)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('created_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'historical status', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalReportType', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('created_date', models.DateField(blank=True, db_index=True, default=datetime.date.today, null=True)), + ('modified_date', models.DateField(blank=True, editable=False, null=True)), + ('name', models.CharField(db_index=True, max_length=128)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('created_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'historical report type', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + ] diff --git a/lideservices/migrations/0012_auto_20191011_1130.py b/lideservices/migrations/0012_auto_20191011_1130.py new file mode 100644 index 0000000..01c154a --- /dev/null +++ b/lideservices/migrations/0012_auto_20191011_1130.py @@ -0,0 +1,34 @@ +# Generated by Django 2.2 on 2019-10-11 11:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('lideservices', '0011_historicalreporttype_historicalstatus_reporttype_status'), + ] + + operations = [ + migrations.AlterField( + model_name='historicalreportfile', + name='report_status', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='lideservices.Status'), + ), + migrations.AlterField( + model_name='historicalreportfile', + name='report_type', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='lideservices.ReportType'), + ), + migrations.AlterField( + model_name='reportfile', + name='report_status', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reportfiles', to='lideservices.Status'), + ), + migrations.AlterField( + model_name='reportfile', + name='report_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reportfiles', to='lideservices.ReportType'), + ), + ] diff --git a/lideservices/migrations/0013_auto_20191011_1139.py b/lideservices/migrations/0013_auto_20191011_1139.py new file mode 100644 index 0000000..d4b9ea6 --- /dev/null +++ b/lideservices/migrations/0013_auto_20191011_1139.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2 on 2019-10-11 11:39 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('lideservices', '0012_auto_20191011_1130'), + ] + + operations = [ + migrations.RenameField( + model_name='historicalreportfile', + old_name='report_status', + new_name='status', + ), + migrations.RenameField( + model_name='reportfile', + old_name='report_status', + new_name='status', + ), + ] diff --git a/lideservices/models.py b/lideservices/models.py index 67b36c5..cb7f19a 100644 --- a/lideservices/models.py +++ b/lideservices/models.py @@ -1832,27 +1832,12 @@ def _get_filename(self): def reportfile_location(self, instance): """Returns a custom location for the report file, in a folder named for its report type""" - return 'reports/{0}/{1}'.format(self.get_report_type_display(), instance) - - # TODO: confirm report names - REPORT_TYPES = ( - (1, 'Inhibition',), - (2, 'ResultsSummary',), - (3, 'IndividualSample',), - (4, 'QualityControl',), - (5, 'ControlsResults',), - ) - - REPORT_STATUSES = ( - (1, 'Pending',), - (2, 'Complete',), - (3, 'Failed',) - ) + return 'reports/{0}/{1}'.format(self.report_type.name, instance) name = property(_get_filename) file = models.FileField(upload_to=reportfile_location, null=True) - report_type = models.IntegerField(choices=REPORT_TYPES) - report_status = models.IntegerField(choices=REPORT_STATUSES) + report_type = models.ForeignKey('ReportType', models.PROTECT, related_name='reportfiles') + status = models.ForeignKey('Status', models.PROTECT, related_name='reportfiles') fail_reason = models.TextField(blank=True) def __str__(self): @@ -1860,3 +1845,21 @@ def __str__(self): class Meta: db_table = "lide_reportfile" + + +class ReportType(NameModel): + + def __str__(self): + return str(self.name) + + class Meta: + db_table = "lide_reporttype" + + +class Status(NameModel): + + def __str__(self): + return str(self.name) + + class Meta: + db_table = "lide_status" diff --git a/lideservices/serializers.py b/lideservices/serializers.py index 89c3d27..627f012 100644 --- a/lideservices/serializers.py +++ b/lideservices/serializers.py @@ -1883,17 +1883,30 @@ class Meta: class ReportFileSerializer(serializers.ModelSerializer): created_by = serializers.StringRelatedField() modified_by = serializers.StringRelatedField() - report_type_string = serializers.SerializerMethodField() - report_status_string = serializers.SerializerMethodField() + report_type_string = serializers.StringRelatedField() + status_string = serializers.StringRelatedField() - def get_report_type_string(self, obj): - return obj.get_report_type_display() - - def get_report_status_string(self, obj): - return obj.get_report_status_display() class Meta: model = ReportFile - fields = ('id', 'name', 'file', 'report_type', 'report_type_string', 'report_status', 'report_status_string', - 'fail_reason', 'created_date', 'created_by', 'modified_date', 'modified_by',) + fields = ('id', 'name', 'file', 'report_type', 'report_type_string', 'status', 'status_string', 'fail_reason', + 'created_date', 'created_by', 'modified_date', 'modified_by',) read_only_fields = ('name',) + + +class ReportTypeSerializer(serializers.ModelSerializer): + created_by = serializers.StringRelatedField() + modified_by = serializers.StringRelatedField() + + class Meta: + model = ReportType + fields = ('id', 'name', 'created_date', 'created_by', 'modified_date', 'modified_by',) + + +class StatusSerializer(serializers.ModelSerializer): + created_by = serializers.StringRelatedField() + modified_by = serializers.StringRelatedField() + + class Meta: + model = Status + fields = ('id', 'name', 'created_date', 'created_by', 'modified_date', 'modified_by',) diff --git a/lideservices/tasks.py b/lideservices/tasks.py index 56e0aff..0b02c75 100644 --- a/lideservices/tasks.py +++ b/lideservices/tasks.py @@ -38,13 +38,13 @@ def generate_inhibition_report(sample, report_file_id, username): new_file_content = ContentFile(json.dumps(data, cls=DecimalEncoder)) report_file.file.save(new_file_name, new_file_content) - report_file.report_status = 2 + report_file.status = Status.objects.filter(id=2).first() report_file.save() return "generate_inhibition_report completed and created file {0}".format(new_file_name) except Exception as exc: message = "generate_inhibition_report failed and no file was created, error message: {0}".format(exc) - report_file.report_status = 3 + report_file.status = Status.objects.filter(id=3).first() report_file.fail_reason = message report_file.save() return message @@ -179,13 +179,13 @@ def generate_results_summary_report(sample, target, statistic, report_file_id, u new_file_content = ContentFile(json.dumps(data, cls=DecimalEncoder)) report_file.file.save(new_file_name, new_file_content) - report_file.report_status = 2 + report_file.status = Status.objects.filter(id=2).first() report_file.save() return "results_summary_report completed and created file {0}".format(new_file_name) except Exception as exc: message = "results_summary_report failed and no file was created, error message: {0}".format(exc) - report_file.report_status = 3 + report_file.status = Status.objects.filter(id=3).first() report_file.fail_reason = message report_file.save() return message @@ -216,13 +216,13 @@ def generate_individual_sample_report(sample, target, report_file_id, username): new_file_content = ContentFile(json.dumps(data, cls=DecimalEncoder)) report_file.file.save(new_file_name, new_file_content) - report_file.report_status = 2 + report_file.status = Status.objects.filter(id=2).first() report_file.save() return "individual_sample_report_task completed and created file {0}".format(new_file_name) except Exception as exc: message = "individual_sample_report_task failed and no file was created, error message: {0}".format(exc) - report_file.report_status = 3 + report_file.status = Status.objects.filter(id=3).first() report_file.fail_reason = message report_file.save() return message @@ -390,13 +390,13 @@ def generate_quality_control_report(samples, report_file_id, username): new_file_content = ContentFile(json.dumps(data, cls=DecimalEncoder)) report_file.file.save(new_file_name, new_file_content) - report_file.report_status = 2 + report_file.status = Status.objects.filter(id=2).first() report_file.save() return "quality_control_report_task completed and created file {0}".format(new_file_name) except Exception as exc: message = "quality_control_report_task failed and no file was created, error message: {0}".format(exc) - report_file.report_status = 3 + report_file.status = Status.objects.filter(id=3).first() report_file.fail_reason = message report_file.save() return message @@ -684,13 +684,13 @@ def generate_control_results_report(sample_ids, target_ids, report_file_id, user new_file_content = ContentFile(json.dumps(data, cls=DecimalEncoder, default=str)) report_file.file.save(new_file_name, new_file_content) - report_file.report_status = 2 + report_file.status = Status.objects.filter(id=2).first() report_file.save() return "control_results_report_task completed and created file {0}".format(new_file_name) except Exception as exc: message = "control_results_report_task failed and no file was created, error message: {0}".format(exc) - report_file.report_status = 3 + report_file.status = Status.objects.filter(id=3).first() report_file.fail_reason = message report_file.save() return message diff --git a/lideservices/urls.py b/lideservices/urls.py index 43d40a5..ac74bed 100644 --- a/lideservices/urls.py +++ b/lideservices/urls.py @@ -41,6 +41,8 @@ router.register(r'otheranalyses', views.OtherAnalysisViewSet, 'otheranalyses') router.register(r'users', views.UserViewSet, 'users') router.register(r'reportfiles', views.ReportFileViewSet, 'reportfiles') +router.register(r'reporttypes', views.ReportTypeViewSet, 'reporttypes') +router.register(r'statuses', views.StatusViewSet, 'statuses') urlpatterns = [ url(r'^', include(router.urls)), diff --git a/lideservices/views.py b/lideservices/views.py index 41babb3..b6c733e 100644 --- a/lideservices/views.py +++ b/lideservices/views.py @@ -389,8 +389,10 @@ def summary_statistics(self, request): sample = request.query_params.get('sample', None) target = request.query_params.get('target', None) statistic = request.query_params.get('statistic', None) + report_type = ReportType.objects.filter(id=2).first() + status = Status.objects.filter(id=1).first() report_file = ReportFile.objects.create( - report_type=2, report_status=1, created_by=request.user, modified_by=request.user) + report_type=report_type, status=status, created_by=request.user, modified_by=request.user) generate_results_summary_report.delay(sample, target, statistic, report_file.id, request.user.username) return JsonResponse({"message": "Request for Results Summary Report received."}, status=200) @@ -398,8 +400,10 @@ def summary_statistics(self, request): def results(self, request): sample = request.query_params.get('sample', None) target = request.query_params.get('target', None) + report_type = ReportType.objects.filter(id=2).first() + status = Status.objects.filter(id=1).first() report_file = ReportFile.objects.create( - report_type=3, report_status=1, created_by=request.user, modified_by=request.user) + report_type=report_type, status=status, created_by=request.user, modified_by=request.user) generate_individual_sample_report.delay(sample, target, report_file.id, request.user.username) return JsonResponse({"message": "Request for Individual Sample Report received."}, status=200) @@ -753,8 +757,10 @@ class SampleExtractionViewSet(HistoryViewSet): @action(detail=False) def inhibition_report(self, request): sample = request.query_params.get('sample', None) + report_type = ReportType.objects.filter(id=1).first() + status = Status.objects.filter(id=1).first() report_file = ReportFile.objects.create( - report_type=1, report_status=1, created_by=request.user, modified_by=request.user) + report_type=report_type, status=status, created_by=request.user, modified_by=request.user) generate_inhibition_report.delay(sample, report_file.id, request.user.username) return JsonResponse({"message": "Request for Inhibition Report received."}, status=200) @@ -1411,8 +1417,10 @@ class QualityControlReportView(views.APIView): def post(self, request): request_data = JSONParser().parse(request) samples = request_data.get('samples', None) + report_type = ReportType.objects.filter(id=4).first() + status = Status.objects.filter(id=1).first() report_file = ReportFile.objects.create( - report_type=4, report_status=1, created_by=request.user, modified_by=request.user) + report_type=report_type, status=status, created_by=request.user, modified_by=request.user) generate_quality_control_report.delay(samples, report_file.id, request.user.username) return JsonResponse({"message": "Request for Inhibition Report received."}, status=200) @@ -1424,15 +1432,16 @@ def post(self, request): request_data = JSONParser().parse(request) sample_ids = request_data.get('samples', None) target_ids = request_data.get('targets', None) + report_type = ReportType.objects.filter(id=5).first() + status = Status.objects.filter(id=1).first() report_file = ReportFile.objects.create( - report_type=5, report_status=1, created_by=request.user, modified_by=request.user) + report_type=report_type, status=status, created_by=request.user, modified_by=request.user) generate_control_results_report.delay(sample_ids, target_ids, report_file.id, request.user.username) return JsonResponse({"message": "Request for Control Results Report received."}, status=200) class ReportFileViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = (permissions.IsAuthenticated,) - # queryset = ReportFile.objects.all() serializer_class = ReportFileSerializer def get_queryset(self): @@ -1447,3 +1456,15 @@ def get_queryset(self): else: queryset = queryset.filter(report_type__exact=report_type) return queryset + + +class ReportTypeViewSet(viewsets.ModelViewSet): + permission_classes = (permissions.IsAuthenticated,) + queryset = ReportType.objects.all() + serializer_class = ReportTypeSerializer + + +class StatusViewSet(viewsets.ModelViewSet): + permission_classes = (permissions.IsAuthenticated,) + queryset = Status.objects.all() + serializer_class = StatusSerializer