summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2024-04-16 16:41:46 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2024-04-16 16:41:46 +0200
commit6e95beabf26ddd3d8de69e34dfbfb97bc1625d80 (patch)
tree364c2688b46f47d3ee9243e70cbaf5485e177b0f
parent303a62efae3d3f568545c682649a29de1fb7fc83 (diff)
downloadIshtar-6e95beabf26ddd3d8de69e34dfbfb97bc1625d80.tar.bz2
Ishtar-6e95beabf26ddd3d8de69e34dfbfb97bc1625d80.zip
🗃️ museum module: new db fields, add admin
-rw-r--r--archaeological_finds/admin.py22
-rw-r--r--archaeological_finds/forms.py2
-rw-r--r--archaeological_finds/migrations/0115_auto_20240208_1636.py432
-rw-r--r--archaeological_finds/models.py102
-rw-r--r--archaeological_finds/models_finds.py275
-rw-r--r--archaeological_finds/templates/ishtar/sheet_find.html2
-rw-r--r--ishtar_common/admin.py4
-rw-r--r--ishtar_common/forms_common.py4
-rw-r--r--ishtar_common/migrations/0236_auto_20240208_1635.py112
-rw-r--r--ishtar_common/models.py80
-rw-r--r--ishtar_common/models_common.py18
-rw-r--r--ishtar_common/templates/ishtar/sheet_document.html2
-rw-r--r--requirements.txt2
13 files changed, 970 insertions, 87 deletions
diff --git a/archaeological_finds/admin.py b/archaeological_finds/admin.py
index 98c86141b..934089a7a 100644
--- a/archaeological_finds/admin.py
+++ b/archaeological_finds/admin.py
@@ -162,6 +162,18 @@ class FunctionalAreaAdmin(GeneralTypeAdmin):
admin_site.register(models.FunctionalArea, FunctionalAreaAdmin)
+@admin.register(models.TechnicalAreaType, site=admin_site)
+class TechnicalAreaTypeAdmin(GeneralTypeAdmin):
+ model = models.TechnicalAreaType
+ autocomplete_fields = ("parent",)
+
+
+@admin.register(models.TechnicalProcessType, site=admin_site)
+class TechnicalProcessTypeAdmin(GeneralTypeAdmin):
+ model = models.TechnicalProcessType
+ autocomplete_fields = ("parent",)
+
+
class MaterialTypeAdmin(GeneralTypeAdmin):
search_fields = ('label', 'parent__label', 'comment',)
model = models.MaterialType
@@ -225,11 +237,11 @@ class TreatmentState(GeneralTypeAdmin):
general_models = [
- models.RemarkabilityType,
- models.IntegrityType,
- models.BatchType, models.AlterationCauseType, models.AlterationType,
- models.TreatmentEmergencyType, models.ObjectTypeQualityType,
- models.MaterialTypeQualityType
+ 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,
]
for model in general_models:
diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py
index 691383cc6..460e34375 100644
--- a/archaeological_finds/forms.py
+++ b/archaeological_finds/forms.py
@@ -277,7 +277,7 @@ class BasicFindForm(CustomForm, ManageOldType):
)
denomination = forms.CharField(label=_("Denomination"), required=False)
previous_id = forms.CharField(label=_("Previous ID"), required=False)
- museum_id = forms.CharField(label=_("Museum inventory number"), required=False)
+ museum_id = forms.CharField(label=_("Museum ID"), required=False)
laboratory_id = forms.CharField(label=_("Laboratory ID"), required=False)
seal_number = forms.CharField(label=_("Seal number"), required=False)
mark = forms.CharField(label=_("Mark"), required=False)
diff --git a/archaeological_finds/migrations/0115_auto_20240208_1636.py b/archaeological_finds/migrations/0115_auto_20240208_1636.py
new file mode 100644
index 000000000..3c39093a6
--- /dev/null
+++ b/archaeological_finds/migrations/0115_auto_20240208_1636.py
@@ -0,0 +1,432 @@
+# Generated by Django 2.2.24 on 2024-02-08 16:36
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models_common
+import re
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0236_auto_20240208_1635'),
+ ('archaeological_finds', '0114_auto_20231115_1617'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='InventoryConformity',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ],
+ options={
+ 'verbose_name': 'Inventory conformity type',
+ 'verbose_name_plural': 'Inventory conformity types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='InventoryMarkingPresence',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ],
+ options={
+ 'verbose_name': 'Presence of inventory marking type',
+ 'verbose_name_plural': 'Presence of inventory marking types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='MarkingType',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ],
+ options={
+ 'verbose_name': 'Marking type',
+ 'verbose_name_plural': 'Marking types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='MuseumCollection',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ],
+ options={
+ 'verbose_name': 'Museum collection',
+ 'verbose_name_plural': 'Museum collections',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='OriginalReproduction',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ],
+ options={
+ 'verbose_name': 'Original/reproduction type',
+ 'verbose_name_plural': 'Original/reproduction types',
+ 'ordering': ('order', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='cache_complete_museum_id',
+ field=models.TextField(blank=True, db_index=True, default='', help_text='Cached value - do not edit', verbose_name='Complete museum ID'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_allocation_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Date of museum allocation'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_custodian_institution',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='deposited', to='ishtar_common.Organization', verbose_name='Custodian institution'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_depositor_inventory_number',
+ field=models.TextField(blank=True, default='', verbose_name='Depositor inventory number'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_donor',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='has_provided', to='ishtar_common.BiographicalNote', verbose_name='Donor, testator or vendor'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_entry_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Museum entry date (exact or start)'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_entry_date_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on museum entry date'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_entry_date_end',
+ field=models.DateField(blank=True, null=True, verbose_name='Museum entry date (end)'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_entry_mode_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on museum entry mode'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_former_collection',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.BiographicalNote', verbose_name='Former collection'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_id_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on museum ID'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_id_prefix',
+ field=models.TextField(blank=True, default='', verbose_name='Museum ID prefix'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_id_suffix',
+ field=models.TextField(blank=True, default='', verbose_name='Museum ID suffix'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_inventory_entry_year',
+ field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Inventory entry year'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_inventory_transcript',
+ field=models.TextField(blank=True, default='', verbose_name='Inventory transcript'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_marking_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on marking'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_non_conformity_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment of non-conformity'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_owner_institution',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owns', to='ishtar_common.Organization', verbose_name='Owner institution'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_purchase_price',
+ field=models.TextField(blank=True, default='', verbose_name='Purchase price'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='quantity_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on quantity'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='cache_complete_museum_id',
+ field=models.TextField(blank=True, db_index=True, default='', help_text='Cached value - do not edit', verbose_name='Complete museum ID'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_allocation_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Date of museum allocation'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_custodian_institution',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Organization', verbose_name='Custodian institution'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_depositor_inventory_number',
+ field=models.TextField(blank=True, default='', verbose_name='Depositor inventory number'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_donor',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.BiographicalNote', verbose_name='Donor, testator or vendor'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_entry_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Museum entry date (exact or start)'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_entry_date_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on museum entry date'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_entry_date_end',
+ field=models.DateField(blank=True, null=True, verbose_name='Museum entry date (end)'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_entry_mode_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on museum entry mode'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_former_collection',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.BiographicalNote', verbose_name='Former collection'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_id_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on museum ID'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_id_prefix',
+ field=models.TextField(blank=True, default='', verbose_name='Museum ID prefix'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_id_suffix',
+ field=models.TextField(blank=True, default='', verbose_name='Museum ID suffix'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_inventory_entry_year',
+ field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Inventory entry year'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_inventory_transcript',
+ field=models.TextField(blank=True, default='', verbose_name='Inventory transcript'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_marking_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on marking'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_non_conformity_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment of non-conformity'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_owner_institution',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Organization', verbose_name='Owner institution'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_purchase_price',
+ field=models.TextField(blank=True, default='', verbose_name='Purchase price'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='quantity_comment',
+ field=models.TextField(blank=True, default='', verbose_name='Comment on quantity'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='functional_areas',
+ field=models.ManyToManyField(blank=True, related_name='find', to='archaeological_finds.FunctionalArea', verbose_name='Functional areas'),
+ ),
+ migrations.AlterField(
+ model_name='find',
+ name='museum_id',
+ field=models.TextField(blank=True, default='', verbose_name='Museum ID'),
+ ),
+ migrations.AlterField(
+ model_name='historicalfind',
+ name='museum_id',
+ field=models.TextField(blank=True, default='', verbose_name='Museum ID'),
+ ),
+ migrations.CreateModel(
+ name='TechnicalProcessType',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.TechnicalProcessType', verbose_name='Parent')),
+ ],
+ options={
+ 'verbose_name': 'Technical process type',
+ 'verbose_name_plural': 'Technical process types',
+ 'ordering': ('order', 'parent__label', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='TechnicalAreaType',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.TechnicalAreaType', verbose_name='Parent')),
+ ],
+ options={
+ 'verbose_name': 'Technical area type',
+ 'verbose_name_plural': 'Technical area types',
+ 'ordering': ('order', 'parent__label', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.CreateModel(
+ name='CollectionEntryModeType',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.CollectionEntryModeType', verbose_name='Parent')),
+ ],
+ options={
+ 'verbose_name': 'Collection entry mode type',
+ 'verbose_name_plural': 'Collection entry mode types',
+ 'ordering': ('order', 'parent__label', 'label'),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_collection',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_collection_of', to='archaeological_finds.MuseumCollection', verbose_name='Collection'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_collection_entry_mode',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.CollectionEntryModeType', verbose_name='Collections entry mode'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_inventory_conformity',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.InventoryConformity', verbose_name='Conformity with inventory'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_inventory_marking_presence',
+ field=models.ManyToManyField(blank=True, related_name='finds', to='archaeological_finds.InventoryMarkingPresence', verbose_name='Presence of inventory marking'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_marking_type',
+ field=models.ManyToManyField(blank=True, related_name='finds', to='archaeological_finds.MarkingType', verbose_name='Type of marking'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='museum_original_repro',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.OriginalReproduction', verbose_name='Original/reproduction'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='technical_areas',
+ field=models.ManyToManyField(blank=True, related_name='find', to='archaeological_finds.TechnicalAreaType', verbose_name='Technical areas'),
+ ),
+ migrations.AddField(
+ model_name='find',
+ name='technical_processes',
+ field=models.ManyToManyField(blank=True, related_name='find', to='archaeological_finds.TechnicalProcessType', verbose_name='Technical processes'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_collection',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.MuseumCollection', verbose_name='Collection'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_collection_entry_mode',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.CollectionEntryModeType', verbose_name='Collections entry mode'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_inventory_conformity',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.InventoryConformity', verbose_name='Conformity with inventory'),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='museum_original_repro',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.OriginalReproduction', verbose_name='Original/reproduction'),
+ ),
+ ]
diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py
index c8c526d2f..052e493a4 100644
--- a/archaeological_finds/models.py
+++ b/archaeological_finds/models.py
@@ -1,71 +1,87 @@
from archaeological_finds.models_finds import (
- MaterialType,
- ConservatoryState,
- CheckedType,
- IntegrityType,
- RemarkabilityType,
- ObjectType,
- BaseFind,
- FindBasket,
- Find,
- Property,
+ AlterationType,
+ AlterationCauseType,
BatchType,
+ BaseFind,
BFBulkView,
+ CheckedType,
+ CollectionEntryModeType,
+ CommunicabilityType,
+ ConservatoryState,
FBulkView,
+ Find,
+ FindBasket,
+ FindInsideContainer,
FirstBaseFindView,
- AlterationType,
- AlterationCauseType,
- TreatmentEmergencyType,
- TreatmentType,
- CommunicabilityType,
+ FunctionalArea,
+ IntegrityType,
+ InventoryConformity,
+ InventoryMarkingPresence,
+ MarkingType,
+ MaterialType,
MaterialTypeQualityType,
+ MuseumCollection,
+ ObjectType,
ObjectTypeQualityType,
- FindInsideContainer,
- FunctionalArea,
+ OriginalReproduction,
+ Property,
+ RemarkabilityType,
+ TechnicalAreaType,
+ TechnicalProcessType,
+ TreatmentEmergencyType,
+ TreatmentType,
)
from archaeological_finds.models_treatments import (
- Treatment,
AbsFindTreatments,
- FindUpstreamTreatments,
+ Treatment,
FindDownstreamTreatments,
+ FindNonModifTreatments,
FindTreatments,
+ FindUpstreamTreatments,
TreatmentFile,
TreatmentFileType,
TreatmentState,
- FindNonModifTreatments,
)
__all__ = [
- "MaterialType",
- "ConservatoryState",
- "IntegrityType",
- "CheckedType",
- "RemarkabilityType",
- "ObjectType",
+ "AbsFindTreatments",
+ "AlterationType",
+ "AlterationCauseType",
"BaseFind",
- "FindBasket",
- "Find",
- "Property",
+ "BatchType",
"BFBulkView",
+ "CheckedType",
+ "CollectionEntryModeType",
+ "CommunicabilityType",
+ "ConservatoryState",
"FBulkView",
+ "Find",
"FirstBaseFindView",
- "AlterationType",
- "AlterationCauseType",
- "TreatmentEmergencyType",
- "BatchType",
- "TreatmentType",
- "TreatmentState",
- "Treatment",
- "AbsFindTreatments",
- "FindUpstreamTreatments",
- "FindNonModifTreatments",
+ "FindBasket",
"FindDownstreamTreatments",
+ "FindInsideContainer",
+ "FindNonModifTreatments",
"FindTreatments",
+ "FindUpstreamTreatments",
"FunctionalArea",
- "TreatmentFile",
- "TreatmentFileType",
- "CommunicabilityType",
+ "IntegrityType",
+ "InventoryConformity",
+ "InventoryMarkingPresence",
+ "MarkingType",
+ "MaterialType",
"MaterialTypeQualityType",
+ "MuseumCollection",
+ "ObjectType",
"ObjectTypeQualityType",
- "FindInsideContainer",
+ "OriginalReproduction",
+ "Property",
+ "RemarkabilityType",
+ "TechnicalAreaType",
+ "TechnicalProcessType",
+ "Treatment",
+ "TreatmentEmergencyType",
+ "TreatmentFile",
+ "TreatmentFileType",
+ "TreatmentType",
+ "TreatmentState",
]
diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py
index cb431f7cc..6c086ea93 100644
--- a/archaeological_finds/models_finds.py
+++ b/archaeological_finds/models_finds.py
@@ -25,48 +25,52 @@ from django.apps import apps
from django.conf import settings
from django.contrib.gis.db import models
from django.contrib.postgres.indexes import GinIndex
-from django.db import connection
from django.db.models import Max, Q, F
from django.db.models.signals import m2m_changed, post_save, post_delete, pre_delete
from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse
-from ishtar_common.utils import ugettext_lazy as _, pgettext_lazy
from ishtar_common.data_importer import post_importer_action, ImporterError
from ishtar_common.utils import (
cached_label_changed,
- post_save_geo,
+ get_generated_id,
m2m_historization_changed,
+ pgettext_lazy,
+ post_save_geo,
+ ugettext_lazy as _
)
from ishtar_common.alternative_configs import ALTERNATE_CONFIGS
-from ishtar_common.model_managers import ExternalIdManager, UUIDModelManager
+from ishtar_common.model_managers import UUIDModelManager
from ishtar_common.models import (
+ BaseHistorizedItem,
+ Basket,
+ BiographicalNote,
+ BulkUpdatedItem,
+ CompleteIdentifierItem,
Document,
+ DocumentItem,
+ document_attached_changed,
GeneralType,
+ GeoItem,
+ get_current_profile,
HierarchicalType,
- BaseHistorizedItem,
+ HistoryModel,
+ Imported,
+ IshtarSiteProfile,
LightHistorizedItem,
+ MainItem,
+ OrderedHierarchicalType,
+ OrderedType,
+ Organization,
OwnPerms,
- Imported,
Person,
- Basket,
post_save_cache,
- ValueGetter,
- get_current_profile,
- IshtarSiteProfile,
- BulkUpdatedItem,
QuickAction,
- MainItem,
- document_attached_changed,
- HistoryModel,
- DynamicRequest,
SearchAltName,
- CompleteIdentifierItem,
SearchVectorConfig,
- DocumentItem,
- GeoItem
+ ValueGetter,
)
from ishtar_common.models_common import HistoricalRecords, SerializeItem, \
GeoVectorData, geodata_attached_changed
@@ -296,6 +300,38 @@ post_save.connect(post_save_cache, sender=FunctionalArea)
post_delete.connect(post_save_cache, sender=FunctionalArea)
+class TechnicalAreaType(OrderedHierarchicalType):
+ class Meta:
+ verbose_name = _("Technical area type")
+ verbose_name_plural = _("Technical area types")
+ ordering = (
+ "order",
+ "parent__label",
+ "label",
+ )
+ ADMIN_SECTION = _("Finds")
+
+
+post_save.connect(post_save_cache, sender=TechnicalAreaType)
+post_delete.connect(post_save_cache, sender=TechnicalAreaType)
+
+
+class TechnicalProcessType(OrderedHierarchicalType):
+ class Meta:
+ verbose_name = _("Technical process type")
+ verbose_name_plural = _("Technical process types")
+ ordering = (
+ "order",
+ "parent__label",
+ "label",
+ )
+ ADMIN_SECTION = _("Finds")
+
+
+post_save.connect(post_save_cache, sender=TechnicalProcessType)
+post_delete.connect(post_save_cache, sender=TechnicalProcessType)
+
+
class ObjectTypeQualityType(GeneralType):
order = models.IntegerField(_("Order"), default=10)
@@ -381,6 +417,99 @@ post_save.connect(post_save_cache, sender=CheckedType)
post_delete.connect(post_save_cache, sender=CheckedType)
+class CollectionEntryModeType(OrderedHierarchicalType):
+ class Meta:
+ verbose_name = _("Collection entry mode type")
+ verbose_name_plural = _("Collection entry mode types")
+ ordering = (
+ "order",
+ "parent__label",
+ "label",
+ )
+ ADMIN_SECTION = _("Museum")
+
+
+post_save.connect(post_save_cache, sender=CollectionEntryModeType)
+post_delete.connect(post_save_cache, sender=CollectionEntryModeType)
+
+
+class InventoryMarkingPresence(OrderedType):
+ class Meta:
+ verbose_name = _("Presence of inventory marking type")
+ verbose_name_plural = _("Presence of inventory marking types")
+ ordering = (
+ "order",
+ "label",
+ )
+ ADMIN_SECTION = _("Museum")
+
+
+post_save.connect(post_save_cache, sender=InventoryMarkingPresence)
+post_delete.connect(post_save_cache, sender=InventoryMarkingPresence)
+
+
+class MarkingType(OrderedType):
+ class Meta:
+ verbose_name = _("Marking type")
+ verbose_name_plural = _("Marking types")
+ ordering = (
+ "order",
+ "label",
+ )
+ ADMIN_SECTION = _("Museum")
+
+
+post_save.connect(post_save_cache, sender=MarkingType)
+post_delete.connect(post_save_cache, sender=MarkingType)
+
+
+class MuseumCollection(OrderedType):
+ class Meta:
+ verbose_name = _("Museum collection")
+ verbose_name_plural = _("Museum collections")
+ ordering = (
+ "order",
+ "label",
+ )
+ ADMIN_SECTION = _("Museum")
+
+
+post_save.connect(post_save_cache, sender=MuseumCollection)
+post_delete.connect(post_save_cache, sender=MuseumCollection)
+
+
+class InventoryConformity(OrderedType):
+ class Meta:
+ verbose_name = _("Inventory conformity type")
+ verbose_name_plural = _("Inventory conformity types")
+ ordering = (
+ "order",
+ "label",
+ )
+ ADMIN_SECTION = _("Museum")
+
+
+post_save.connect(post_save_cache, sender=InventoryConformity)
+post_delete.connect(post_save_cache, sender=InventoryConformity)
+
+
+class OriginalReproduction(OrderedType):
+ class Meta:
+ verbose_name = _("Original/reproduction type")
+ verbose_name_plural = _("Original/reproduction types")
+ ordering = (
+ "order",
+ "label",
+ )
+ ADMIN_SECTION = _("Museum")
+
+
+post_save.connect(post_save_cache, sender=OriginalReproduction)
+post_delete.connect(post_save_cache, sender=OriginalReproduction)
+
+
+
+
class BFBulkView(object):
CREATE_SQL = """
CREATE VIEW basefind_cached_bulk_update
@@ -1550,6 +1679,7 @@ class Find(
BASE_SEARCH_VECTORS = [
SearchVectorConfig("cached_label", "raw"),
SearchVectorConfig("index", "raw"),
+ SearchVectorConfig("cache_complete_museum_id", "raw"),
SearchVectorConfig("label", "raw"),
SearchVectorConfig("description", "local"),
SearchVectorConfig("mark"),
@@ -1668,6 +1798,7 @@ class Find(
"alteration_causes",
]
CACHED_LABELS = [
+ "cache_complete_museum_id",
"cached_label",
"cached_periods",
"cached_object_types",
@@ -1703,7 +1834,11 @@ class Find(
order = models.IntegerField(_("Order"), default=1)
label = models.TextField(_("Free ID"))
denomination = models.TextField(_("Denomination"), blank=True, default="")
- museum_id = models.TextField(_("Museum inventory number"), blank=True, default="")
+ # museum module IDs
+ museum_id_prefix = models.TextField(_("Museum ID prefix"), blank=True, default="")
+ museum_id = models.TextField(_("Museum ID"), blank=True, default="")
+ museum_id_suffix = models.TextField(_("Museum ID suffix"), blank=True, default="")
+ museum_id_comment = models.TextField(_("Comment on museum ID"), blank=True, default="")
laboratory_id = models.TextField(_("Laboratory ID"), blank=True, default="")
description = models.TextField(_("Description"), blank=True, default="")
decoration = models.TextField(_("Decoration"), blank=True, default="")
@@ -1731,6 +1866,12 @@ class Find(
_("Weight unit"), max_length=4, blank=True, null=True, choices=WEIGHT_UNIT
)
find_number = models.IntegerField(_("Number of remains"), blank=True, null=True)
+ min_number_of_individuals = models.IntegerField(
+ _("Minimum number of individuals (MNI)"), blank=True, null=True
+ )
+ quantity_comment = models.TextField(
+ _("Comment on quantity"), blank=True, default=""
+ )
upstream_treatment = models.ForeignKey(
"Treatment",
blank=True,
@@ -1783,7 +1924,19 @@ class Find(
)
functional_areas = models.ManyToManyField(
FunctionalArea,
- verbose_name=_("Functional area"),
+ verbose_name=_("Functional areas"),
+ related_name="find",
+ blank=True,
+ )
+ technical_areas = models.ManyToManyField(
+ TechnicalAreaType,
+ verbose_name=_("Technical areas"),
+ related_name="find",
+ blank=True,
+ )
+ technical_processes = models.ManyToManyField(
+ TechnicalProcessType,
+ verbose_name=_("Technical processes"),
related_name="find",
blank=True,
)
@@ -1805,9 +1958,6 @@ class Find(
related_name="find",
blank=True,
)
- min_number_of_individuals = models.IntegerField(
- _("Minimum number of individuals (MNI)"), blank=True, null=True
- )
length = models.FloatField(_("Length (cm)"), blank=True, null=True)
width = models.FloatField(_("Width (cm)"), blank=True, null=True)
height = models.FloatField(_("Height (cm)"), blank=True, null=True)
@@ -1841,14 +1991,72 @@ class Find(
check_date = models.DateField(_("Check date"), default=datetime.date.today)
estimated_value = models.FloatField(_("Estimated value"), blank=True, null=True)
collection = models.ForeignKey(
- "archaeological_warehouse.Warehouse",
+ "archaeological_warehouse.Warehouse", blank=True, null=True, on_delete=models.SET_NULL,
+ related_name="finds",
verbose_name=_("Collection"),
+ help_text=_("Do not use - need evolutions"),
+ )
+ # museum module
+ museum_owner_institution = models.ForeignKey(
+ Organization, blank=True, null=True, on_delete=models.SET_NULL,
+ related_name="owns",
+ verbose_name=_("Owner institution"),
+ )
+ museum_custodian_institution = models.ForeignKey(
+ Organization, blank=True, null=True, on_delete=models.SET_NULL,
+ related_name="deposited",
+ verbose_name=_("Custodian institution"),
+ )
+ museum_depositor_inventory_number = models.TextField(_("Depositor inventory number"), blank=True, default="")
+ museum_collection_entry_mode = models.ForeignKey(
+ CollectionEntryModeType, blank=True, null=True, on_delete=models.SET_NULL,
+ verbose_name=_("Collections entry mode"),
+ )
+ museum_entry_mode_comment = models.TextField(_("Comment on museum entry mode"), blank=True, default="")
+ museum_entry_date = models.DateField(_("Museum entry date (exact or start)"), blank=True, null=True)
+ museum_entry_date_end = models.DateField(_("Museum entry date (end)"), blank=True, null=True)
+ museum_entry_date_comment = models.TextField(_("Comment on museum entry date"), blank=True, default="")
+ museum_donor = models.ForeignKey(
+ BiographicalNote, blank=True, null=True, on_delete=models.SET_NULL,
+ related_name='has_provided',
+ verbose_name=_("Donor, testator or vendor"),
+ )
+ museum_inventory_marking_presence = models.ManyToManyField(
+ InventoryMarkingPresence, blank=True,
+ related_name="finds",
+ verbose_name=_("Presence of inventory marking"),
+ )
+ museum_marking_type = models.ManyToManyField(
+ MarkingType,
+ verbose_name=_("Type of marking"),
blank=True,
- null=True,
related_name="finds",
- on_delete=models.SET_NULL,
- help_text=_("Do not use - need evolutions"),
)
+ museum_marking_comment = models.TextField(_("Comment on marking"), blank=True, default="")
+ museum_collection = models.ForeignKey(
+ MuseumCollection, blank=True, null=True, on_delete=models.SET_NULL,
+ related_name='current_collection_of',
+ verbose_name=_("Collection"),
+ )
+ museum_former_collection = models.ForeignKey(
+ BiographicalNote, blank=True, null=True, on_delete=models.SET_NULL,
+ verbose_name=_("Former collection"),
+ )
+ museum_inventory_entry_year = models.PositiveIntegerField(
+ _("Inventory entry year"), blank=True, null=True
+ )
+ museum_inventory_conformity = models.ForeignKey(
+ InventoryConformity, blank=True, null=True, on_delete=models.SET_NULL,
+ verbose_name=_("Conformity with inventory"),
+ )
+ museum_non_conformity_comment = models.TextField(_("Comment of non-conformity"), blank=True, default="")
+ museum_inventory_transcript = models.TextField(_("Inventory transcript"), blank=True, default="")
+ museum_original_repro = models.ForeignKey(
+ OriginalReproduction, blank=True, null=True, on_delete=models.SET_NULL,
+ verbose_name=_("Original/reproduction"),
+ )
+ museum_allocation_date = models.DateField(_("Date of museum allocation"), blank=True, null=True)
+ museum_purchase_price = models.TextField(_("Purchase price"), blank=True, default="")
# preservation module
conservatory_state = models.ForeignKey(
@@ -1924,6 +2132,13 @@ class Find(
default="",
help_text=_("Generated automatically - do not edit"),
)
+ cache_complete_museum_id = models.TextField(
+ _("Complete museum ID"),
+ blank=True,
+ default="",
+ db_index=True,
+ help_text=_("Cached value - do not edit"),
+ )
history = HistoricalRecords(bases=[HistoryModel])
BASKET_MODEL = FindBasket
@@ -2585,6 +2800,12 @@ class Find(
return "-"
return self.base_finds.all()[0].cached_label
+ def complete_museum_id(self):
+ return self.cache_complete_museum_id
+
+ def _generate_cache_complete_museum_id(self):
+ return get_generated_id("find_complete_museum_id", self) or ""
+
def _generate_cached_periods(self):
return " & ".join([dating.period.label for dating in self.datings.all()])
diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html
index 076c20c47..fdbc0c6c1 100644
--- a/archaeological_finds/templates/ishtar/sheet_find.html
+++ b/archaeological_finds/templates/ishtar/sheet_find.html
@@ -158,7 +158,7 @@
{% field_flex "Free ID" item.label %}
{% field_flex "Previous ID" item.previous_id %}
{% field_flex "Excavation ID" item.excavation_ids %}
- {% field_flex "Museum inventory number" item.museum_id %}
+ {% field_flex "Museum ID" item.museum_id %}
{% field_flex "Laboratory ID" item.laboratory_id %}
{% field_flex "Seal number" item.seal_number %}
{% trans "Administrative index" as admin_index_label %}
diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py
index 50b6d05b3..e5cb1b67b 100644
--- a/ishtar_common/admin.py
+++ b/ishtar_common/admin.py
@@ -553,6 +553,7 @@ class IshtarSiteProfileAdmin(admin.ModelAdmin):
"mapping",
"preventive_operator",
"underwater",
+ "museum",
),
}),
(_("Advanced configuration"), {
@@ -1349,9 +1350,10 @@ class GeneralTypeAdmin(ChangeParentAdmin, ImportActionAdmin, ImportJSONActionAdm
general_models = [
models.SourceType,
models.AuthorType,
- models.LicenseType,
models.Language,
+ models.LicenseType,
models.PersonType,
+ models.ShootingAngle,
models_common.GeoProviderType,
models_common.GeoDataType,
models_common.GeoOriginType,
diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py
index 7f37e86ce..e2597175b 100644
--- a/ishtar_common/forms_common.py
+++ b/ishtar_common/forms_common.py
@@ -1986,7 +1986,7 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):
required=False,
)
licenses = widgets.Select2MultipleField(
- label=_("Licenses"), required=False, model=models.LicenseType
+ label=_("Rights of use / licenses"), required=False, model=models.LicenseType
)
tags = widgets.Select2MultipleField(
label=_("Tags"),
@@ -2367,7 +2367,7 @@ class DocumentSelect(HistorySelect):
language = forms.ChoiceField(label=_("Language"), choices=[])
isbn = forms.CharField(label=_("ISBN"))
issn = forms.CharField(label=_("ISSN"))
- licenses = forms.ChoiceField(label=_("License"), choices=[])
+ licenses = forms.ChoiceField(label=_("Rights of use / licenses"), choices=[])
comment = forms.CharField(label=_("Comment"))
additional_information = forms.CharField(label=_("Additional informations"))
diff --git a/ishtar_common/migrations/0236_auto_20240208_1635.py b/ishtar_common/migrations/0236_auto_20240208_1635.py
new file mode 100644
index 000000000..48b1060d6
--- /dev/null
+++ b/ishtar_common/migrations/0236_auto_20240208_1635.py
@@ -0,0 +1,112 @@
+# Generated by Django 2.2.24 on 2024-02-08 16:35
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import ishtar_common.models_common
+import re
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0235_default_geo_types'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ShootingAngle',
+ 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')),
+ ('order', models.IntegerField(default=10, verbose_name='Order')),
+ ],
+ options={
+ 'verbose_name': 'Shooting angle',
+ 'verbose_name_plural': 'Shooting angles',
+ 'ordering': ('label',),
+ },
+ bases=(ishtar_common.models_common.Cached, models.Model),
+ ),
+ migrations.AlterModelOptions(
+ name='licensetype',
+ options={'ordering': ('parent__label', 'order', 'label'), 'verbose_name': 'License type', 'verbose_name_plural': 'License types'},
+ ),
+ migrations.AddField(
+ model_name='document',
+ name='copyright',
+ field=models.TextField(blank=True, default='', verbose_name='Copyright'),
+ ),
+ migrations.AddField(
+ model_name='document',
+ name='rights_owner',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.Organization', verbose_name='Rights owner'),
+ ),
+ migrations.AddField(
+ model_name='historicaldocument',
+ name='copyright',
+ field=models.TextField(blank=True, default='', verbose_name='Copyright'),
+ ),
+ migrations.AddField(
+ model_name='historicaldocument',
+ name='rights_owner',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Organization', verbose_name='Rights owner'),
+ ),
+ migrations.AddField(
+ model_name='historicalorganization',
+ name='museum_museofile_id',
+ field=models.TextField(blank=True, default='', verbose_name='Museofile id'),
+ ),
+ migrations.AddField(
+ model_name='licensetype',
+ name='order',
+ field=models.IntegerField(default=10, verbose_name='Order'),
+ ),
+ migrations.AddField(
+ model_name='licensetype',
+ name='parent',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.LicenseType', verbose_name='Parent'),
+ ),
+ migrations.AddField(
+ model_name='organization',
+ name='museum_museofile_id',
+ field=models.TextField(blank=True, default='', verbose_name='Museofile id'),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='licenses',
+ field=models.ManyToManyField(blank=True, to='ishtar_common.LicenseType', verbose_name='Rights of use / license'),
+ ),
+ migrations.AlterField(
+ model_name='gdprlog',
+ name='activity',
+ field=models.CharField(choices=[('DC', 'Directory consultation'), ('DE', 'Directory export'), ('PV', "Viewing a person's notice"), ('PE', "Exporting a person's notice"), ('PC', 'Person creation'), ('PM', 'Person modification'), ('Pm', 'Person merge'), ('PD', 'Person deletion'), ('AC', 'Admin - Directory consultation'), ('AV', 'Admin - Person view'), ('AM', 'Admin - Person modification'), ('AD', 'Admin - Person deletion')], max_length=2, verbose_name='Activity'),
+ ),
+ migrations.CreateModel(
+ name='BiographicalNote',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('denomination', models.TextField(verbose_name='Denomination')),
+ ('last_name', models.TextField(blank=True, default='', verbose_name='Last name')),
+ ('first_name', models.TextField(blank=True, default='', verbose_name='First name')),
+ ('birth_year', models.PositiveIntegerField(blank=True, null=True, verbose_name='Year of birth')),
+ ('death_year', models.PositiveIntegerField(blank=True, null=True, verbose_name='Year of death')),
+ ('biography', models.TextField(blank=True, default='', verbose_name='Biography')),
+ ('biography_format', models.CharField(blank=True, choices=[('NO', 'None'), ('MD', 'Markdown'), ('HT', 'HTML')], default='NO', max_length=2, verbose_name='Biography format')),
+ ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.Person', verbose_name='Person')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='document',
+ name='shooting_angle',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.ShootingAngle', verbose_name='Shooting angle'),
+ ),
+ migrations.AddField(
+ model_name='historicaldocument',
+ name='shooting_angle',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.ShootingAngle', verbose_name='Shooting angle'),
+ ),
+ ]
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index c978b087b..bd457e970 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -24,6 +24,7 @@ from ipware import get_client_ip
import sys
from bs4 import BeautifulSoup
+import bleach
import copy
import datetime
import inspect
@@ -78,6 +79,7 @@ from django.template import Context, Template
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.utils.functional import lazy
+from django.utils.safestring import mark_safe
from ishtar_common.data_importer import post_importer_action
from ishtar_common.utils import (
ugettext_lazy as _,
@@ -141,6 +143,8 @@ from ishtar_common.utils import (
from ishtar_common.models_common import (
GeneralType,
HierarchicalType,
+ OrderedHierarchicalType,
+ OrderedType,
BaseHistorizedItem,
LightHistorizedItem,
HistoricalRecords,
@@ -194,6 +198,8 @@ __all__ = [
"Regexp",
"ImportTarget",
"ItemKey",
+ "OrderedHierarchicalType",
+ "OrderedType",
"TargetKey",
"FormaterType",
"Import",
@@ -2612,6 +2618,7 @@ class Organization(Address, Merge, OwnPerms, BaseGenderedType, ValueGetter, Main
default="",
help_text=documentation_get_gender_values,
)
+ museum_museofile_id = models.TextField(_("Museofile id"), blank=True, default="")
cached_label = models.TextField(
_("Cached name"), blank=True, default="", db_index=True
)
@@ -3204,6 +3211,38 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem):
post_save.connect(cached_label_changed, sender=Person)
+TEXT_FORMAT = (
+ ("NO", _("None")),
+ ("MD", _("Markdown")),
+ ("HT", _("HTML")),
+)
+
+
+def text_format(text, text_format):
+ if text_format == "MD":
+ return mark_safe(markdown(text))
+ elif text_format == "HT":
+ return mark_safe(bleach.clean(text))
+ return text_format
+
+
+class BiographicalNote(models.Model):
+ denomination = models.TextField(_("Denomination"))
+ last_name = models.TextField(_("Last name"), blank=True, default="")
+ first_name = models.TextField(_("First name"), blank=True, default="")
+ birth_year = models.PositiveIntegerField(_("Year of birth"), blank=True, null=True)
+ death_year = models.PositiveIntegerField(_("Year of death"), blank=True, null=True)
+ biography = models.TextField(_("Biography"), blank=True, default="")
+ biography_format = models.CharField(
+ _("Biography format"), blank=True, default="NO", max_length=2, choices=TEXT_FORMAT
+ )
+ person = models.ForeignKey(Person, verbose_name=_("Person"), blank=True, null=True, on_delete=models.SET_NULL)
+
+ @property
+ def formatted_biography(self):
+ return text_format(self.biography, self.biography_format)
+
+
GDPR_ACTIVITY = (
("DC", _("Directory consultation")),
("DE", _("Directory export")),
@@ -3939,16 +3978,20 @@ post_save.connect(post_save_cache, sender=Format)
post_delete.connect(post_save_cache, sender=Format)
-class LicenseType(GeneralType):
+class LicenseType(OrderedHierarchicalType):
url = models.URLField(_("URL"), blank=True, null=True)
class Meta:
verbose_name = _("License type")
verbose_name_plural = _("License types")
- ordering = ("label",)
+ ordering = ("parent__label", "order", "label",)
ADMIN_SECTION = _("Documents")
+post_save.connect(post_save_cache, sender=LicenseType)
+post_delete.connect(post_save_cache, sender=LicenseType)
+
+
class DocumentTag(GeneralType):
SLUG = "documenttag"
@@ -3959,8 +4002,20 @@ class DocumentTag(GeneralType):
ADMIN_SECTION = _("Documents")
-post_save.connect(post_save_cache, sender=LicenseType)
-post_delete.connect(post_save_cache, sender=LicenseType)
+post_save.connect(post_save_cache, sender=DocumentTag)
+post_delete.connect(post_save_cache, sender=DocumentTag)
+
+
+class ShootingAngle(OrderedType):
+ class Meta:
+ verbose_name = _("Shooting angle")
+ verbose_name_plural = _("Shooting angles")
+ ordering = ("order", "label",)
+ ADMIN_SECTION = _("Documents")
+
+
+post_save.connect(post_save_cache, sender=ShootingAngle)
+post_delete.connect(post_save_cache, sender=ShootingAngle)
class Document(
@@ -4391,9 +4446,17 @@ class Document(
publishing_year = models.PositiveIntegerField(
_("Year of publication"), blank=True, null=True
)
+ rights_owner = models.ForeignKey(
+ Organization,
+ verbose_name=_("Rights owner"),
+ blank=True,
+ null=True,
+ on_delete=models.SET_NULL,
+ )
licenses = models.ManyToManyField(
- LicenseType, verbose_name=_("License"), blank=True
+ LicenseType, verbose_name=_("Rights of use / license"), blank=True
)
+ copyright = models.TextField(_("Copyright"), blank=True, default="")
tags = models.ManyToManyField(DocumentTag, verbose_name=_("Tags"), blank=True)
language = models.ForeignKey(
Language,
@@ -4439,6 +4502,13 @@ class Document(
null=True,
)
scale = models.CharField(_("Scale"), max_length=30, null=True, blank=True)
+ shooting_angle = models.ForeignKey(
+ ShootingAngle,
+ verbose_name=_("Shooting angle"),
+ on_delete=models.SET_NULL,
+ blank=True,
+ null=True,
+ )
authors = models.ManyToManyField(
Author, verbose_name=_("Authors"), related_name="documents",
blank=True
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index 928b22630..c6b1316e4 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -725,6 +725,17 @@ class GeneralType(Cached, models.Model):
item.generate_key()
+class OrderedModel(models.Model):
+ order = models.IntegerField(_("Order"), default=10)
+ class Meta:
+ abstract = True
+
+
+class OrderedType(OrderedModel, GeneralType):
+ class Meta:
+ abstract = True
+
+
class HierarchicalType(GeneralType):
parent = models.ForeignKey(
"self",
@@ -758,6 +769,11 @@ class HierarchicalType(GeneralType):
parent = parent.parent
+class OrderedHierarchicalType(OrderedModel, HierarchicalType):
+ class Meta:
+ abstract = True
+
+
class StatisticItem:
STATISTIC_MODALITIES = [] # example: "year", "operation_type__label"
STATISTIC_MODALITIES_OPTIONS = OrderedDict() # example:
@@ -3297,7 +3313,7 @@ class MainItem(ShortMenuItem, SerializeItem, SheetItem):
if not getattr(request.user, "ishtaruser", None):
return False
user = request.user
- return user.ishtaruser.has_right(action_name, request.session)\
+ return user.ishtaruser.has_right(action_name, request.session)
def get_extra_actions(self, request):
if not hasattr(self, "SLUG"):
diff --git a/ishtar_common/templates/ishtar/sheet_document.html b/ishtar_common/templates/ishtar/sheet_document.html
index e4ce47af5..304d13579 100644
--- a/ishtar_common/templates/ishtar/sheet_document.html
+++ b/ishtar_common/templates/ishtar/sheet_document.html
@@ -86,7 +86,7 @@
{% field_flex "Language" item.language %}
{% field_flex "ISBN" item.isbn %}
{% field_flex "ISSN" item.issn %}
- {% field_flex_multiple_obj "Licenses" item 'licenses' %}
+ {% field_flex_multiple_obj "Rights of use / licenses" item 'licenses' %}
{% endif %}
{% if item.source or item.source_free_input %}
diff --git a/requirements.txt b/requirements.txt
index 604c879ba..ffc44643e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -33,6 +33,8 @@ requests==2.25
# celery==4.2.1 ## not mandatory
# 5.0.0
+bleach==3.2.1
+
djangorestframework==3.12
# old 3.9