Skip to content

Commit

Permalink
Release 0.9.4
Browse files Browse the repository at this point in the history
  • Loading branch information
lucalianas committed Mar 28, 2022
2 parents e07cb0d + e95a814 commit ca407c1
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 56 deletions.
2 changes: 1 addition & 1 deletion promort/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.9.3
0.9.4
89 changes: 50 additions & 39 deletions promort/predictions_manager/management/commands/tissue_to_rois.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import requests
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError
from predictions_manager.models import TissueFragment
from predictions_manager.models import TissueFragment, Prediction
from reviews_manager.models import ROIsAnnotationStep
from rois_manager.models import Core, Slice
from shapely.geometry import Polygon
Expand Down Expand Up @@ -64,51 +64,58 @@ def handle(self, *args, **opts):

for step in annotation_steps:
logger.info("Processing ROIs annotation step %s", step.label)
fragments = TissueFragment.objects.filter(
collection__prediction__slide=step.slide
)
if not fragments:
logger.info(
"Skipping step %s, no tissue fragment found",
step,
)
continue
latest_prediction = Prediction.objects.filter(
slide=step.slide, type='TISSUE'
).order_by('-creation_date').first()

fragments_collection = latest_prediction.fragments_collection.order_by('-creation_date').first()

if fragments_collection and fragments_collection.fragments.count() > 0:
fragments = fragments_collection.fragments.all()

slide_bounds = self._get_slide_bounds(step.slide)
slide_bounds = self._get_slide_bounds(step.slide)

slide_mpp = step.slide.image_microns_per_pixel
all_shapes = [json.loads(fragment.shape_json) for fragment in fragments]
grouped_shapes = self._group_nearest_cores(all_shapes)
for idx, shapes in enumerate(grouped_shapes):
slice_label = idx + 1
shapes_coords = self._get_slice_coordinates(shapes)
slide_mpp = step.slide.image_microns_per_pixel
all_shapes = [json.loads(fragment.shape_json) for fragment in fragments]
grouped_shapes = self._group_nearest_cores(all_shapes)
for idx, shapes in enumerate(grouped_shapes):
slice_label = idx + 1
shapes_coords = self._get_slice_coordinates(shapes)

slice_obj = self._create_slice(
shapes_coords,
slice_label,
len(shapes),
step,
user,
slide_bounds,
)
logger.info("Slice saved with ID %d", slice_obj.id)
for core_index, core in enumerate(shapes):
logger.info(
"Loading core %d of %d",
core_index + 1,
len(shapes),
)
core_obj = self._create_core(
core["coordinates"],
core["length"] * slide_mpp,
core["area"] * math.pow(slide_mpp, 2),
slice_obj,
slice_obj = self._create_slice(
shapes_coords,
slice_label,
core_index + 1,
len(shapes),
step,
user,
slide_bounds,
fragments_collection
)
logger.info("Core saved with ID %d", core_obj.id)
logger.info("Slice saved with ID %d", slice_obj.id)
for core_index, core in enumerate(shapes):
logger.info(
"Loading core %d of %d",
core_index + 1,
len(shapes),
)
core_obj = self._create_core(
core["coordinates"],
core["length"] * slide_mpp,
core["area"] * math.pow(slide_mpp, 2),
slice_obj,
slice_label,
core_index + 1,
user,
slide_bounds,
fragments_collection
)
logger.info("Core saved with ID %d", core_obj.id)
else:
logger.info(
"Skipping prediction %s for step %s, no tissue fragment found",
latest_prediction.label, step.label,
)
continue
else:
logger.info("== There are no suitable ROIs annotation steps")
logger.info("== Job completed ==")
Expand Down Expand Up @@ -167,6 +174,7 @@ def _create_slice(
annotation_step,
user,
slide_bounds,
collection
):
slice_coordinates = self._adjust_roi_coordinates(
slice_coordinates, slide_bounds
Expand All @@ -181,6 +189,7 @@ def _create_slice(
author=user,
roi_json=json.dumps(roi_json),
total_cores=cores_count,
source_collection=collection
)
slice_.save()
return slice_
Expand All @@ -195,6 +204,7 @@ def _create_core(
core_id,
user,
slide_bounds,
collection
):
core_coordinates = self._adjust_roi_coordinates(core_coordinates, slide_bounds)
roi_json = self._create_roi_json(
Expand All @@ -207,6 +217,7 @@ def _create_core(
roi_json=json.dumps(roi_json),
length=core_length,
area=core_area,
source_collection=collection
)
core.save()
return core
Expand Down
19 changes: 19 additions & 0 deletions promort/predictions_manager/migrations/0004_auto_20220303_1539.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.1.13 on 2022-03-03 15:39

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('predictions_manager', '0003_prediction_review_required'),
]

operations = [
migrations.AlterField(
model_name='tissuefragmentscollection',
name='prediction',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='fragments_collection', to='predictions_manager.prediction'),
),
]
29 changes: 29 additions & 0 deletions promort/predictions_manager/migrations/0005_auto_20220318_1531.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 3.1.9 on 2022-03-18 15:31

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('predictions_manager', '0004_auto_20220303_1539'),
]

operations = [
migrations.CreateModel(
name='Provenance',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('model', models.TextField()),
('start_date', models.DateTimeField()),
('end_date', models.DateTimeField()),
('params', models.JSONField()),
],
),
migrations.AlterField(
model_name='prediction',
name='provenance',
field=models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='predictions_manager.provenance'),
),
]
23 changes: 23 additions & 0 deletions promort/predictions_manager/migrations/0006_auto_20220324_0910.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.1.13 on 2022-03-24 09:10

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('predictions_manager', '0005_auto_20220318_1531'),
]

operations = [
migrations.RemoveField(
model_name='prediction',
name='provenance',
),
migrations.AddField(
model_name='provenance',
name='prediction',
field=models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='predictions_manager.prediction'),
),
]
32 changes: 24 additions & 8 deletions promort/predictions_manager/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,45 @@ class Prediction(models.Model):

label = models.CharField(max_length=100, unique=True)
creation_date = models.DateTimeField(auto_now_add=True)
slide = models.ForeignKey(Slide, on_delete=models.PROTECT, blank=False,
related_name='predictions')
type = models.CharField(max_length=7, choices=PREDICTION_TYPES, blank=False, null=False)
slide = models.ForeignKey(
Slide, on_delete=models.PROTECT, blank=False, related_name="predictions"
)
type = models.CharField(
max_length=7, choices=PREDICTION_TYPES, blank=False, null=False
)
omero_id = models.IntegerField(blank=True, null=True, default=None)
provenance = models.TextField(blank=True, null=True)
review_required = models.BooleanField(blank=False, null=False, default=False)

def require_review(self):
self.review_required = True
self.save()


class TissueFragmentsCollection(models.Model):
prediction = models.ForeignKey(Prediction, on_delete=models.PROTECT, blank=False,
related_name='tissue_fragments')
related_name='fragments_collection')
creation_date = models.DateTimeField(auto_now_add=True)

def get_slide(self):
return self.prediction.slide


class TissueFragment(models.Model):
collection = models.ForeignKey(TissueFragmentsCollection, on_delete=models.PROTECT, blank=False,
related_name='fragments')
collection = models.ForeignKey(
TissueFragmentsCollection,
on_delete=models.PROTECT,
blank=False,
related_name="fragments",
)
shape_json = models.TextField(blank=False)
creation_date = models.DateTimeField(auto_now_add=True)


class Provenance(models.Model):
prediction = models.OneToOneField(
Prediction, on_delete=models.PROTECT, blank=True, null=True, default=None
)
model = models.TextField(blank=False, null=False)
start_date = models.DateTimeField(blank=False, null=False)
end_date = models.DateTimeField(blank=False, null=False)
params = models.JSONField(blank=False, null=False)
26 changes: 19 additions & 7 deletions promort/predictions_manager/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,38 @@
except ImportError:
import json

from predictions_manager.models import (Prediction, Provenance, TissueFragment,
TissueFragmentsCollection)
from rest_framework import serializers

from predictions_manager.models import Prediction, TissueFragmentsCollection, TissueFragment
from slides_manager.serializers import SlideSerializer

class ProvenanceSerializer(serializers.ModelSerializer):
class Meta:
model = Provenance
fields = ('id', 'model', 'start_date', 'end_date','params')


class PredictionSerializer(serializers.ModelSerializer):
provenance = ProvenanceSerializer(required=False)

class Meta:
model = Prediction
fields = ('id', 'label', 'creation_date', 'slide', 'type', 'omero_id', 'provenance',
'review_required')
read_only_fields = ('id', 'creation_date')

def validate_provenance(self, value):
def create(self, validated_data):
try:
json.loads(value)
return value
except ValueError:
raise serializers.ValidationError('Not a valid JSON in \'provenance\' field')
provenance_kwargs = validated_data.pop('provenance')
except KeyError:
provenance_kwargs = {}
prediction = Prediction.objects.create(**validated_data)
if provenance_kwargs:
provenance_kwargs['prediction'] = prediction
prov = Provenance.objects.create(**provenance_kwargs)

return prediction



class PredictionDetailsSerializer(serializers.ModelSerializer):
Expand Down
20 changes: 20 additions & 0 deletions promort/predictions_manager/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# 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.

import datetime as dt
import json

import factory
Expand All @@ -25,6 +26,7 @@
from pytest_factoryboy import register
from shapely.affinity import translate
from shapely.geometry import box
from slides_manager.models import Case, Slide


@register
Expand Down Expand Up @@ -159,3 +161,21 @@ def _create(n_steps, with_fragments):
return steps

return _create


@fixture
def prediction_data(provenance_data: bool, depends_on: bool):

case = Case.objects.create(id="case")
slide = Slide.objects.create(id="slide", case=case)
now = dt.datetime.now()
data = {"label": "label", "slide": slide.id, "type": "TUMOR"}

if provenance_data:
data["provenance"] = {
"model": "model",
"start_date": now,
"end_date": now,
"params": "{}",
}
return data
Loading

0 comments on commit ca407c1

Please sign in to comment.