diff options
| -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 | 
