diff --git a/django/cantusdb_project/main_app/identifiers.py b/django/cantusdb_project/main_app/identifiers.py
index 4ab27e298..b74bd2798 100644
--- a/django/cantusdb_project/main_app/identifiers.py
+++ b/django/cantusdb_project/main_app/identifiers.py
@@ -5,6 +5,7 @@ class ExternalIdentifiers:
GND = 4
BNF = 5
LC = 6
+ DIAMM = 7
IDENTIFIER_TYPES = (
@@ -14,6 +15,7 @@ class ExternalIdentifiers:
(ExternalIdentifiers.GND, "GND (Gemeinsame Normdatei)"),
(ExternalIdentifiers.BNF, "Bibliothèque national de France"),
(ExternalIdentifiers.LC, "Library of Congress"),
+ (ExternalIdentifiers.DIAMM, "Digital Image Archive of Medieval Music"),
)
TYPE_PREFIX = {
@@ -23,4 +25,5 @@ class ExternalIdentifiers:
ExternalIdentifiers.GND: ("dnb", "https://d-nb.info/gnd/"),
ExternalIdentifiers.BNF: ("bnf", "https://catalogue.bnf.fr/ark:/12148/cb"),
ExternalIdentifiers.LC: ("lc", "https://id.loc.gov/authorities/"),
+ ExternalIdentifiers.DIAMM: ("diamm", "https://www.diamm.ac.uk/"),
}
diff --git a/django/cantusdb_project/main_app/models/institution.py b/django/cantusdb_project/main_app/models/institution.py
index 28a6f7ea6..350ad7fbb 100644
--- a/django/cantusdb_project/main_app/models/institution.py
+++ b/django/cantusdb_project/main_app/models/institution.py
@@ -11,6 +11,7 @@
class Institution(BaseModel):
class Meta:
+ ordering = ["country", "city", "name"]
constraints = [
CheckConstraint(
check=~(Q(is_private_collector=True) & Q(siglum__isnull=False)),
diff --git a/django/cantusdb_project/main_app/templates/institution_detail.html b/django/cantusdb_project/main_app/templates/institution_detail.html
new file mode 100644
index 000000000..c862ae679
--- /dev/null
+++ b/django/cantusdb_project/main_app/templates/institution_detail.html
@@ -0,0 +1,71 @@
+{% extends "base.html" %}
+
+{% block title %}
+
+{% endblock %}
+
+{% block content %}
+
+
+ {% include "global_search_bar.html" %}
+
+
{{ institution.name }} {% if institution.siglum %}({{ institution.siglum }}){% endif %}
+
{{ institution.city }}, {{ institution.country }}
+ {% if institution_authorities %}
+
+
+
+ {% for authority in institution_authorities %}
+ View this institution in
{{ authority.0 }}
+ {% endfor %}
+
+
+ {% endif %}
+
+
+ {% if num_cantus_sources > 0 %}
+
+
Cantus Database
+
+
+
+ Shelfmark
+
+
+
+ {% for source in cantus_sources %}
+
+
+ {{ source.shelfmark }}
+
+
+ {% endfor %}
+
+
+
+ {% endif %}
+
+ {% if num_bower_sources > 0 %}
+
+
Bower Sequence Database
+
+
+ {% endif %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/django/cantusdb_project/main_app/templates/institution_list.html b/django/cantusdb_project/main_app/templates/institution_list.html
new file mode 100644
index 000000000..0bc32c6f3
--- /dev/null
+++ b/django/cantusdb_project/main_app/templates/institution_list.html
@@ -0,0 +1,44 @@
+{% extends "base.html" %}
+
+{% block title %}
+
+{% endblock %}
+
+{% block content %}
+
+
+ {% include "global_search_bar.html" %}
+
+
Institutions
+
Displaying {{ page_obj.start_index }}-{{ page_obj.end_index }} of {{ page_obj.paginator.count }}
+
+ {% include "pagination.html" %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/django/cantusdb_project/main_app/templates/source_detail.html b/django/cantusdb_project/main_app/templates/source_detail.html
index 1340829c0..357f5dc46 100644
--- a/django/cantusdb_project/main_app/templates/source_detail.html
+++ b/django/cantusdb_project/main_app/templates/source_detail.html
@@ -37,6 +37,10 @@ {{ source.heading }}
{% if source.holding_institution %}
Siglum
{{ source.short_heading }}
+ Holding Institution
+
+ {{ source.holding_institution }}
+
{% endif %}
{% if source.summary %}
diff --git a/django/cantusdb_project/main_app/urls.py b/django/cantusdb_project/main_app/urls.py
index 4769faade..2d8959982 100644
--- a/django/cantusdb_project/main_app/urls.py
+++ b/django/cantusdb_project/main_app/urls.py
@@ -22,6 +22,7 @@
articles_list_export,
flatpages_list_export,
)
+from main_app.views.institution import InstitutionListView, InstitutionDetailView
from main_app.views.redirect import (
redirect_chants,
redirect_genre,
@@ -256,6 +257,17 @@
IndexerListView.as_view(),
name="indexer-list",
),
+ # institution
+ path(
+ "institutions/",
+ InstitutionListView.as_view(),
+ name="institution-list",
+ ),
+ path(
+ "institution/",
+ InstitutionDetailView.as_view(),
+ name="institution-detail",
+ ),
# notation
path(
"notation/",
diff --git a/django/cantusdb_project/main_app/views/institution.py b/django/cantusdb_project/main_app/views/institution.py
new file mode 100644
index 000000000..d9e0fe45e
--- /dev/null
+++ b/django/cantusdb_project/main_app/views/institution.py
@@ -0,0 +1,77 @@
+from django.db.models import Count, Subquery, OuterRef, Aggregate, F, Q, Func
+from django.views.generic import DetailView, ListView
+
+from main_app.identifiers import IDENTIFIER_TYPES, TYPE_PREFIX
+from main_app.models import Institution, Source, Segment, InstitutionIdentifier
+
+
+class InstitutionListView(ListView):
+ model = Institution
+ context_object_name = "institutions"
+ paginate_by = 100
+ template_name = "institution_list.html"
+
+ def get_queryset(self):
+ display_unpublished: bool = self.request.user.is_authenticated
+
+ # uses a subquery to get a count of the sources, filtering by published
+ # sources only it the user is not logged in.
+ qargs = {"holding_institution": OuterRef("pk")}
+ if display_unpublished is False:
+ qargs["published"] = True
+
+ sources = (
+ Source.objects.filter(**qargs)
+ .annotate(c=Func(F("id"), function="COUNT"))
+ .values("c")
+ )
+
+ # Only display institution records if they have sources in them that the user
+ # can access.
+ qset = Institution.objects.annotate(num_sources=Subquery(sources)).filter(
+ num_sources__gt=0
+ )
+ return qset
+
+
+class InstitutionDetailView(DetailView):
+ model = Institution
+ context_object_name = "institution"
+ template_name = "institution_detail.html"
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ institution = self.get_object()
+
+ # Show the Cantus and Bower sources in separate tables, and pre-format
+ # the external authority links.
+ cantus_segment = Segment.objects.get(id=4063)
+ bower_segment = Segment.objects.get(id=4064)
+ cantus_sources = Source.objects.filter(
+ holding_institution=institution, segment=cantus_segment
+ ).select_related("holding_institution")
+ bower_sources = Source.objects.filter(
+ holding_institution=institution, segment=bower_segment
+ ).select_related("holding_institution")
+ institution_authorities = InstitutionIdentifier.objects.filter(
+ institution=institution
+ )
+
+ display_unpublished = self.request.user.is_authenticated
+ if display_unpublished is False:
+ cantus_sources = cantus_sources.filter(published=True)
+ bower_sources = bower_sources.filter(published=True)
+
+ formatted_authorities = []
+ for authority in institution_authorities:
+ formatted_authorities.append(
+ (authority.identifier_label, authority.identifier_url)
+ )
+
+ context["cantus_sources"] = cantus_sources
+ context["num_cantus_sources"] = cantus_sources.count()
+ context["bower_sources"] = bower_sources
+ context["num_bower_sources"] = bower_sources.count()
+ context["institution_authorities"] = formatted_authorities
+
+ return context