Skip to content

Commit

Permalink
Merge pull request #456 from UCL/feature/duplicating-testimonia
Browse files Browse the repository at this point in the history
Feature/duplicating testimonia
  • Loading branch information
acholyn authored Jun 18, 2024
2 parents aca247a + 791ceb9 commit 143be32
Show file tree
Hide file tree
Showing 16 changed files with 417 additions and 181 deletions.
38 changes: 38 additions & 0 deletions src/rard/research/migrations/0072_adding_testimonia_duplicates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("research", "0071_add_duplicates_field"),
]

operations = [
migrations.AddField(
model_name="anonymousfragment",
name="duplicate_ts",
field=models.ManyToManyField(
blank=True,
related_name="anonymousfragment_duplicate_ts",
to="research.Testimonium",
),
),
migrations.AddField(
model_name="fragment",
name="duplicate_ts",
field=models.ManyToManyField(
blank=True,
related_name="fragment_duplicate_ts",
to="research.Testimonium",
),
),
migrations.AddField(
model_name="testimonium",
name="duplicate_ts",
field=models.ManyToManyField(
blank=True,
related_name="testimonium_duplicate_ts",
to="research.Testimonium",
),
),
]
18 changes: 9 additions & 9 deletions src/rard/research/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,19 +571,19 @@ def mentioned_in_list(self):
related_name="%(class)s_duplicate_anonfragments",
)

# if converted to a testimonium, we want to transfer duplicates
duplicate_ts = models.ManyToManyField(
"Testimonium",
blank=True,
related_name="%(class)s_duplicate_ts",
)

@property
def duplicates_list(self):
duplicates = list(self.duplicate_frags.all())
duplicates.extend(self.duplicate_afs.all())
if hasattr(self, "fragment_duplicate_fragments"):
duplicates.extend(self.fragment_duplicate_fragments.all())
if hasattr(self, "anonymousfragment_duplicate_fragments"):
duplicates.extend(self.anonymousfragment_duplicate_fragments.all())
if hasattr(self, "fragment_duplicate_anonfragments"):
duplicates.extend(self.fragment_duplicate_anonfragments.all())
if hasattr(self, "anonymousfragment_duplicate_anonfragments"):
duplicates.extend(self.anonymousfragment_duplicate_anonfragments.all())
return list(set(duplicates))
duplicates.extend(self.duplicate_ts.all())
return duplicates

def __str__(self):
return self.get_display_name()
Expand Down
134 changes: 133 additions & 1 deletion src/rard/research/tests/views/test_testimonium.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import pytest
from django.core.exceptions import ObjectDoesNotExist
from django.test import RequestFactory, TestCase
from django.urls import reverse

from rard.research.models import CitingWork, Testimonium
from rard.research.models import (
ApparatusCriticusItem,
CitingWork,
Concordance,
Fragment,
OriginalText,
Reference,
Testimonium,
TextObjectField,
Translation,
)
from rard.research.views import (
TestimoniumCreateView,
TestimoniumDeleteView,
TestimoniumDetailView,
TestimoniumListView,
TestimoniumUpdateView,
duplicate_fragment,
)
from rard.users.tests.factories import UserFactory
from rard.utils.convertors import (
convert_testimonium_to_unlinked_fragment,
convert_unlinked_fragment_to_testimonium,
)

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -107,3 +123,119 @@ def test_permissions(self):
self.assertIn(
"research.view_testimonium", TestimoniumDetailView.permission_required
)


class TestTestimoniumDuplicationView(TestCase):
def setUp(self):
self.cw = CitingWork.objects.create(title="citing work title")
self.tes = Testimonium.objects.create(name="test testimonium")
self.ot = OriginalText.objects.create(
owner=self.tes,
content="Original Text test",
citing_work=self.cw,
reference_order="reference order",
)
self.con = Concordance.objects.create(
original_text=self.ot, source="tester", identifier="123"
)
self.apc = ApparatusCriticusItem.objects.create(
parent=self.ot, content="critical test", object_id=23
)
self.tr = Translation.objects.create(
translated_text="translation of text", original_text=self.ot
)
self.ref = Reference.objects.create(editor="test", original_text=self.ot)

def compare_model_objects(self, original, duplicate):
for field in original._meta.fields:
if field.name in [
"id",
"created",
"modified",
"commentary",
"plain_commentary",
"object_id",
"original_text",
"order",
"model",
]:
continue
if field.is_relation and getattr(original, field.name):
# If the field is a relation, compare the related objects
related_original = getattr(original, field.name)
related_duplicate = getattr(duplicate, field.name)
self.compare_model_objects(related_original, related_duplicate)
else:
# For regular fields or null relations, compare their values
value1 = getattr(original, field.name)
value2 = getattr(duplicate, field.name)
self.assertEqual(value1, value2)

def test_testimonium_duplication(self):
url = reverse(
"testimonium:duplicate",
kwargs={"pk": self.tes.pk, "model_name": "testimonium"},
)
request = RequestFactory().get(url)
request.user = UserFactory.create()
response = duplicate_fragment(request, pk=self.tes.pk, model_name="testimonium")

duplicate_pk = response.url.split("/")[-2]
duplicate_frag = Fragment.objects.get(pk=duplicate_pk)
duplicate_ot = duplicate_frag.original_texts.first()
duplicate_ref = duplicate_ot.references.first()
duplicate_apc = duplicate_ot.apparatus_criticus_items.first()
duplicate_con = duplicate_ot.concordances.first()
duplicate_tr = Translation.objects.filter(original_text=duplicate_ot).first()

self.compare_model_objects(self.tes, duplicate_frag)
self.compare_model_objects(self.ot, duplicate_ot)
self.compare_model_objects(self.ref, duplicate_ref)
self.compare_model_objects(self.apc, duplicate_apc)
self.compare_model_objects(self.con, duplicate_con)
self.compare_model_objects(self.tr, duplicate_tr)
self.assertIn(duplicate_frag, self.tes.duplicates_list)
self.assertIn(self.tes, duplicate_frag.duplicates_list)


class TestTestimoniumConversionViews(TestCase):
def setUp(self):
self.unlinked_fragment = self.create_fragment(Fragment, "ufr")
self.testimonium = self.create_fragment(Testimonium, "tes")

def create_fragment(self, model, name):
fragment = model.objects.create(name=name)
citing_work = CitingWork.objects.create(title="title")
OriginalText.objects.create(
owner=fragment, citing_work=citing_work, content="sic semper tyrannis"
)
fragment.date_range = "509 BCE - 31 BCE"
fragment.order_year = -509
fragment.collection_id = 1
fragment.commentary = TextObjectField.objects.create(content="hello")
fragment.save()
return fragment

def test_convert_unlinked_fragment_to_testimonium(self):
source_pk = self.unlinked_fragment.pk
new_testimonium = convert_unlinked_fragment_to_testimonium(
self.unlinked_fragment
)
with self.assertRaises(ObjectDoesNotExist):
Fragment.objects.get(pk=source_pk)
self.assertEqual(
new_testimonium.original_texts.first().content, "sic semper tyrannis"
)
self.assertEqual(new_testimonium.date_range, "509 BCE - 31 BCE")
self.assertEqual(new_testimonium.commentary.content, "hello")

def test_convert_testimonium_to_unlinked_fragment(self):
source_pk = self.testimonium.pk
new_fragment = convert_testimonium_to_unlinked_fragment(self.testimonium)
with self.assertRaises(ObjectDoesNotExist):
Testimonium.objects.get(pk=source_pk)
self.assertEqual(
new_fragment.original_texts.first().content, "sic semper tyrannis"
)
self.assertEqual(new_fragment.date_range, "509 BCE - 31 BCE")
self.assertEqual(new_fragment.commentary.content, "hello")
16 changes: 16 additions & 0 deletions src/rard/research/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@
views.UnlinkedFragmentConvertToAnonymousView.as_view(),
name="convert_to_anonymous",
),
path(
"<pk>/convert-to-testimonium/",
views.UnlinkedFragmentConvertToTestimoniumView.as_view(),
name="convert_to_testimonium",
),
path(
"<pk>/duplicate",
views.duplicate_fragment,
Expand Down Expand Up @@ -482,6 +487,17 @@
views.RemoveTestimoniumLinkView.as_view(),
name="remove_testimonium_link",
),
path(
"<pk>/convert-to-fragment/",
views.TestimoniumConvertToUnlinkedFragmentView.as_view(),
name="convert_to_fragment",
),
path(
"<pk>/duplicate",
views.duplicate_fragment,
{"model_name": "testimonium"},
name="duplicate",
),
path(
"<pk>/delete/",
views.TestimoniumDeleteView.as_view(),
Expand Down
4 changes: 4 additions & 0 deletions src/rard/research/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
RemoveAppositumLinkView,
RemoveFragmentLinkView,
UnlinkedFragmentConvertToAnonymousView,
UnlinkedFragmentConvertToTestimoniumView,
UnlinkedFragmentListView,
duplicate_fragment,
fetch_fragments,
Expand All @@ -104,6 +105,7 @@
RemoveTestimoniumLinkView,
TestimoniumAddWorkLinkView,
TestimoniumCommentaryView,
TestimoniumConvertToUnlinkedFragmentView,
TestimoniumCreateView,
TestimoniumDeleteView,
TestimoniumDetailView,
Expand Down Expand Up @@ -216,6 +218,7 @@
"FragmentCreateView",
"FragmentDeleteView",
"UnlinkedFragmentConvertToAnonymousView",
"UnlinkedFragmentConvertToTestimoniumView",
"FragmentDetailView",
"FragmentListView",
"FragmentOriginalTextCreateView",
Expand Down Expand Up @@ -251,6 +254,7 @@
"TestimoniumPublicCommentaryView",
"TextObjectFieldCommentListView",
"TestimoniumOriginalTextCreateView",
"TestimoniumConvertToUnlinkedFragmentView",
"TopicCreateView",
"TopicDeleteView",
"TopicDetailView",
Expand Down
37 changes: 29 additions & 8 deletions src/rard/research/views/fragment.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
Fragment,
OriginalText,
Reference,
Testimonium,
Topic,
Translation,
Work,
Expand All @@ -61,6 +62,8 @@
from rard.utils.convertors import (
convert_anonymous_fragment_to_fragment,
convert_unlinked_fragment_to_anonymous_fragment,
convert_unlinked_fragment_to_testimonium,
transfer_duplicates,
)
from rard.utils.shared_functions import reassign_to_unknown

Expand Down Expand Up @@ -885,6 +888,21 @@ def post(self, request, *args, **kwargs):
return HttpResponseBadRequest()


@method_decorator(require_POST, name="dispatch")
class UnlinkedFragmentConvertToTestimoniumView(AnonymousFragmentConvertToFragmentView):
model = Fragment
permission_required = "research.change_fragment"

def post(self, request, *args, **kwargs):
fragment = self.get_object()
if fragment.is_unlinked:
testimonium = convert_unlinked_fragment_to_testimonium(self.get_object())
success_url = reverse("testimonium:detail", kwargs={"pk": testimonium.pk})
return HttpResponseRedirect(success_url)
else:
return HttpResponseBadRequest()


class FragmentUpdateAntiquariansView(FragmentUpdateView):
model = Fragment
form_class = FragmentAntiquariansForm
Expand Down Expand Up @@ -1175,6 +1193,9 @@ def duplicate_fragment(request, pk, model_name):

elif model_name == "fragment":
original_fragment = get_object_or_404(Fragment, pk=pk)

elif model_name == "testimonium":
original_fragment = get_object_or_404(Testimonium, pk=pk)
else:
raise BadRequest("model name not recognised")

Expand Down Expand Up @@ -1298,19 +1319,19 @@ def duplicate_fragment(request, pk, model_name):
new_fragment.original_texts.set(new_original_texts)

# Duplicate relationships to topics
new_fragment.topics.set(original_fragment.topics.all())

# add duplication relationships
if original_fragment.duplicates_list:
for frag in original_fragment.duplicate_frags.all():
new_fragment.duplicate_frags.add(frag)
for af in original_fragment.duplicate_afs.all():
new_fragment.duplicate_afs.add(af)
if not model_name == "testimonium":
new_fragment.topics.set(original_fragment.topics.all())

# Add original fragment's duplication relationships to new fragment
transfer_duplicates(original_fragment, new_fragment)
# Make new a duplicate of original
original_fragment.duplicate_frags.add(new_fragment)
# Make original a duplicate of new
if model_name == "fragment":
new_fragment.duplicate_frags.add(original_fragment)
elif model_name == "anonymousfragment":
new_fragment.duplicate_afs.add(original_fragment)
elif model_name == "testimonium":
new_fragment.duplicate_ts.add(original_fragment)

return redirect("fragment:detail", pk=new_fragment.pk)
17 changes: 16 additions & 1 deletion src/rard/research/views/testimonium.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@
)
from rard.research.models import Antiquarian, Testimonium
from rard.research.models.base import TestimoniumLink
from rard.research.views.fragment import HistoricalBaseCreateView
from rard.research.views.fragment import (
AnonymousFragmentConvertToFragmentView,
HistoricalBaseCreateView,
)
from rard.research.views.mixins import (
CanLockMixin,
CheckLockMixin,
GetWorkLinkRequestDataMixin,
TextObjectFieldUpdateMixin,
TextObjectFieldViewMixin,
)
from rard.utils.convertors import convert_testimonium_to_unlinked_fragment
from rard.utils.shared_functions import reassign_to_unknown


Expand Down Expand Up @@ -336,3 +340,14 @@ def delete(self, request, *args, **kwargs):
self.object.delete()

return HttpResponseRedirect(success_url)


@method_decorator(require_POST, name="dispatch")
class TestimoniumConvertToUnlinkedFragmentView(AnonymousFragmentConvertToFragmentView):
model = Testimonium
permission_required = "research.change_fragment"

def post(self, request, *args, **kwargs):
fragment = convert_testimonium_to_unlinked_fragment(self.get_object())
success_url = reverse("fragment:detail", kwargs={"pk": fragment.pk})
return HttpResponseRedirect(success_url)
Loading

0 comments on commit 143be32

Please sign in to comment.