diff --git a/.github/workflows/django_tests.yml b/.github/workflows/django_tests.yml new file mode 100644 index 000000000..b175071e2 --- /dev/null +++ b/.github/workflows/django_tests.yml @@ -0,0 +1,29 @@ +name: django-tests +on: + pull_request: + types: [opened, synchronize] +jobs: + run-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: mkdir -p config/envs + - uses: SpicyPizza/create-envfile@v2.0 + with: + envkey_POSTGRES_DB: test_cantusdb + envkey_POSTGRES_USER: test_user + envkey_POSTGRES_HOST: postgres + envkey_POSTGRES_PORT: 5432 + envkey_PROJECT_ENVIRONMENT: PRODUCTION + envkey_CANTUSDB_STATIC_ROOT: /path/to/static + envkey_CANTUSDB_MEDIA_ROOT: /path/to/media + envkey_CANTUSDB_HOST: somehost + envkey_CANTUSDB_SECRET_KEY: "hereisakey1234" + envkey_POSTGRES_PASSWORD: woahagreatpasswordabc + envkey_AWS_EMAIL_HOST_USER: test_user + envkey_AWS_EMAIL_HOST_PASSWORD: test_password + directory: config/envs + file_name: dev_env + - run: docker compose -f docker-compose-development.yml build + - run: docker compose -f docker-compose-development.yml up -d + - run: docker compose -f docker-compose-development.yml exec -T django python manage.py test main_app.tests \ No newline at end of file diff --git a/config/nginx/conf.d/cantusdb.conf.development b/config/nginx/conf.d/cantusdb.conf.development index 1b8e0b654..972c91519 100644 --- a/config/nginx/conf.d/cantusdb.conf.development +++ b/config/nginx/conf.d/cantusdb.conf.development @@ -23,17 +23,9 @@ server { alias /resources/api_cache/concordances.json; expires modified +24h; } - - location = /style.css { - root /; - } - location = /background.jpg { - root /; - } - location = /CantusLogoSmall.gif { - root /; - } - location = /favicon.ico { + + error_page 500 /500.html; + location = /500.html { root /; } @@ -45,4 +37,4 @@ server { location = /504.html { root /; } -} \ No newline at end of file +} diff --git a/cron/management/manage.sh b/cron/management/manage.sh index 0419f28fa..0dabc8e94 100644 --- a/cron/management/manage.sh +++ b/cron/management/manage.sh @@ -7,4 +7,4 @@ DOCKER_COMPOSE_FILE=$1 # This is the path to the docker-compose file. COMMAND=$2 # This is the command to execute. -/usr/local/bin/docker compose -f $DOCKER_COMPOSE_FILE exec -T django python manage.py $COMMAND +/usr/bin/docker compose -f $DOCKER_COMPOSE_FILE exec -T django python manage.py $COMMAND diff --git a/django/cantusdb_project/cantusindex.py b/django/cantusdb_project/cantusindex.py index 210aa0505..b00ef8ee3 100644 --- a/django/cantusdb_project/cantusindex.py +++ b/django/cantusdb_project/cantusindex.py @@ -91,9 +91,12 @@ def get_suggested_chant( # mostly, in case of a timeout within get_json_from_ci_api return None - fulltext: str = json["info"]["field_full_text"] - incipit: str = " ".join(fulltext.split(" ")[:5]) - genre_name: str = json["info"]["field_genre"] + try: + fulltext: str = json["info"]["field_full_text"] + incipit: str = " ".join(fulltext.split(" ")[:5]) + genre_name: str = json["info"]["field_genre"] + except TypeError: + return None genre_id: Optional[int] = None try: genre_id = Genre.objects.get(name=genre_name).id diff --git a/django/cantusdb_project/main_app/admin.py b/django/cantusdb_project/main_app/admin.py deleted file mode 100644 index e23b07503..000000000 --- a/django/cantusdb_project/main_app/admin.py +++ /dev/null @@ -1,236 +0,0 @@ -from django.contrib import admin -from reversion.admin import VersionAdmin -from main_app.models import * -from main_app.forms import ( - AdminCenturyForm, - AdminChantForm, - AdminFeastForm, - AdminGenreForm, - AdminNotationForm, - AdminOfficeForm, - AdminProvenanceForm, - AdminRismSiglumForm, - AdminSegmentForm, - AdminSequenceForm, - AdminSourceForm, -) - -# these fields should not be editable by all classes -EXCLUDE = ("json_info",) - -READ_ONLY = ( - "created_by", - "last_updated_by", - "date_created", - "date_updated", -) - - -class BaseModelAdmin(VersionAdmin): - exclude = EXCLUDE - readonly_fields = READ_ONLY - - # if an object is created in the admin interface, assign the user to the created_by field - # else if an object is updated in the admin interface, assign the user to the last_updated_by field - def save_model(self, request, obj, form, change): - if change: - obj.last_updated_by = request.user - else: - obj.created_by = request.user - super().save_model(request, obj, form, change) - - -class CenturyAdmin(BaseModelAdmin): - search_fields = ("name",) - form = AdminCenturyForm - - -class ChantAdmin(BaseModelAdmin): - @admin.display(description="Source Siglum") - def get_source_siglum(self, obj): - if obj.source: - return obj.source.siglum - - list_display = ( - "incipit", - "get_source_siglum", - "genre", - ) - search_fields = ( - "title", - "incipit", - "cantus_id", - "id", - ) - - readonly_fields = READ_ONLY + ("incipit",) - - list_filter = ( - "genre", - "office", - ) - exclude = EXCLUDE + ( - "col1", - "col2", - "col3", - "next_chant", - "s_sequence", - "is_last_chant_in_feast", - "visible_status", - "date", - "volpiano_notes", - "volpiano_intervals", - "title", - "differentiae_database", - ) - form = AdminChantForm - raw_id_fields = ( - "source", - "feast", - ) - ordering = ("source__siglum",) - - -class DifferentiaAdmin(BaseModelAdmin): - search_fields = ( - "differentia_id", - "id", - ) - - -class FeastAdmin(BaseModelAdmin): - search_fields = ( - "name", - "feast_code", - ) - list_display = ( - "name", - "month", - "day", - "feast_code", - ) - form = AdminFeastForm - - -class GenreAdmin(BaseModelAdmin): - search_fields = ("name",) - form = AdminGenreForm - - -class NotationAdmin(BaseModelAdmin): - search_fields = ("name",) - form = AdminNotationForm - - -class OfficeAdmin(BaseModelAdmin): - search_fields = ("name",) - form = AdminOfficeForm - - -class ProvenanceAdmin(BaseModelAdmin): - search_fields = ("name",) - form = AdminProvenanceForm - - -class RismSiglumAdmin(BaseModelAdmin): - search_fields = ("name",) - form = AdminRismSiglumForm - - -class SegmentAdmin(BaseModelAdmin): - search_fields = ("name",) - form = AdminSegmentForm - - -class SequenceAdmin(BaseModelAdmin): - @admin.display(description="Source Siglum") - def get_source_siglum(self, obj): - if obj.source: - return obj.source.siglum - - search_fields = ( - "title", - "incipit", - "cantus_id", - "id", - ) - exclude = EXCLUDE + ( - "c_sequence", - "next_chant", - "is_last_chant_in_feast", - "visible_status", - ) - list_display = ("incipit", "get_source_siglum", "genre") - list_filter = ( - "genre", - "office", - ) - raw_id_fields = ( - "source", - "feast", - ) - readonly_fields = READ_ONLY + ("incipit",) - ordering = ("source__siglum",) - form = AdminSequenceForm - - -class SourceAdmin(BaseModelAdmin): - exclude = EXCLUDE + ("source_status",) - - # These search fields are also available on the user-source inline relationship in the user admin page - search_fields = ( - "siglum", - "title", - "id", - ) - readonly_fields = READ_ONLY + ( - "number_of_chants", - "number_of_melodies", - "date_created", - "date_updated", - ) - # from the Django docs: - # Adding a ManyToManyField to this list will instead use a nifty unobtrusive JavaScript “filter” interface - # that allows searching within the options. The unselected and selected options appear in two boxes side by side. - filter_horizontal = ( - "century", - "notation", - "current_editors", - "inventoried_by", - "full_text_entered_by", - "melodies_entered_by", - "proofreaders", - "other_editors", - ) - - list_display = ( - "title", - "siglum", - "id", - ) - - list_filter = ( - "full_source", - "segment", - "source_status", - "published", - "century", - ) - - ordering = ("siglum",) - - form = AdminSourceForm - - -admin.site.register(Century, CenturyAdmin) -admin.site.register(Chant, ChantAdmin) -admin.site.register(Differentia, DifferentiaAdmin) -admin.site.register(Feast, FeastAdmin) -admin.site.register(Genre, GenreAdmin) -admin.site.register(Notation, NotationAdmin) -admin.site.register(Office, OfficeAdmin) -admin.site.register(Provenance, ProvenanceAdmin) -admin.site.register(RismSiglum, RismSiglumAdmin) -admin.site.register(Segment, SegmentAdmin) -admin.site.register(Sequence, SequenceAdmin) -admin.site.register(Source, SourceAdmin) diff --git a/django/cantusdb_project/main_app/admin/__init__.py b/django/cantusdb_project/main_app/admin/__init__.py new file mode 100644 index 000000000..6839ad2d8 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/__init__.py @@ -0,0 +1,14 @@ +from main_app.admin.century import CenturyAdmin +from main_app.admin.chant import ChantAdmin +from main_app.admin.differentia import DifferentiaAdmin +from main_app.admin.feast import FeastAdmin +from main_app.admin.genre import GenreAdmin +from main_app.admin.notation import NotationAdmin +from main_app.admin.office import OfficeAdmin +from main_app.admin.provenance import ProvenanceAdmin +from main_app.admin.rism_siglum import RismSiglumAdmin +from main_app.admin.segment import SegmentAdmin +from main_app.admin.sequence import SequenceAdmin +from main_app.admin.source import SourceAdmin +from main_app.admin.institution import InstitutionAdmin +from main_app.admin.institution_identifier import InstitutionIdentifierAdmin diff --git a/django/cantusdb_project/main_app/admin/base_admin.py b/django/cantusdb_project/main_app/admin/base_admin.py new file mode 100644 index 000000000..98963f68e --- /dev/null +++ b/django/cantusdb_project/main_app/admin/base_admin.py @@ -0,0 +1,27 @@ +from reversion.admin import VersionAdmin + + +# these fields should not be editable by all classes +EXCLUDE = ("json_info",) + + +READ_ONLY = ( + "created_by", + "last_updated_by", + "date_created", + "date_updated", +) + + +class BaseModelAdmin(VersionAdmin): + exclude = EXCLUDE + readonly_fields = READ_ONLY + + # if an object is created in the admin interface, assign the user to the created_by field + # else if an object is updated in the admin interface, assign the user to the last_updated_by field + def save_model(self, request, obj, form, change): + if change: + obj.last_updated_by = request.user + else: + obj.created_by = request.user + super().save_model(request, obj, form, change) diff --git a/django/cantusdb_project/main_app/admin/century.py b/django/cantusdb_project/main_app/admin/century.py new file mode 100644 index 000000000..31dc76c20 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/century.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminCenturyForm +from main_app.models import Century + + +@admin.register(Century) +class CenturyAdmin(BaseModelAdmin): + search_fields = ("name",) + form = AdminCenturyForm diff --git a/django/cantusdb_project/main_app/admin/chant.py b/django/cantusdb_project/main_app/admin/chant.py new file mode 100644 index 000000000..ce2c8b775 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/chant.py @@ -0,0 +1,53 @@ +from django.contrib import admin + +from main_app.admin.base_admin import EXCLUDE, READ_ONLY, BaseModelAdmin +from main_app.forms import AdminChantForm +from main_app.models import Chant + + +@admin.register(Chant) +class ChantAdmin(BaseModelAdmin): + + @admin.display(description="Source Siglum") + def get_source_siglum(self, obj): + if obj.source: + return obj.source.siglum + + list_display = ( + "incipit", + "get_source_siglum", + "genre", + ) + search_fields = ( + "title", + "incipit", + "cantus_id", + "id", + ) + + readonly_fields = READ_ONLY + ("incipit",) + + list_filter = ( + "genre", + "office", + ) + exclude = EXCLUDE + ( + "col1", + "col2", + "col3", + "next_chant", + "s_sequence", + "is_last_chant_in_feast", + "visible_status", + "date", + "volpiano_notes", + "volpiano_intervals", + "title", + "differentiae_database", + ) + form = AdminChantForm + raw_id_fields = ( + "source", + "feast", + ) + ordering = ("source__siglum",) diff --git a/django/cantusdb_project/main_app/admin/differentia.py b/django/cantusdb_project/main_app/admin/differentia.py new file mode 100644 index 000000000..7d823d5ae --- /dev/null +++ b/django/cantusdb_project/main_app/admin/differentia.py @@ -0,0 +1,12 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.models import Differentia + + +@admin.register(Differentia) +class DifferentiaAdmin(BaseModelAdmin): + search_fields = ( + "differentia_id", + "id", + ) diff --git a/django/cantusdb_project/main_app/admin/feast.py b/django/cantusdb_project/main_app/admin/feast.py new file mode 100644 index 000000000..81a070ef0 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/feast.py @@ -0,0 +1,20 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminFeastForm +from main_app.models import Feast + + +@admin.register(Feast) +class FeastAdmin(BaseModelAdmin): + search_fields = ( + "name", + "feast_code", + ) + list_display = ( + "name", + "month", + "day", + "feast_code", + ) + form = AdminFeastForm diff --git a/django/cantusdb_project/main_app/admin/genre.py b/django/cantusdb_project/main_app/admin/genre.py new file mode 100644 index 000000000..86f7ba84a --- /dev/null +++ b/django/cantusdb_project/main_app/admin/genre.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminGenreForm +from main_app.models import Genre + + +@admin.register(Genre) +class GenreAdmin(BaseModelAdmin): + search_fields = ("name",) + form = AdminGenreForm diff --git a/django/cantusdb_project/main_app/admin/institution.py b/django/cantusdb_project/main_app/admin/institution.py new file mode 100644 index 000000000..71d9c9d48 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/institution.py @@ -0,0 +1,25 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.models import Institution, InstitutionIdentifier + + +class InstitutionIdentifierInline(admin.TabularInline): + model = InstitutionIdentifier + extra = 0 + exclude = ["created_by", "last_updated_by"] + + +@admin.register(Institution) +class InstitutionAdmin(BaseModelAdmin): + list_display = ("name", "siglum", "get_city_region", "country") + search_fields = ("name", "siglum", "city", "region", "alternate_names") + list_filter = ("city",) + inlines = (InstitutionIdentifierInline,) + + def get_city_region(self, obj) -> str: + city: str = obj.city if obj.city else "[No city]" + region: str = f"({obj.region})" if obj.region else "" + return f"{city} {region}" + + get_city_region.short_description = "City" diff --git a/django/cantusdb_project/main_app/admin/institution_identifier.py b/django/cantusdb_project/main_app/admin/institution_identifier.py new file mode 100644 index 000000000..6eb1288c9 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/institution_identifier.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.models import InstitutionIdentifier + + +@admin.register(InstitutionIdentifier) +class InstitutionIdentifierAdmin(BaseModelAdmin): + list_display = ('identifier', 'identifier_type') + raw_id_fields = ("institution",) diff --git a/django/cantusdb_project/main_app/admin/notation.py b/django/cantusdb_project/main_app/admin/notation.py new file mode 100644 index 000000000..c257b2d35 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/notation.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminNotationForm +from main_app.models import Notation + + +@admin.register(Notation) +class NotationAdmin(BaseModelAdmin): + search_fields = ("name",) + form = AdminNotationForm diff --git a/django/cantusdb_project/main_app/admin/office.py b/django/cantusdb_project/main_app/admin/office.py new file mode 100644 index 000000000..d31d56534 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/office.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminOfficeForm +from main_app.models import Office + + +@admin.register(Office) +class OfficeAdmin(BaseModelAdmin): + search_fields = ("name",) + form = AdminOfficeForm diff --git a/django/cantusdb_project/main_app/admin/provenance.py b/django/cantusdb_project/main_app/admin/provenance.py new file mode 100644 index 000000000..130b35eca --- /dev/null +++ b/django/cantusdb_project/main_app/admin/provenance.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminProvenanceForm +from main_app.models import Provenance + + +@admin.register(Provenance) +class ProvenanceAdmin(BaseModelAdmin): + search_fields = ("name",) + form = AdminProvenanceForm diff --git a/django/cantusdb_project/main_app/admin/rism_siglum.py b/django/cantusdb_project/main_app/admin/rism_siglum.py new file mode 100644 index 000000000..36113bec8 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/rism_siglum.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminRismSiglumForm +from main_app.models import RismSiglum + + +@admin.register(RismSiglum) +class RismSiglumAdmin(BaseModelAdmin): + search_fields = ("name",) + form = AdminRismSiglumForm diff --git a/django/cantusdb_project/main_app/admin/segment.py b/django/cantusdb_project/main_app/admin/segment.py new file mode 100644 index 000000000..b8a16a6b0 --- /dev/null +++ b/django/cantusdb_project/main_app/admin/segment.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin +from main_app.forms import AdminSegmentForm +from main_app.models import Segment + + +@admin.register(Segment) +class SegmentAdmin(BaseModelAdmin): + search_fields = ("name",) + form = AdminSegmentForm diff --git a/django/cantusdb_project/main_app/admin/sequence.py b/django/cantusdb_project/main_app/admin/sequence.py new file mode 100644 index 000000000..9c7868e4d --- /dev/null +++ b/django/cantusdb_project/main_app/admin/sequence.py @@ -0,0 +1,38 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin, EXCLUDE, READ_ONLY +from main_app.forms import AdminSequenceForm +from main_app.models import Sequence + + +@admin.register(Sequence) +class SequenceAdmin(BaseModelAdmin): + @admin.display(description="Source Siglum") + def get_source_siglum(self, obj): + if obj.source: + return obj.source.siglum + + search_fields = ( + "title", + "incipit", + "cantus_id", + "id", + ) + exclude = EXCLUDE + ( + "c_sequence", + "next_chant", + "is_last_chant_in_feast", + "visible_status", + ) + list_display = ("incipit", "get_source_siglum", "genre") + list_filter = ( + "genre", + "office", + ) + raw_id_fields = ( + "source", + "feast", + ) + readonly_fields = READ_ONLY + ("incipit",) + ordering = ("source__siglum",) + form = AdminSequenceForm diff --git a/django/cantusdb_project/main_app/admin/source.py b/django/cantusdb_project/main_app/admin/source.py new file mode 100644 index 000000000..d047d518d --- /dev/null +++ b/django/cantusdb_project/main_app/admin/source.py @@ -0,0 +1,54 @@ +from django.contrib import admin + +from main_app.admin.base_admin import BaseModelAdmin, EXCLUDE, READ_ONLY +from main_app.forms import AdminSourceForm +from main_app.models import Source + + +@admin.register(Source) +class SourceAdmin(BaseModelAdmin): + exclude = EXCLUDE + ("source_status",) + + # These search fields are also available on the user-source inline relationship in the user admin page + search_fields = ( + "siglum", + "title", + "id", + ) + readonly_fields = READ_ONLY + ( + "number_of_chants", + "number_of_melodies", + "date_created", + "date_updated", + ) + # from the Django docs: + # Adding a ManyToManyField to this list will instead use a nifty unobtrusive JavaScript “filter” interface + # that allows searching within the options. The unselected and selected options appear in two boxes side by side. + filter_horizontal = ( + "century", + "notation", + "current_editors", + "inventoried_by", + "full_text_entered_by", + "melodies_entered_by", + "proofreaders", + "other_editors", + ) + + list_display = ( + "title", + "siglum", + "id", + ) + + list_filter = ( + "full_source", + "segment", + "source_status", + "published", + "century", + ) + + ordering = ("siglum",) + + form = AdminSourceForm diff --git a/django/cantusdb_project/main_app/forms.py b/django/cantusdb_project/main_app/forms.py index 100027609..41abb97b3 100644 --- a/django/cantusdb_project/main_app/forms.py +++ b/django/cantusdb_project/main_app/forms.py @@ -83,13 +83,14 @@ class Meta: "content_structure", "indexing_notes", "addendum", - "segment", - "liturgical_function", - "polyphony", - "cm_melody_id", - "incipit_of_refrain", - "later_addition", - "rubrics", + # See issue #1521: Temporarily commenting out segment-related functions on Chant + # "segment", + # "liturgical_function", + # "polyphony", + # "cm_melody_id", + # "incipit_of_refrain", + # "later_addition", + # "rubrics", ] # the widgets dictionary is ignored for a model field with a non-empty # choices attribute. In this case, you must override the form field to @@ -148,13 +149,14 @@ class Meta: "Mass Alleluias. Punctuation is omitted.", ) - segment = SelectWidgetNameModelChoiceField( - queryset=Segment.objects.all().order_by("id"), - required=True, - initial=Segment.objects.get(id=4063), # Default to the "Cantus" segment - help_text="Select the Database segment that the chant belongs to. " - "In most cases, this will be the CANTUS segment.", - ) + # See issue #1521: Temporarily commenting out segment-related functions on Chant + # segment = SelectWidgetNameModelChoiceField( + # queryset=Segment.objects.all().order_by("id"), + # required=True, + # initial=Segment.objects.get(id=4063), # Default to the "Cantus" segment + # help_text="Select the Database segment that the chant belongs to. " + # "In most cases, this will be the CANTUS segment.", + # ) # automatically computed fields # source and incipit are mandatory fields in model, @@ -281,13 +283,14 @@ class Meta: "manuscript_full_text_proofread", "volpiano_proofread", "proofread_by", - "segment", - "liturgical_function", - "polyphony", - "cm_melody_id", - "incipit_of_refrain", - "later_addition", - "rubrics", + # See issue #1521: Temporarily commenting out segment-related functions on Chant + # "segment", + # "liturgical_function", + # "polyphony", + # "cm_melody_id", + # "incipit_of_refrain", + # "later_addition", + # "rubrics", ] widgets = { # manuscript_full_text_std_spelling: defined below (required) @@ -317,12 +320,13 @@ class Meta: "proofread_by": autocomplete.ModelSelect2Multiple( url="proofread-by-autocomplete" ), - "polyphony": SelectWidget(), - "liturgical_function": SelectWidget(), - "cm_melody_id": TextInputWidget(), - "incipit_of_refrain": TextInputWidget(), - "later_addition": TextInputWidget(), - "rubrics": TextInputWidget(), + # See issue #1521: Temporarily commenting out segment-related functions on Chant + # "polyphony": SelectWidget(), + # "liturgical_function": SelectWidget(), + # "cm_melody_id": TextInputWidget(), + # "incipit_of_refrain": TextInputWidget(), + # "later_addition": TextInputWidget(), + # "rubrics": TextInputWidget(), } manuscript_full_text_std_spelling = forms.CharField( @@ -347,12 +351,13 @@ class Meta: help_text="Each folio starts with '1'.", ) - segment = SelectWidgetNameModelChoiceField( - queryset=Segment.objects.all().order_by("id"), - required=True, - help_text="Select the Database segment that the chant belongs to. " - "In most cases, this will be the CANTUS segment.", - ) + # See issue #1521: Temporarily commenting out segment-related functions on Chant + # segment = SelectWidgetNameModelChoiceField( + # queryset=Segment.objects.all().order_by("id"), + # required=True, + # help_text="Select the Database segment that the chant belongs to. " + # "In most cases, this will be the CANTUS segment.", + # ) class SourceEditForm(forms.ModelForm): diff --git a/django/cantusdb_project/main_app/identifiers.py b/django/cantusdb_project/main_app/identifiers.py new file mode 100644 index 000000000..4ab27e298 --- /dev/null +++ b/django/cantusdb_project/main_app/identifiers.py @@ -0,0 +1,26 @@ +class ExternalIdentifiers: + RISM = 1 + VIAF = 2 + WIKIDATA = 3 + GND = 4 + BNF = 5 + LC = 6 + + +IDENTIFIER_TYPES = ( + (ExternalIdentifiers.RISM, "RISM Online"), + (ExternalIdentifiers.VIAF, "VIAF"), + (ExternalIdentifiers.WIKIDATA, "Wikidata"), + (ExternalIdentifiers.GND, "GND (Gemeinsame Normdatei)"), + (ExternalIdentifiers.BNF, "Bibliothèque national de France"), + (ExternalIdentifiers.LC, "Library of Congress"), +) + +TYPE_PREFIX = { + ExternalIdentifiers.RISM: ("rism", "https://rism.online/"), + ExternalIdentifiers.VIAF: ("viaf", "https://viaf.org/viaf/"), + ExternalIdentifiers.WIKIDATA: ("wkp", "https://www.wikidata.org/wiki/"), + 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/"), +} diff --git a/django/cantusdb_project/main_app/migrations/0012_alter_source_date_alter_source_title.py b/django/cantusdb_project/main_app/migrations/0012_alter_source_date_alter_source_title.py new file mode 100644 index 000000000..7eae069bf --- /dev/null +++ b/django/cantusdb_project/main_app/migrations/0012_alter_source_date_alter_source_title.py @@ -0,0 +1,30 @@ +# Generated by Django 4.1.6 on 2024-06-06 12:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("main_app", "0011_chant_cm_melody_id_chant_incipit_of_refrain_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="source", + name="date", + field=models.CharField( + blank=True, + help_text='Date of the source (e.g. "1200s", "1300-1350", etc.)', + max_length=63, + null=True, + ), + ), + migrations.AlterField( + model_name="source", + name="title", + field=models.CharField( + help_text="Full Source Identification (City, Archive, Shelf-mark)", + max_length=255, + ), + ), + ] diff --git a/django/cantusdb_project/main_app/migrations/0013_institution.py b/django/cantusdb_project/main_app/migrations/0013_institution.py new file mode 100644 index 000000000..b206c5342 --- /dev/null +++ b/django/cantusdb_project/main_app/migrations/0013_institution.py @@ -0,0 +1,67 @@ +# Generated by Django 4.1.6 on 2024-06-06 12:06 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("main_app", "0012_alter_source_date_alter_source_title"), + ] + + operations = [ + migrations.CreateModel( + name="Institution", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "date_created", + models.DateTimeField( + auto_now_add=True, help_text="The date this entry was created" + ), + ), + ( + "date_updated", + models.DateTimeField( + auto_now=True, help_text="The date this entry was updated" + ), + ), + ("name", models.CharField(default="s.n.", max_length=255)), + ("siglum", models.CharField(default="XX-Nn", max_length=32)), + ("city", models.CharField(blank=True, max_length=64, null=True)), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_created_by_user", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "last_updated_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_last_updated_by_user", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/django/cantusdb_project/main_app/migrations/0014_institutionidentifier.py b/django/cantusdb_project/main_app/migrations/0014_institutionidentifier.py new file mode 100644 index 000000000..f544fd859 --- /dev/null +++ b/django/cantusdb_project/main_app/migrations/0014_institutionidentifier.py @@ -0,0 +1,92 @@ +# Generated by Django 4.1.6 on 2024-06-06 12:16 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("main_app", "0013_institution"), + ] + + operations = [ + migrations.CreateModel( + name="InstitutionIdentifier", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "date_created", + models.DateTimeField( + auto_now_add=True, help_text="The date this entry was created" + ), + ), + ( + "date_updated", + models.DateTimeField( + auto_now=True, help_text="The date this entry was updated" + ), + ), + ( + "identifier", + models.CharField( + help_text="Do not provide the full URL here; only the identifier.", + max_length=512, + ), + ), + ( + "identifier_type", + models.IntegerField( + choices=[ + (1, "RISM Online"), + (2, "VIAF"), + (3, "Wikidata"), + (4, "GND (Gemeinsame Normdatei)"), + (5, "Bibliothèque national de France"), + (6, "Library of Congress"), + ] + ), + ), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_created_by_user", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "institution", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="identifiers", + to="main_app.institution", + ), + ), + ( + "last_updated_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_last_updated_by_user", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/django/cantusdb_project/main_app/migrations/0015_source_holding_institution.py b/django/cantusdb_project/main_app/migrations/0015_source_holding_institution.py new file mode 100644 index 000000000..2de78ae33 --- /dev/null +++ b/django/cantusdb_project/main_app/migrations/0015_source_holding_institution.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.11 on 2024-06-06 13:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("main_app", "0014_institutionidentifier"), + ] + + operations = [ + migrations.AddField( + model_name="source", + name="holding_institution", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="main_app.institution", + ), + ), + ] diff --git a/django/cantusdb_project/main_app/migrations/0016_institution_alternate_names_institution_region_squashed_0017_institution_country_alter_institution_city.py b/django/cantusdb_project/main_app/migrations/0016_institution_alternate_names_institution_region_squashed_0017_institution_country_alter_institution_city.py new file mode 100644 index 000000000..15e17ec59 --- /dev/null +++ b/django/cantusdb_project/main_app/migrations/0016_institution_alternate_names_institution_region_squashed_0017_institution_country_alter_institution_city.py @@ -0,0 +1,51 @@ +# Generated by Django 4.1.6 on 2024-06-11 09:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + replaces = [ + ("main_app", "0016_institution_alternate_names_institution_region"), + ("main_app", "0017_institution_country_alter_institution_city"), + ] + + dependencies = [ + ("main_app", "0015_source_holding_institution"), + ] + + operations = [ + migrations.AddField( + model_name="institution", + name="alternate_names", + field=models.TextField( + blank=True, + help_text="Enter alternate names on separate lines.", + null=True, + ), + ), + migrations.AddField( + model_name="institution", + name="region", + field=models.CharField( + blank=True, + help_text='Province / State / Canton / County. Used to disambiguate cities, e.g., "London (Ontario)".', + max_length=64, + null=True, + ), + ), + migrations.AddField( + model_name="institution", + name="country", + field=models.CharField(default="s.l.", max_length=64), + ), + migrations.AlterField( + model_name="institution", + name="city", + field=models.CharField( + blank=True, + help_text="City / Town / Village / Settlement", + max_length=64, + null=True, + ), + ), + ] diff --git a/django/cantusdb_project/main_app/migrations/0018_institution_former_sigla.py b/django/cantusdb_project/main_app/migrations/0018_institution_former_sigla.py new file mode 100644 index 000000000..cdd13afe9 --- /dev/null +++ b/django/cantusdb_project/main_app/migrations/0018_institution_former_sigla.py @@ -0,0 +1,22 @@ +# Generated by Django 4.1.6 on 2024-06-11 15:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ( + "main_app", + "0016_institution_alternate_names_institution_region_squashed_0017_institution_country_alter_institution_city", + ), + ] + + operations = [ + migrations.AddField( + model_name="institution", + name="former_sigla", + field=models.TextField( + blank=True, help_text="Enter former sigla on separate lines.", null=True + ), + ), + ] diff --git a/django/cantusdb_project/main_app/models/__init__.py b/django/cantusdb_project/main_app/models/__init__.py index 67d135611..9959354e9 100644 --- a/django/cantusdb_project/main_app/models/__init__.py +++ b/django/cantusdb_project/main_app/models/__init__.py @@ -11,3 +11,5 @@ from main_app.models.sequence import Sequence from main_app.models.rism_siglum import RismSiglum from main_app.models.source import Source +from main_app.models.institution import Institution +from main_app.models.institution_identifier import InstitutionIdentifier diff --git a/django/cantusdb_project/main_app/models/institution.py b/django/cantusdb_project/main_app/models/institution.py new file mode 100644 index 000000000..02fd1f19e --- /dev/null +++ b/django/cantusdb_project/main_app/models/institution.py @@ -0,0 +1,29 @@ +from django.db import models + +from main_app.models import BaseModel + + +region_help_text = """Province / State / Canton / County. Used to disambiguate cities, e.g., "London (Ontario)".""" +city_help_text = """City / Town / Village / Settlement""" + + +class Institution(BaseModel): + name = models.CharField(max_length=255, default="s.n.") + siglum = models.CharField(max_length=32, default="XX-Nn") + city = models.CharField( + max_length=64, blank=True, null=True, help_text=city_help_text + ) + region = models.CharField( + max_length=64, blank=True, null=True, help_text=region_help_text + ) + country = models.CharField(max_length=64, default="s.l.") + alternate_names = models.TextField( + blank=True, null=True, help_text="Enter alternate names on separate lines." + ) + former_sigla = models.TextField( + blank=True, null=True, help_text="Enter former sigla on separate lines." + ) + + def __str__(self) -> str: + sigl: str = f"({self.siglum})" if self.siglum else "" + return f"{self.name} {sigl}" diff --git a/django/cantusdb_project/main_app/models/institution_identifier.py b/django/cantusdb_project/main_app/models/institution_identifier.py new file mode 100644 index 000000000..6e0c4df85 --- /dev/null +++ b/django/cantusdb_project/main_app/models/institution_identifier.py @@ -0,0 +1,33 @@ +from django.db import models + +from main_app.identifiers import IDENTIFIER_TYPES, TYPE_PREFIX +from main_app.models import BaseModel + + +class InstitutionIdentifier(BaseModel): + identifier = models.CharField( + max_length=512, + help_text="Do not provide the full URL here; only the identifier.", + ) + identifier_type = models.IntegerField(choices=IDENTIFIER_TYPES) + institution = models.ForeignKey( + "Institution", related_name="identifiers", on_delete=models.CASCADE + ) + + def __str__(self): + return f"{self.identifier_prefix}:{self.identifier}" + + @property + def identifier_label(self) -> str: + d: dict[int, str] = dict(IDENTIFIER_TYPES) + return d[self.identifier_type] + + @property + def identifier_prefix(self) -> str: + (pfx, _) = TYPE_PREFIX[self.identifier_type] + return pfx + + @property + def identifier_url(self) -> str: + (_, url) = TYPE_PREFIX[self.identifier_type] + return f"{url}{self.identifier}" diff --git a/django/cantusdb_project/main_app/models/source.py b/django/cantusdb_project/main_app/models/source.py index 6d0efe68b..e49363306 100644 --- a/django/cantusdb_project/main_app/models/source.py +++ b/django/cantusdb_project/main_app/models/source.py @@ -44,6 +44,12 @@ class Source(BaseModel): null=True, blank=True, ) + holding_institution = models.ForeignKey( + "Institution", + on_delete=models.PROTECT, + null=True, + blank=True, + ) provenance = models.ForeignKey( "Provenance", on_delete=models.PROTECT, diff --git a/django/cantusdb_project/main_app/templates/chant_create.html b/django/cantusdb_project/main_app/templates/chant_create.html index 39bb752cc..3e60159bc 100644 --- a/django/cantusdb_project/main_app/templates/chant_create.html +++ b/django/cantusdb_project/main_app/templates/chant_create.html @@ -84,7 +84,8 @@
The server encountered a temporary error and could not fulfill your request.
+Our apologies. Please wait a few moments and try again.
+The server encountered a temporary error and could not fulfill your request.
Our apologies. Please wait a few moments and try again.
diff --git a/nginx/error_pages/504.html b/nginx/error_pages/504.html index fc06a4805..74fb4a365 100644 --- a/nginx/error_pages/504.html +++ b/nginx/error_pages/504.html @@ -2,12 +2,12 @@The server took too long to respond and could not fulfill your request.
Our apologies. Please wait a few moments and try again.
diff --git a/nginx/error_pages/CantusLogoSmall.gif b/nginx/error_pages/CantusLogoSmall.gif deleted file mode 100644 index 7c2b19440..000000000 Binary files a/nginx/error_pages/CantusLogoSmall.gif and /dev/null differ diff --git a/nginx/error_pages/background.jpg b/nginx/error_pages/background.jpg deleted file mode 100644 index 746a98a51..000000000 Binary files a/nginx/error_pages/background.jpg and /dev/null differ diff --git a/nginx/error_pages/favicon.ico b/nginx/error_pages/favicon.ico deleted file mode 100644 index 07a2d4dbf..000000000 Binary files a/nginx/error_pages/favicon.ico and /dev/null differ diff --git a/nginx/password/.htpasswd b/nginx/password/.htpasswd new file mode 100644 index 000000000..4453f326c --- /dev/null +++ b/nginx/password/.htpasswd @@ -0,0 +1 @@ +# Note: This file is intentionally empty for development and production environments. It will be replaced with a file containing a hashed password on the staging server by ansible. \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 648ffd283..2ab6d339e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -84,13 +84,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -609,13 +609,13 @@ files = [ [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -647,23 +647,23 @@ type = ["mypy (>=1.8)"] [[package]] name = "psycopg" -version = "3.1.19" +version = "3.1.18" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"}, - {file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"}, + {file = "psycopg-3.1.18-py3-none-any.whl", hash = "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e"}, + {file = "psycopg-3.1.18.tar.gz", hash = "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b"}, ] [package.dependencies] -psycopg-binary = {version = "3.1.19", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} +psycopg-binary = {version = "3.1.18", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} typing-extensions = ">=4.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.1.19)"] -c = ["psycopg-c (==3.1.19)"] +binary = ["psycopg-binary (==3.1.18)"] +c = ["psycopg-c (==3.1.18)"] dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] @@ -671,74 +671,76 @@ test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6 [[package]] name = "psycopg-binary" -version = "3.1.19" +version = "3.1.18" description = "PostgreSQL database adapter for Python -- C optimisation distribution" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg_binary-3.1.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7204818f05151dd08f8f851defb01972ec9d2cc925608eb0de232563f203f354"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4e67fd86758dbeac85641419a54f84d74495a8683b58ad5dfad08b7fc37a8f"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12173e34b176e93ad2da913de30f774d5119c2d4d4640c6858d2d77dfa6c9bf"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052f5193304066318853b4b2e248f523c8f52b371fc4e95d4ef63baee3f30955"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29008f3f8977f600b8a7fb07c2e041b01645b08121760609cc45e861a0364dc9"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6a9a651a08d876303ed059c9553df18b3c13c3406584a70a8f37f1a1fe2709"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:91a645e6468c4f064b7f4f3b81074bdd68fe5aa2b8c5107de15dcd85ba6141be"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5c6956808fd5cf0576de5a602243af8e04594b25b9a28675feddc71c5526410a"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:1622ca27d5a7a98f7d8f35e8b146dc7efda4a4b6241d2edf7e076bd6bcecbeb4"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a100482950a55228f648bd382bb71bfaff520002f29845274fccbbf02e28bd52"}, - {file = "psycopg_binary-3.1.19-cp310-cp310-win_amd64.whl", hash = "sha256:955ca8905c0251fc4af7ce0a20999e824a25652f53a558ab548b60969f1f368e"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cf49e91dcf699b8a449944ed898ef1466b39b92720613838791a551bc8f587a"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:964c307e400c5f33fa762ba1e19853e048814fcfbd9679cc923431adb7a2ead2"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433924e1b14074798331dc2bfae2af452ed7888067f2fc145835704d8981b15"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00879d4c6be4b3afc510073f48a5e960f797200e261ab3d9bd9b7746a08c669d"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a6997c80f86d3dd80a4f078bb3b200079c47eeda4fd409d8899b883c90d2ac"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0106e42b481677c41caa69474fe530f786dcef88b11b70000f0e45a03534bc8f"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81efe09ba27533e35709905c3061db4dc9fb814f637360578d065e2061fbb116"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d312d6dddc18d9c164e1893706269c293cba1923118349d375962b1188dafb01"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:bfd2c734da9950f7afaad5f132088e0e1478f32f042881fca6651bb0c8d14206"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8a732610a5a6b4f06dadcf9288688a8ff202fd556d971436a123b7adb85596e2"}, - {file = "psycopg_binary-3.1.19-cp311-cp311-win_amd64.whl", hash = "sha256:321814a9a3ad785855a821b842aba08ca1b7de7dfb2979a2f0492dca9ec4ae70"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4aa0ca13bb8a725bb6d12c13999217fd5bc8b86a12589f28a74b93e076fbb959"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:469424e354ebcec949aa6aa30e5a9edc352a899d9a68ad7a48f97df83cc914cf"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04f5349313529ae1f1c42fe1aa0443faaf50fdf12d13866c2cc49683bfa53d0"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959feabddc7fffac89b054d6f23f3b3c62d7d3c90cd414a02e3747495597f150"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e9da624a6ca4bc5f7fa1f03f8485446b5b81d5787b6beea2b4f8d9dbef878ad7"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1823221a6b96e38b15686170d4fc5b36073efcb87cce7d3da660440b50077f6"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:866db42f986298f0cf15d805225eb8df2228bf19f7997d7f1cb5f388cbfc6a0f"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:738c34657305b5973af6dbb6711b07b179dfdd21196d60039ca30a74bafe9648"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb9758473200384a04374d0e0cac6f451218ff6945a024f65a1526802c34e56e"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e991632777e217953ac960726158987da684086dd813ac85038c595e7382c91"}, - {file = "psycopg_binary-3.1.19-cp312-cp312-win_amd64.whl", hash = "sha256:1d87484dd42c8783c44a30400949efb3d81ef2487eaa7d64d1c54df90cf8b97a"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d1d1723d7449c12bb61aca7eb6e0c6ab2863cd8dc0019273cc4d4a1982f84bdb"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e538a8671005641fa195eab962f85cf0504defbd3b548c4c8fc27102a59f687b"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c50592bc8517092f40979e4a5d934f96a1737a77724bb1d121eb78b614b30fc8"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95f16ae82bc242b76cd3c3e5156441e2bd85ff9ec3a9869d750aad443e46073c"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebd1e98e865e9a28ce0cb2c25b7dfd752f0d1f0a423165b55cd32a431dcc0f4"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:49cd7af7d49e438a39593d1dd8cab106a1912536c2b78a4d814ebdff2786094e"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:affebd61aa3b7a8880fd4ac3ee94722940125ff83ff485e1a7c76be9adaabb38"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d1bac282f140fa092f2bbb6c36ed82270b4a21a6fc55d4b16748ed9f55e50fdb"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1285aa54449e362b1d30d92b2dc042ad3ee80f479cc4e323448d0a0a8a1641fa"}, - {file = "psycopg_binary-3.1.19-cp37-cp37m-win_amd64.whl", hash = "sha256:6cff31af8155dc9ee364098a328bab688c887c732c66b8d027e5b03818ca0287"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d9b689c4a17dd3130791dcbb8c30dbf05602f7c2d56c792e193fb49adc7bf5f8"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:017518bd2de4851adc826a224fb105411e148ad845e11355edd6786ba3dfedf5"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c35fd811f339a3cbe7f9b54b2d9a5e592e57426c6cc1051632a62c59c4810208"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38ed45ec9673709bfa5bc17f140e71dd4cca56d4e58ef7fd50d5a5043a4f55c6"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:433f1c256108f9e26f480a8cd6ddb0fb37dbc87d7f5a97e4540a9da9b881f23f"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ed61e43bf5dc8d0936daf03a19fef3168d64191dbe66483f7ad08c4cea0bc36b"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ae8109ff9fdf1fa0cb87ab6645298693fdd2666a7f5f85660df88f6965e0bb7"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a53809ee02e3952fae7977c19b30fd828bd117b8f5edf17a3a94212feb57faaf"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9d39d5ffc151fb33bcd55b99b0e8957299c0b1b3e5a1a5f4399c1287ef0051a9"}, - {file = "psycopg_binary-3.1.19-cp38-cp38-win_amd64.whl", hash = "sha256:e14bc8250000921fcccd53722f86b3b3d1b57db901e206e49e2ab2afc5919c2d"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd88c5cea4efe614d5004fb5f5dcdea3d7d59422be796689e779e03363102d24"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:621a814e60825162d38760c66351b4df679fd422c848b7c2f86ad399bff27145"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46e50c05952b59a214e27d3606f6d510aaa429daed898e16b8a37bfbacc81acc"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03354a9db667c27946e70162cb0042c3929154167f3678a30d23cebfe0ad55b5"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c2f3b79037581afec7baa2bdbcb0a1787f1758744a7662099b0eca2d721cb"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6469ebd9e93327e9f5f36dcf8692fb1e7aeaf70087c1c15d4f2c020e0be3a891"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:85bca9765c04b6be90cb46e7566ffe0faa2d7480ff5c8d5e055ac427f039fd24"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:a836610d5c75e9cff98b9fdb3559c007c785c09eaa84a60d5d10ef6f85f671e8"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8de7a1d9fb3518cc6b58e3c80b75a824209ad52b90c542686c912db8553dad"}, - {file = "psycopg_binary-3.1.19-cp39-cp39-win_amd64.whl", hash = "sha256:76fcd33342f38e35cd6b5408f1bc117d55ab8b16e5019d99b6d3ce0356c51717"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c323103dfa663b88204cf5f028e83c77d7a715f9b6f51d2bbc8184b99ddd90a"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:887f8d856c91510148be942c7acd702ccf761a05f59f8abc123c22ab77b5a16c"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d322ba72cde4ca2eefc2196dad9ad7e52451acd2f04e3688d590290625d0c970"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:489aa4fe5a0b653b68341e9e44af247dedbbc655326854aa34c163ef1bcb3143"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ff0948457bfa8c0d35c46e3a75193906d1c275538877ba65907fd67aa059ad"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15e3653c82384b043d820fc637199b5c6a36b37fa4a4943e0652785bb2bad5d"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f8ff3bc08b43f36fdc24fedb86d42749298a458c4724fb588c4d76823ac39f54"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1729d0e3dfe2546d823841eb7a3d003144189d6f5e138ee63e5227f8b75276a5"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:13bcd3742112446037d15e360b27a03af4b5afcf767f5ee374ef8f5dd7571b31"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:320047e3d3554b857e16c2b6b615a85e0db6a02426f4d203a4594a2f125dfe57"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-win_amd64.whl", hash = "sha256:888a72c2aca4316ca6d4a619291b805677bae99bba2f6e31a3c18424a48c7e4d"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e4de16a637ec190cbee82e0c2dc4860fed17a23a35f7a1e6dc479a5c6876722"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6432047b8b24ef97e3fbee1d1593a0faaa9544c7a41a2c67d1f10e7621374c83"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d684227ef8212e27da5f2aff9d4d303cc30b27ac1702d4f6881935549486dd5"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67284e2e450dc7a9e4d76e78c0bd357dc946334a3d410defaeb2635607f632cd"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c9b6bd7fb5c6638cb32469674707649b526acfe786ba6d5a78ca4293d87bae4"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7121acc783c4e86d2d320a7fb803460fab158a7f0a04c5e8c5d49065118c1e73"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e28ff8f3de7b56588c2a398dc135fd9f157d12c612bd3daa7e6ba9872337f6f5"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c84a0174109f329eeda169004c7b7ca2e884a6305acab4a39600be67f915ed38"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:531381f6647fc267383dca88dbe8a70d0feff433a8e3d0c4939201fea7ae1b82"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b293e01057e63c3ac0002aa132a1071ce0fdb13b9ee2b6b45d3abdb3525c597d"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-win_amd64.whl", hash = "sha256:780a90bcb69bf27a8b08bc35b958e974cb6ea7a04cdec69e737f66378a344d68"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:87dd9154b757a5fbf6d590f6f6ea75f4ad7b764a813ae04b1d91a70713f414a1"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f876ebbf92db70125f6375f91ab4bc6b27648aa68f90d661b1fc5affb4c9731c"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d2f0cb45e4574f8b2fe7c6d0a0e2eb58903a4fd1fbaf60954fba82d595ab7"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd27f713f2e5ef3fd6796e66c1a5203a27a30ecb847be27a78e1df8a9a5ae68c"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c38a4796abf7380f83b1653c2711cb2449dd0b2e5aca1caa75447d6fa5179c69"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2f7f95746efd1be2dc240248cc157f4315db3fd09fef2adfcc2a76e24aa5741"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4085f56a8d4fc8b455e8f44380705c7795be5317419aa5f8214f315e4205d804"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2e2484ae835dedc80cdc7f1b1a939377dc967fed862262cfd097aa9f50cade46"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c2b039ae0c45eee4cd85300ef802c0f97d0afc78350946a5d0ec77dd2d7e834"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f54978c4b646dec77fefd8485fa82ec1a87807f334004372af1aaa6de9539a5"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-win_amd64.whl", hash = "sha256:9ffcbbd389e486d3fd83d30107bbf8b27845a295051ccabde240f235d04ed921"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c76659ae29a84f2c14f56aad305dd00eb685bd88f8c0a3281a9a4bc6bd7d2aa7"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7afcd6f1d55992f26d9ff7b0bd4ee6b475eb43aa3f054d67d32e09f18b0065"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:639dd78ac09b144b0119076783cb64e1128cc8612243e9701d1503c816750b2e"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1cf59e0bb12e031a48bb628aae32df3d0c98fd6c759cb89f464b1047f0ca9c8"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e262398e5d51563093edf30612cd1e20fedd932ad0994697d7781ca4880cdc3d"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:59701118c7d8842e451f1e562d08e8708b3f5d14974eefbce9374badd723c4ae"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dea4a59da7850192fdead9da888e6b96166e90608cf39e17b503f45826b16f84"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4575da95fc441244a0e2ebaf33a2b2f74164603341d2046b5cde0a9aa86aa7e2"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:812726266ab96de681f2c7dbd6b734d327f493a78357fcc16b2ac86ff4f4e080"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-win_amd64.whl", hash = "sha256:3e7ce4d988112ca6c75765c7f24c83bdc476a6a5ce00878df6c140ca32c3e16d"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:02bd4da45d5ee9941432e2e9bf36fa71a3ac21c6536fe7366d1bd3dd70d6b1e7"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39242546383f6b97032de7af30edb483d237a0616f6050512eee7b218a2aa8ee"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d46ae44d66bf6058a812467f6ae84e4e157dee281bfb1cfaeca07dee07452e85"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad35ac7fd989184bf4d38a87decfb5a262b419e8ba8dcaeec97848817412c64a"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:247474af262bdd5559ee6e669926c4f23e9cf53dae2d34c4d991723c72196404"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ebecbf2406cd6875bdd2453e31067d1bd8efe96705a9489ef37e93b50dc6f09"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1859aeb2133f5ecdd9cbcee155f5e38699afc06a365f903b1512c765fd8d457e"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:da917f6df8c6b2002043193cb0d74cc173b3af7eb5800ad69c4e1fbac2a71c30"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9e24e7b6a68a51cc3b162d0339ae4e1263b253e887987d5c759652f5692b5efe"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e252d66276c992319ed6cd69a3ffa17538943954075051e992143ccbf6dc3d3e"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-win_amd64.whl", hash = "sha256:5d6e860edf877d4413e4a807e837d55e3a7c7df701e9d6943c06e460fa6c058f"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eea5f14933177ffe5c40b200f04f814258cc14b14a71024ad109f308e8bad414"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:824a1bfd0db96cc6bef2d1e52d9e0963f5bf653dd5bc3ab519a38f5e6f21c299"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87e9eeb80ce8ec8c2783f29bce9a50bbcd2e2342a340f159c3326bf4697afa1"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91074f78a9f890af5f2c786691575b6b93a4967ad6b8c5a90101f7b8c1a91d9c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e05f6825f8db4428782135e6986fec79b139210398f3710ed4aa6ef41473c008"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f68ac2364a50d4cf9bb803b4341e83678668f1881a253e1224574921c69868c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7ac1785d67241d5074f8086705fa68e046becea27964267ab3abd392481d7773"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cd2a9f7f0d4dacc5b9ce7f0e767ae6cc64153264151f50698898c42cabffec0c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:3e4b0bb91da6f2238dbd4fbb4afc40dfb4f045bb611b92fce4d381b26413c686"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:74e498586b72fb819ca8ea82107747d0cb6e00ae685ea6d1ab3f929318a8ce2d"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:d4422af5232699f14b7266a754da49dc9bcd45eba244cf3812307934cd5d6679"}, ] [[package]] @@ -909,13 +911,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.12.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, - {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -1117,4 +1119,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "af458bd43661966daeb26a077486bc69cee1287ab9d72a85807257b204961c0c" +content-hash = "6f5003b8a6d7561adb30e8a4aa55fd51fb24acc55698082fbc00e54df7d5ac96" diff --git a/pyproject.toml b/pyproject.toml index f2d4828fd..01c817acf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ gunicorn = "22.0.0" requests = "2.32.2" ujson = "5.9.0" volpiano-display-utilities = { git = "https://github.com/DDMAL/volpiano-display-utilities.git", tag = "v1.1.3" } -psycopg = {extras = ["binary"], version = "^3.1.18"} +psycopg = {extras = ["binary"], version = "3.1.18"} [tool.poetry.group.dev] optional = true