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