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

Add related entities to document detail page (#1625) #1626

Merged
merged 1 commit into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 38 additions & 0 deletions geniza/corpus/templates/corpus/document_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,44 @@ <h2>{% translate 'Description' %}</h2>
<p>{{ document.description|pgp_urlize }}</p>
</section>

{# related people #}
{% if related_people.exists %}
<section class="related">
{# Translators: heading label for document related people #}
<h2>{% translate "Related People" %}</h2>
<dl>
{% regroup related_people by type as person_relations %}
{% for relation_type in person_relations %}
<dt>{{ relation_type.grouper }}</dt>
<dd>
{% for relation in relation_type.list %}
{% if relation.person.get_absolute_url %}<a data-turbo="false" href="{{ relation.person.get_absolute_url }}">{{ relation.person }}</a>{% else %}{{ relation.person }}{% endif %}{% if not forloop.last %}, {% endif %}
{% endfor %}
</dd>
{% endfor %}
</dl>
</section>
{% endif %}

{# related places #}
{% if related_places.exists %}
<section class="related">
{# Translators: heading label for document related places #}
<h2>{% translate "Related Places" %}</h2>
<dl>
{% regroup related_places.all by type as place_relations %}
{% for relation_type in place_relations %}
<dt>{{ relation_type.grouper }}</dt>
<dd>
{% for relation in relation_type.list %}
<a data-turbo="false" href="{{ relation.place.get_absolute_url }}">{{ relation.place }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</dd>
{% endfor %}
</dl>
</section>
{% endif %}

{% if document.tags.exists %}
<section>
{# Translators: label for tags on a document #}
Expand Down
77 changes: 77 additions & 0 deletions geniza/corpus/tests/test_corpus_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@
old_pgp_tabulate_data,
pgp_metadata_for_old_site,
)
from geniza.entities.models import (
DocumentPlaceRelation,
Person,
PersonDocumentRelation,
PersonDocumentRelationType,
Place,
)
from geniza.footnotes.forms import SourceChoiceForm
from geniza.footnotes.models import Creator, Footnote, Source, SourceType

Expand Down Expand Up @@ -176,6 +183,76 @@ def test_get_context_data(self, client, document, source):
assert "transcription" in response.context["default_shown"]
assert "transcription" not in response.context["disabled"]

# related people and places should be empty querysets
assert response.context["related_people"].count() == 0
assert response.context["related_places"].count() == 0

# add related people
abu = Person.objects.create(slug="abu-imran")
ezra = Person.objects.create(slug="ezra-b-hillel")
nahray = Person.objects.create(slug="nahray")
(author, _) = PersonDocumentRelationType.objects.get_or_create(name="Author")
(recipient, _) = PersonDocumentRelationType.objects.get_or_create(
name="Recipient"
)
PersonDocumentRelation.objects.create(
person=ezra, type=recipient, document=document
)
PersonDocumentRelation.objects.create(
person=abu, type=recipient, document=document
)
PersonDocumentRelation.objects.create(
person=nahray, type=author, document=document
)
response = client.get(reverse("corpus:document", args=(document.pk,)))
assert response.context["related_people"].count() == 3
# should sort alphabetically by type, then slug (name)
assert response.context["related_people"].first().person.pk == nahray.pk
assert response.context["related_people"][1].person.pk == abu.pk

# add related place
fustat = Place.objects.create(slug="fustat")
DocumentPlaceRelation.objects.create(place=fustat, document=document)
assert response.context["related_places"].count() == 1
assert response.context["related_places"].first().place.pk == fustat.pk

def test_related_entities(
self, client, document, person, person_diacritic, person_multiname
):
# add related people
person.has_page = True
person.save()
(author, _) = PersonDocumentRelationType.objects.get_or_create(name="Author")
(recipient, _) = PersonDocumentRelationType.objects.get_or_create(
name="Recipient"
)
PersonDocumentRelation.objects.create(
person=person_multiname, type=recipient, document=document
)
PersonDocumentRelation.objects.create(
person=person_diacritic, type=recipient, document=document
)
PersonDocumentRelation.objects.create(
person=person, type=author, document=document
)
# add related place
fustat = Place.objects.create(slug="fustat")
DocumentPlaceRelation.objects.create(place=fustat, document=document)

# should group "recipient" people together and join their names by comma
response = client.get(reverse("corpus:document", args=(document.pk,)))
# should be "Halfon, Zed" = recipients
print(response.content)
assertContains(response, f"{person_diacritic}, {person_multiname}", html=True)
# should link to author because has_page=True
assertContains(
response, f'<a data-turbo="false" href="{person.get_absolute_url()}">'
)
# should link to place
assertContains(
response, f'<a data-turbo="false" href="{fustat.get_absolute_url()}">'
)


@pytest.mark.django_db
def test_old_pgp_tabulate_data():
Expand Down
7 changes: 7 additions & 0 deletions geniza/corpus/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,13 @@ def get_context_data(self, **kwargs):
for panel in ["images", "translation", "transcription"]
if panel not in available_panels
],
# related entities: sorted by type for grouping, and slug for alphabetization
"related_people": self.object.persondocumentrelation_set.order_by(
"type__name", "person__slug"
),
"related_places": self.object.documentplacerelation_set.order_by(
"type__name", "place__slug"
),
}
)
return context_data
Expand Down
11 changes: 11 additions & 0 deletions sitemedia/scss/base/_typography.scss
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,17 @@ $text-size-7xl: 2.25rem; // = 36px
line-height: 1.5;
}

// small body text, e.g. for descriptions in search results
@mixin body-sm {
font-family: fonts.$primary;
font-size: $text-size-sm;
line-height: 17 / 14;
@include breakpoints.for-tablet-landscape-up {
font-size: $text-size-lg;
line-height: 25 / 18;
}
}

// label for unpublished records in scholarship records view
@mixin unpublished {
@include caption;
Expand Down
1 change: 1 addition & 0 deletions sitemedia/scss/components/_transcription.scss
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
background-color: var(--background-light);
cursor: default;
&:after,
&:before,
svg {
color: var(--disabled);
}
Expand Down
48 changes: 44 additions & 4 deletions sitemedia/scss/pages/_document.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ main.document {
@include breakpoints.for-tablet-landscape-up {
margin-left: 35px;
}
:first-child {
margin-top: spacing.$spacing-md;
> :first-child {
margin-top: spacing.$spacing-sm;
@include breakpoints.for-tablet-landscape-up {
margin-top: 0px;
}
Expand All @@ -102,9 +102,10 @@ main.document {
margin-left: 0;
}
}
// "what's in the PGP" and "description"
// "what's in the PGP", "description", "related people/places"
section.content-stats,
section.description {
section.description,
section.related {
margin: spacing.$spacing-md 0 0;
@include breakpoints.for-tablet-landscape-up {
margin: 2rem 0 0;
Expand Down Expand Up @@ -165,6 +166,7 @@ main.document {
font-size: typography.$text-size-xl;
@include breakpoints.for-tablet-landscape-up {
font-size: typography.$text-size-4xl;
padding-bottom: 2px;
}
}
li.translation-count::before {
Expand All @@ -185,6 +187,44 @@ main.document {
padding-left: 1.25rem;
}
}
// related entities
section.related {
dl {
margin: 0 0 0 1.25rem;
dt {
padding-top: 0.5rem;
@include typography.body-italic;
}
dd + dt {
padding-top: 1rem;
margin-top: 1rem;
border-top: 1px solid var(--tabs-bottom);
}
dd {
@include typography.body-sm;
}
@include breakpoints.for-tablet-landscape-up {
display: grid;
grid-template-columns: 33% 1fr;
margin: 1.25rem 0 0 1.25rem;
gap: 0.5rem 0;
dt,
dd {
padding-top: 0;
@include typography.body;
font-style: normal;
}
dd + dt,
dd + dt + dd {
margin-top: 0;
padding-top: 0.5rem;
border-top: 1px solid var(--tabs-bottom);
}
}
}
padding-bottom: 2rem;
border-bottom: 1px solid var(--disabled);
}
// link to download transcription
section.transcription-link {
margin-top: spacing.$spacing-md;
Expand Down
Loading