summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2024-11-22 12:33:44 +0100
committerÉtienne Loks <etienne.loks@iggdrasil.net>2025-02-19 14:45:56 +0100
commitf10b03c55ece933e4277cdf1e7d4acfba9fdd7ed (patch)
treedf505f12323d4bcc2e3f63521c9ada64b3585e7f
parente7dfaffc372292d8467da8a8be872e9ac9456618 (diff)
downloadIshtar-f10b03c55ece933e4277cdf1e7d4acfba9fdd7ed.tar.bz2
Ishtar-f10b03c55ece933e4277cdf1e7d4acfba9fdd7ed.zip
🗃️ database: exhibition
-rw-r--r--Makefile.example1
-rw-r--r--archaeological_finds/admin.py20
-rw-r--r--archaeological_finds/fixtures/initial_data-fr.json9
-rw-r--r--archaeological_finds/ishtar_menu.py4
-rw-r--r--archaeological_finds/migrations/0133_exhibition.py131
-rw-r--r--archaeological_finds/models.py4
-rw-r--r--archaeological_finds/models_treatments.py98
-rw-r--r--archaeological_finds/urls.py15
8 files changed, 274 insertions, 8 deletions
diff --git a/Makefile.example b/Makefile.example
index c286a877e..59cd303cf 100644
--- a/Makefile.example
+++ b/Makefile.example
@@ -318,6 +318,7 @@ fixtures_finds:
archaeological_finds.batchtype \
archaeological_finds.checkedtype \
archaeological_finds.treatmentfiletype \
+ archaeological_finds.exhibitiontype \
archaeological_finds.checkedtype \
archaeological_finds.technicalprocesstype \
archaeological_finds.technicalareatype \
diff --git a/archaeological_finds/admin.py b/archaeological_finds/admin.py
index b616b3dc7..669c41ead 100644
--- a/archaeological_finds/admin.py
+++ b/archaeological_finds/admin.py
@@ -228,21 +228,37 @@ class ConservatoryStateAdmin(GeneralTypeAdmin):
@admin.register(models.TreatmentFileType, site=admin_site)
-class TreatmentFileType(GeneralTypeAdmin):
+class TreatmentFileTypeAdmin(GeneralTypeAdmin):
extra_list_display = ["treatment_type"]
@admin.register(models.TreatmentState, site=admin_site)
-class TreatmentState(GeneralTypeAdmin):
+class TreatmentStateAdmin(GeneralTypeAdmin):
extra_list_display = ["order", "executed"]
+@admin.register(models.Exhibition, site=admin_site)
+class ExhibitionAdmin(HistorizedObjectAdmin):
+ list_display = ('name', 'year', 'reference', 'exhibition_type')
+ list_filter = ('exhibition_type',)
+ search_fields = ('name', 'reference')
+ model = models.Exhibition
+ autocomplete_fields = HistorizedObjectAdmin.autocomplete_fields + [
+ 'in_charge', 'treatment_files'
+ ]
+ readonly_fields = HistorizedObjectAdmin.readonly_fields + [
+ 'ishtar_users',
+ ]
+ exclude = ["documents", "main_image"]
+
+
general_models = [
models.AlterationCauseType, models.AlterationType, models.BatchType,
models.CollectionEntryModeType, models.IntegrityType, models.InventoryConformity,
models.InventoryMarkingPresence, models.MarkingType, models.MaterialTypeQualityType,
models.MuseumCollection, models.ObjectTypeQualityType, models.OriginalReproduction,
models.RemarkabilityType, models.TreatmentEmergencyType, models.DiscoveryMethod,
+ models.ExhibitionType
]
for model in general_models:
diff --git a/archaeological_finds/fixtures/initial_data-fr.json b/archaeological_finds/fixtures/initial_data-fr.json
index ef6af8b9e..b969d15ca 100644
--- a/archaeological_finds/fixtures/initial_data-fr.json
+++ b/archaeological_finds/fixtures/initial_data-fr.json
@@ -7082,5 +7082,14 @@
"available": true,
"order": 30
}
+},
+{
+ "model": "archaeological_finds.exhibitiontype",
+ "fields": {
+ "label": "Exposition",
+ "txt_idx": "exhibition",
+ "comment": "",
+ "available": true
+ }
}
]
diff --git a/archaeological_finds/ishtar_menu.py b/archaeological_finds/ishtar_menu.py
index 967d3267c..0326932e6 100644
--- a/archaeological_finds/ishtar_menu.py
+++ b/archaeological_finds/ishtar_menu.py
@@ -143,8 +143,8 @@ MENU_SECTIONS = [
model=models.TreatmentFile,
profile_restriction="museum",
access_controls=[
- "archaeological_finds.view_treatmentfile",
- "archaeological_finds.view_own_treatmentfile"
+ "archaeological_finds.view_exhibition",
+ "archaeological_finds.view_own_exhibition"
],
),
MenuItem(
diff --git a/archaeological_finds/migrations/0133_exhibition.py b/archaeological_finds/migrations/0133_exhibition.py
new file mode 100644
index 000000000..a4a29168e
--- /dev/null
+++ b/archaeological_finds/migrations/0133_exhibition.py
@@ -0,0 +1,131 @@
+# Generated by Django 2.2.24 on 2024-11-21 13:05
+
+import archaeological_finds.models_treatments
+import datetime
+from django.conf import settings
+import django.contrib.postgres.fields.jsonb
+import django.contrib.postgres.indexes
+import django.contrib.postgres.search
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models
+import ishtar_common.models_common
+import ishtar_common.utils
+import re
+import simple_history.models
+
+
+def create_default(apps, __):
+ ExhibitionType = apps.get_model("archaeological_finds", "ExhibitionType")
+ ExhibitionType.objects.get_or_create(
+ txt_idx="exhibition", defaults={"label": "Exposition"}
+ )
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('ishtar_common', '0258_rename_perm_query'),
+ ('archaeological_finds', '0132_exhibitions_basket_for_treatment'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ExhibitionType',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('label', models.TextField(verbose_name='Label')),
+ ('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')], verbose_name='Textual ID')),
+ ('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
+ ('available', models.BooleanField(default=True, verbose_name='Available')),
+ ],
+ options={
+ 'verbose_name': 'Exhibition type',
+ 'verbose_name_plural': 'Exhibition types',
+ 'ordering': ('label',),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='HistoricalExhibition',
+ fields=[
+ ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
+ ('search_vector', django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector')),
+ ('data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict)),
+ ('last_modified', models.DateTimeField(blank=True, default=datetime.datetime.now)),
+ ('created', models.DateTimeField(blank=True, default=datetime.datetime.now)),
+ ('history_m2m', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict)),
+ ('locked', models.BooleanField(default=False, verbose_name='Item locked for edition')),
+ ('qrcode', models.TextField(blank=True, max_length=255, null=True)),
+ ('name', models.TextField(verbose_name='Name')),
+ ('year', models.IntegerField(default=ishtar_common.utils.get_current_year, verbose_name='Year')),
+ ('reference', models.TextField(blank=True, default='-', null=True, verbose_name='Reference')),
+ ('associated_basket_id', models.PositiveIntegerField(blank=True, help_text='Reference basket', null=True, verbose_name='Basket ID')),
+ ('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
+ ('history_id', models.AutoField(primary_key=True, serialize=False)),
+ ('history_date', models.DateTimeField()),
+ ('history_change_reason', models.CharField(max_length=100, null=True)),
+ ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
+ ('exhibition_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.ExhibitionType', verbose_name='Exhibition type')),
+ ('history_creator', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Creator')),
+ ('history_modifier', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Last editor')),
+ ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
+ ('in_charge', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Person', verbose_name='Person in charge')),
+ ('lock_user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Locked by')),
+ ('main_image', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Document', verbose_name='Main image')),
+ ],
+ options={
+ 'verbose_name': 'historical Exhibition',
+ 'ordering': ('-history_date', '-history_id'),
+ 'get_latest_by': 'history_date',
+ },
+ bases=(simple_history.models.HistoricalChanges, models.Model),
+ ),
+ migrations.CreateModel(
+ name='Exhibition',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('search_vector', django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector')),
+ ('data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict)),
+ ('last_modified', models.DateTimeField(blank=True, default=datetime.datetime.now)),
+ ('created', models.DateTimeField(blank=True, default=datetime.datetime.now)),
+ ('history_m2m', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict)),
+ ('locked', models.BooleanField(default=False, verbose_name='Item locked for edition')),
+ ('qrcode', models.ImageField(blank=True, max_length=255, null=True, upload_to=ishtar_common.utils.get_image_path)),
+ ('name', models.TextField(verbose_name='Name')),
+ ('year', models.IntegerField(default=ishtar_common.utils.get_current_year, verbose_name='Year')),
+ ('reference', models.TextField(blank=True, default='-', null=True, verbose_name='Reference')),
+ ('associated_basket_id', models.PositiveIntegerField(blank=True, help_text='Reference basket', null=True, verbose_name='Basket ID')),
+ ('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
+ ('documents', models.ManyToManyField(blank=True, related_name='exhibitions', to='ishtar_common.Document', verbose_name='Documents')),
+ ('exhibition_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='archaeological_finds.ExhibitionType', verbose_name='Exhibition type')),
+ ('history_creator', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Creator')),
+ ('history_modifier', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Last editor')),
+ ('imports', models.ManyToManyField(blank=True, related_name='imported_archaeological_finds_exhibition', to='ishtar_common.Import', verbose_name='Created by imports')),
+ ('imports_updated', models.ManyToManyField(blank=True, related_name='import_updated_archaeological_finds_exhibition', to='ishtar_common.Import', verbose_name='Updated by imports')),
+ ('in_charge', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='exhibitions', to='ishtar_common.Person', verbose_name='Person in charge')),
+ ('ishtar_users', models.ManyToManyField(blank=True, related_name='exhibition_associated', to='ishtar_common.IshtarUser')),
+ ('lock_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Locked by')),
+ ('main_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_image_exhibitions', to='ishtar_common.Document', verbose_name='Main image')),
+ ('treatment_files', models.ManyToManyField(blank=True, related_name='exhibitions', to='archaeological_finds.TreatmentFile', verbose_name='Loans')),
+ ],
+ options={
+ 'verbose_name': 'Exhibition',
+ 'verbose_name_plural': 'Exhibitions',
+ 'ordering': ('year', 'name'),
+ 'permissions': (('view_own_exhibition', 'Can view own Exhibition'), ('change_own_exhibition', 'Can change own Exhibition'), ('delete_own_exhibition', 'Can delete own Exhibition')),
+ },
+ bases=(ishtar_common.models_common.DocumentItem, ishtar_common.models_common.StatisticItem, ishtar_common.models_common.TemplateItem, models.Model, ishtar_common.models_common.CachedGen, ishtar_common.models_common.FixAssociated, ishtar_common.models_common.ImageContainerModel, ishtar_common.utils.OwnPerms, ishtar_common.models.ValueGetter, ishtar_common.models_common.MainItem, archaeological_finds.models_treatments.AssociatedFindBasket),
+ ),
+ migrations.AddIndex(
+ model_name='exhibition',
+ index=django.contrib.postgres.indexes.GinIndex(fields=['data'], name='archaeologi_data_dd22e0_gin'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='exhibition',
+ unique_together={('year', 'name')},
+ ),
+ migrations.RunPython(create_default),
+ ]
diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py
index ce2098d8d..90ca4bffd 100644
--- a/archaeological_finds/models.py
+++ b/archaeological_finds/models.py
@@ -35,6 +35,8 @@ from archaeological_finds.models_finds import (
)
from archaeological_finds.models_treatments import (
AbsFindTreatments,
+ Exhibition,
+ ExhibitionType,
Treatment,
FindDownstreamTreatments,
FindNonModifTreatments,
@@ -57,6 +59,8 @@ __all__ = [
"CommunicabilityType",
"ConservatoryState",
"DiscoveryMethod",
+ "Exhibition",
+ "ExhibitionType",
"FBulkView",
"Find",
"FirstBaseFindView",
diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py
index 9a9455ea1..933df0fa8 100644
--- a/archaeological_finds/models_treatments.py
+++ b/archaeological_finds/models_treatments.py
@@ -1454,3 +1454,101 @@ class TreatmentFile(
m2m_changed.connect(document_attached_changed, sender=TreatmentFile.documents.through)
post_save.connect(cached_label_changed, sender=TreatmentFile)
+
+
+class ExhibitionType(GeneralType):
+ class Meta:
+ verbose_name = _("Exhibition type")
+ verbose_name_plural = _("Exhibition types")
+ ordering = ("label",)
+ ADMIN_SECTION = _("Treatments")
+
+
+class Exhibition(
+ DocumentItem,
+ BaseHistorizedItem,
+ CompleteIdentifierItem,
+ OwnPerms,
+ ValueGetter,
+ MainItem,
+ AssociatedFindBasket,
+):
+ SLUG = "exhibition"
+ APP = "archaeological_finds"
+ MODEL = SLUG
+ SHOW_URL = "show-exhibition"
+ DELETE_URL = "delete-exhibition"
+ TABLE_COLS = ["year", "reference", "name"]
+ BASE_SEARCH_VECTORS = [
+ SearchVectorConfig("exhibition_type__label"),
+ SearchVectorConfig("reference"),
+ SearchVectorConfig("name"),
+ SearchVectorConfig("comment", "local"),
+ ]
+ name = models.TextField(_("Name"))
+ exhibition_type = models.ForeignKey(
+ ExhibitionType,
+ verbose_name=_("Exhibition type"),
+ on_delete=models.PROTECT,
+ )
+ year = models.IntegerField(_("Year"), default=get_current_year)
+ reference = models.TextField(
+ _("Reference"), blank=True, null=True, default="-"
+ )
+ in_charge = models.ForeignKey(
+ Person,
+ related_name="exhibitions",
+ verbose_name=_("Person in charge"),
+ on_delete=models.SET_NULL,
+ blank=True,
+ null=True,
+ )
+ # prevent circular imports...
+ associated_basket_id = models.PositiveIntegerField(
+ verbose_name=_("Basket ID"), blank=True, null=True,
+ help_text=_("Reference basket")
+ )
+ comment = models.TextField(_("Comment"), blank=True, default="")
+ treatment_files = models.ManyToManyField(
+ TreatmentFile,
+ related_name="exhibitions",
+ verbose_name=_("Loans"),
+ blank=True,
+ )
+ documents = models.ManyToManyField(
+ Document,
+ related_name="exhibitions",
+ verbose_name=_("Documents"),
+ blank=True,
+ )
+ main_image = models.ForeignKey(
+ Document,
+ related_name="main_image_exhibitions",
+ on_delete=models.SET_NULL,
+ verbose_name=_("Main image"),
+ blank=True,
+ null=True,
+ )
+ timestamp_geo = None
+ timestamp_label = None
+ complete_identifier = None
+ custom_index = None
+ need_update = None
+ cached_label = None
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _("Exhibition")
+ verbose_name_plural = _("Exhibitions")
+ unique_together = ("year", "name")
+ permissions = (
+ ("view_own_exhibition", "Can view own Exhibition"),
+ ("change_own_exhibition", "Can change own Exhibition"),
+ ("delete_own_exhibition", "Can delete own Exhibition"),
+ )
+ ordering = ("year", "name")
+ indexes = [
+ GinIndex(fields=["data"]),
+ ]
+ ADMIN_SECTION = _("Treatments")
+
diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py
index 2af33537f..467df9770 100644
--- a/archaeological_finds/urls.py
+++ b/archaeological_finds/urls.py
@@ -442,16 +442,23 @@ urlpatterns = [
path(
"exhibition/",
check_permissions(
- ["archaeological_finds.view_treatmentfile",
- "archaeological_finds.view_own_treatmentfile"]
+ ["archaeological_finds.view_exhibition",
+ "archaeological_finds.view_own_exhibition"]
)(views.exhibition_wizard),
name="exhibition-search",
),
path(
+ "exhibition/create/",
+ check_permissions(
+ ["archaeological_finds.add_exhibition"]
+ )(views.ExhibitionCreateView.as_view()),
+ name="exhibition-create",
+ ),
+ path(
"exhibition/<step>/",
check_permissions(
- ["archaeological_finds.view_treatmentfile",
- "archaeological_finds.view_own_treatmentfile"]
+ ["archaeological_finds.view_exhibition",
+ "archaeological_finds.view_own_exhibition"]
)(views.exhibition_wizard),
name="exhibition-search",
),