Skip to content

Commit

Permalink
Handle unpublished / Goitein in citations (#1502)
Browse files Browse the repository at this point in the history
  • Loading branch information
blms committed Feb 10, 2025
1 parent b48c97b commit 3fbb144
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 83 deletions.
6 changes: 6 additions & 0 deletions geniza/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ def simplify_quotes(text):
return text.translate(smart_quote_conversion)


def list_to_string(lst):
"""Helper function to join a list with commas, and 'and' before the final
entry."""
return " and ".join([", ".join(lst[:-1]), lst[-1]] if len(lst) > 2 else lst)


## adapted from tabular_export.admin
class Echo(object):
# See https://docs.djangoproject.com/en/1.8/howto/outputting-csv/#streaming-csv-files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ <h2>{{ image_info.shelfmark }} {{ image_info.label }}</h2>
{% else %}
{% if forloop.first %}
<span data-transcription-target="transcriptionFullLabel" class="current-transcription{% if document.digital_editions.count > 1 %} multiple{% endif %}">
{{ document.digital_editions.0.source.formatted_display|safe }}
{{ document.digital_editions.0.display|safe }}
</span>
{% endif %}
{% if not image_info.excluded %}
Expand All @@ -276,7 +276,7 @@ <h2>{{ image_info.shelfmark }} {{ image_info.label }}</h2>
{% for edition in document.digital_editions.all %}
<div class="transcription ed-{{ edition.pk }}"
data-ittpanel-target="transcription"
data-label="{{ edition.source.formatted_display }}"
data-label="{{ edition.display }}"
lang="{{ document.primary_lang_code|default:"" }}"
dir="rtl"
{% if document.primary_script %}
Expand Down Expand Up @@ -305,7 +305,7 @@ <h2>{{ image_info.shelfmark }} {{ image_info.label }}</h2>
{% else %}
{% if forloop.first %}
<span data-transcription-target="translationFullLabel" class="current-translation{% if document.digital_editions.count > 1 %} multiple{% endif %}">
{{ document.default_translation.source.formatted_display|safe }}
{{ document.default_translation.display|safe }}
</span>
{% endif %}
{% if not image_info.excluded %}
Expand All @@ -314,7 +314,7 @@ <h2>{{ image_info.shelfmark }} {{ image_info.label }}</h2>
{% for translation in document.digital_translations.all %}
<div class="translation tr-{{ translation.pk }}"
data-ittpanel-target="translation"
data-label="{{ translation.source.formatted_display }}"
data-label="{{ translation.display }}"
{% if translation.source.languages.exists %}lang="{{ translation.source.languages.all.0.code }}" dir="{{ translation.source.languages.all.0.direction }}"{% else %}dir="ltr"{% endif %}
>
{% for html_section in translation.content_html|dict_item:canvas_uri %}
Expand Down
54 changes: 3 additions & 51 deletions geniza/corpus/templatetags/corpus_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,56 +201,8 @@ def is_index_cards(source):
)


def list_to_string(lst):
"""Helper function to join a list with commas, and 'and' before the final
entry."""
return " and ".join([", ".join(lst[:-1]), lst[-1]] if len(lst) > 2 else lst)


@register.filter
def process_citation(source):
"""For scholarship records list: handle Goitein index cards by including
URLs and index card numbers when available, and by adding the attribution
to the PGL."""
citation = source.grouper.formatted_display(format_index_cards=True)
if "unpublished index cards" in citation:
all_cards = []
# add card numbers
for fn in source.list:
# remove "Card" or "card" from the location field first, we just want #NNN
loc = re.sub("[Cc]ard ", "", fn.location)
card_str = ""
# add URL if present
if loc and fn.url:
card_str = f'<a href="{fn.url}">{loc}</a>'
elif loc:
card_str = str(loc)
if card_str:
all_cards.append(card_str)
if all_cards:
# if card #s are present, include them as a list in the citation
joined = list_to_string(all_cards)
citation = citation[0:-1] + f", {joined}."
# add PGL attribution
citation += " Princeton Geniza Lab, Princeton University."
elif source.grouper.source_type.type == "Unpublished":
relations = [r for rs in [fn.doc_relation for fn in source.list] for r in rs]
if (
Footnote.DIGITAL_EDITION in relations
or Footnote.DIGITAL_TRANSLATION in relations
):
authors = [
c.creator.firstname_lastname()
for c in source.grouper.authorship_set.order_by("creator__last_name")
]
citation = list_to_string(authors)
relation_display = list_to_string(
[fn.get_doc_relation_display() for fn in source.list]
)
citation += f"'s {relation_display.lower()}"
if source.grouper.year:
citation += f" ({source.grouper.year})"
citation += ", available online through the Princeton Geniza Project at "
permalink = source.list[0].content_object.permalink
citation += f'<a href="{permalink}">{permalink}</a>.'
return mark_safe(citation)
"""For scholarship records list: handle grouped citations by passing to
Footnote.display_multiple class method."""
return mark_safe(Footnote.display_multiple(source.list))
2 changes: 1 addition & 1 deletion geniza/corpus/tests/test_corpus_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ def test_old_pgp_edition():
doc.footnotes.add(fn)

edition_str = old_pgp_edition(doc.editions())
assert edition_str == f"Ed. {fn.display()}"
assert edition_str == f"Ed. {fn.display(old_pgp=True)}"

source2 = Source.objects.create(title_en="Arabic dictionary", source_type=book)
fn2 = Footnote.objects.create(
Expand Down
2 changes: 1 addition & 1 deletion geniza/corpus/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ def old_pgp_edition(editions):
"%s%s%s"
% (
"and trans. " if Footnote.TRANSLATION in fn.doc_relation else "",
fn.display().strip("."),
fn.display(old_pgp=True).strip("."),
" %s" % fn.url if fn.url else "",
)
for fn in editions
Expand Down
20 changes: 18 additions & 2 deletions geniza/footnotes/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,27 @@ def unpublished_editions(db):
@pytest.fixture
def index_cards(db):
# fixture for unpublished index cards
unpub = SourceType.objects.get(type="Unpublished")
(unpub, _) = SourceType.objects.get_or_create(type="Unpublished")
source = Source.objects.create(
source_type=unpub, title_en="index cards", volume="CUL"
)
author = Creator.objects.create(last_name_en="Goitein", first_name_en="S. D.")
(author, _) = Creator.objects.get_or_create(
last_name_en="Goitein", first_name_en="S. D."
)
Authorship.objects.create(creator=author, source=source)
return source


@pytest.fixture
def goitein_editions(db):
# fixture for Goitein unpublished editions
(unpub, _) = SourceType.objects.get_or_create(type="Unpublished")
source = Source.objects.create(
source_type=unpub, title_en="unpublished editions", volume="CUL"
)
(author, _) = Creator.objects.get_or_create(
last_name_en="Goitein", first_name_en="S. D."
)
Authorship.objects.create(creator=author, source=source)
return source

Expand Down
133 changes: 117 additions & 16 deletions geniza/footnotes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@
from django.db.models import Count, Q
from django.db.models.functions import NullIf
from django.db.models.query import Prefetch
from django.urls import reverse
from django.utils.html import strip_tags
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from gfklookupwidget.fields import GfkLookupField
from modeltranslation.manager import MultilingualManager, MultilingualQuerySet
from multiselectfield import MultiSelectField

from geniza.common.fields import NaturalSortField
from geniza.common.models import TrackChangesModel
from geniza.common.utils import list_to_string
from geniza.footnotes.utils import HTMLLineNumberParser


Expand Down Expand Up @@ -266,7 +265,7 @@ def formatted_display(self, extra_fields=True, format_index_cards=False):
# only when extra_fields enabled, or there is no author

# Translators: Placeholder for when a work has no title available
work_title = gettext("[digital geniza document edition]")
work_title = self.source_type.type.lower()

# Wrap title in link to URL
if self.url and work_title:
Expand Down Expand Up @@ -398,7 +397,6 @@ def formatted_display(self, extra_fields=True, format_index_cards=False):
# L. B. Yarbrough (in Hebrew) (no comma)
# Author (1964) (no comma)
# Author, Journal 6 (1964) (comma)
# Author, [digital geniza document edition] (comma)
use_comma = (
extra_fields
or self.title
Expand Down Expand Up @@ -591,20 +589,123 @@ def __str__(self):
)
return f"{rel} of {self.content_object}"

def display(self):
"""format footnote for display; used on document detail page
and metadata export for old pgp site"""
# source, location. notes.
# source. notes.
# source, location.
parts = [self.source.display()]
if self.notes:
# uppercase first letter of notes if not capitalized
notes = self.notes[0].upper() + self.notes[1:]
# append period to notes if not present
parts.extend([" ", notes.strip("."), "."])
def display(self, old_pgp=False):
"""format footnote for display; used on document detail page"""
# normally, just use source display.
# for unpublished records:
# source author (year), with minor emendations by person.
parts = [self.source.display() if old_pgp else self.source.formatted_display()]
if self.source.source_type.type == "Unpublished":
authors = [
c.creator.firstname_lastname()
for c in self.source.authorship_set.order_by("creator__last_name")
]
parts = [list_to_string(authors)]
relation_display = self.get_doc_relation_display()
if "Goitein" in parts[0]:
# handle goitein, "unpublished" not "digital"
relation_display = relation_display.replace("Digital", "unpublished")
if relation_display:
parts.append(f"'s {relation_display.lower()}")
if self.source.year:
parts.append(f" ({self.source.year})")
elif "Goitein" in parts[0]:
# handle goitein years
parts.append(f" (1950–85)")
# add emendations
if self.emendations:
parts.append(f", with minor emendations by {self.emendations}")
parts.append(".")
return "".join(parts)

@classmethod
def display_multiple(cls, footnotes):
"""Display method for multiple footnotes, grouped by source, on the scholarship
records/bibliography page. Normally uses source formatted display, but requires
special handling for unpublished source records with multiple footnotes.
Handles Goitein index cards by including URLs and index card numbers when
available, and by adding the attribution to the PGL."""
if not footnotes:
# bail out with empty string on no footnotes provided
return ""
source = footnotes[0].source
# start with source formatted display
citation = source.formatted_display(format_index_cards=True)
# handle Goitein index cards
if "unpublished index cards" in citation:
all_cards = []
# add card numbers
for fn in footnotes:
# remove "Card" or "card" from the location field first, we just want #NNN
loc = re.sub("[Cc]ard ", "", fn.location)
card_str = ""
# add URL if present
if loc and fn.url:
card_str = f'<a href="{fn.url}">{loc}</a>'
elif loc:
card_str = str(loc)
if card_str:
all_cards.append(card_str)
if all_cards:
# if card #s are present, include them as a list in the citation
joined = list_to_string(all_cards)
citation = citation[0:-1] + f", {joined}."
# add PGL attribution
citation += " Princeton Geniza Lab, Princeton University."
elif source.source_type.type == "Unpublished":
# handle other unpublished sources
authors = [
c.creator.firstname_lastname()
for c in source.authorship_set.order_by("creator__last_name")
]
citation = list_to_string(authors)
# get doc relations across all footnotes
if "Goitein" in citation:
# handle goitein unpublished work (non-index cards):
# should say "unpublished edition" or "unpublished translation"
relation_set = set(
[
re.sub(
r"([dD]igital )?([Ee]dition|[Tt]ranslation)",
r"unpublished \2",
fn.get_doc_relation_display(),
)
for fn in footnotes
]
)
else:
relation_set = set([fn.get_doc_relation_display() for fn in footnotes])
relation_display = list_to_string(sorted(list(relation_set)))
if relation_display:
citation += f"'s {relation_display.lower()}"
# handle languages (some unpublished records have this)
if source.languages.exists():
for lang in source.languages.all():
if "English" not in str(lang) and "Unspecified" not in str(lang):
citation += " (in %s)" % lang
# year if specified, and special range for Goitein
if source.year:
citation += f" ({source.year})"
elif "Goitein" in citation:
citation += f" (1950–85)"
# handle minor emendations if specified
emendations = list_to_string(
[fn.emendations for fn in footnotes if fn.emendations]
)
if emendations:
citation += f", with minor emendations by {emendations}"
# include information about PGP for digital edition/translation
relations = [r for rs in [fn.doc_relation for fn in footnotes] for r in rs]
if cls.DIGITAL_EDITION in relations or cls.DIGITAL_TRANSLATION in relations:
citation += (
", available online through the Princeton Geniza Project at "
)
permalink = footnotes[0].content_object.permalink
citation += f'<a href="{permalink}">{permalink}</a>'
citation += "."

return citation

def has_url(self):
"""Admin display field indicating if footnote has a url."""
return bool(self.url)
Expand Down
Loading

0 comments on commit 3fbb144

Please sign in to comment.