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

Deployment to Prod on October 9th #631

Merged
merged 22 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cd5a143
wip
RichGriff Sep 23, 2024
dd0d403
updated main docker build
RichGriff Sep 25, 2024
3961fb4
Adding comments to case elements
cptanalatriste Sep 26, 2024
a4cfccb
Merge pull request #618 from alan-turing-institute/437-user-story-imp…
cptanalatriste Sep 26, 2024
6ed8950
Merge branch 'develop' into 437-element-comments
RichGriff Sep 26, 2024
58a82a4
Update docs-publish.yaml
chrisdburr Sep 26, 2024
a7bceba
Fixing migration problems
cptanalatriste Sep 26, 2024
874235f
Merge pull request #621 from alan-turing-institute/437-user-story-imp…
cptanalatriste Sep 26, 2024
a710440
Users with view access to a case should have access to its comments
cptanalatriste Sep 27, 2024
2dd9fcb
Merge pull request #623 from alan-turing-institute/437-user-story-imp…
cptanalatriste Sep 27, 2024
a7c51df
Node commentsa added
RichGriff Sep 28, 2024
916245c
style: pre-commit fixes
pre-commit-ci[bot] Sep 28, 2024
305090a
Merge pull request #624 from alan-turing-institute/437-element-comments
RichGriff Sep 28, 2024
1933869
Using new api route for screenshots
RichGriff Sep 28, 2024
60f2c7e
style: pre-commit fixes
pre-commit-ci[bot] Sep 28, 2024
dfaf0c6
Merge pull request #625 from alan-turing-institute/new-media-storage
RichGriff Sep 28, 2024
323cb96
Enforcing a single image per assurance case
cptanalatriste Sep 30, 2024
01a8a0f
Merge pull request #626 from alan-turing-institute/474-bug-set-up-an-…
cptanalatriste Sep 30, 2024
bba3905
Reviewer comment form fix
RichGriff Sep 30, 2024
826d566
wip
RichGriff Sep 30, 2024
d9e4d25
Can only edit/delete own comments
RichGriff Oct 1, 2024
f09b173
Merge pull request #629 from alan-turing-institute/627-reviewer-comments
RichGriff Oct 1, 2024
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
1 change: 0 additions & 1 deletion .github/workflows/docs-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ on:
push:
branches:
- main
- develop
release:
types:
- published
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/frontend-docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
--build-arg NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }} \
--build-arg NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }} \
--build-arg API_URL=https://eap-backend.azurewebsites.net/ \
--build-arg NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL }} \
--build-arg NEXTAUTH_URL=${{ secrets.NEXT_PUBLIC_API_URL }} \
-t turingassuranceplatform/eap_frontend:main \
-t turingassuranceplatform/eap_frontend:${{ env.commit_date }}.${{ env.sha_short }} \
-f docker/staging/Dockerfile .
Expand Down
80 changes: 80 additions & 0 deletions eap_backend/eap_api/migrations/0024_auto_20240926_1231.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Generated by Django 3.2.8 on 2024-09-26 12:31

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


class Migration(migrations.Migration):

dependencies = [
("eap_api", "0023_assurancecaseimage"),
]

operations = [
migrations.AddField(
model_name="comment",
name="context",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="comments",
to="eap_api.context",
),
),
migrations.AddField(
model_name="comment",
name="evidence",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="comments",
to="eap_api.evidence",
),
),
migrations.AddField(
model_name="comment",
name="goal",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="comments",
to="eap_api.toplevelnormativegoal",
),
),
migrations.AddField(
model_name="comment",
name="property_claim",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="comments",
to="eap_api.propertyclaim",
),
),
migrations.AddField(
model_name="comment",
name="strategy",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="comments",
to="eap_api.strategy",
),
),
migrations.AlterField(
model_name="comment",
name="assurance_case",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="comments",
to="eap_api.assurancecase",
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.8 on 2024-09-30 09:21

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


class Migration(migrations.Migration):

dependencies = [
("eap_api", "0024_auto_20240926_1231"),
]

operations = [
migrations.AlterField(
model_name="assurancecaseimage",
name="assurance_case",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="case_image",
to="eap_api.assurancecase",
unique=True,
),
),
]
76 changes: 59 additions & 17 deletions eap_backend/eap_api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,23 +100,6 @@ def was_published_recently(self):
return self.created_date >= timezone.now() - datetime.timedelta(days=1)


class Comment(models.Model):
author = models.ForeignKey(
EAPUser, related_name="comments", on_delete=models.CASCADE
)
assurance_case = models.ForeignKey(
AssuranceCase, related_name="comments", on_delete=models.CASCADE
)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return f"Comment by {self.author} on {self.assurance_case}"

class Meta:
ordering = ["created_at"]


class TopLevelNormativeGoal(CaseItem):
keywords = models.CharField(max_length=3000)
assurance_case = models.ForeignKey(
Expand Down Expand Up @@ -254,5 +237,64 @@ class AssuranceCaseImage(models.Model):
AssuranceCase,
related_name="case_image",
on_delete=models.CASCADE,
unique=True,
)
image = models.ImageField(upload_to="images/", default=None)


class Comment(models.Model):
author = models.ForeignKey(
EAPUser, related_name="comments", on_delete=models.CASCADE
)

assurance_case = models.ForeignKey(
AssuranceCase,
related_name="comments",
on_delete=models.CASCADE,
null=True,
default=None,
)
goal = models.ForeignKey(
TopLevelNormativeGoal,
related_name="comments",
on_delete=models.CASCADE,
default=None,
null=True,
)
strategy = models.ForeignKey(
Strategy,
related_name="comments",
on_delete=models.CASCADE,
default=None,
null=True,
)
property_claim = models.ForeignKey(
PropertyClaim,
related_name="comments",
on_delete=models.CASCADE,
default=None,
null=True,
)
evidence = models.ForeignKey(
Evidence,
related_name="comments",
on_delete=models.CASCADE,
default=None,
null=True,
)
context = models.ForeignKey(
Context,
related_name="comments",
on_delete=models.CASCADE,
default=None,
null=True,
)

content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return f"Comment by {self.author} on {self.assurance_case}"

class Meta:
ordering = ["created_at"]
13 changes: 13 additions & 0 deletions eap_backend/eap_api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ class Meta:
"id",
"author",
"assurance_case",
"goal",
"strategy",
"property_claim",
"evidence",
"context",
"content",
"created_at",
)
Expand Down Expand Up @@ -391,6 +396,14 @@ class Meta:
model = AssuranceCaseImage
fields = ("id", "assurance_case_id", "image")

def create(self, validated_data: dict):
case_image, _ = AssuranceCaseImage.objects.update_or_create(
assurance_case=validated_data.get("assurance_case"),
defaults={"image": validated_data.get("image")},
)

return case_image


class StrategySerializer(serializers.ModelSerializer):
goal_id = serializers.PrimaryKeyRelatedField(
Expand Down
2 changes: 1 addition & 1 deletion eap_backend/eap_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
name="attach_property_claim",
),
path(
"cases/<int:assurance_case_id>/comments/",
"<str:element_name>/<int:element_id>/comments/",
views.comment_list,
name="comment_list",
),
Expand Down
33 changes: 31 additions & 2 deletions eap_backend/eap_api/view_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import functools
from typing import Any, Callable, Literal, Optional, Union, cast
from typing import Any, Callable, Literal, Optional, Type, Union, cast

from django.db import models
from django.db.models.query import QuerySet
from django.forms.models import model_to_dict
from django.http import JsonResponse
Expand Down Expand Up @@ -245,6 +246,34 @@ def _move_to_sandbox(
case_item.save()


class CommentUtils:
@staticmethod
def get_model_instance(
element_name: str, element_id: int
) -> CaseItem | AssuranceCase:

model_class: Type[models.Model] | None = None

if element_name == "cases":
model_class = AssuranceCase
elif element_name == "propertyclaims":
model_class = PropertyClaim
elif element_name == "goals":
model_class = TopLevelNormativeGoal
elif element_name == "strategies":
model_class = Strategy
elif element_name == "contexts":
model_class = Context
elif element_name == "evidence":
model_class = Evidence

if model_class is None:
error_message: str = f"Invalid URL {element_name}/{element_id}"
raise ValueError(error_message)

return model_class.objects.get(pk=element_id)


class UpdateIdentifierUtils:
@staticmethod
def update_identifiers(
Expand Down Expand Up @@ -773,7 +802,7 @@ def save_json_tree(data, obj_type, parent_id=None, parent_type=None):


def get_case_permissions(
case: AssuranceCase, user: EAPUser
case: AssuranceCase | int | str, user: EAPUser
) -> Literal["manage"] | Literal["edit"] | Literal["review"] | Literal["view"] | None:
"""
See if the user is allowed to view or edit the case.
Expand Down
34 changes: 22 additions & 12 deletions eap_backend/eap_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@
StrategySerializer,
TopLevelNormativeGoalSerializer,
UsernameAwareUserSerializer,
get_case_id,
)
from .view_utils import (
CommentUtils,
SandboxUtils,
ShareAssuranceCaseUtils,
SocialAuthenticationUtils,
Expand Down Expand Up @@ -252,7 +254,6 @@ def case_list(request):
@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def case_image(request: HttpRequest, pk) -> Response:
print(f"{pk=}")
if request.method == "GET":
try:
assurance_case: AssuranceCaseImage = AssuranceCaseImage.objects.get(
Expand Down Expand Up @@ -904,24 +905,32 @@ def github_repository_list(request):

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def comment_list(request, assurance_case_id):
def comment_list(request: HttpRequest, element_name: str, element_id: int):
"""
List all comments for an assurance case, or create a new comment.
List all comments for an case element, or create a new comment.
"""
permissions = get_case_permissions(assurance_case_id, request.user)
if permissions is None or permissions == "view":
return HttpResponse(status=403)
model_instance = CommentUtils.get_model_instance(element_name, element_id)
assurance_case_id: int | None = None
if isinstance(model_instance, AssuranceCase):
assurance_case_id = model_instance.pk
else:
assurance_case_id = get_case_id(model_instance)

permissions: str | None = get_case_permissions(
cast(int, assurance_case_id), cast(EAPUser, request.user)
)

if permissions is None:
return HttpResponse(status=status.HTTP_403_FORBIDDEN)

if request.method == "GET":
comments = Comment.objects.filter(assurance_case_id=assurance_case_id)
serializer = CommentSerializer(comments, many=True)
serializer = CommentSerializer(model_instance.comments, many=True)
return Response(serializer.data)

elif request.method == "POST":
if permissions not in ["manage", "edit", "review"]:
return HttpResponse(status=status.HTTP_403_FORBIDDEN)
data = request.data.copy()
data["assurance_case_id"] = (
assurance_case_id # Ensure assurance_case_id is set in the data
)
serializer = CommentSerializer(data=data)
if serializer.is_valid():
# Ensure the author is set to the current user
Expand All @@ -938,8 +947,9 @@ def comment_detail(request, pk):
"""
Retrieve, update or delete a specific comment.
"""

try:
comment = Comment.objects.get(id=pk)
comment = Comment.objects.get(id=pk, author=request.user)
except Comment.DoesNotExist:
return HttpResponse(status=404)

Expand Down
3 changes: 2 additions & 1 deletion eap_backend/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1767,7 +1767,8 @@ def _check_status_on_edit(self, status_code: int):
def _check_status_on_comment(self, status_code: int):
response_post: HttpResponse = self.client.post(
reverse(
"comment_list", kwargs={"assurance_case_id": self.assurance_case.pk}
"comment_list",
kwargs={"element_name": "cases", "element_id": self.assurance_case.pk},
),
data=json.dumps(
{"content": "A comment", "assurance_case": self.assurance_case.pk}
Expand Down
Loading
Loading