Skip to content

Commit

Permalink
Merge pull request #1398 from DDMAL/staging
Browse files Browse the repository at this point in the history
Merge staging into production, 2024 Apr 04 edition
  • Loading branch information
jacobdgm committed Apr 5, 2024
2 parents 57db703 + b151163 commit d6546ea
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 80 deletions.
1 change: 1 addition & 0 deletions django/cantusdb_project/cantusdb/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""

from django.contrib import admin
from django.urls import path
from django.urls import include
Expand Down
1 change: 1 addition & 0 deletions django/cantusdb_project/main_app/models/base_model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Defines a BaseModel to be extended by all other models"""

from typing import List
from django.db import models
from django.urls import reverse
Expand Down
1 change: 1 addition & 0 deletions django/cantusdb_project/main_app/tests/make_fakes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Functions to make fake objects to be used for testing"""

import random
from faker import Faker

Expand Down
103 changes: 87 additions & 16 deletions django/cantusdb_project/main_app/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2886,7 +2886,7 @@ def test_volpiano_signal(self):
"c_sequence": "1",
# liquescents, to be converted to lowercase
# vv v v v v v v
"volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz"
"volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz",
# ^ ^ ^ ^ ^ ^ ^^^^^^^^
# clefs, accidentals, etc., to be deleted
},
Expand All @@ -2909,6 +2909,52 @@ def test_volpiano_signal(self):
self.assertEqual(chant_2.volpiano, "abacadaeafagahaja")
self.assertEqual(chant_2.volpiano_intervals, "1-12-23-34-45-56-67-78-8")

def test_initial_values(self):
# create a chant with a known folio, feast, office, c_sequence and image_link
source: Source = make_fake_source()
folio: str = "001r"
sequence: int = 1
feast: Feast = make_fake_feast()
office: Office = make_fake_office()
image_link: str = "https://www.youtube.com/watch?v=9bZkp7q19f0"
self.client.post(
reverse("chant-create", args=[source.id]),
{
"manuscript_full_text_std_spelling": "this is a bog standard manuscript textful spelling",
"folio": folio,
"c_sequence": str(sequence),
"feast": feast.id,
"office": office.id,
"image_link": image_link,
},
)

# when we request the Chant Create page, the same folio, feast, office and image_link should
# be preselected, and c_sequence should be incremented by 1.
response = self.client.get(
reverse("chant-create", args=[source.id]),
)

observed_initial_folio: int = response.context["form"].initial["folio"]
with self.subTest(subtest="test initial value of folio field"):
self.assertEqual(observed_initial_folio, folio)

observed_initial_feast: int = response.context["form"].initial["feast"]
with self.subTest(subtest="test initial value of feast feild"):
self.assertEqual(observed_initial_feast, feast.id)

observed_initial_office: int = response.context["form"].initial["office"]
with self.subTest(subtest="test initial value of office field"):
self.assertEqual(observed_initial_office, office.id)

observed_initial_sequence: int = response.context["form"].initial["c_sequence"]
with self.subTest(subtest="test initial value of c_sequence field"):
self.assertEqual(observed_initial_sequence, sequence + 1)

observed_initial_image: int = response.context["form"].initial["image_link"]
with self.subTest(subtest="test initial value of image_link field"):
self.assertEqual(observed_initial_image, image_link)


class ChantDeleteViewTest(TestCase):
@classmethod
Expand Down Expand Up @@ -3033,7 +3079,7 @@ def test_volpiano_signal(self):
"c_sequence": "1",
# liquescents, to be converted to lowercase
# vv v v v v v v
"volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz"
"volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz",
# ^ ^ ^ ^ ^ ^ ^^^^^^^^
# clefs, accidentals, etc., to be deleted
},
Expand Down Expand Up @@ -4510,7 +4556,9 @@ def test_dd_column(self):
response = self.client.get(reverse("source-inventory", args=[source.id]))
html: str = str(response.content)
self.assertIn(diff_id, html)
expected_html_substring: str = f'<a href="https://differentiaedatabase.ca/differentia/{diff_id}" target="_blank">'
expected_html_substring: str = (
f'<a href="https://differentiaedatabase.ca/differentia/{diff_id}" target="_blank">'
)
self.assertIn(expected_html_substring, html)

def test_redirect_with_source_parameter(self):
Expand Down Expand Up @@ -5180,9 +5228,16 @@ def test_url(self):

def test_content(self):
NUM_CHANTS = 5
source = make_fake_source(published=True)
source_siglum = "SourceSiglum"
chant_siglum = "ChantSiglum" # OldCantus chants/sequences had a "siglum"
# field, which would sometimes get out of date when the chant's source's siglum
# was updated. We keep the chant siglum field around to ensure no data is
# inadvertently lost, but we need to ensure it is never displayed publicly.
source = make_fake_source(published=True, siglum=source_siglum)
for _ in range(NUM_CHANTS):
make_fake_chant(source=source)
chant = make_fake_chant(source=source)
chant.siglum = chant_siglum
chant.save()
response = self.client.get(reverse("csv-export", args=[source.id]))
content = response.content.decode("utf-8")
split_content = list(csv.reader(content.splitlines(), delimiter=","))
Expand Down Expand Up @@ -5213,11 +5268,21 @@ def test_content(self):
"node_id",
]
for t in expected_column_titles:
self.assertIn(t, header)

self.assertEqual(len(rows), NUM_CHANTS)
for row in rows:
self.assertEqual(len(header), len(row))
with self.subTest(expected_column=t):
self.assertIn(t, header)
with self.subTest(subtest="ensure a row exists for each chant"):
self.assertEqual(len(rows), NUM_CHANTS)
with self.subTest(
subtest="ensure all rows have the same number of columns as the header"
):
for row in rows:
self.assertEqual(len(header), len(row))
with self.subTest(
"ensure we only ever display chants' sources' sigla, and never the "
"value stored in chants' siglum fields"
):
for row in rows:
self.assertEqual(row[0], source_siglum)

def test_published_vs_unpublished(self):
published_source = make_fake_source(published=True)
Expand All @@ -5243,12 +5308,18 @@ def test_csv_export_on_source_with_sequences(self):
split_content = list(csv.reader(content.splitlines(), delimiter=","))
header, rows = split_content[0], split_content[1:]

self.assertEqual(len(rows), NUM_SEQUENCES)
for row in rows:
self.assertEqual(len(header), len(row))
self.assertNotEqual(
row[3], ""
) # ensure that the .s_sequence field is being written to the "sequence" column
with self.subTest(subtest="ensure a row exists for each sequence"):
self.assertEqual(len(rows), NUM_SEQUENCES)
with self.subTest(
subtest="ensure all rows have the same number of columns as the header"
):
for row in rows:
self.assertEqual(len(header), len(row))
with self.subTest(
subtest="ensure .s_sequence field is being written to the 'sequence' column"
):
for row in rows:
self.assertNotEqual(row[3], "")


class ChangePasswordViewTest(TestCase):
Expand Down
5 changes: 0 additions & 5 deletions django/cantusdb_project/main_app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,11 +391,6 @@
views.csv_export_redirect_from_old_path,
name="csv-export-old-path",
),
path(
"ajax/concordance/<str:cantus_id>",
views.ajax_concordance_list,
name="ajax-concordance",
),
# content overview (for project managers)
path(
"content-overview/",
Expand Down
2 changes: 2 additions & 0 deletions django/cantusdb_project/main_app/views/chant.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,13 +805,15 @@ def get_initial(self):
}
latest_folio = latest_chant.folio if latest_chant.folio else "001r"
latest_feast = latest_chant.feast.id if latest_chant.feast else ""
latest_office = latest_chant.office.id if latest_chant.office else ""
latest_seq = (
latest_chant.c_sequence if latest_chant.c_sequence is not None else 0
)
latest_image = latest_chant.image_link if latest_chant.image_link else ""
return {
"folio": latest_folio,
"feast": latest_feast,
"office": latest_office,
"c_sequence": latest_seq + 1,
"image_link": latest_image,
}
Expand Down
58 changes: 2 additions & 56 deletions django/cantusdb_project/main_app/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,61 +69,6 @@ def items_count(request):
return render(request, "items_count.html", context)


def ajax_concordance_list(request, cantus_id):
"""
Function-based view responding to the AJAX call for concordance list on the chant detail page,
accessed with ``chants/<int:pk>``, click on "Display concordances of this chant"
Args:
cantus_id (str): The Cantus ID of the requested concordances group
Returns:
JsonResponse: A response to the AJAX call, to be unpacked by the frontend js code
"""
chants = Chant.objects.filter(cantus_id=cantus_id)
seqs = Sequence.objects.filter(cantus_id=cantus_id)

display_unpublished = request.user.is_authenticated
if not display_unpublished:
chants = chants.filter(source__published=True)
seqs = seqs.filter(source__published=True)

if seqs:
chants = chants.union(seqs).order_by("siglum", "folio")
else:
chants = chants.order_by("siglum", "folio")
# queryset(list of dictionaries)
concordance_values = chants.values(
"siglum",
"folio",
"incipit",
"office__name",
"genre__name",
"position",
"feast__name",
"mode",
"image_link",
)

concordances = list(concordance_values)
for i, concordance in enumerate(concordances):
# some chants do not have a source
# for those chants, do not return source link
if chants[i].source:
concordance["source_link"] = chants[i].source.get_absolute_url()
if chants[i].search_vector:
concordance["chant_link"] = chants[i].get_absolute_url()
else:
concordance["chant_link"] = reverse("sequence-detail", args=[chants[i].id])
concordance["db"] = "CD"

concordance_count = len(concordances)
return JsonResponse(
{"concordances": concordances, "concordance_count": concordance_count},
safe=True,
)


def ajax_melody_list(request, cantus_id) -> JsonResponse:
"""
Function-based view responding to the AJAX call for melody list on the chant detail page,
Expand Down Expand Up @@ -235,6 +180,7 @@ def csv_export(request, source_id):
"node_id",
]
)
siglum = source.siglum
for entry in entries:
feast = entry.feast.name if entry.feast else ""
office = entry.office.name if entry.office else ""
Expand All @@ -243,7 +189,7 @@ def csv_export(request, source_id):

writer.writerow(
[
entry.siglum,
siglum,
entry.marginalia,
entry.folio,
# if entry has a c_sequence, it's a Chant. If it doesn't, it's a Sequence, so write its s_sequence
Expand Down
6 changes: 3 additions & 3 deletions django/cantusdb_project/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ asgiref==3.6.0
astroid==2.4.2
attrs==19.3.0
backports.zoneinfo==0.2.1
black==21.10b0
black==24.3.0
certifi==2023.7.22
chardet==3.0.4
charset-normalizer==2.0.12
click==7.1.2
click==8.0.0
coverage==5.3.1
Django==4.2.11
django-autocomplete-light==3.9.4
Expand Down Expand Up @@ -36,7 +36,7 @@ sqlparse==0.4.4
text-unidecode==1.3
toml==0.10.1
typed-ast==1.4.1
typing-extensions==3.10.0.0
typing-extensions==4.0.1
ujson==5.9.0
urllib3==1.26.18
volpiano-display-utilities @ git+https://github.com/DDMAL/[email protected]
Expand Down
1 change: 1 addition & 0 deletions scripts/parse_link_checker_output.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Modules"""

import json
import sys
from pathlib import Path
Expand Down

0 comments on commit d6546ea

Please sign in to comment.