diff options
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 |
commit | 6e95beabf26ddd3d8de69e34dfbfb97bc1625d80 (patch) | |
tree | 364c2688b46f47d3ee9243e70cbaf5485e177b0f | |
parent | 303a62efae3d3f568545c682649a29de1fb7fc83 (diff) | |
download | Ishtar-6e95beabf26ddd3d8de69e34dfbfb97bc1625d80.tar.bz2 Ishtar-6e95beabf26ddd3d8de69e34dfbfb97bc1625d80.zip |
🗃️ museum module: new db fields, add admin
-rw-r--r-- | archaeological_finds/admin.py | 22 | ||||
-rw-r--r-- | archaeological_finds/forms.py | 2 | ||||
-rw-r--r-- | archaeological_finds/migrations/0115_auto_20240208_1636.py | 432 | ||||
-rw-r--r-- | archaeological_finds/models.py | 102 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 275 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/sheet_find.html | 2 | ||||
-rw-r--r-- | ishtar_common/admin.py | 4 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 4 | ||||
-rw-r--r-- | ishtar_common/migrations/0236_auto_20240208_1635.py | 112 | ||||
-rw-r--r-- | ishtar_common/models.py | 80 | ||||
-rw-r--r-- | ishtar_common/models_common.py | 18 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/sheet_document.html | 2 | ||||
-rw-r--r-- | requirements.txt | 2 |
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 |