From 34dd205d3a92d479991ac1d4fb3a6944fa2b5005 Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Wed, 30 Mar 2022 11:51:35 +0200 Subject: [PATCH 1/4] fix: fixed slices generation properly grouping cores based on Y coordinates --- .../management/commands/tissue_to_rois.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/promort/predictions_manager/management/commands/tissue_to_rois.py b/promort/predictions_manager/management/commands/tissue_to_rois.py index c96a157..df49813 100644 --- a/promort/predictions_manager/management/commands/tissue_to_rois.py +++ b/promort/predictions_manager/management/commands/tissue_to_rois.py @@ -277,17 +277,19 @@ def _get_core_bounds(self, core: Dict) -> Dict: except IndexError: raise InvalidPolygonError() - def _group_nearest_cores(self, cores, height_tolerance=0.01): + def _group_nearest_cores(self, cores): cores_map, sorted_y_coords = self._get_sorted_cores_map(cores) cores_groups = list() - tolerance = sorted_y_coords[-1][1] * height_tolerance current_group = cores_map[sorted_y_coords[0]] + cg_max_y = sorted_y_coords[0][1] for i, yc in enumerate(sorted_y_coords[1:]): - if yc[0] <= sorted_y_coords[i][1] + tolerance: + if yc[0] <= cg_max_y: current_group.extend(cores_map[yc]) + cg_max_y = max([cg_max_y, yc[1]]) else: cores_groups.append(current_group) current_group = cores_map[yc] + cg_max_y = yc[1] cores_groups.append(current_group) return cores_groups From 8e0bcb49d4c2440acb7c79a5cedd0d2518f30309 Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Mon, 11 Apr 2022 16:25:48 +0200 Subject: [PATCH 2/4] feat: added filters for tissue type and reviewer --- .../commands/extract_focus_regions.py | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/promort/rois_manager/management/commands/extract_focus_regions.py b/promort/rois_manager/management/commands/extract_focus_regions.py index 39f31cf..4298570 100644 --- a/promort/rois_manager/management/commands/extract_focus_regions.py +++ b/promort/rois_manager/management/commands/extract_focus_regions.py @@ -44,9 +44,18 @@ def add_arguments(self, parser): help='path of the output folder for the extracted JSON objects') parser.add_argument('--limit-bounds', dest='limit_bounds', action='store_true', help='extract ROIs considering only the non-empty slide region') - - def _load_rois_annotation_steps(self): - steps = ROIsAnnotationStep.objects.filter(completion_date__isnull=False) + parser.add_argument('--reviewer', dest='reviewer', type=str, + help='filter review steps by reviewer username') + parser.add_argument('--tissue-type', dest='tissue_type', type=str, + choices=['TUMOR', 'NORMAL'], help='filter focus regions by tissue type') + + def _load_rois_annotation_steps(self, reviewer=None): + if reviewer is not None: + logger.info(f'Filtering by reviewer: {reviewer}') + steps = ROIsAnnotationStep.objects.filter(completion_date__isnull=False, + rois_annotation__reviewer__username=reviewer) + else: + steps = ROIsAnnotationStep.objects.filter(completion_date__isnull=False) return steps def _get_slide_bounds(self, slide): @@ -112,8 +121,11 @@ def _dump_details(self, details, out_folder): writer.writeheader() writer.writerows(details) - def _dump_focus_regions(self, step, out_folder, limit_bounds): - focus_regions = step.focus_regions + def _dump_focus_regions(self, step, out_folder, limit_bounds, tissue_type=None): + if tissue_type is None: + focus_regions = step.focus_regions + else: + focus_regions = [fr for fr in step.focus_regions if fr.tissue_status == tissue_type] slide = step.slide logger.info('Loading info for slide %s', slide.id) if not limit_bounds: @@ -135,13 +147,13 @@ def _dump_focus_regions(self, step, out_folder, limit_bounds): focus_regions_details.append(frd) self._dump_details(focus_regions_details, out_path) - def _export_data(self, out_folder, limit_bounds=False): - steps = self._load_rois_annotation_steps() + def _export_data(self, out_folder, limit_bounds=False, reviewer=None, tissue_type=None): + steps = self._load_rois_annotation_steps(reviewer) logger.info('Loaded %d ROIs Annotation Steps', len(steps)) for s in steps: - self._dump_focus_regions(s, out_folder, limit_bounds) + self._dump_focus_regions(s, out_folder, limit_bounds, tissue_type) def handle(self, *args, **opts): logger.info('=== Starting export job ===') - self._export_data(opts['out_folder'], opts['limit_bounds']) + self._export_data(opts['out_folder'], opts['limit_bounds'], opts['reviewer'], opts['tissue_type']) logger.info('=== Export completed ===') From 75ed60abacaa7cb8792f8b0a59fb036cba11f4d2 Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Thu, 5 May 2022 11:27:19 +0200 Subject: [PATCH 3/4] feat: show model for prediction review items in worklist --- promort/reviews_manager/serializers.py | 13 +++++++++++-- .../templates/worklist/pending_reviews.html | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/promort/reviews_manager/serializers.py b/promort/reviews_manager/serializers.py index 77c56ab..24e2e03 100644 --- a/promort/reviews_manager/serializers.py +++ b/promort/reviews_manager/serializers.py @@ -18,6 +18,7 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from django.contrib.auth.models import User +from predictions_manager.models import Provenance from rest_framework import serializers @@ -280,6 +281,7 @@ class PredictionReviewSerializer(serializers.ModelSerializer): slug_field='username', queryset=User.objects.all() ) + model = serializers.SerializerMethodField() started = serializers.SerializerMethodField() completed = serializers.SerializerMethodField() annotation_type = serializers.SerializerMethodField() @@ -288,9 +290,16 @@ class PredictionReviewSerializer(serializers.ModelSerializer): class Meta: model = PredictionReview - fields = ('id', 'label', 'annotation_type', 'prediction', 'slide', 'reviewer', + fields = ('id', 'label', 'annotation_type', 'prediction', 'slide', 'reviewer', 'model', 'creation_date', 'start_date', 'completion_date', 'started', 'completed') - read_only_fields = ('id', 'creation_date', 'started', 'completed', 'annotation_type') + read_only_fields = ('id', 'creation_date', 'started', 'completed', 'annotation_type', 'model') + + @staticmethod + def get_model(obj): + try: + return Provenance.objects.filter(prediction=obj.prediction).first().model + except AttributeError: + return None @staticmethod def get_started(obj): diff --git a/promort/static_src/templates/worklist/pending_reviews.html b/promort/static_src/templates/worklist/pending_reviews.html index a14e681..b188d5b 100644 --- a/promort/static_src/templates/worklist/pending_reviews.html +++ b/promort/static_src/templates/worklist/pending_reviews.html @@ -35,6 +35,9 @@

WORKLIST - Pending annotations

Review ID: {{ annotation.extended_label }}

Review Type: {{ annotation.annotation_type }}

+

+ Model: {{ annotation.model }} +

Slides count: {{ annotation.steps_count | number:0 }}

From 37f7aff5de8547b755cb5e6717f35958fd00518d Mon Sep 17 00:00:00 2001 From: Luca Lianas Date: Thu, 5 May 2022 11:27:41 +0200 Subject: [PATCH 4/4] build: preparing new release --- promort/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/promort/VERSION b/promort/VERSION index 2bd77c7..45dfa9b 100644 --- a/promort/VERSION +++ b/promort/VERSION @@ -1 +1 @@ -0.9.4 \ No newline at end of file +0.9.4-2 \ No newline at end of file