Skip to content

Commit

Permalink
Merge pull request #532 from alan-turing-institute/439-backend-user-s…
Browse files Browse the repository at this point in the history
…tory-preserve-child-elements-on-parent-deletion

[Backend][WIP] Attaching/Reataching Case Elements
  • Loading branch information
cptanalatriste authored Jul 8, 2024
2 parents 0ae246a + 9e25921 commit a7862ed
Show file tree
Hide file tree
Showing 9 changed files with 898 additions and 36 deletions.
108 changes: 108 additions & 0 deletions eap_backend/eap_api/migrations/0017_auto_20240703_0831.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Generated by Django 3.2.8 on 2024-07-03 08:31

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


class Migration(migrations.Migration):

dependencies = [
("eap_api", "0016_auto_20240613_1028"),
]

operations = [
migrations.AddField(
model_name="context",
name="assurance_case",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="contexts",
to="eap_api.assurancecase",
),
),
migrations.AddField(
model_name="context",
name="in_sandbox",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="evidence",
name="assurance_case",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="evidence",
to="eap_api.assurancecase",
),
),
migrations.AddField(
model_name="evidence",
name="in_sandbox",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="propertyclaim",
name="assurance_case",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="property_claims",
to="eap_api.assurancecase",
),
),
migrations.AddField(
model_name="propertyclaim",
name="in_sandbox",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="strategy",
name="assurance_case",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="strategies",
to="eap_api.assurancecase",
),
),
migrations.AddField(
model_name="strategy",
name="in_sandbox",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="toplevelnormativegoal",
name="in_sandbox",
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name="context",
name="name",
field=models.CharField(blank=True, max_length=200),
),
migrations.AlterField(
model_name="evidence",
name="name",
field=models.CharField(blank=True, max_length=200),
),
migrations.AlterField(
model_name="propertyclaim",
name="name",
field=models.CharField(blank=True, max_length=200),
),
migrations.AlterField(
model_name="strategy",
name="name",
field=models.CharField(blank=True, max_length=200),
),
migrations.AlterField(
model_name="toplevelnormativegoal",
name="name",
field=models.CharField(blank=True, max_length=200),
),
]
24 changes: 24 additions & 0 deletions eap_backend/eap_api/migrations/0018_alter_context_goal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.8 on 2024-07-03 14:16

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


class Migration(migrations.Migration):

dependencies = [
("eap_api", "0017_auto_20240703_0831"),
]

operations = [
migrations.AlterField(
model_name="context",
name="goal",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="context",
to="eap_api.toplevelnormativegoal",
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 3.2.8 on 2024-07-08 09:00

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("eap_api", "0017_auto_20240704_1601"),
("eap_api", "0018_alter_context_goal"),
]

operations = []
44 changes: 40 additions & 4 deletions eap_backend/eap_api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class CaseItem(models.Model):
long_description = models.CharField(max_length=3000)
shape = Shape
created_date = models.DateTimeField(auto_now_add=True)
in_sandbox = models.BooleanField(default=False)

class Meta:
abstract = True
Expand Down Expand Up @@ -125,7 +126,18 @@ def __str__(self):
class Context(CaseItem):
shape = Shape.ROUNDED_RECTANGLE
goal = models.ForeignKey(
TopLevelNormativeGoal, related_name="context", on_delete=models.CASCADE
TopLevelNormativeGoal,
related_name="context",
on_delete=models.CASCADE,
null=True,
)

assurance_case = models.ForeignKey(
AssuranceCase,
related_name="contexts",
on_delete=models.CASCADE,
default=None,
null=True,
)


Expand All @@ -137,6 +149,14 @@ class Strategy(CaseItem):
on_delete=models.CASCADE,
)

assurance_case = models.ForeignKey(
AssuranceCase,
related_name="strategies",
on_delete=models.CASCADE,
default=None,
null=True,
)

def __str__(self):
return self.name

Expand Down Expand Up @@ -174,6 +194,14 @@ class ClaimType(models.TextChoices):
on_delete=models.CASCADE,
)

assurance_case = models.ForeignKey(
AssuranceCase,
related_name="property_claims",
on_delete=models.CASCADE,
default=None,
null=True,
)

level = models.PositiveIntegerField()

def save(self, *args, **kwargs):
Expand All @@ -182,16 +210,16 @@ def save(self, *args, **kwargs):
)

error_message: str = ""
if parent_count != 1:
error_message = "A PropertyClaim should have exactly one parent."
if parent_count > 1:
error_message = "A PropertyClaim should have at most one parent."
raise ValueError(error_message)

if self.property_claim is not None and self.property_claim.pk == self.pk:
error_message = "A PropertyClaim cannot be the parent of itself."
raise ValueError(error_message)

try:
parent_level = self.property_claim.level
parent_level = self.property_claim.level # type:ignore[attr-defined]
except AttributeError:
parent_level = 0

Expand All @@ -204,3 +232,11 @@ class Evidence(CaseItem):
URL = models.CharField(max_length=3000, null=True, blank=True)
shape = Shape.CYLINDER
property_claim = models.ManyToManyField(PropertyClaim, related_name="evidence")

assurance_case = models.ForeignKey(
AssuranceCase,
related_name="evidence",
on_delete=models.CASCADE,
default=None,
null=True,
)
35 changes: 35 additions & 0 deletions eap_backend/eap_api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from typing import cast

from django.db.models.query import QuerySet
from rest_framework import serializers
from rest_framework.serializers import ReturnDict

from .github import Github, register_social_user
from .models import (
Expand Down Expand Up @@ -126,6 +130,34 @@ class Meta:
)


class SandboxSerializer(serializers.ModelSerializer):

contexts = serializers.SerializerMethodField()
evidence = serializers.SerializerMethodField()
property_claims = serializers.SerializerMethodField()

class Meta:
model = AssuranceCase
fields = ["contexts", "evidence", "property_claims"]

def get_contexts(self, assurance_case: AssuranceCase) -> ReturnDict:
sandbox_contexts: QuerySet = assurance_case.contexts.filter(in_sandbox=True) # type: ignore[attr-defined]
context_serializer = ContextSerializer(sandbox_contexts, many=True)
return cast(ReturnDict, context_serializer.data)

def get_evidence(self, assurance_case: AssuranceCase) -> ReturnDict:
sandbox_evidence: QuerySet = assurance_case.evidence.filter(in_sandbox=True) # type: ignore[attr-defined]
evidence_serializer = EvidenceSerializer(sandbox_evidence, many=True)
return cast(ReturnDict, evidence_serializer.data)

def get_property_claims(self, assurance_case: AssuranceCase) -> ReturnDict:
sandbox_property_claims: QuerySet = assurance_case.property_claims.filter(in_sandbox=True) # type: ignore[attr-defined]
property_claim_serializer = PropertyClaimSerializer(
sandbox_property_claims, many=True
)
return cast(ReturnDict, property_claim_serializer.data)


class TopLevelNormativeGoalSerializer(serializers.ModelSerializer):
assurance_case_id = serializers.PrimaryKeyRelatedField(
source="assurance_case", queryset=AssuranceCase.objects.all()
Expand Down Expand Up @@ -169,6 +201,7 @@ class Meta:
"long_description",
"created_date",
"goal_id",
"in_sandbox",
)

extra_kwargs = {"name": {"allow_null": True, "required": False}}
Expand Down Expand Up @@ -218,6 +251,7 @@ class Meta:
"property_claims",
"evidence",
"strategy_id",
"in_sandbox",
)

extra_kwargs = {
Expand All @@ -243,6 +277,7 @@ class Meta:
"long_description",
"URL",
"property_claim_id",
"in_sandbox",
)

extra_kwargs = {"name": {"allow_null": True, "required": False}}
Expand Down
26 changes: 21 additions & 5 deletions eap_backend/eap_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
path("groups/<int:pk>/", views.group_detail, name="group_detail"),
path("cases/", views.case_list, name="case_list"),
path("cases/<int:pk>/", views.case_detail, name="case_detail"),
path("cases/<int:pk>/sandbox", views.case_sandbox, name="case_sandbox"),
path(
"cases/<int:pk>/update-ids",
views.case_update_identifiers,
Expand All @@ -21,11 +22,28 @@
path("goals/<int:pk>/", views.goal_detail, name="goal_detail"),
path("contexts/", views.context_list, name="context_list"),
path("contexts/<int:pk>/", views.context_detail, name="context_detail"),
path("contexts/<int:pk>/detach", views.detach_context, name="detach_context"),
path("contexts/<int:pk>/attach", views.attach_context, name="attach_context"),
path(
"propertyclaims/",
views.property_claim_list,
name="property_claim_list",
),
path(
"propertyclaims/<int:pk>/",
views.property_claim_detail,
name="property_claim_detail",
),
path(
"propertyclaims/<int:pk>/detach",
views.detach_property_claim,
name="detach_property_claim",
),
path(
"propertyclaims/<int:pk>/attach",
views.attach_property_claim,
name="attach_property_claim",
),
path(
"cases/<int:assurance_case_id>/comments/",
views.comment_list,
Expand All @@ -40,13 +58,10 @@
path(
"comments/<int:pk>/", views.CommentEdit.as_view(), name="comment_edit"
), # Use the view class
path(
"propertyclaims/<int:pk>/",
views.property_claim_detail,
name="property_claim_detail",
),
path("evidence/", views.evidence_list, name="evidence_list"),
path("evidence/<int:pk>/", views.evidence_detail, name="evidence_detail"),
path("evidence/<int:pk>/detach", views.detach_evidence, name="detach_evidence"),
path("evidence/<int:pk>/attach", views.attach_evidence, name="attach_evidence"),
path(
"parents/<str:item_type>/<int:pk>",
views.parents,
Expand All @@ -55,6 +70,7 @@
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
path("strategies/", views.strategies_list, name="strategies_list"),
path("strategies/<int:pk>/", views.strategy_detail, name="strategy_detail"),
path("strategies/<int:pk>/detach", views.detach_strategy, name="detach_strategy"),
path("auth/github/", views.GithubSocialAuthView.as_view()),
path(
"users/<int:pk>/github_repositories/",
Expand Down
Loading

0 comments on commit a7862ed

Please sign in to comment.