Skip to content

Commit

Permalink
feat: parler poc for Person/name
Browse files Browse the repository at this point in the history
- modified entity form and filter set for person
- parler works but migrations are buggy; needed some editing
- post_save signal to save translations/transliterations
automatically ONLY on create
- management command to transliterate all Person names
- modified querysets for work and instances to show author name
- according to the selected language
- TODO: Use HistoryGenericTable and fix the bug accessing translated
field there (currently using tables.table)
  • Loading branch information
gythaogg committed Nov 17, 2024
1 parent 834d2e3 commit 4fba520
Show file tree
Hide file tree
Showing 16 changed files with 373 additions and 50 deletions.
11 changes: 11 additions & 0 deletions apis_ontology/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.contrib import admin
from parler.admin import TranslatableAdmin

from .models import Person


@admin.register(Person)
class PersonAdmin(TranslatableAdmin):
# TODO: allow editing the translations here?
list_display = ["name", "all_languages_column"]
pass
9 changes: 6 additions & 3 deletions apis_ontology/filtersets.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf.global_settings import LANGUAGE_CODE
import django_filters
from apis_core.apis_entities.filtersets import (
ABSTRACT_ENTITY_FILTERS_EXCLUDE,
Expand All @@ -9,6 +10,7 @@
from apis_core.relations.models import Relation
from django.apps import apps
from django.db import models
from django.utils.translation import get_language

from apis_ontology.forms import (
PersonSearchForm,
Expand Down Expand Up @@ -212,9 +214,10 @@ class Meta:
)

def custom_name_search(self, queryset, name, value):
name_query = models.Q(name__icontains=value) | models.Q(
alternative_names__icontains=value
)
name_query = models.Q(
translations__name__icontains=value,
translations__language_code=get_language(),
) | models.Q(alternative_names__icontains=value)
if value.isdigit():
name_query = name_query | models.Q(pk=int(value))

Expand Down
15 changes: 12 additions & 3 deletions apis_ontology/forms.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from apis_core.relations.models import Relation
from apis_core.relations.forms import RelationForm
from django import forms
from apis_core.generic.forms import GenericFilterSetForm, GenericModelForm
from django.forms.models import ModelChoiceField
from django.apps import apps
from parler.fields import TranslatedField
from parler.forms import TranslatableModelForm


class TibscholEntityForm(GenericModelForm):
Expand Down Expand Up @@ -35,7 +35,9 @@ class PlaceForm(TibscholEntityForm):
]


class PersonForm(TibscholEntityForm):
class PersonForm(TranslatableModelForm, TibscholEntityForm):
name = TranslatedField()

field_order = [
"name",
"alternative_names",
Expand All @@ -49,6 +51,13 @@ class PersonForm(TibscholEntityForm):
"review",
]

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields["name"].help_text = (
"Equivalent transliterations in Tibetan/English inputs will be automatically provided at the time of creation. However, updates to this field will NOT trigger any updates to the values in other languages."
)


class WorkForm(TibscholEntityForm):
field_order = [
Expand Down
26 changes: 26 additions & 0 deletions apis_ontology/management/commands/support_tibetan_script_001.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import pyewts
from django.core.management.base import BaseCommand
from tqdm.auto import tqdm
from apis_ontology.models import Person
from tqdm.auto import tqdm

TIBETAN = "es"


class Command(BaseCommand):
help = "add Tibetan transliterations for Person - names"

def handle(self, *args, **options):
converter = pyewts.pyewts()

for p in tqdm(Person.objects.all()):
tibetan_name = converter.toUnicode(p.name_latin)
p.set_current_language(TIBETAN)
p.name = tibetan_name
p.save()

self.stdout.write(
self.style.SUCCESS(
f"{len(Person.objects.all())} names were successfully transliterated into Tibetan."
)
)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Generated by Django 5.1.3 on 2024-11-17 00:11

import django.db.models.deletion
import parler.fields
import parler.models
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("apis_ontology", "0031_versioninstancecopiedwrittendownatplace_and_more"),
]

operations = [
migrations.RenameField(
model_name="person",
old_name="name",
new_name="name_latin",
),
migrations.RenameField(
model_name="versionperson",
old_name="name",
new_name="name_latin",
),
migrations.CreateModel(
name="PersonTranslation",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(
blank=True, default="", max_length=255, verbose_name="Name"
),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="apis_ontology.person",
),
),
],
options={
"verbose_name": "person Translation",
"db_table": "apis_ontology_person_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatableModel, models.Model),
),
migrations.RunSQL(
"INSERT INTO apis_ontology_person_translation(language_code, name , master_id) SELECT 'en', name_latin, rootobject_ptr_id FROM apis_ontology_person;"
),
]
37 changes: 31 additions & 6 deletions apis_ontology/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from parler.models import TranslatableModel, TranslatedFields

from apis_ontology.querysets import PersonQuerySet
from django.utils.translation import get_language

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -69,7 +73,12 @@ def uri(self):


class Person(
VersionMixin, LegacyStuffMixin, LegacyDateMixin, TibScholEntityMixin, AbstractEntity
VersionMixin,
LegacyStuffMixin,
LegacyDateMixin,
TibScholEntityMixin,
AbstractEntity,
TranslatableModel,
):
class_uri = "http://id.loc.gov/ontologies/bibframe/Person"

Expand All @@ -92,18 +101,27 @@ class Person(
]
NATIONALITY = [("Indic", "Indic"), ("Tibetan", "Tibetan")]

name = models.CharField(max_length=255, blank=True, default="", verbose_name="Name")
name_latin = models.CharField(
max_length=255, blank=True, default="", verbose_name="Name"
)
translations = TranslatedFields(
name=models.CharField(
max_length=255, blank=True, default="", verbose_name="Name"
)
)
gender = models.CharField(max_length=6, choices=GENDERS, default="male")
nationality = models.CharField(
max_length=10, choices=NATIONALITY, blank=True, null=True
)

def __str__(self):
return f"{self.name} ({self.pk})"

class Meta:
verbose_name = _("person")
verbose_name_plural = _("Persons")

def __str__(self):
return f"{self.name} ({self.pk})"
objects = PersonQuerySet.as_manager()


class Place(
Expand Down Expand Up @@ -137,6 +155,8 @@ def __str__(self):

class WorkQuerySet(QuerySet):
def with_author(self):
current_language = get_language()

return self.annotate(
# Subquery to get the Person ID related to the Work through PersonAuthorOfWork
author_id=Subquery(
Expand All @@ -146,7 +166,9 @@ def with_author(self):
),
# Subquery to get the Person's name based on the person_id from above
author_name=Subquery(
Person.objects.filter(id=OuterRef("author_id")).values("name")[:1]
Person.objects.filter(id=OuterRef("author_id"))
.filter(translations__language_code=current_language)
.values("translations__name")[:1]
),
)

Expand Down Expand Up @@ -202,6 +224,7 @@ def __str__(self):

class InstanceQuerySet(QuerySet):
def with_author(self):
current_language = get_language()
return self.annotate(
# Subquery to get the Person ID related to the Work through PersonAuthorOfWork
work_id=Subquery(
Expand All @@ -216,7 +239,9 @@ def with_author(self):
),
# Subquery to get the Person's name based on the person_id from above
author_name=Subquery(
Person.objects.filter(id=OuterRef("author_id")).values("name")[:1]
Person.objects.filter(id=OuterRef("author_id"))
.filter(translations__language_code=current_language)
.values("translations__name")[:1]
),
)

Expand Down
5 changes: 5 additions & 0 deletions apis_ontology/querysets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from parler.managers import TranslatableQuerySet


class PersonQuerySet(TranslatableQuerySet):
pass
51 changes: 51 additions & 0 deletions apis_ontology/settings/server_settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import os
from apis_acdhch_default_settings.settings import *
from django.utils.translation import gettext_lazy as _
from django.conf.locale import LANG_INFO

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
Expand All @@ -17,6 +20,7 @@
"apis_core.history",
"django_acdhch_functions",
"django_select2",
"parler",
]
INSTALLED_APPS.remove("apis_ontology")
INSTALLED_APPS.insert(0, "apis_ontology")
Expand Down Expand Up @@ -65,4 +69,51 @@

MIDDLEWARE += [
"simple_history.middleware.HistoryRequestMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware", # Enables language switching based on session
# "apis_ontology.middleware.language_change_middleware.LanguageChangeMiddleware",
]


LANG_INFO.update(
{
"bo": {
"bidi": False, # Set to True if the language is written right-to-left
"code": "bo",
"name": "Tibetan",
"name_local": "བོད་ཡིག", # Native name
},
}
)

LANGUAGE_CODE = "en" # This will be the default language

# Locale paths (optional if you store translations in a custom directory)
# We currently use only model translations on specific fields
# LOCALE_PATHS = [
# BASE_DIR / 'locale',
# ]

PARLER_LANGUAGES = {
None: ( # Site ID 1
{"code": "en"},
{"code": "es"},
),
"default": {
"fallback": "en", # Use English if translation is missing
"hide_untranslated": False, # Show default values for missing translations
},
}


## List of available languages in the app
LANGUAGES = [
("en", _("English")),
("es", _("Tibetan")),
]

LOCALE_PATHS = (os.path.join(BASE_DIR, "apis_ontology", "locale/"),)

USE_I18N = True
USE_L10N = True
USE_TZ = True
Loading

0 comments on commit 4fba520

Please sign in to comment.