Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/sckan-324 - journey computation #381

Merged
merged 31 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ddd7d9c
SCKAN-324 - Add journey_description and journey_entities fields to Co…
D-GopalKrishna Dec 10, 2024
22ba03f
SCKAN-324 - Add migration to populate journey_description and journey…
D-GopalKrishna Dec 10, 2024
ea1297f
SCKAN-324 - adapt partial_update method in ViaViewSet and Destination…
D-GopalKrishna Dec 10, 2024
5b03c6d
Merge remote-tracking branch 'refs/remotes/origin/develop'
D-GopalKrishna Dec 10, 2024
ae6bcb0
Merge remote-tracking branch 'origin/develop' into feature/SCKAN-324
D-GopalKrishna Dec 18, 2024
01436c0
SCKAN-324 - refactor: replace journey_description and journey_entitie…
D-GopalKrishna Dec 18, 2024
b8a1d9e
SCKAN-324 - refactor: simplify journey compilation and entity buildin…
D-GopalKrishna Dec 18, 2024
50978d1
SCKAN-324 - refactor: remove save entities logic from partial_update …
D-GopalKrishna Dec 18, 2024
51b3838
SCKAN-324 - refactor tests to adapt to new consolidated_path service
D-GopalKrishna Dec 18, 2024
b934de3
SCKAN-324 - cleanup - simplify the assignment of compile_journey in s…
D-GopalKrishna Dec 18, 2024
99d1238
SCKAN-324 - do not save journey on every save of ConnectivityStatement
D-GopalKrishna Dec 20, 2024
466d8b3
SCKAN-324 refactor: move journey recompilation to signal handlers
D-GopalKrishna Dec 20, 2024
0abd47e
SCKAN-364 - add statement_prefix and statement_suffix fields to Conne…
D-GopalKrishna Jan 5, 2025
dce73c1
SCKAN-364 - extract prefix and suffix generation for statement previe…
D-GopalKrishna Jan 5, 2025
5f20fd0
SCKAN-364 - simplify statement preview creation by using prefix and s…
D-GopalKrishna Jan 5, 2025
8c0d561
SCKAN-364 - update prefix and suffix using signals for statement_prev…
D-GopalKrishna Jan 5, 2025
a98e76c
SCKAN-364 - move statement preview creation to statement service
D-GopalKrishna Jan 5, 2025
7f0574f
SCKAN-364 - export statement_preview in object text when - alert note…
D-GopalKrishna Jan 5, 2025
cc18966
SCKAN-324 add journey_path to readonly fields in ConnectivityStatemen…
D-GopalKrishna Jan 14, 2025
2b3c1f1
SCKAN-364: Use existing cs instance for prefix instead of querying again
D-GopalKrishna Jan 14, 2025
953dcc5
Merge branch 'develop' into feature/SCKAN-324
D-GopalKrishna Jan 16, 2025
5c23eca
SCKAN-324 - remove via query by adding logic for destination_layer
D-GopalKrishna Jan 16, 2025
b2d2bfe
SCKAN-364 - revert: fix alert text for object text
D-GopalKrishna Jan 16, 2025
c15e960
SCKAN-364 - isolate the migration statement preview logic to migratio…
D-GopalKrishna Jan 17, 2025
546abf1
SCKAN-324 - isolate migration for update journey field logic to migra…
D-GopalKrishna Jan 17, 2025
8f1622a
SCKAN-324 chore: Remove unused parameters
afonsobspinto Jan 17, 2025
f5f6bc9
SCKAN-364 feat: Update signals logic
afonsobspinto Jan 17, 2025
00adcf8
Merge pull request #404 from MetaCell/feature/SCKAN-364
afonsobspinto Jan 17, 2025
7829dd3
Merge branch 'develop'
D-GopalKrishna Jan 20, 2025
a39d5a5
feat: fix migration and add journey_path field to 0066 connectivityst…
D-GopalKrishna Jan 20, 2025
af39df9
SCKAN-324 - refactor/split connectivity statement migrations
D-GopalKrishna Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions backend/composer/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,20 +252,23 @@ class ModelRetrieveViewSet(
# mixins.DestroyModelMixin,
# mixins.ListModelMixin,
viewsets.GenericViewSet,
): ...
):
...


class ModelCreateRetrieveViewSet(
ModelRetrieveViewSet,
mixins.CreateModelMixin,
mixins.ListModelMixin,
): ...
):
...


class ModelNoDeleteViewSet(
ModelCreateRetrieveViewSet,
mixins.UpdateModelMixin,
): ...
):
...


class AnatomicalEntityViewSet(viewsets.ReadOnlyModelViewSet):
Expand Down Expand Up @@ -506,7 +509,8 @@ def my(self, request, *args, **kwargs):
msg = "User not logged in."
raise ValidationError(msg, code="authorization")

profile, created = Profile.objects.get_or_create(user=self.request.user)
profile, created = Profile.objects.get_or_create(
user=self.request.user)
return Response(self.get_serializer(profile).data)


Expand Down Expand Up @@ -547,6 +551,7 @@ class StatementAlertViewSet(viewsets.ModelViewSet):
serializer_class = StatementAlertSerializer
permission_classes = [IsOwnerOfConnectivityStatementOrReadOnly]


@extend_schema(
responses=OpenApiTypes.OBJECT,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.1.4 on 2024-12-18 08:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("composer", "0065_alter_statementalert_text"),
]

operations = [
migrations.AddField(
model_name="connectivitystatement",
name="journey_path",
field=models.JSONField(blank=True, null=True),
),
]
23 changes: 23 additions & 0 deletions backend/composer/migrations/0067_auto_20241218_0928.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.1.4 on 2024-12-18 08:28

from django.db import migrations
from django.db import migrations
from composer.services.graph_service import compile_journey


def update_journey_fields(apps, schema_editor):
ConnectivityStatement = apps.get_model('composer', 'ConnectivityStatement')
for cs in ConnectivityStatement.objects.all():
cs.journey_path = compile_journey(cs)
cs.save(update_fields=["journey_path"])


class Migration(migrations.Migration):

dependencies = [
("composer", "0066_connectivitystatement_journey_path"),
D-GopalKrishna marked this conversation as resolved.
Show resolved Hide resolved
]

operations = [
migrations.RunPython(update_journey_fields),
]
9 changes: 4 additions & 5 deletions backend/composer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db.models.expressions import F
from django.forms.widgets import Input as InputWidget
from django_fsm import FSMField, transition
from composer.services.graph_service import build_journey_description, build_journey_entities

from composer.services.layers_service import update_from_entities_on_deletion
from composer.services.state_services import (
Expand All @@ -21,7 +22,6 @@
ViaType,
Projection,
)
from .services.graph_service import compile_journey
from .utils import doi_uri, pmcid_uri, pmid_uri, create_reference_uri


Expand Down Expand Up @@ -567,6 +567,7 @@ class ConnectivityStatement(models.Model):
)
created_date = models.DateTimeField(auto_now_add=True, db_index=True)
modified_date = models.DateTimeField(auto_now=True, db_index=True)
journey_path = models.JSONField(null=True, blank=True)

def __str__(self):
suffix = ""
Expand Down Expand Up @@ -683,11 +684,10 @@ def get_previous_layer_entities(self, via_order):
return set(self.via_set.get(order=via_order - 1).anatomical_entities.all())

def get_journey(self):
return compile_journey(self)['journey']
return build_journey_description(self.journey_path, self)

def get_entities_journey(self):
entities_journey = compile_journey(self)['entities']
return entities_journey
return build_journey_entities(self.journey_path, self)

def get_laterality_description(self):
laterality_map = {
Expand Down Expand Up @@ -716,7 +716,6 @@ def save(self, *args, **kwargs):
self.reference_uri = create_reference_uri(self.pk)
self.save(update_fields=["reference_uri"])


def set_origins(self, origin_ids):
self.origins.set(origin_ids, clear=True)
self.save()
Expand Down
75 changes: 49 additions & 26 deletions backend/composer/services/graph_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ def consolidate_paths(paths):

paths = consolidated + [paths[i] for i in range(len(paths)) if i not in used_indices]

return paths, [[((node[1].replace(JOURNEY_DELIMITER, ' or '), node[2]) if (
node[2] == 0 or path.index(node) == len(path) - 1) else (
node[1].replace(JOURNEY_DELIMITER, ', '), node[2])) for node in path] for path in paths]
return paths


def can_merge(path1, path2):
Expand Down Expand Up @@ -144,7 +142,7 @@ def merge_paths(path1, path2):
return merged_path


def compile_journey(connectivity_statement) -> dict:
def compile_journey(connectivity_statement) -> List[str]:
"""
Generates a string of descriptions of journey paths for a given connectivity statement.

Expand All @@ -157,17 +155,55 @@ def compile_journey(connectivity_statement) -> dict:
# Extract origins, vias, and destinations from the connectivity statement
Via = apps.get_model('composer', 'Via')
Destination = apps.get_model('composer', 'Destination')
Origin = apps.get_model('composer', 'AnatomicalEntity')

origins = list(connectivity_statement.origins.all())

vias = list(Via.objects.filter(connectivity_statement=connectivity_statement))
destinations = list(Destination.objects.filter(connectivity_statement=connectivity_statement))
vias = list(Via.objects.filter(connectivity_statement__id=connectivity_statement.id))
destinations = list(Destination.objects.filter(connectivity_statement__id=connectivity_statement.id))
origins = list(Origin.objects.filter(origins_relations__id=connectivity_statement.id).distinct())
afonsobspinto marked this conversation as resolved.
Show resolved Hide resolved
# origins = list(connectivity_statement.origins.all())

# Generate all paths and then consolidate them
all_paths2 = generate_paths(origins, vias, destinations)
consolidated_paths, journey_paths = consolidate_paths(all_paths2)
consolidated_paths = consolidate_paths(all_paths2)
return consolidated_paths


def get_journey_path_from_consolidated_paths(consolidated_paths):
journey_paths = [[((node[1].replace(JOURNEY_DELIMITER, ' or '), node[2]) if (
node[2] == 0 or path.index(node) == len(path) - 1) else (
node[1].replace(JOURNEY_DELIMITER, ', '), node[2])) for node in path] for path in consolidated_paths]
return journey_paths


def build_journey_description(consolidated_paths, connectivity_statement):
afonsobspinto marked this conversation as resolved.
Show resolved Hide resolved
journey_paths = get_journey_path_from_consolidated_paths(
consolidated_paths)
Via = apps.get_model('composer', 'Via')
vias = list(Via.objects.filter(
afonsobspinto marked this conversation as resolved.
Show resolved Hide resolved
connectivity_statement=connectivity_statement))

# Create sentences for each journey path
journey_descriptions = []
for path in journey_paths:
origin_names = path[0][0]
destination_names = path[-1][0]
via_names = ' via '.join([node for node, layer in path if 0 < layer < len(vias) + 1])

if via_names:
sentence = f"from {origin_names} to {destination_names} via {via_names}"
else:
sentence = f"from {origin_names} to {destination_names}"

journey_descriptions.append(sentence)
return journey_descriptions


def build_journey_entities(consolidated_paths, connectivity_statement):
afonsobspinto marked this conversation as resolved.
Show resolved Hide resolved
entities = []
Via = apps.get_model('composer', 'Via')
vias = list(Via.objects.filter(
afonsobspinto marked this conversation as resolved.
Show resolved Hide resolved
connectivity_statement=connectivity_statement))

for path in consolidated_paths:
origin_splits = path[0][0].split(JOURNEY_DELIMITER)
destination_splits = path[-1][0].split(JOURNEY_DELIMITER)
Expand All @@ -181,22 +217,9 @@ def compile_journey(connectivity_statement) -> dict:
'vias': [{'label': node, 'id': node_id} for node_id, node, layer in path if 0 < layer < len(vias) + 1]
}
entities.append(entity)
return entities

# Create sentences for each journey path
journey_descriptions = []
for path in journey_paths:
origin_names = path[0][0]
destination_names = path[-1][0]
via_names = ' via '.join([node for node, layer in path if 0 < layer < len(vias) + 1])

if via_names:
sentence = f"from {origin_names} to {destination_names} via {via_names}"
else:
sentence = f"from {origin_names} to {destination_names}"

journey_descriptions.append(sentence)

return {
'journey': journey_descriptions,
'entities': entities
}
def recompile_journey_path(instance):
instance.journey_path = compile_journey(instance)
instance.save(update_fields=["journey_path"])
11 changes: 11 additions & 0 deletions backend/composer/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
Via,
)
from .services.export_services import compute_metrics, ConnectivityStatementStateService
from .services.graph_service import recompile_journey_path



@receiver(post_save, sender=ExportBatch)
Expand Down Expand Up @@ -101,6 +103,8 @@ def connectivity_statement_origins_changed(sender, instance, action, pk_set, **k
pass
except ValueError:
pass
recompile_journey_path(instance)


# Call `update_from_entities_on_deletion` for each removed entity
if action == "post_remove" and pk_set:
Expand All @@ -118,6 +122,7 @@ def via_anatomical_entities_changed(sender, instance, action, pk_set, **kwargs):
pass
except ValueError:
pass
recompile_journey_path(instance.connectivity_statement)

# Call `update_from_entities_on_deletion` for each removed entity
if action == "post_remove" and pk_set:
Expand All @@ -135,6 +140,8 @@ def via_from_entities_changed(sender, instance, action, **kwargs):
pass
except ValueError:
pass
recompile_journey_path(instance.connectivity_statement)



# Signals for Destination anatomical_entities
Expand All @@ -147,6 +154,8 @@ def destination_anatomical_entities_changed(sender, instance, action, **kwargs):
pass
except ValueError:
pass
recompile_journey_path(instance.connectivity_statement)



# Signals for Destination from_entities
Expand All @@ -159,6 +168,8 @@ def destination_from_entities_changed(sender, instance, action, **kwargs):
pass
except ValueError:
pass
recompile_journey_path(instance.connectivity_statement)



# Signals for Via model changes
Expand Down
Loading
Loading