From f3cf7d15d71c950299caecbab03bbef269158f3b Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Sat, 4 Dec 2021 16:40:40 +0100 Subject: [PATCH 1/9] feat: export gleason elements metadata --- .../management/commands/get_gleason_data.py | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 promort/clinical_annotations_manager/management/commands/get_gleason_data.py diff --git a/promort/clinical_annotations_manager/management/commands/get_gleason_data.py b/promort/clinical_annotations_manager/management/commands/get_gleason_data.py new file mode 100644 index 0000000..7829875 --- /dev/null +++ b/promort/clinical_annotations_manager/management/commands/get_gleason_data.py @@ -0,0 +1,95 @@ +# Copyright (c) 2021, CRS4 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from django.core.management.base import BaseCommand +from django.core.paginator import Paginator +from clinical_annotations_manager.models import GleasonElement + +from csv import DictWriter + +import logging + +logger = logging.getLogger('promort_commands') + + +class Command(BaseCommand): + help = """ + Export existing Gleason items data to CSV. + No ROIs are exported, only metadata; to export ROIs use the extract_gleason_elements command. + """ + + def add_arguments(self, parser): + parser.add_argument('--output_file', dest='output', type=str, required=True, + help='path of the output CSV file') + parser.add_argument('--page_size', dest='page_size', type=int, default=0, + help='the number of records retrieved for each page (this will enable pagination)') + + def _dump_row(self, gleason_element, csv_writer): + try: + creation_start_date = gleason_element.creation_start_date.strftime('%Y-%m-%d %H:%M:%S') + except AttributeError: + creation_start_date = None + fr_ann = gleason_element.focus_region_annotation + csv_writer.writerow( + { + 'case_id': fr_ann.focus_region.core.slice.slide.case.id, + 'slide_id': fr_ann.focus_region.core.slice.slide.id, + 'rois_review_step_id': fr_ann.annotation_step.rois_review_step.label, + 'clinical_review_step_id': fr_ann.annotation_step.label, + 'reviewer': fr_ann.author.username, + 'focus_region_id': fr_ann.focus_region.id, + 'focus_region_label': fr_ann.focus_region.label, + 'core_id': fr_ann.focus_region.core.id, + 'core_label': fr_ann.focus_region.core.label, + 'gleason_element_id': gleason_element.id, + 'gleason_type': gleason_element.gleason_type, + 'creation_start_date': creation_start_date, + 'creation_date': gleason_element.creation_date.strftime('%Y-%m-%d %H:%M:%S') + } + ) + + def _dump_data(self, page_size, csv_writer): + if page_size > 0: + logger.info('Pagination enabled (%d records for page)', page_size) + ge_qs = GleasonElement.objects.get_queryset().order_by('creation_date') + paginator = Paginator(ge_qs, page_size) + for x in paginator.page_range: + logger.info(f'-- page {x} --') + page = paginator.page(x) + for ge in page.object_list: + self._dump_row(ge, csv_writer) + else: + logger.info('Loading full batch') + gleason_elements = GleasonElement.objects.all() + for ge in gleason_elements: + self._dump_row(ge, csv_writer) + + def _export_data(self, out_file, page_size): + header = ['case_id', 'slide_id', 'rois_review_step_id', 'clinical_review_step_id', 'reviewer', 'focus_region_id', + 'focus_region_label', 'core_id', 'core_label', 'gleason_element_id', 'gleason_type', + 'creation_start_date', 'creation_date'] + with open(out_file, 'w') as ofile: + writer = DictWriter(ofile, delimiter=',', fieldnames=header) + writer.writeheader() + self._dump_data(page_size, writer) + + def handle(self, *args, **opts): + logger.info('=== Starting export job ===') + self._export_data(opts['output'], opts['page_size']) + logger.info('=== Data saved to {0} ==='.format(opts['output'])) From 2d412d62e4794a231e34702f0903e8c193d3911b Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Mon, 29 Nov 2021 16:20:28 +0100 Subject: [PATCH 2/9] refactor: renamed "creation_start_date" fields into "action_start_time" --- .../commands/get_cores_clinical_data.py | 8 ++-- .../get_focus_regions_clinical_data.py | 8 ++-- .../commands/get_slices_clinical_data.py | 8 ++-- .../migrations/0018_auto_20211128_1525.py | 33 ++++++++++++++++ .../clinical_annotations_manager/models.py | 8 ++-- .../serializers.py | 8 ++-- .../management/commands/get_cores_data.py | 8 ++-- .../commands/get_focus_regions_data.py | 8 ++-- .../management/commands/get_slices_data.py | 8 ++-- .../migrations/0022_auto_20211129_1509.py | 28 ++++++++++++++ promort/rois_manager/models.py | 6 +-- promort/rois_manager/serializers.py | 10 ++--- ...linical_annotations_manager.controllers.js | 38 +++++++++---------- .../rois_manager/rois_manager.controllers.js | 24 ++++++------ .../js/rois_manager/rois_manager.services.js | 12 +++--- 15 files changed, 138 insertions(+), 77 deletions(-) create mode 100644 promort/clinical_annotations_manager/migrations/0018_auto_20211128_1525.py create mode 100644 promort/rois_manager/migrations/0022_auto_20211129_1509.py diff --git a/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py b/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py index c0f7590..5ae37e6 100644 --- a/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py +++ b/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py @@ -57,9 +57,9 @@ def _dump_data(self, page_size, csv_writer): def _dump_row(self, core_annotation, csv_writer): try: - creation_start_date = core_annotation.creation_start_date.strftime('%Y-%m-%d %H:%M:%S') + action_start_time = core_annotation.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: - creation_start_date = None + action_start_time = None csv_writer.writerow( { 'case_id': core_annotation.core.slice.slide.case.id, @@ -69,7 +69,7 @@ def _dump_row(self, core_annotation, csv_writer): 'reviewer': core_annotation.author.username, 'core_id': core_annotation.core.id, 'core_label': core_annotation.core.label, - 'creation_start_date': creation_start_date, + 'action_start_time': action_start_time, 'creation_date': core_annotation.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'primary_gleason': core_annotation.primary_gleason, 'secondary_gleason': core_annotation.secondary_gleason, @@ -79,7 +79,7 @@ def _dump_row(self, core_annotation, csv_writer): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'clinical_review_step_id', 'reviewer', - 'core_id', 'core_label', 'creation_start_date', 'creation_date', 'primary_gleason', + 'core_id', 'core_label', 'action_start_time', 'creation_date', 'primary_gleason', 'secondary_gleason', 'gleason_group_who_16'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) diff --git a/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py b/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py index dcc9662..78e0304 100644 --- a/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py +++ b/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py @@ -56,9 +56,9 @@ def _dump_data(self, page_size, csv_writer): def _dump_row(self, focus_region_annotation, csv_writer): try: - creation_start_date = focus_region_annotation.creation_start_date.strftime('%Y-%m-%d %H:%M:%S') + action_start_time = focus_region_annotation.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: - creation_start_date = None + action_start_time = None csv_writer.writerow( { 'case_id': focus_region_annotation.focus_region.core.slice.slide.case.id, @@ -70,7 +70,7 @@ def _dump_row(self, focus_region_annotation, csv_writer): 'focus_region_label': focus_region_annotation.focus_region.label, 'core_id': focus_region_annotation.focus_region.core.id, 'core_label': focus_region_annotation.focus_region.core.label, - 'creation_start_date': creation_start_date, + 'action_start_time': action_start_time, 'creation_date': focus_region_annotation.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'perineural_involvement': focus_region_annotation.perineural_involvement, 'intraductal_carcinoma': focus_region_annotation.intraductal_carcinoma, @@ -88,7 +88,7 @@ def _dump_row(self, focus_region_annotation, csv_writer): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'clinical_review_step_id', 'reviewer', - 'focus_region_id', 'focus_region_label', 'core_id', 'core_label', 'creation_start_date', + 'focus_region_id', 'focus_region_label', 'core_id', 'core_label', 'action_start_time', 'creation_date', 'perineural_involvement', 'intraductal_carcinoma', 'ductal_carcinoma', 'poorly_formed_glands', 'cribriform_pattern', 'small_cell_signet_ring', 'hypernephroid_pattern', 'mucinous', 'comedo_necrosis', 'total_gleason_4_area', 'gleason_4_percentage'] diff --git a/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py b/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py index b7e0d2c..d497a2c 100644 --- a/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py +++ b/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py @@ -57,9 +57,9 @@ def _dump_data(self, page_size, csv_writer): def _dump_row(self, slice_annotation, csv_writer): try: - creation_start_date = slice_annotation.creation_start_date.strftime('%Y-%m-%d %H:%M:%S') + action_start_time = slice_annotation.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: - creation_start_date = None + action_start_time = None csv_writer.writerow( { 'case_id': slice_annotation.slice.slide.case.id, @@ -69,7 +69,7 @@ def _dump_row(self, slice_annotation, csv_writer): 'reviewer': slice_annotation.author.username, 'slice_id': slice_annotation.slice.id, 'slice_label': slice_annotation.slice.label, - 'creation_start_date': creation_start_date, + 'action_start_time': action_start_time, 'creation_date': slice_annotation.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'high_grade_pin': slice_annotation.high_grade_pin, 'pah': slice_annotation.pah, @@ -83,7 +83,7 @@ def _dump_row(self, slice_annotation, csv_writer): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'clinical_review_step_id', 'reviewer', - 'slice_id', 'slice_label', 'creation_start_date', 'creation_date', 'high_grade_pin', 'pah', + 'slice_id', 'slice_label', 'action_start_time', 'creation_date', 'high_grade_pin', 'pah', 'chronic_inflammation', 'acute_inflammation', 'periglandular_inflammation', 'intraglandular_inflammation', 'stromal_inflammation'] with open(out_file, 'w') as ofile: diff --git a/promort/clinical_annotations_manager/migrations/0018_auto_20211128_1525.py b/promort/clinical_annotations_manager/migrations/0018_auto_20211128_1525.py new file mode 100644 index 0000000..e11e980 --- /dev/null +++ b/promort/clinical_annotations_manager/migrations/0018_auto_20211128_1525.py @@ -0,0 +1,33 @@ +# Generated by Django 3.1.13 on 2021-11-28 15:25 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('clinical_annotations_manager', '0017_auto_20210424_1321'), + ] + + operations = [ + migrations.RenameField( + model_name='coreannotation', + old_name='creation_start_date', + new_name='action_start_time', + ), + migrations.RenameField( + model_name='focusregionannotation', + old_name='creation_start_date', + new_name='action_start_time', + ), + migrations.RenameField( + model_name='gleasonelement', + old_name='creation_start_date', + new_name='action_start_time', + ), + migrations.RenameField( + model_name='sliceannotation', + old_name='creation_start_date', + new_name='action_start_time', + ), + ] diff --git a/promort/clinical_annotations_manager/models.py b/promort/clinical_annotations_manager/models.py index d4333f7..c623f9d 100644 --- a/promort/clinical_annotations_manager/models.py +++ b/promort/clinical_annotations_manager/models.py @@ -30,7 +30,7 @@ class SliceAnnotation(models.Model): related_name='clinical_annotations') annotation_step = models.ForeignKey(ClinicalAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='slice_annotations') - creation_start_date = models.DateTimeField(null=True, default=None) + action_start_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) high_grade_pin = models.BooleanField(blank=False, null=False, default=False) pah = models.BooleanField(blank=False, null=False, default=False) @@ -85,7 +85,7 @@ class CoreAnnotation(models.Model): related_name='clinical_annotations') annotation_step = models.ForeignKey(ClinicalAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='core_annotations') - creation_start_date = models.DateTimeField(null=True, default=None) + action_start_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) primary_gleason = models.IntegerField(blank=False) secondary_gleason = models.IntegerField(blank=False) @@ -132,7 +132,7 @@ class FocusRegionAnnotation(models.Model): blank=False, related_name='clinical_annotations') annotation_step = models.ForeignKey(ClinicalAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='focus_region_annotations') - creation_start_date = models.DateTimeField(null=True, default=None) + action_start_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) # cancerous region fields perineural_involvement = models.BooleanField(blank=False, null=False, default=False) @@ -190,7 +190,7 @@ class GleasonElement(models.Model): cellular_density_helper_json = models.TextField(blank=True, null=True) cellular_density = models.IntegerField(blank=True, null=True) cells_count = models.IntegerField(blank=True, null=True) - creation_start_date = models.DateTimeField(null=True, default=None) + action_start_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) def get_gleason_type_label(self): diff --git a/promort/clinical_annotations_manager/serializers.py b/promort/clinical_annotations_manager/serializers.py index 472e815..a33d2cd 100644 --- a/promort/clinical_annotations_manager/serializers.py +++ b/promort/clinical_annotations_manager/serializers.py @@ -41,7 +41,7 @@ class SliceAnnotationSerializer(serializers.ModelSerializer): class Meta: model = SliceAnnotation - fields = ('id', 'author', 'slice', 'annotation_step', 'creation_start_date', 'creation_date', 'high_grade_pin', + fields = ('id', 'author', 'slice', 'annotation_step', 'action_start_time', 'creation_date', 'high_grade_pin', 'pah', 'chronic_inflammation', 'acute_inflammation', 'periglandular_inflammation', 'intraglandular_inflammation', 'stromal_inflammation', 'gleason_4_percentage') read_only_fields = ('id', 'creation_date', 'gleason_4_percentage') @@ -73,7 +73,7 @@ class CoreAnnotationSerializer(serializers.ModelSerializer): class Meta: model = CoreAnnotation - fields = ('id', 'author', 'core', 'annotation_step', 'creation_start_date', 'creation_date', 'primary_gleason', + fields = ('id', 'author', 'core', 'annotation_step', 'action_start_time', 'creation_date', 'primary_gleason', 'secondary_gleason', 'gleason_score', 'gleason_4_percentage', 'gleason_group') read_only_fields = ('id', 'creation_date', 'gleason_score', 'gleason_4_percentage') write_only_fields = ('annotation_step',) @@ -105,7 +105,7 @@ class Meta: model = GleasonElement fields = ('id', 'gleason_type', 'gleason_label', 'json_path', 'area', 'cellular_density_helper_json', 'cellular_density', 'cells_count', - 'creation_date', 'creation_start_date') + 'creation_date', 'action_start_time') read_only_fields = ('gleason_label',) @staticmethod @@ -140,7 +140,7 @@ class FocusRegionAnnotationSerializer(serializers.ModelSerializer): class Meta: model = FocusRegionAnnotation - fields = ('id', 'author', 'focus_region', 'annotation_step', 'creation_start_date', 'creation_date', + fields = ('id', 'author', 'focus_region', 'annotation_step', 'action_start_time', 'creation_date', 'perineural_involvement', 'intraductal_carcinoma', 'ductal_carcinoma', 'poorly_formed_glands', 'cribriform_pattern', 'small_cell_signet_ring', 'hypernephroid_pattern', 'mucinous', 'comedo_necrosis', 'inflammation', 'pah', 'atrophic_lesions', 'adenosis', diff --git a/promort/rois_manager/management/commands/get_cores_data.py b/promort/rois_manager/management/commands/get_cores_data.py index be0aacf..81a9f9e 100644 --- a/promort/rois_manager/management/commands/get_cores_data.py +++ b/promort/rois_manager/management/commands/get_cores_data.py @@ -57,9 +57,9 @@ def _dump_data(self, page_size, csv_writer): def _dump_row(self, core, csv_writer): try: - creation_start_date = core.creation_start_date.strftime('%Y-%m-%d %H:%M:%S') + action_start_time = core.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: - creation_start_date = None + action_start_time = None csv_writer.writerow( { 'case_id': core.slice.slide.case.id, @@ -68,7 +68,7 @@ def _dump_row(self, core, csv_writer): 'parent_slice_id': core.slice.id, 'core_label': core.label, 'core_id': core.id, - 'creation_start_date': creation_start_date, + 'action_start_time': action_start_time, 'creation_date': core.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'reviewer': core.author.username, 'length': core.length, @@ -82,7 +82,7 @@ def _dump_row(self, core, csv_writer): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'roi_review_step_id', 'parent_slice_id', 'core_label', 'core_id', - 'creation_start_date', 'creation_date', 'reviewer', 'length', 'area', 'tumor_length', + 'action_start_time', 'creation_date', 'reviewer', 'length', 'area', 'tumor_length', 'positive_core', 'normal_tissue_percentage', 'total_tumor_area'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) diff --git a/promort/rois_manager/management/commands/get_focus_regions_data.py b/promort/rois_manager/management/commands/get_focus_regions_data.py index ba7900f..e9a13bb 100644 --- a/promort/rois_manager/management/commands/get_focus_regions_data.py +++ b/promort/rois_manager/management/commands/get_focus_regions_data.py @@ -57,9 +57,9 @@ def _dump_data(self, page_size, csv_writer): def _dump_row(self, focus_region, csv_writer): try: - creation_start_date = focus_region.creation_start_date.strftime('%Y-%m-%d %H:%M:%S') + action_start_time = focus_region.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: - creation_start_date = None + action_start_time = None csv_writer.writerow( { 'case_id': focus_region.core.slice.slide.case.id, @@ -69,7 +69,7 @@ def _dump_row(self, focus_region, csv_writer): 'parent_core_label': focus_region.core.label, 'focus_region_label': focus_region.label, 'focus_region_id': focus_region.id, - 'creation_start_date': creation_start_date, + 'action_start_time': action_start_time, 'creation_date': focus_region.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'reviewer': focus_region.author.username, 'length': focus_region.length, @@ -89,7 +89,7 @@ def _get_region_tissue_status(self, focus_region): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'parent_core_id', 'parent_core_label', - 'focus_region_label', 'focus_region_id', 'creation_start_date', 'creation_date', 'reviewer', 'length', + 'focus_region_label', 'focus_region_id', 'action_start_time', 'creation_date', 'reviewer', 'length', 'area', 'tissue_status', 'core_coverage_percentage'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) diff --git a/promort/rois_manager/management/commands/get_slices_data.py b/promort/rois_manager/management/commands/get_slices_data.py index 33c48cb..468c1b6 100644 --- a/promort/rois_manager/management/commands/get_slices_data.py +++ b/promort/rois_manager/management/commands/get_slices_data.py @@ -57,9 +57,9 @@ def _dump_data(self, page_size, csv_writer): def _dump_row(self, slice, csv_writer): try: - creation_start_date = slice.creation_start_date.strftime('%Y-%m-%d %H:%M:%S') + action_start_time = slice.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: - creation_start_date = None + action_start_time = None csv_writer.writerow( { 'case_id': slice.slide.case.id, @@ -67,7 +67,7 @@ def _dump_row(self, slice, csv_writer): 'roi_review_step_id': slice.annotation_step.label, 'slice_label': slice.label, 'slice_id': slice.id, - 'creation_start_date': creation_start_date, + 'action_start_time': action_start_time, 'creation_date': slice.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'reviewer': slice.author.username, 'positive_slice': slice.is_positive(), @@ -77,7 +77,7 @@ def _dump_row(self, slice, csv_writer): ) def _export_data(self, out_file, page_size): - header = ['case_id', 'slide_id', 'roi_review_step_id', 'slice_label', 'slice_id', 'creation_start_date', + header = ['case_id', 'slide_id', 'roi_review_step_id', 'slice_label', 'slice_id', 'action_start_time', 'creation_date', 'reviewer', 'positive_slice', 'positive_cores', 'total_cores'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) diff --git a/promort/rois_manager/migrations/0022_auto_20211129_1509.py b/promort/rois_manager/migrations/0022_auto_20211129_1509.py new file mode 100644 index 0000000..7d404de --- /dev/null +++ b/promort/rois_manager/migrations/0022_auto_20211129_1509.py @@ -0,0 +1,28 @@ +# Generated by Django 3.1.13 on 2021-11-29 15:09 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('rois_manager', '0021_auto_20210424_1316'), + ] + + operations = [ + migrations.RenameField( + model_name='core', + old_name='creation_start_date', + new_name='action_start_time', + ), + migrations.RenameField( + model_name='focusregion', + old_name='creation_start_date', + new_name='action_start_time', + ), + migrations.RenameField( + model_name='slice', + old_name='creation_start_date', + new_name='action_start_time', + ), + ] diff --git a/promort/rois_manager/models.py b/promort/rois_manager/models.py index 0978ee8..cc4d6e8 100644 --- a/promort/rois_manager/models.py +++ b/promort/rois_manager/models.py @@ -29,7 +29,7 @@ class Slice(models.Model): author = models.ForeignKey(User, on_delete=models.PROTECT, blank=False) annotation_step = models.ForeignKey(ROIsAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='slices') - creation_start_date = models.DateTimeField(null=True, default=None) + action_start_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(auto_now_add=True) roi_json = models.TextField(blank=False) total_cores = models.IntegerField(blank=False, default=0) @@ -63,7 +63,7 @@ class Core(models.Model): blank=False, related_name='cores') author = models.ForeignKey(User, on_delete=models.PROTECT, blank=False) - creation_start_date = models.DateTimeField(null=True, default=None) + action_start_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(auto_now_add=True) roi_json = models.TextField(blank=False) length = models.FloatField(blank=False, default=0.0) @@ -103,7 +103,7 @@ class FocusRegion(models.Model): blank=False, related_name='focus_regions') author = models.ForeignKey(User, on_delete=models.PROTECT, blank=False) - creation_start_date = models.DateTimeField(null=True, default=None) + action_start_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(auto_now_add=True) roi_json = models.TextField(blank=False) length = models.FloatField(blank=False, default=0.0) diff --git a/promort/rois_manager/serializers.py b/promort/rois_manager/serializers.py index e4c30d3..f3c82d0 100644 --- a/promort/rois_manager/serializers.py +++ b/promort/rois_manager/serializers.py @@ -41,7 +41,7 @@ class SliceSerializer(serializers.ModelSerializer): class Meta: model = Slice - fields = ('id', 'label', 'case', 'slide', 'author', 'annotation_step', 'creation_start_date', 'creation_date', + fields = ('id', 'label', 'case', 'slide', 'author', 'annotation_step', 'action_start_time', 'creation_date', 'roi_json', 'total_cores', 'positive_cores_count', 'cores_count') read_only_fields = ('id', 'case', 'creation_date', 'positive_cores_count', 'cores_count') write_only_fields = ('annotation_step',) @@ -79,7 +79,7 @@ class CoreSerializer(serializers.ModelSerializer): class Meta: model = Core - fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'creation_start_date', 'creation_date', + fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'action_start_time', 'creation_date', 'roi_json', 'length', 'area', 'tumor_length', 'positive', 'focus_regions_count', 'normal_tissue_percentage') read_only_fields = ('id', 'case', 'slide', 'creation_date', 'positive', 'focus_regions_count', @@ -124,7 +124,7 @@ class FocusRegionSerializer(serializers.ModelSerializer): class Meta: model = FocusRegion - fields = ('id', 'label', 'case', 'slide', 'core', 'author', 'creation_start_date', 'creation_date', 'roi_json', + fields = ('id', 'label', 'case', 'slide', 'core', 'author', 'action_start_time', 'creation_date', 'roi_json', 'length', 'area', 'tissue_status', 'core_coverage_percentage') read_only_fields = ('id', 'case', 'slide', 'creation_date', 'core_coverage_percentage') @@ -161,7 +161,7 @@ class CoreDetailsSerializer(serializers.ModelSerializer): class Meta: model = Core - fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'creation_start_date', 'creation_date', 'roi_json', + fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'action_start_time', 'creation_date', 'roi_json', 'length', 'area', 'tumor_length', 'positive', 'focus_regions', 'normal_tissue_percentage') read_only_fields = ('id', 'case', 'slide', 'creation_date', 'positive', 'normal_tissue_percentage') @@ -194,7 +194,7 @@ class SliceDetailsSerializer(serializers.ModelSerializer): class Meta: model = Slice - fields = ('id', 'label', 'slide', 'author', 'creation_start_date', 'creation_date', 'roi_json', + fields = ('id', 'label', 'slide', 'author', 'action_start_time', 'creation_date', 'roi_json', 'total_cores', 'positive_cores_count', 'cores') read_only_fields = ('id', 'creation_date', 'positive_cores_count') diff --git a/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js b/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js index 40449e1..b2eb1c5 100644 --- a/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js +++ b/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js @@ -44,7 +44,7 @@ vm.slide_id = undefined; vm.slide_index = undefined; vm.case_id = undefined; - vm.clinial_annotation_label = undefined; + vm.clinical_annotation_label = undefined; vm.clinical_annotation_step_label = undefined; vm.slices_map = undefined; @@ -62,7 +62,7 @@ }; vm.roisTreeLocked = false; - vm._registreSlice = _registerSlice; + vm._registerSlice = _registerSlice; vm._registerCore = _registerCore; vm._registerFocusRegion = _registerFocusRegion; vm._getSliceLabel = _getSliceLabel; @@ -134,7 +134,7 @@ $scope.$on('slice.new', function(event, slice_info) { - vm._registreSlice(slice_info); + vm._registerSlice(slice_info); vm.allModesOff(); var $tree = $("#rois_tree"); var $new_slice_item = $(vm._createListItem(slice_info.label, @@ -698,7 +698,7 @@ vm.intraglandularInflammation = false; vm.stromalInflammation = false; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; vm.clinical_annotation_step_label = undefined; @@ -725,7 +725,7 @@ vm.slice_label = response.data.label; vm.totalCores = response.data.total_cores; vm.positiveCores = response.data.positive_cores_count; - vm.creationStartDate = new Date(); + vm.actionStartTime = new Date(); } function getSliceErrorFn(response) { @@ -746,7 +746,7 @@ vm.periglandularInflammation = false; vm.intraglandularInflammation = false; vm.stromalInflammation = false; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; } function isReadOnly() { @@ -783,7 +783,7 @@ periglandular_inflammation: vm.periglandularInflammation, intraglandular_inflammation: vm.intraglandularInflammation, stromal_inflammation: vm.stromalInflammation, - creation_start_date: vm.creationStartDate + action_start_time: vm.actionStartTime }; SliceAnnotationsManagerService.createAnnotation(vm.slice_id, vm.clinical_annotation_step_label, obj_config) .then(createAnnotationSuccessFn, createAnnotationErrorFn); @@ -936,7 +936,7 @@ vm.gradeGroupWho = undefined; vm.gradeGroupWhoLabel = ''; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; vm.clinical_annotation_step_label = undefined; @@ -992,7 +992,7 @@ vm.updateCoreLength(); vm.tumorLength = response.data.tumor_length; vm.updateTumorLength(); - vm.creationStartDate = new Date(); + vm.actionStartTime = new Date(); } function getCoreErrorFn(response) { @@ -1012,7 +1012,7 @@ vm.gradeGroupWho = undefined; vm.gradeGroupWhoLabel = ''; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; } function isReadOnly() { @@ -1073,7 +1073,7 @@ primary_gleason: Number(vm.primaryGleason), secondary_gleason: Number(vm.secondaryGleason), gleason_group: vm.gradeGroupWho, - creation_start_date: vm.creationStartDate + action_start_time: vm.actionStartTime } CoreAnnotationsManagerService.createAnnotation(vm.core_id, vm.clinical_annotation_step_label, obj_config) .then(createAnnotationSuccessFn, createAnnotationErrorFn); @@ -1338,7 +1338,7 @@ vm.cellularDensity = undefined; vm.cellsCount = undefined; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; vm.scaledRegionLength = undefined; vm.regionLengthScaleFactor = undefined; @@ -1367,7 +1367,7 @@ vm.tmpGleasonType = undefined; vm.tmpGleasonCellsCount = undefined; - vm.tmpGleasonCreationStartDate = undefined; + vm.tmpGleasonActionStartTime = undefined; vm.gleasonElements = undefined; vm.gleasonElementsLabels = undefined; @@ -1453,7 +1453,7 @@ vm.updateRegionLength(); vm.coreCoveragePercentage = Number(parseFloat(response.data.core_coverage_percentage).toFixed(3)); vm.focusRegionTissueStatus = response.data.tissue_status; - vm.creationStartDate = new Date(); + vm.actionStartTime = new Date(); ClinicalAnnotationStepManagerService.fetchGleasonElementTypes() .then(fetchGleasonElementTypesSuccessFn); @@ -1507,7 +1507,7 @@ vm.adenosis = false; vm.cellsCount = undefined; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; vm.gleasonElements = {}; vm.gleasonElementsLabels = []; @@ -1627,7 +1627,7 @@ } ); vm.ruler_tool_active = true; - vm.tmpGleasonCreationStartDate = new Date(); + vm.tmpGleasonActionStartTime = new Date(); } function temporaryRulerExists() { @@ -1690,7 +1690,7 @@ .unbind('area_ruler_empty_intersection') .unbind('area_ruler_cleared'); vm.ruler_tool_active = false; - vm.tmpGleasonCreationStartDate = undefined; + vm.tmpGleasonActionStartTime = undefined; AnnotationsViewerService.disableActiveTool(); } @@ -1735,7 +1735,7 @@ gleason_type: vm.tmpGleasonType, gleason_label: vm.gleason_types_map[vm.tmpGleasonType], creation_date: new Date(), - creation_start_date: vm.tmpGleasonCreationStartDate + action_start_time: vm.tmpGleasonActionStartTime }; vm.gleasonElementsLabels.push(gleason_shape_id); vm.gleasonElements[gleason_shape_id] = tmp_g_object; @@ -1799,7 +1799,7 @@ adenosis: vm.adenosis, cells_count: vm.cellsCount, gleason_elements: gleason_elements, - creation_start_date: vm.creationStartDate + action_start_time: vm.actionStartTime }; FocusRegionAnnotationsManagerService.createAnnotation(vm.focus_region_id, vm.clinical_annotation_step_label, obj_config) diff --git a/promort/src/js/rois_manager/rois_manager.controllers.js b/promort/src/js/rois_manager/rois_manager.controllers.js index 5fed38f..4f54ab7 100644 --- a/promort/src/js/rois_manager/rois_manager.controllers.js +++ b/promort/src/js/rois_manager/rois_manager.controllers.js @@ -682,7 +682,7 @@ vm.shape = undefined; vm.totalCores = 0; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; vm.edit_shape_label = false; vm.previuos_shape_label = undefined; @@ -752,7 +752,7 @@ function() { vm.default_shape_label = AnnotationsViewerService.getFirstAvailableLabel('slice'); vm.shape_label = vm.default_shape_label; - vm.creationStartDate = new Date(); + vm.actionStartTime = new Date(); } ); } @@ -996,7 +996,7 @@ vm.totalCores = 0; vm.shape_label = undefined; vm.default_shape_label = undefined; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; } function abortTool() { @@ -1049,7 +1049,7 @@ closeByDocument: false }); ROIsAnnotationStepManagerService.createSlice(vm.annotation_step_label, vm.slide_id, vm.shape.shape_id, - vm.shape, vm.totalCores, vm.creationStartDate) + vm.shape, vm.totalCores, vm.actionStartTime) .then(createSliceSuccessFn, createSliceErrorFn); function createSliceSuccessFn(response) { @@ -1273,7 +1273,7 @@ vm.coreArea = undefined; vm.tumorLength = undefined; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; vm.scaledCoreLength = undefined; vm.coreLengthScaleFactor = undefined; @@ -1411,7 +1411,7 @@ function() { vm.default_shape_label = AnnotationsViewerService.getFirstAvailableLabel('core'); vm.shape_label = vm.default_shape_label; - vm.creationStartDate = new Date(); + vm.actionStartTime = new Date(); } ); } @@ -1879,7 +1879,7 @@ vm.deleteTumorRuler(); vm.shape_label = undefined; vm.default_shape_label = undefined; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; } function abortTool() { @@ -1992,7 +1992,7 @@ closeByDocument: false }); SlicesManagerService.createCore(vm.parentSlice.id, vm.shape.shape_id, vm.shape, - vm.coreLength, vm.coreArea, vm.tumorLength, vm.creationStartDate) + vm.coreLength, vm.coreArea, vm.tumorLength, vm.actionStartTime) .then(createCoreSuccessFn, createCoreErrorFn); function createCoreSuccessFn(response) { @@ -2621,7 +2621,7 @@ vm.coreCoverage = undefined; vm.tissueStatus = undefined; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; vm.scaledRegionLength = undefined; vm.regionLengthScaleFactor = undefined; @@ -2740,7 +2740,7 @@ function() { vm.default_shape_label = AnnotationsViewerService.getFirstAvailableLabel('focus_region'); vm.shape_label = vm.default_shape_label; - vm.creationStartDate = new Date(); + vm.actionStartTime = new Date(); } ); } @@ -3146,7 +3146,7 @@ vm.isTumor = false; vm.shape_label = undefined; vm.default_shape_label = undefined; - vm.creationStartDate = undefined; + vm.actionStartTime = undefined; } function abortTool() { @@ -3220,7 +3220,7 @@ closeByDocument: false }); CoresManagerService.createFocusRegion(vm.parentCore.id, vm.shape.shape_id, vm.shape, - vm.regionLength, vm.regionArea, vm.tissueStatus, vm.creationStartDate) + vm.regionLength, vm.regionArea, vm.tissueStatus, vm.actionStartTime) .then(createFocusRegionSuccessFn, createFocusRegionErrorFn); function createFocusRegionSuccessFn(response) { diff --git a/promort/src/js/rois_manager/rois_manager.services.js b/promort/src/js/rois_manager/rois_manager.services.js index 4641f04..aca96d4 100644 --- a/promort/src/js/rois_manager/rois_manager.services.js +++ b/promort/src/js/rois_manager/rois_manager.services.js @@ -40,13 +40,13 @@ return ROIsAnnotationStepManagerService; - function createSlice(step_label, slide_id, slice_label, roi_json, total_cores, creation_start_date) { + function createSlice(step_label, slide_id, slice_label, roi_json, total_cores, action_start_time) { var params = { label: slice_label, slide: slide_id, roi_json: JSON.stringify(roi_json), total_cores: total_cores, - creation_start_date: creation_start_date + action_start_time: action_start_time }; return $http.post('/api/rois_annotation_steps/' + step_label + '/slices/', params); } @@ -91,14 +91,14 @@ return $http.delete('/api/slices/' + slice_id + '/'); } - function createCore(slice_id, core_label, roi_json, length, area, tumor_length, creation_start_date) { + function createCore(slice_id, core_label, roi_json, length, area, tumor_length, action_start_time) { var params = { label: core_label, roi_json: JSON.stringify(roi_json), length: length, area: area, tumor_length: tumor_length, - creation_start_date: creation_start_date + action_start_time: action_start_time }; return $http.post('/api/slices/' + slice_id + '/cores/', params); } @@ -133,14 +133,14 @@ } function createFocusRegion(core_id, focus_region_label, roi_json, length, area, tissue_status, - creation_start_date) { + action_start_time) { var params = { label: focus_region_label, roi_json: JSON.stringify(roi_json), length: length, area: area, tissue_status: tissue_status, - creation_start_date: creation_start_date + action_start_time: action_start_time }; return $http.post('/api/cores/' + core_id + '/focus_regions/', params); } From 03211a8a99ebffc4e8752a56f208752ac682b806 Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Wed, 1 Dec 2021 10:43:34 +0100 Subject: [PATCH 3/9] feat: added action_complete_time field --- .../migrations/0019_auto_20211201_0933.py | 33 +++++++++++++++++++ .../clinical_annotations_manager/models.py | 4 +++ .../serializers.py | 22 +++++++------ .../migrations/0023_auto_20211201_0933.py | 28 ++++++++++++++++ promort/rois_manager/models.py | 3 ++ promort/rois_manager/serializers.py | 21 ++++++------ 6 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 promort/clinical_annotations_manager/migrations/0019_auto_20211201_0933.py create mode 100644 promort/rois_manager/migrations/0023_auto_20211201_0933.py diff --git a/promort/clinical_annotations_manager/migrations/0019_auto_20211201_0933.py b/promort/clinical_annotations_manager/migrations/0019_auto_20211201_0933.py new file mode 100644 index 0000000..b83c616 --- /dev/null +++ b/promort/clinical_annotations_manager/migrations/0019_auto_20211201_0933.py @@ -0,0 +1,33 @@ +# Generated by Django 3.1.13 on 2021-12-01 09:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('clinical_annotations_manager', '0018_auto_20211128_1525'), + ] + + operations = [ + migrations.AddField( + model_name='coreannotation', + name='action_complete_time', + field=models.DateTimeField(default=None, null=True), + ), + migrations.AddField( + model_name='focusregionannotation', + name='action_complete_time', + field=models.DateTimeField(default=None, null=True), + ), + migrations.AddField( + model_name='gleasonelement', + name='action_complete_time', + field=models.DateTimeField(default=None, null=True), + ), + migrations.AddField( + model_name='sliceannotation', + name='action_complete_time', + field=models.DateTimeField(default=None, null=True), + ), + ] diff --git a/promort/clinical_annotations_manager/models.py b/promort/clinical_annotations_manager/models.py index c623f9d..4637cd4 100644 --- a/promort/clinical_annotations_manager/models.py +++ b/promort/clinical_annotations_manager/models.py @@ -31,6 +31,7 @@ class SliceAnnotation(models.Model): annotation_step = models.ForeignKey(ClinicalAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='slice_annotations') action_start_time = models.DateTimeField(null=True, default=None) + action_complete_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) high_grade_pin = models.BooleanField(blank=False, null=False, default=False) pah = models.BooleanField(blank=False, null=False, default=False) @@ -86,6 +87,7 @@ class CoreAnnotation(models.Model): annotation_step = models.ForeignKey(ClinicalAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='core_annotations') action_start_time = models.DateTimeField(null=True, default=None) + action_complete_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) primary_gleason = models.IntegerField(blank=False) secondary_gleason = models.IntegerField(blank=False) @@ -133,6 +135,7 @@ class FocusRegionAnnotation(models.Model): annotation_step = models.ForeignKey(ClinicalAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='focus_region_annotations') action_start_time = models.DateTimeField(null=True, default=None) + action_complete_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) # cancerous region fields perineural_involvement = models.BooleanField(blank=False, null=False, default=False) @@ -191,6 +194,7 @@ class GleasonElement(models.Model): cellular_density = models.IntegerField(blank=True, null=True) cells_count = models.IntegerField(blank=True, null=True) action_start_time = models.DateTimeField(null=True, default=None) + action_complete_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(default=timezone.now) def get_gleason_type_label(self): diff --git a/promort/clinical_annotations_manager/serializers.py b/promort/clinical_annotations_manager/serializers.py index a33d2cd..97365b7 100644 --- a/promort/clinical_annotations_manager/serializers.py +++ b/promort/clinical_annotations_manager/serializers.py @@ -41,9 +41,10 @@ class SliceAnnotationSerializer(serializers.ModelSerializer): class Meta: model = SliceAnnotation - fields = ('id', 'author', 'slice', 'annotation_step', 'action_start_time', 'creation_date', 'high_grade_pin', - 'pah', 'chronic_inflammation', 'acute_inflammation', 'periglandular_inflammation', - 'intraglandular_inflammation', 'stromal_inflammation', 'gleason_4_percentage') + fields = ('id', 'author', 'slice', 'annotation_step', 'action_start_time', 'action_complete_time', + 'creation_date', 'high_grade_pin', 'pah', 'chronic_inflammation', 'acute_inflammation', + 'periglandular_inflammation', 'intraglandular_inflammation', 'stromal_inflammation', + 'gleason_4_percentage') read_only_fields = ('id', 'creation_date', 'gleason_4_percentage') write_only_fields = ('annotation_step',) @@ -73,8 +74,9 @@ class CoreAnnotationSerializer(serializers.ModelSerializer): class Meta: model = CoreAnnotation - fields = ('id', 'author', 'core', 'annotation_step', 'action_start_time', 'creation_date', 'primary_gleason', - 'secondary_gleason', 'gleason_score', 'gleason_4_percentage', 'gleason_group') + fields = ('id', 'author', 'core', 'annotation_step', 'action_start_time', 'action_complete_time', + 'creation_date', 'primary_gleason', 'secondary_gleason', 'gleason_score', + 'gleason_4_percentage', 'gleason_group') read_only_fields = ('id', 'creation_date', 'gleason_score', 'gleason_4_percentage') write_only_fields = ('annotation_step',) @@ -105,7 +107,7 @@ class Meta: model = GleasonElement fields = ('id', 'gleason_type', 'gleason_label', 'json_path', 'area', 'cellular_density_helper_json', 'cellular_density', 'cells_count', - 'creation_date', 'action_start_time') + 'creation_date', 'action_start_time', 'action_complete_time') read_only_fields = ('gleason_label',) @staticmethod @@ -140,10 +142,10 @@ class FocusRegionAnnotationSerializer(serializers.ModelSerializer): class Meta: model = FocusRegionAnnotation - fields = ('id', 'author', 'focus_region', 'annotation_step', 'action_start_time', 'creation_date', - 'perineural_involvement', 'intraductal_carcinoma', 'ductal_carcinoma', 'poorly_formed_glands', - 'cribriform_pattern', 'small_cell_signet_ring', 'hypernephroid_pattern', 'mucinous', - 'comedo_necrosis', 'inflammation', 'pah', 'atrophic_lesions', 'adenosis', + fields = ('id', 'author', 'focus_region', 'annotation_step', 'action_start_time', 'action_complete_time', + 'creation_date', 'perineural_involvement', 'intraductal_carcinoma', 'ductal_carcinoma', + 'poorly_formed_glands', 'cribriform_pattern', 'small_cell_signet_ring', 'hypernephroid_pattern', + 'mucinous', 'comedo_necrosis', 'inflammation', 'pah', 'atrophic_lesions', 'adenosis', 'cellular_density_helper_json', 'cellular_density', 'cells_count', 'gleason_elements') read_only_fields = ('creation_date',) write_only_fields = ('id', 'annotation_step', 'gleason_elements', 'author') diff --git a/promort/rois_manager/migrations/0023_auto_20211201_0933.py b/promort/rois_manager/migrations/0023_auto_20211201_0933.py new file mode 100644 index 0000000..9935109 --- /dev/null +++ b/promort/rois_manager/migrations/0023_auto_20211201_0933.py @@ -0,0 +1,28 @@ +# Generated by Django 3.1.13 on 2021-12-01 09:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('rois_manager', '0022_auto_20211129_1509'), + ] + + operations = [ + migrations.AddField( + model_name='core', + name='action_complete_time', + field=models.DateTimeField(default=None, null=True), + ), + migrations.AddField( + model_name='focusregion', + name='action_complete_time', + field=models.DateTimeField(default=None, null=True), + ), + migrations.AddField( + model_name='slice', + name='action_complete_time', + field=models.DateTimeField(default=None, null=True), + ), + ] diff --git a/promort/rois_manager/models.py b/promort/rois_manager/models.py index cc4d6e8..0630692 100644 --- a/promort/rois_manager/models.py +++ b/promort/rois_manager/models.py @@ -30,6 +30,7 @@ class Slice(models.Model): annotation_step = models.ForeignKey(ROIsAnnotationStep, on_delete=models.PROTECT, blank=False, related_name='slices') action_start_time = models.DateTimeField(null=True, default=None) + action_complete_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(auto_now_add=True) roi_json = models.TextField(blank=False) total_cores = models.IntegerField(blank=False, default=0) @@ -64,6 +65,7 @@ class Core(models.Model): author = models.ForeignKey(User, on_delete=models.PROTECT, blank=False) action_start_time = models.DateTimeField(null=True, default=None) + action_complete_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(auto_now_add=True) roi_json = models.TextField(blank=False) length = models.FloatField(blank=False, default=0.0) @@ -104,6 +106,7 @@ class FocusRegion(models.Model): author = models.ForeignKey(User, on_delete=models.PROTECT, blank=False) action_start_time = models.DateTimeField(null=True, default=None) + action_complete_time = models.DateTimeField(null=True, default=None) creation_date = models.DateTimeField(auto_now_add=True) roi_json = models.TextField(blank=False) length = models.FloatField(blank=False, default=0.0) diff --git a/promort/rois_manager/serializers.py b/promort/rois_manager/serializers.py index f3c82d0..8829868 100644 --- a/promort/rois_manager/serializers.py +++ b/promort/rois_manager/serializers.py @@ -41,8 +41,8 @@ class SliceSerializer(serializers.ModelSerializer): class Meta: model = Slice - fields = ('id', 'label', 'case', 'slide', 'author', 'annotation_step', 'action_start_time', 'creation_date', - 'roi_json', 'total_cores', 'positive_cores_count', 'cores_count') + fields = ('id', 'label', 'case', 'slide', 'author', 'annotation_step', 'action_start_time', 'action_complete_time', + 'creation_date', 'roi_json', 'total_cores', 'positive_cores_count', 'cores_count') read_only_fields = ('id', 'case', 'creation_date', 'positive_cores_count', 'cores_count') write_only_fields = ('annotation_step',) @@ -79,8 +79,8 @@ class CoreSerializer(serializers.ModelSerializer): class Meta: model = Core - fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'action_start_time', 'creation_date', - 'roi_json', 'length', 'area', 'tumor_length', 'positive', 'focus_regions_count', + fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'action_start_time', 'action_complete_time', + 'creation_date', 'roi_json', 'length', 'area', 'tumor_length', 'positive', 'focus_regions_count', 'normal_tissue_percentage') read_only_fields = ('id', 'case', 'slide', 'creation_date', 'positive', 'focus_regions_count', 'normal_tissue_percentage') @@ -124,8 +124,8 @@ class FocusRegionSerializer(serializers.ModelSerializer): class Meta: model = FocusRegion - fields = ('id', 'label', 'case', 'slide', 'core', 'author', 'action_start_time', 'creation_date', 'roi_json', - 'length', 'area', 'tissue_status', 'core_coverage_percentage') + fields = ('id', 'label', 'case', 'slide', 'core', 'author', 'action_start_time', 'action_complete_time', + 'creation_date', 'roi_json', 'length', 'area', 'tissue_status', 'core_coverage_percentage') read_only_fields = ('id', 'case', 'slide', 'creation_date', 'core_coverage_percentage') def validate_roi_json(self, value): @@ -161,8 +161,9 @@ class CoreDetailsSerializer(serializers.ModelSerializer): class Meta: model = Core - fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'action_start_time', 'creation_date', 'roi_json', - 'length', 'area', 'tumor_length', 'positive', 'focus_regions', 'normal_tissue_percentage') + fields = ('id', 'label', 'case', 'slide', 'slice', 'author', 'action_start_time', 'action_complete_time', + 'creation_date', 'roi_json', 'length', 'area', 'tumor_length', 'positive', 'focus_regions', + 'normal_tissue_percentage') read_only_fields = ('id', 'case', 'slide', 'creation_date', 'positive', 'normal_tissue_percentage') @staticmethod @@ -194,8 +195,8 @@ class SliceDetailsSerializer(serializers.ModelSerializer): class Meta: model = Slice - fields = ('id', 'label', 'slide', 'author', 'action_start_time', 'creation_date', 'roi_json', - 'total_cores', 'positive_cores_count', 'cores') + fields = ('id', 'label', 'slide', 'author', 'action_start_time', 'action_complete_time', 'creation_date', + 'roi_json', 'total_cores', 'positive_cores_count', 'cores') read_only_fields = ('id', 'creation_date', 'positive_cores_count') def get_positive_cores_count(self, obj): From 243caa9448c7794a103f6f3afc87171368ea512a Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Thu, 2 Dec 2021 18:25:53 +0100 Subject: [PATCH 4/9] feat: export action_complete_time --- .../management/commands/get_cores_clinical_data.py | 9 +++++++-- .../commands/get_focus_regions_clinical_data.py | 11 ++++++++--- .../management/commands/get_slices_clinical_data.py | 9 +++++++-- .../management/commands/get_cores_data.py | 13 +++++++++---- .../management/commands/get_focus_regions_data.py | 9 +++++++-- .../management/commands/get_slices_data.py | 12 +++++++++--- 6 files changed, 47 insertions(+), 16 deletions(-) diff --git a/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py b/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py index 5ae37e6..86bfa65 100644 --- a/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py +++ b/promort/clinical_annotations_manager/management/commands/get_cores_clinical_data.py @@ -60,6 +60,10 @@ def _dump_row(self, core_annotation, csv_writer): action_start_time = core_annotation.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: action_start_time = None + try: + action_complete_time = core_annotation.action_complete_time.strftime('%Y-%m-%d %H:%M:%S') + except AttributeError: + action_complete_time = None csv_writer.writerow( { 'case_id': core_annotation.core.slice.slide.case.id, @@ -70,6 +74,7 @@ def _dump_row(self, core_annotation, csv_writer): 'core_id': core_annotation.core.id, 'core_label': core_annotation.core.label, 'action_start_time': action_start_time, + 'action_complete_time': action_complete_time, 'creation_date': core_annotation.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'primary_gleason': core_annotation.primary_gleason, 'secondary_gleason': core_annotation.secondary_gleason, @@ -79,8 +84,8 @@ def _dump_row(self, core_annotation, csv_writer): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'clinical_review_step_id', 'reviewer', - 'core_id', 'core_label', 'action_start_time', 'creation_date', 'primary_gleason', - 'secondary_gleason', 'gleason_group_who_16'] + 'core_id', 'core_label', 'action_start_time', 'action_complete_time', 'creation_date', + 'primary_gleason', 'secondary_gleason', 'gleason_group_who_16'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) writer.writeheader() diff --git a/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py b/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py index 78e0304..7396243 100644 --- a/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py +++ b/promort/clinical_annotations_manager/management/commands/get_focus_regions_clinical_data.py @@ -59,6 +59,10 @@ def _dump_row(self, focus_region_annotation, csv_writer): action_start_time = focus_region_annotation.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: action_start_time = None + try: + action_complete_time = focus_region_annotation.action_complete_time.strftime('%Y-%m-%d %H:%M:%S') + except AttributeError: + action_complete_time = None csv_writer.writerow( { 'case_id': focus_region_annotation.focus_region.core.slice.slide.case.id, @@ -71,6 +75,7 @@ def _dump_row(self, focus_region_annotation, csv_writer): 'core_id': focus_region_annotation.focus_region.core.id, 'core_label': focus_region_annotation.focus_region.core.label, 'action_start_time': action_start_time, + 'action_complete_time': action_complete_time, 'creation_date': focus_region_annotation.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'perineural_involvement': focus_region_annotation.perineural_involvement, 'intraductal_carcinoma': focus_region_annotation.intraductal_carcinoma, @@ -89,9 +94,9 @@ def _dump_row(self, focus_region_annotation, csv_writer): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'clinical_review_step_id', 'reviewer', 'focus_region_id', 'focus_region_label', 'core_id', 'core_label', 'action_start_time', - 'creation_date', 'perineural_involvement', 'intraductal_carcinoma', 'ductal_carcinoma', - 'poorly_formed_glands', 'cribriform_pattern', 'small_cell_signet_ring', 'hypernephroid_pattern', - 'mucinous', 'comedo_necrosis', 'total_gleason_4_area', 'gleason_4_percentage'] + 'action_complete_time', 'creation_date', 'perineural_involvement', 'intraductal_carcinoma', + 'ductal_carcinoma', 'poorly_formed_glands', 'cribriform_pattern', 'small_cell_signet_ring', + 'hypernephroid_pattern', 'mucinous', 'comedo_necrosis', 'total_gleason_4_area', 'gleason_4_percentage'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) writer.writeheader() diff --git a/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py b/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py index d497a2c..f048dec 100644 --- a/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py +++ b/promort/clinical_annotations_manager/management/commands/get_slices_clinical_data.py @@ -60,6 +60,10 @@ def _dump_row(self, slice_annotation, csv_writer): action_start_time = slice_annotation.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: action_start_time = None + try: + action_complete_time = slice_annotation.action_complete_time.strftime('%Y-%m-%d %H:%M:%S') + except AttributeError: + action_complete_time = None csv_writer.writerow( { 'case_id': slice_annotation.slice.slide.case.id, @@ -70,6 +74,7 @@ def _dump_row(self, slice_annotation, csv_writer): 'slice_id': slice_annotation.slice.id, 'slice_label': slice_annotation.slice.label, 'action_start_time': action_start_time, + 'action_complete_time': action_complete_time, 'creation_date': slice_annotation.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'high_grade_pin': slice_annotation.high_grade_pin, 'pah': slice_annotation.pah, @@ -83,8 +88,8 @@ def _dump_row(self, slice_annotation, csv_writer): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'clinical_review_step_id', 'reviewer', - 'slice_id', 'slice_label', 'action_start_time', 'creation_date', 'high_grade_pin', 'pah', - 'chronic_inflammation', 'acute_inflammation', 'periglandular_inflammation', + 'slice_id', 'slice_label', 'action_start_time', 'action_complete_time', 'creation_date', + 'high_grade_pin', 'pah', 'chronic_inflammation', 'acute_inflammation', 'periglandular_inflammation', 'intraglandular_inflammation', 'stromal_inflammation'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) diff --git a/promort/rois_manager/management/commands/get_cores_data.py b/promort/rois_manager/management/commands/get_cores_data.py index 81a9f9e..b01835a 100644 --- a/promort/rois_manager/management/commands/get_cores_data.py +++ b/promort/rois_manager/management/commands/get_cores_data.py @@ -60,15 +60,20 @@ def _dump_row(self, core, csv_writer): action_start_time = core.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: action_start_time = None + try: + action_complete_time = core.action_complete_time.strftime('%Y-%m-%d %H:%M:%S') + except AttributeError: + action_complete_time = None csv_writer.writerow( { 'case_id': core.slice.slide.case.id, 'slide_id': core.slice.slide.id, - 'roi_review_step_id': core.slice.annotation_step.label, + 'rois_review_step_id': core.slice.annotation_step.label, 'parent_slice_id': core.slice.id, 'core_label': core.label, 'core_id': core.id, 'action_start_time': action_start_time, + 'action_complete_time': action_complete_time, 'creation_date': core.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'reviewer': core.author.username, 'length': core.length, @@ -81,9 +86,9 @@ def _dump_row(self, core, csv_writer): ) def _export_data(self, out_file, page_size): - header = ['case_id', 'slide_id', 'roi_review_step_id', 'parent_slice_id', 'core_label', 'core_id', - 'action_start_time', 'creation_date', 'reviewer', 'length', 'area', 'tumor_length', - 'positive_core', 'normal_tissue_percentage', 'total_tumor_area'] + header = ['case_id', 'slide_id', 'rois_review_step_id', 'parent_slice_id', 'core_label', 'core_id', + 'action_start_time', 'action_complete_time', 'creation_date', 'reviewer', 'length', 'area', + 'tumor_length', 'positive_core', 'normal_tissue_percentage', 'total_tumor_area'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) writer.writeheader() diff --git a/promort/rois_manager/management/commands/get_focus_regions_data.py b/promort/rois_manager/management/commands/get_focus_regions_data.py index e9a13bb..1104598 100644 --- a/promort/rois_manager/management/commands/get_focus_regions_data.py +++ b/promort/rois_manager/management/commands/get_focus_regions_data.py @@ -60,6 +60,10 @@ def _dump_row(self, focus_region, csv_writer): action_start_time = focus_region.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: action_start_time = None + try: + action_complete_time = focus_region.action_complete_time.strftime('%Y-%m-%d %H:%M:%S') + except AttributeError: + action_complete_time = None csv_writer.writerow( { 'case_id': focus_region.core.slice.slide.case.id, @@ -70,6 +74,7 @@ def _dump_row(self, focus_region, csv_writer): 'focus_region_label': focus_region.label, 'focus_region_id': focus_region.id, 'action_start_time': action_start_time, + 'action_complete_time': action_complete_time, 'creation_date': focus_region.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'reviewer': focus_region.author.username, 'length': focus_region.length, @@ -89,8 +94,8 @@ def _get_region_tissue_status(self, focus_region): def _export_data(self, out_file, page_size): header = ['case_id', 'slide_id', 'rois_review_step_id', 'parent_core_id', 'parent_core_label', - 'focus_region_label', 'focus_region_id', 'action_start_time', 'creation_date', 'reviewer', 'length', - 'area', 'tissue_status', 'core_coverage_percentage'] + 'focus_region_label', 'focus_region_id', 'action_start_time', 'action_complete_time', 'creation_date', + 'reviewer', 'length', 'area', 'tissue_status', 'core_coverage_percentage'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) writer.writeheader() diff --git a/promort/rois_manager/management/commands/get_slices_data.py b/promort/rois_manager/management/commands/get_slices_data.py index 468c1b6..ec26f39 100644 --- a/promort/rois_manager/management/commands/get_slices_data.py +++ b/promort/rois_manager/management/commands/get_slices_data.py @@ -60,14 +60,19 @@ def _dump_row(self, slice, csv_writer): action_start_time = slice.action_start_time.strftime('%Y-%m-%d %H:%M:%S') except AttributeError: action_start_time = None + try: + action_complete_time = slice.action_complete_time.strftime('%Y-%m-%d %H:%M:%S') + except AttributeError: + action_complete_time = None csv_writer.writerow( { 'case_id': slice.slide.case.id, 'slide_id': slice.slide.id, - 'roi_review_step_id': slice.annotation_step.label, + 'rois_review_step_id': slice.annotation_step.label, 'slice_label': slice.label, 'slice_id': slice.id, 'action_start_time': action_start_time, + 'action_complete_time': action_complete_time, 'creation_date': slice.creation_date.strftime('%Y-%m-%d %H:%M:%S'), 'reviewer': slice.author.username, 'positive_slice': slice.is_positive(), @@ -77,8 +82,9 @@ def _dump_row(self, slice, csv_writer): ) def _export_data(self, out_file, page_size): - header = ['case_id', 'slide_id', 'roi_review_step_id', 'slice_label', 'slice_id', 'action_start_time', - 'creation_date', 'reviewer', 'positive_slice', 'positive_cores', 'total_cores'] + header = ['case_id', 'slide_id', 'rois_review_step_id', 'slice_label', 'slice_id', 'action_start_time', + 'action_complete_time', 'creation_date', 'reviewer', 'positive_slice', + 'positive_cores', 'total_cores'] with open(out_file, 'w') as ofile: writer = DictWriter(ofile, delimiter=',', fieldnames=header) writer.writeheader() From ccc6538a7be2d20d24834aa54bd15be2cca5917e Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Fri, 3 Dec 2021 16:44:31 +0100 Subject: [PATCH 5/9] fix: fixed wrong URL --- promort/promort/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/promort/promort/urls.py b/promort/promort/urls.py index 08ec636..b86fe86 100644 --- a/promort/promort/urls.py +++ b/promort/promort/urls.py @@ -151,7 +151,7 @@ def to_url(self, value): path('api/focus_regions//clinical_annotations/', FocusRegionAnnotationList.as_view()), path( - 'api/focus_regions//clinical_annotations/(/', + 'api/focus_regions//clinical_annotations//', FocusRegionAnnotationDetail.as_view()), # ROIs annotations From ca4da4840f6c6bf6bf49f4dd72cdc94af1bee8de Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Fri, 3 Dec 2021 16:56:15 +0100 Subject: [PATCH 6/9] feat: action_complete_time is acquired client side --- .../clinical_annotations_manager.controllers.js | 12 ++++++++---- .../js/rois_manager/rois_manager.controllers.js | 14 +++++++------- .../js/rois_manager/rois_manager.services.js | 17 +++++++++++------ 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js b/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js index b2eb1c5..a277818 100644 --- a/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js +++ b/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js @@ -783,7 +783,8 @@ periglandular_inflammation: vm.periglandularInflammation, intraglandular_inflammation: vm.intraglandularInflammation, stromal_inflammation: vm.stromalInflammation, - action_start_time: vm.actionStartTime + action_start_time: vm.actionStartTime, + action_complete_time: new Date() }; SliceAnnotationsManagerService.createAnnotation(vm.slice_id, vm.clinical_annotation_step_label, obj_config) .then(createAnnotationSuccessFn, createAnnotationErrorFn); @@ -1073,7 +1074,8 @@ primary_gleason: Number(vm.primaryGleason), secondary_gleason: Number(vm.secondaryGleason), gleason_group: vm.gradeGroupWho, - action_start_time: vm.actionStartTime + action_start_time: vm.actionStartTime, + action_complete_time: new Date() } CoreAnnotationsManagerService.createAnnotation(vm.core_id, vm.clinical_annotation_step_label, obj_config) .then(createAnnotationSuccessFn, createAnnotationErrorFn); @@ -1735,7 +1737,8 @@ gleason_type: vm.tmpGleasonType, gleason_label: vm.gleason_types_map[vm.tmpGleasonType], creation_date: new Date(), - action_start_time: vm.tmpGleasonActionStartTime + action_start_time: vm.tmpGleasonActionStartTime, + action_complete_time: new Date() }; vm.gleasonElementsLabels.push(gleason_shape_id); vm.gleasonElements[gleason_shape_id] = tmp_g_object; @@ -1799,7 +1802,8 @@ adenosis: vm.adenosis, cells_count: vm.cellsCount, gleason_elements: gleason_elements, - action_start_time: vm.actionStartTime + action_start_time: vm.actionStartTime, + action_complete_time: new Date() }; FocusRegionAnnotationsManagerService.createAnnotation(vm.focus_region_id, vm.clinical_annotation_step_label, obj_config) diff --git a/promort/src/js/rois_manager/rois_manager.controllers.js b/promort/src/js/rois_manager/rois_manager.controllers.js index 4f54ab7..075c0a7 100644 --- a/promort/src/js/rois_manager/rois_manager.controllers.js +++ b/promort/src/js/rois_manager/rois_manager.controllers.js @@ -685,7 +685,7 @@ vm.actionStartTime = undefined; vm.edit_shape_label = false; - vm.previuos_shape_label = undefined; + vm.previous_shape_label = undefined; vm.active_tool = undefined; vm.polygon_tool_paused = false; @@ -797,7 +797,7 @@ function activateEditLabelMode() { vm.edit_shape_label = true; - vm.previuos_shape_label = vm.shape_label; + vm.previous_shape_label = vm.shape_label; } function labelValid() { @@ -824,7 +824,7 @@ } function deactivateEditLabelMode() { - vm.previuos_shape_label = undefined; + vm.previous_shape_label = undefined; vm.edit_shape_label = false; // if a shape already exists, change its name if (typeof vm.shape !== 'undefined' && vm.shape.shape_id !== vm.shape_label) { @@ -836,7 +836,7 @@ } function abortEditLabelMode() { - vm.shape_label = vm.previuos_shape_label; + vm.shape_label = vm.previous_shape_label; vm.deactivateEditLabelMode(); } @@ -1049,7 +1049,7 @@ closeByDocument: false }); ROIsAnnotationStepManagerService.createSlice(vm.annotation_step_label, vm.slide_id, vm.shape.shape_id, - vm.shape, vm.totalCores, vm.actionStartTime) + vm.shape, vm.totalCores, vm.actionStartTime, new Date()) .then(createSliceSuccessFn, createSliceErrorFn); function createSliceSuccessFn(response) { @@ -1992,7 +1992,7 @@ closeByDocument: false }); SlicesManagerService.createCore(vm.parentSlice.id, vm.shape.shape_id, vm.shape, - vm.coreLength, vm.coreArea, vm.tumorLength, vm.actionStartTime) + vm.coreLength, vm.coreArea, vm.tumorLength, vm.actionStartTime, new Date()) .then(createCoreSuccessFn, createCoreErrorFn); function createCoreSuccessFn(response) { @@ -3220,7 +3220,7 @@ closeByDocument: false }); CoresManagerService.createFocusRegion(vm.parentCore.id, vm.shape.shape_id, vm.shape, - vm.regionLength, vm.regionArea, vm.tissueStatus, vm.actionStartTime) + vm.regionLength, vm.regionArea, vm.tissueStatus, vm.actionStartTime, new Date()) .then(createFocusRegionSuccessFn, createFocusRegionErrorFn); function createFocusRegionSuccessFn(response) { diff --git a/promort/src/js/rois_manager/rois_manager.services.js b/promort/src/js/rois_manager/rois_manager.services.js index aca96d4..ace7504 100644 --- a/promort/src/js/rois_manager/rois_manager.services.js +++ b/promort/src/js/rois_manager/rois_manager.services.js @@ -40,13 +40,15 @@ return ROIsAnnotationStepManagerService; - function createSlice(step_label, slide_id, slice_label, roi_json, total_cores, action_start_time) { + function createSlice(step_label, slide_id, slice_label, roi_json, total_cores, + action_start_time, action_complete_time) { var params = { label: slice_label, slide: slide_id, roi_json: JSON.stringify(roi_json), total_cores: total_cores, - action_start_time: action_start_time + action_start_time: action_start_time, + action_complete_time: action_complete_time }; return $http.post('/api/rois_annotation_steps/' + step_label + '/slices/', params); } @@ -91,14 +93,16 @@ return $http.delete('/api/slices/' + slice_id + '/'); } - function createCore(slice_id, core_label, roi_json, length, area, tumor_length, action_start_time) { + function createCore(slice_id, core_label, roi_json, length, area, tumor_length, + action_start_time, action_complete_time) { var params = { label: core_label, roi_json: JSON.stringify(roi_json), length: length, area: area, tumor_length: tumor_length, - action_start_time: action_start_time + action_start_time: action_start_time, + action_complete_time: action_complete_time }; return $http.post('/api/slices/' + slice_id + '/cores/', params); } @@ -133,14 +137,15 @@ } function createFocusRegion(core_id, focus_region_label, roi_json, length, area, tissue_status, - action_start_time) { + action_start_time, action_complete_time) { var params = { label: focus_region_label, roi_json: JSON.stringify(roi_json), length: length, area: area, tissue_status: tissue_status, - action_start_time: action_start_time + action_start_time: action_start_time, + action_complete_time: action_complete_time }; return $http.post('/api/cores/' + core_id + '/focus_regions/', params); } From 0936ed59fbbe1080ec030a7af28f1d27d8eeec44 Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Mon, 6 Dec 2021 10:42:08 +0100 Subject: [PATCH 7/9] feat: action duration is exposed as a model method action duration is calculated when necessary and exposed as a model's method, not need to add a field (contrart to what is suggested in issue #123) --- .../clinical_annotations_manager/models.py | 24 +++++++++++++++++++ promort/rois_manager/models.py | 18 ++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/promort/clinical_annotations_manager/models.py b/promort/clinical_annotations_manager/models.py index 4637cd4..052da71 100644 --- a/promort/clinical_annotations_manager/models.py +++ b/promort/clinical_annotations_manager/models.py @@ -71,6 +71,12 @@ def get_gleason_4_percentage(self): except ZeroDivisionError: return -1 + def get_action_duration(self): + if self.action_start_time and self.action_complete_time: + return (self.action_complete_time-self.action_start_time).total_seconds() + else: + return None + class CoreAnnotation(models.Model): GLEASON_GROUP_WHO_16 = ( @@ -127,6 +133,12 @@ def get_grade_group_text(self): if choice[0] == self.gleason_group: return choice[1] + def get_action_duration(self): + if self.action_start_time and self.action_complete_time: + return (self.action_complete_time-self.action_start_time).total_seconds() + else: + return None + class FocusRegionAnnotation(models.Model): author = models.ForeignKey(User, on_delete=models.PROTECT, blank=False) @@ -176,6 +188,12 @@ def get_gleason_4_percentage(self): except ZeroDivisionError: return -1 + def get_action_duration(self): + if self.action_start_time and self.action_complete_time: + return (self.action_complete_time-self.action_start_time).total_seconds() + else: + return None + class GleasonElement(models.Model): GLEASON_TYPES = ( @@ -201,3 +219,9 @@ def get_gleason_type_label(self): for choice in self.GLEASON_TYPES: if choice[0] == self.gleason_type: return choice[1] + + def get_action_duration(self): + if self.action_start_time and self.action_complete_time: + return (self.action_complete_time-self.action_start_time).total_seconds() + else: + return None diff --git a/promort/rois_manager/models.py b/promort/rois_manager/models.py index 0630692..2b3f253 100644 --- a/promort/rois_manager/models.py +++ b/promort/rois_manager/models.py @@ -57,6 +57,12 @@ def get_focus_regions(self): focus_regions.extend(core.focus_regions.all()) return focus_regions + def get_action_duration(self): + if self.action_start_time and self.action_complete_time: + return (self.action_complete_time-self.action_start_time).total_seconds() + else: + return None + class Core(models.Model): label = models.CharField(max_length=25, blank=False) @@ -92,6 +98,12 @@ def is_positive(self): return True return False + def get_action_duration(self): + if self.action_start_time and self.action_complete_time: + return (self.action_complete_time-self.action_start_time).total_seconds() + else: + return None + class FocusRegion(models.Model): TISSUE_STATUS_CHOICES = ( @@ -127,3 +139,9 @@ def is_stressed_region(self): def is_normal_region(self): return self.tissue_status == 'NORMAL' + + def get_action_duration(self): + if self.action_start_time and self.action_complete_time: + return (self.action_complete_time-self.action_start_time).total_seconds() + else: + return None From 681c85487ebc85218da817886398727248ce6970 Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Mon, 6 Dec 2021 16:16:32 +0100 Subject: [PATCH 8/9] feat: gleason elements' creation date is server assigned this field is now used, as well as for the other annotation objects, as timestamp for the moment when the object is saved on the server --- .../migrations/0020_auto_20211206_1018.py | 30 +++++++++++++++++++ ...linical_annotations_manager.controllers.js | 1 - 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 promort/clinical_annotations_manager/migrations/0020_auto_20211206_1018.py diff --git a/promort/clinical_annotations_manager/migrations/0020_auto_20211206_1018.py b/promort/clinical_annotations_manager/migrations/0020_auto_20211206_1018.py new file mode 100644 index 0000000..0e451ff --- /dev/null +++ b/promort/clinical_annotations_manager/migrations/0020_auto_20211206_1018.py @@ -0,0 +1,30 @@ +# Generated by Django 3.1.13 on 2021-12-06 10:18 + +from django.db import migrations + + +def update_action_complete_time(apps, schema_editor): + GleasonElements = apps.get_model('clinical_annotations_manager', 'GleasonElement') + for g_element in GleasonElements.objects.all(): + g_element.action_complete_time = g_element.creation_date + g_element.creation_date = g_element.focus_region_annotation.creation_date + g_element.save() + + +def reverse_action_complete_time(apps, schema_editor): + GleasonElements = apps.get_model('clinical_annotations_manager', 'GleasonElement') + for g_element in GleasonElements.objects.all(): + g_element.creation_date = g_element.action_complete_time + g_element.action_complete_time = None + g_element.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('clinical_annotations_manager', '0019_auto_20211201_0933'), + ] + + operations = [ + migrations.RunPython(update_action_complete_time, reverse_action_complete_time), + ] diff --git a/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js b/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js index a277818..2548915 100644 --- a/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js +++ b/promort/src/js/clinical_annotations_manager/clinical_annotations_manager.controllers.js @@ -1736,7 +1736,6 @@ cells_count: vm.tmpGleasonCellsCount, gleason_type: vm.tmpGleasonType, gleason_label: vm.gleason_types_map[vm.tmpGleasonType], - creation_date: new Date(), action_start_time: vm.tmpGleasonActionStartTime, action_complete_time: new Date() }; From c36d9b65fa63b0e8b6acdaf4e709b6f17a52e21e Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Mon, 6 Dec 2021 17:25:27 +0100 Subject: [PATCH 9/9] docs: preparing new release --- promort/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/promort/VERSION b/promort/VERSION index 899f24f..f514a2f 100644 --- a/promort/VERSION +++ b/promort/VERSION @@ -1 +1 @@ -0.9.0 \ No newline at end of file +0.9.1 \ No newline at end of file