diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2025-02-21 17:06:53 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2025-02-24 18:09:08 +0100 |
commit | bc6cf94def73926e6f6bb986d61e5861a17584e2 (patch) | |
tree | ba4a7438cd6594c02938ad3211d523c6dd28a79f | |
parent | 1b72554283f5c9e0644262a3f182409376870991 (diff) | |
download | Ishtar-bc6cf94def73926e6f6bb986d61e5861a17584e2.tar.bz2 Ishtar-bc6cf94def73926e6f6bb986d61e5861a17584e2.zip |
✨ treatment state refactoring -> distinct input status and treatment status (refs #6182) - treatments: search criteria
18 files changed, 503 insertions, 135 deletions
diff --git a/Makefile.example b/Makefile.example index 855b13951..aba1818d3 100644 --- a/Makefile.example +++ b/Makefile.example @@ -309,7 +309,8 @@ fixtures_finds: archaeological_finds.materialtype \ archaeological_finds.materialtypequalitytype \ archaeological_finds.treatmenttype \ - archaeological_finds.treatmentstate \ + archaeological_finds.treatmentinputstatus \ + archaeological_finds.treatmentstatus \ archaeological_finds.conservatorystate \ archaeological_finds.remarkabilitytype \ archaeological_finds.objecttype \ diff --git a/archaeological_finds/admin.py b/archaeological_finds/admin.py index 5536901cf..39a8b16a4 100644 --- a/archaeological_finds/admin.py +++ b/archaeological_finds/admin.py @@ -101,7 +101,7 @@ admin_site.register(models.Property, PropertyAdmin) class TreatmentAdmin(HistorizedObjectAdmin): list_display = ('year', 'index', 'label', 'treatment_types_lbl', 'location', 'downstream_lbl', 'upstream_lbl', 'container', 'person') - list_filter = ('treatment_types', 'treatment_state', 'year') + list_filter = ('treatment_types', 'input_status', 'treatment_status', 'year') model = models.Treatment readonly_fields = HistorizedObjectAdmin.readonly_fields + [ 'cached_label', 'downstream_lbl', 'upstream_lbl', @@ -232,11 +232,16 @@ class TreatmentFileTypeAdmin(GeneralTypeAdmin): extra_list_display = ["treatment_type"] -@admin.register(models.TreatmentState, site=admin_site) -class TreatmentStateAdmin(GeneralTypeAdmin): +@admin.register(models.TreatmentInputStatus, site=admin_site) +class TreatmentInputStatusAdmin(GeneralTypeAdmin): extra_list_display = ["order", "executed"] +@admin.register(models.TreatmentStatus, site=admin_site) +class TreatmentStatusAdmin(GeneralTypeAdmin): + extra_list_display = ["order"] + + @admin.register(models.Exhibition, site=admin_site) class ExhibitionAdmin(HistorizedObjectAdmin): list_display = ('name', 'year', 'reference', 'exhibition_type') diff --git a/archaeological_finds/forms_treatments.py b/archaeological_finds/forms_treatments.py index 936834267..b4cd973c1 100644 --- a/archaeological_finds/forms_treatments.py +++ b/archaeological_finds/forms_treatments.py @@ -64,6 +64,38 @@ class TreatmentSelect(DocumentItemSelect): associated_model=Person), label=_("Scientific monitoring manager")) treatment_types = forms.ChoiceField(label=_("Treatment type"), choices=[]) + label = forms.CharField(label=_("Label"), + max_length=200, required=False) + reference = forms.CharField( + label=_("Reference"), max_length=200, required=False) + treatment_status = forms.ChoiceField(label=_("Status"), choices=[]) + input_status = forms.ChoiceField(label=_("Input status"), choices=[]) + location = forms.IntegerField( + label=_("Location"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-warehouse'), associated_model=Warehouse), + validators=[valid_id(Warehouse)]) + scientific_monitoring_manager = forms.IntegerField( + label=_("Scientific monitoring manager"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-person'), associated_model=Person), + ) + person = forms.IntegerField( + label=_("Responsible of the treatment"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-person'), associated_model=Person), + ) + organization = forms.IntegerField( + label=_("Organization"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-organization'), + associated_model=Organization), + ) + TYPES = [ + FieldType('treatment_status', models.TreatmentStatus), + FieldType('input_status', models.TreatmentInputStatus), + FieldType('treatment_type', models.TreatmentType), + ] def __init__(self, *args, **kwargs): super(TreatmentSelect, self).__init__(*args, **kwargs) @@ -88,36 +120,39 @@ class TreatmentFormSelection(CustomForm, forms.Form): validators=[valid_id(models.Treatment)]) -class TreatmentStateSelect(forms.Select): +class TreatmentInputStatusSelect(forms.Select): def render(self, name, value, attrs=None, renderer=None): - rendered = super(TreatmentStateSelect, self).render(name, value, attrs, - renderer) + rendered = super().render(name, value, attrs, renderer) executed_id = [ - str(t.pk) for t in models.TreatmentState.objects.filter( + str(t.pk) for t in models.TreatmentInputStatus.objects.filter( executed=True).all()] if not executed_id: return rendered message = '<i class="fa fa-exclamation-triangle" ' \ 'aria-hidden="true"></i> ' - message += str(_("Once validated, this state is not reversible.")) + message += str(_("Once validated, this input status is not reversible.")) + message += " " + str( + _("Depending on the type of treatment, container change and find " + "separation/grouping will be carried out on a database basis.") + ) rendered += """ - <div id="%(name)s-msg"class="alert alert-warning d-none">%(message)s</div> - <script type='text/javascript'> - var %(id)s_exec_list = ['%(exec_list)s']; - var %(id)s_check = function(){ - var current_val = $("#id_%(name)s").val(); - if (%(id)s_exec_list.indexOf(current_val) != -1){ - $("#%(name)s-msg").removeClass("d-none"); - } else { - $("#%(name)s-msg").addClass("d-none"); - } - }; - - $("#id_%(name)s").change(%(id)s_check); - setTimeout(function(){ %(id)s_check(); }, 500); - </script>""" % {"name": name, "id": name.replace("-", "_"), - "exec_list": "','".join(executed_id), - "message": message} +<div id="%(name)s-msg"class="alert alert-warning d-none">%(message)s</div> +<script type='text/javascript'> +var %(id)s_exec_list = ['%(exec_list)s']; +var %(id)s_check = function(){ + var current_val = $("#id_%(name)s").val(); + if (%(id)s_exec_list.indexOf(current_val) != -1){ + $("#%(name)s-msg").removeClass("d-none"); + } else { + $("#%(name)s-msg").addClass("d-none"); + } +}; + +$("#id_%(name)s").change(%(id)s_check); +setTimeout(function(){ %(id)s_check(); }, 500); +</script>""" % {"name": name, "id": name.replace("-", "_"), + "exec_list": "','".join(executed_id), + "message": message} return rendered @@ -128,32 +163,51 @@ class BaseTreatmentForm(CustomForm, ManageOldType): form_admin_name = _("Treatment - 020 - General") form_slug = "treatment-020-general" base_models = ['treatment_type'] - associated_models = {'treatment_type': models.TreatmentType, - 'person': Person, - 'scientific_monitoring_manager': Person, - 'location': Warehouse, - 'organization': Organization, - 'container': Container, - 'treatment_state': models.TreatmentState, - } + associated_models = { + 'treatment_type': models.TreatmentType, + 'person': Person, + 'scientific_monitoring_manager': Person, + 'location': Warehouse, + 'organization': Organization, + 'container': Container, + 'treatment_status': models.TreatmentStatus, + 'input_status': models.TreatmentInputStatus, + } + HEADERS = { + "treatment_type": FormHeader(_("General")), + "location": FormHeader(_("Location/responsability")), + "start_date": FormHeader(_("Details")), + } file_upload = True need_user_for_initialization = True treatment_type = widgets.Select2MultipleField( label=_("Treatment type"), choices=[], widget=widgets.CheckboxSelectMultiple) - treatment_state = forms.ChoiceField(label=_("State"), choices=[], - widget=TreatmentStateSelect) year = forms.IntegerField(label=_("Year"), initial=lambda: datetime.datetime.now().year, validators=[validators.MinValueValidator(1000), validators.MaxValueValidator(2100)]) + label = forms.CharField(label=_("Label"), + max_length=200, required=False) + reference = forms.CharField( + label=_("Reference"), max_length=200, required=False) + treatment_status = forms.ChoiceField(label=_("Status"), choices=[]) + input_status = forms.ChoiceField(label=_("Input status"), choices=[], + widget=TreatmentInputStatusSelect) location = forms.IntegerField( label=_("Location"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-warehouse'), associated_model=Warehouse, new=True), validators=[valid_id(Warehouse)]) + container = forms.IntegerField( + label=_("Destination container (relevant for treatment that change " + "location)"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-container'), + associated_model=Container, new=True), + validators=[valid_id(Container)], required=False) scientific_monitoring_manager = forms.IntegerField( label=_("Scientific monitoring manager"), widget=widgets.JQueryAutoComplete( @@ -161,7 +215,7 @@ class BaseTreatmentForm(CustomForm, ManageOldType): new=True), validators=[valid_id(Person)], required=False) person = forms.IntegerField( - label=_("Responsible"), + label=_("Responsible of the treatment"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-person'), associated_model=Person, new=True), @@ -172,22 +226,11 @@ class BaseTreatmentForm(CustomForm, ManageOldType): reverse_lazy('autocomplete-organization'), associated_model=Organization, new=True), validators=[valid_id(Organization)], required=False) - label = forms.CharField(label=_("Label"), - max_length=200, required=False) - reference = forms.CharField( - label=_("Reference"), max_length=200, required=False) # external_id = forms.CharField( # label=_("External ref."), max_length=200, required=False) start_date = DateField(label=_("Start date"), required=False, initial=datetime.date.today) - end_date = DateField(label=_("Closing date"), required=False) - container = forms.IntegerField( - label=_("Destination container (relevant for treatment that change " - "location)"), - widget=widgets.JQueryAutoComplete( - reverse_lazy('autocomplete-container'), - associated_model=Container, new=True), - validators=[valid_id(Container)], required=False) + end_date = DateField(label=_("End date"), required=False) goal = forms.CharField(label=_("Goal"), widget=forms.Textarea, required=False) description = forms.CharField(label=_("Description"), @@ -206,7 +249,8 @@ class BaseTreatmentForm(CustomForm, ManageOldType): widget=forms.HiddenInput) TYPES = [ - FieldType('treatment_state', models.TreatmentState, True), + FieldType('treatment_status', models.TreatmentStatus, True), + FieldType('input_status', models.TreatmentInputStatus, True), FieldType( 'treatment_type', models.TreatmentType, is_multiple=True, extra_args={'dct': {'upstream_is_many': False, @@ -238,9 +282,9 @@ class BaseTreatmentForm(CustomForm, ManageOldType): initial = kwargs.get('initial', {}) if initial.get('executed', False): - self.fields['treatment_state'].choices = \ - models.TreatmentState.get_types(empty_first=False, - dct={'executed': True}) + self.fields['input_status'].choices = \ + models.TreatmentInputStatus.get_types( + empty_first=False, dct={'executed': True}) q = Person.objects.filter(ishtaruser__pk=user.pk) if q.count(): @@ -288,8 +332,8 @@ class BaseTreatmentForm(CustomForm, ManageOldType): if tp.change_reference_location ] - if (change_ref_location or change_current_location - ) and restore_reference_location: + if (change_ref_location or change_current_location) and \ + restore_reference_location: if change_ref_location: raise forms.ValidationError( str( @@ -332,7 +376,8 @@ class N1TreatmentForm(BaseTreatmentForm): form_slug = "treatmentn1-020-general" TYPES = [ - FieldType('treatment_state', models.TreatmentState, True, + FieldType('treatment_status', models.TreatmentStatus, True), + FieldType('input_status', models.TreatmentInputStatus, True, extra_args={'dct': {"executed": True}}), FieldType( 'treatment_type', models.TreatmentType, is_multiple=True, @@ -347,7 +392,8 @@ class OneNTreatmentForm(BaseTreatmentForm): form_slug = "treatment1n-020-general" TYPES = [ - FieldType('treatment_state', models.TreatmentState, True, + FieldType('treatment_status', models.TreatmentStatus, True), + FieldType('input_status', models.TreatmentInputStatus, True, extra_args={'dct': {"executed": True}}), FieldType( 'treatment_type', models.TreatmentType, is_multiple=True, @@ -534,7 +580,8 @@ class QAFindTreatmentForm(QABasePackagingForm): if self.cleaned_data['create_treatment']: treat_type = models.TreatmentType.objects.get( pk=self.cleaned_data['treatment_type']) - treat_state = models.TreatmentState.get_completed_state() + treat_input_status = models.TreatmentInputStatus.get_validated_state() + treat_status = models.TreatmentStatus.get_completed_state() t = models.Treatment.objects.create( container=container, year=self.cleaned_data['year'], @@ -543,7 +590,8 @@ class QAFindTreatmentForm(QABasePackagingForm): person_id=self.cleaned_data['person'], organization_id=self.cleaned_data['organization'], history_modifier=user, - treatment_state=treat_state + input_status=treat_input_status, + treatment_status=treat_status, ) t.treatment_types.add(treat_type) t.save(items=items) @@ -670,7 +718,7 @@ class TreatmentFileSelect(DocumentItemSelect): associated_model=Organization), validators=[valid_id(Organization)]) - end_date = DateField(label=_("Closing date"), required=False) + end_date = DateField(label=_("End date"), required=False) exhibition_start = DateField(label=_("Exhibition start date")) exhibition_end = DateField(label=_("Exhibition end date")) @@ -762,7 +810,7 @@ class TreatmentFileForm(CustomForm, ManageOldType): ) creation_date = DateField(label=_("Start date"), required=False, initial=lambda: datetime.datetime.now()) - end_date = DateField(label=_("Closing date"), required=False) + end_date = DateField(label=_("End date"), required=False) TYPES = [ FieldType('type', models.TreatmentFileType), diff --git a/archaeological_finds/migrations/0102_link_apps.py b/archaeological_finds/migrations/0102_link_apps.py index a39fd7c8f..40b6ff882 100644 --- a/archaeological_finds/migrations/0102_link_apps.py +++ b/archaeological_finds/migrations/0102_link_apps.py @@ -82,7 +82,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='treatment', name='treatment_state', - field=models.ForeignKey(default=archaeological_finds.models_treatments.TreatmentState.get_default, on_delete=django.db.models.deletion.CASCADE, to='archaeological_finds.TreatmentState', verbose_name='State'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_finds.TreatmentState', verbose_name='State'), ), migrations.AddField( model_name='treatment', diff --git a/archaeological_finds/migrations/0103_auto_20200129_1944.py b/archaeological_finds/migrations/0103_auto_20200129_1944.py index f127c71c8..3739a6de6 100644 --- a/archaeological_finds/migrations/0103_auto_20200129_1944.py +++ b/archaeological_finds/migrations/0103_auto_20200129_1944.py @@ -308,7 +308,6 @@ class Migration(migrations.Migration): model_name='historicaltreatment', name='treatment_state', field=models.ForeignKey(blank=True, db_constraint=False, - default=archaeological_finds.models_treatments.TreatmentState.get_default, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', diff --git a/archaeological_finds/migrations/0109_auto_20220328_1222.py b/archaeological_finds/migrations/0109_auto_20220328_1222.py index abbae69a4..a451d8142 100644 --- a/archaeological_finds/migrations/0109_auto_20220328_1222.py +++ b/archaeological_finds/migrations/0109_auto_20220328_1222.py @@ -157,7 +157,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='treatment', name='treatment_state', - field=models.ForeignKey(default=archaeological_finds.models_treatments.TreatmentState.get_default, on_delete=django.db.models.deletion.PROTECT, to='archaeological_finds.TreatmentState', verbose_name='State'), + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='archaeological_finds.TreatmentState', verbose_name='State'), ), migrations.AlterField( model_name='treatmentfile', diff --git a/archaeological_finds/migrations/0136_treatment_status_refactoring.py b/archaeological_finds/migrations/0136_treatment_status_refactoring.py new file mode 100644 index 000000000..987b995af --- /dev/null +++ b/archaeological_finds/migrations/0136_treatment_status_refactoring.py @@ -0,0 +1,83 @@ +# Generated by Django 2.2.28 on 2025-02-21 14:00 + +import archaeological_finds.models_treatments +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 = [ + ('archaeological_finds', '0135_exhibition_description'), + ] + + operations = [ + migrations.CreateModel( + name='TreatmentInputStatus', + 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')), + ('executed', models.BooleanField(default=False, verbose_name='Treatment is executed')), + ], + options={ + 'verbose_name': 'Treatment - Input status type', + 'verbose_name_plural': 'Treatment - Input status types', + 'ordering': ('order', 'label'), + }, + bases=(ishtar_common.models_common.Cached, models.Model), + ), + migrations.CreateModel( + name='TreatmentStatus', + 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': 'Treatment - Treatment status type', + 'verbose_name_plural': 'Treatment - Treatment status types', + 'ordering': ('order', 'label'), + }, + bases=(ishtar_common.models_common.Cached, models.Model), + ), + migrations.AlterField( + model_name='historicaltreatment', + name='treatment_state', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.TreatmentState', verbose_name='State - deprecated'), + ), + migrations.AlterField( + model_name='treatment', + name='treatment_state', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='archaeological_finds.TreatmentState', verbose_name='State - deprecated'), + ), + migrations.AddField( + model_name='historicaltreatment', + name='input_status', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.TreatmentInputStatus', verbose_name='Input status'), + ), + migrations.AddField( + model_name='historicaltreatment', + name='treatment_status', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_finds.TreatmentStatus', verbose_name='Treatment status'), + ), + migrations.AddField( + model_name='treatment', + name='input_status', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.TreatmentInputStatus', verbose_name='Input status'), + ), + migrations.AddField( + model_name='treatment', + name='treatment_status', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_finds.TreatmentStatus', verbose_name='Treatment status'), + ), + ] diff --git a/archaeological_finds/migrations/0137_data_migration_treatment_status_inputstatus.py b/archaeological_finds/migrations/0137_data_migration_treatment_status_inputstatus.py new file mode 100644 index 000000000..b1f2077a3 --- /dev/null +++ b/archaeological_finds/migrations/0137_data_migration_treatment_status_inputstatus.py @@ -0,0 +1,51 @@ +import os + +from django.db import migrations +from django.core.management import call_command + + +def load_data(apps, __): + TreatmentStatus = apps.get_model("archaeological_finds", "treatmentstatus") + if not TreatmentStatus.objects.count(): + migration = "0137_data_migration_treatmentstatus.json" + json_path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1] + [migration]) + call_command("loaddata", json_path) + + TreatmentInputStatus = apps.get_model("archaeological_finds", "treatmentinputstatus") + if not TreatmentInputStatus.objects.count(): + migration = "0137_data_migration_treatmentinputstatus.json" + json_path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1] + [migration]) + call_command("loaddata", json_path) + + input_status_executed = TreatmentInputStatus.objects.filter(executed=True).all()[0] + input_status_draft = TreatmentInputStatus.objects.filter(executed=False).all()[0] + + Treatment = apps.get_model("archaeological_finds", "treatment") + try: + TreatmentState = apps.get_model("archaeological_finds", "treatmentstate") + except LookupError: + # treatment state model deleted + return + for treatment_state in TreatmentState.objects.all(): + q = TreatmentStatus.objects.filter(txt_idx=treatment_state.txt_idx) + if not q.exists(): + print(f"Equivalence for treatment state: '{treatment_state.txt_idx}' not found") + continue + equiv = q.all()[0] + q = Treatment.objects.filter(treatment_state=treatment_state) + q.update(treatment_status=equiv) + if treatment_state.executed: + q.update(input_status=input_status_executed) + else: + q.update(input_status=input_status_draft) + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_finds', '0136_treatment_status_refactoring'), + ] + + operations = [ + migrations.RunPython(load_data) + ] diff --git a/archaeological_finds/migrations/0137_data_migration_treatmentinputstatus.json b/archaeological_finds/migrations/0137_data_migration_treatmentinputstatus.json new file mode 100644 index 000000000..100dee461 --- /dev/null +++ b/archaeological_finds/migrations/0137_data_migration_treatmentinputstatus.json @@ -0,0 +1,24 @@ +[ +{ + "model": "archaeological_finds.treatmentinputstatus", + "fields": { + "label": "Brouillon", + "txt_idx": "draft", + "comment": "", + "available": true, + "order": 10, + "executed": false + } +}, +{ + "model": "archaeological_finds.treatmentinputstatus", + "fields": { + "label": "Valid\u00e9", + "txt_idx": "validated", + "comment": "", + "available": true, + "order": 20, + "executed": true + } +} +] diff --git a/archaeological_finds/migrations/0137_data_migration_treatmentstatus.json b/archaeological_finds/migrations/0137_data_migration_treatmentstatus.json new file mode 100644 index 000000000..28f725d8a --- /dev/null +++ b/archaeological_finds/migrations/0137_data_migration_treatmentstatus.json @@ -0,0 +1,62 @@ +[ +{ + "model": "archaeological_finds.treatmentstatus", + "fields": { + "label": "Pr\u00e9vu", + "txt_idx": "planned", + "comment": "", + "available": true, + "order": 30 + } +}, +{ + "model": "archaeological_finds.treatmentstatus", + "fields": { + "label": "En cours", + "txt_idx": "in-progress", + "comment": "", + "available": true, + "order": 10 + } +}, +{ + "model": "archaeological_finds.treatmentstatus", + "fields": { + "label": "Achev\u00e9", + "txt_idx": "completed", + "comment": "", + "available": true, + "order": 20 + } +}, +{ + "model": "archaeological_finds.treatmentstatus", + "fields": { + "label": "Inconnu", + "txt_idx": "unknown", + "comment": "", + "available": true, + "order": 60 + } +}, +{ + "model": "archaeological_finds.treatmentstatus", + "fields": { + "label": "\u00c0 confirmer", + "txt_idx": "to-be-confirmed", + "comment": "", + "available": true, + "order": 40 + } +}, +{ + "model": "archaeological_finds.treatmentstatus", + "fields": { + "label": "Abandonn\u00e9", + "txt_idx": "cancelled", + "comment": "", + "available": true, + "order": 50 + } +} +] diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py index 90ca4bffd..c03e17f18 100644 --- a/archaeological_finds/models.py +++ b/archaeological_finds/models.py @@ -44,7 +44,9 @@ from archaeological_finds.models_treatments import ( FindUpstreamTreatments, TreatmentFile, TreatmentFileType, - TreatmentState, + TreatmentInputStatus, + TreatmentState, # deprecated + TreatmentStatus, ) __all__ = [ @@ -90,6 +92,8 @@ __all__ = [ "TreatmentEmergencyType", "TreatmentFile", "TreatmentFileType", - "TreatmentType", + "TreatmentInputStatus", + "TreatmentStatus", "TreatmentState", + "TreatmentType", ] diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py index 81b1df7df..0c684d98a 100644 --- a/archaeological_finds/models_treatments.py +++ b/archaeological_finds/models_treatments.py @@ -41,22 +41,23 @@ from archaeological_context_records.models import Dating from archaeological_warehouse.models import Warehouse, Container from ishtar_common.model_managers import ExternalIdManager from ishtar_common.models import ( + BaseHistorizedItem, + DashboardFormItem, Document, + document_attached_changed, + DocumentItem, GeneralType, + HistoryModel, ImageModel, - BaseHistorizedItem, + MainItem, + OrderedType, + Organization, OwnPerms, Person, - Organization, - ValueGetter, post_save_cache, - ShortMenuItem, - DashboardFormItem, - document_attached_changed, - MainItem, - HistoryModel, SearchVectorConfig, - DocumentItem, + ShortMenuItem, + ValueGetter, ) from ishtar_common.models_common import CompleteIdentifierItem, HistoricalRecords from ishtar_common.utils import ( @@ -73,8 +74,25 @@ class TreatmentState(GeneralType): order = models.IntegerField(verbose_name=_("Order"), default=10) class Meta: - verbose_name = _("Treatment state type") - verbose_name_plural = _("Treatment state types") + verbose_name = _("Treatment state type - deprecated") + verbose_name_plural = _("Treatment state types - deprecated") + ordering = ( + "order", + "label", + ) + ADMIN_SECTION = _("Treatments") + + +post_save.connect(post_save_cache, sender=TreatmentState) +post_delete.connect(post_save_cache, sender=TreatmentState) + + +class TreatmentInputStatus(OrderedType): + executed = models.BooleanField(_("Treatment is executed"), default=False) + + class Meta: + verbose_name = _("Treatment - Input status type") + verbose_name_plural = _("Treatment - Input status types") ordering = ( "order", "label", @@ -83,23 +101,47 @@ class TreatmentState(GeneralType): @classmethod def get_default(cls): - q = cls.objects.filter(executed=True) + q = cls.objects.filter(executed=False) if not q.count(): return None return q.all()[0].pk @classmethod + def get_validated_state(cls): + treat_state, __ = cls.objects.get_or_create( + txt_idx='validated', + defaults={ + 'label': _("Validated"), 'executed': True, + 'available': True}) + return treat_state + + +post_save.connect(post_save_cache, sender=TreatmentInputStatus) +post_delete.connect(post_save_cache, sender=TreatmentInputStatus) + + +class TreatmentStatus(OrderedType): + class Meta: + verbose_name = _("Treatment - Treatment status type") + verbose_name_plural = _("Treatment - Treatment status types") + ordering = ( + "order", + "label", + ) + ADMIN_SECTION = _("Treatments") + + @classmethod def get_completed_state(cls): treat_state, __ = cls.objects.get_or_create( txt_idx='completed', defaults={ - 'label': _("Completed"), 'executed': True, + 'label': _("Completed"), 'available': True}) return treat_state -post_save.connect(post_save_cache, sender=TreatmentState) -post_delete.connect(post_save_cache, sender=TreatmentState) +post_save.connect(post_save_cache, sender=TreatmentStatus) +post_delete.connect(post_save_cache, sender=TreatmentStatus) class AssociatedFindBasket: @@ -171,7 +213,7 @@ class Treatment( "year", "index", "treatment_types__label", - "treatment_state__label", + "treatment_status__label", "label", "scientific_monitoring_manager__cached_label", "person__cached_label", @@ -195,8 +237,8 @@ class Treatment( "downstream_cached_label": _("Downstream find"), "upstream_cached_label": _("Upstream find"), "treatment_types__label": _("Type"), - "treatment_state__label": _("State"), - "person__cached_label": _("Responsible"), + "treatment_status__label": _("State"), + "person__cached_label": _("Responsible of the treatment"), "scientific_monitoring_manager__cached_label": _( "Scientific monitoring manager" ), @@ -232,6 +274,26 @@ class Treatment( "scientific_monitoring_manager__cached_label__iexact", related_name="scientific_monitoring_manager" ), + "input_status": SearchAltName( + pgettext_lazy("key for text search", "input-status"), + "input_status__label__iexact", + ), + "treatment_status": SearchAltName( + pgettext_lazy("key for text search", "treatment-status"), + "treatment_status__label__iexact", + ), + "location": SearchAltName( + pgettext_lazy("key for text search", "location"), + "location__name__iexact", + ), + "person": SearchAltName( + pgettext_lazy("key for text search", "responsible-of-treatment"), + "person__cached_label__iexact", + ), + "organization": SearchAltName( + pgettext_lazy("key for text search", "organization"), + "organization__name__iexact", + ), } ALT_NAMES.update(BaseHistorizedItem.ALT_NAMES) ALT_NAMES.update(DocumentItem.ALT_NAMES) @@ -242,7 +304,7 @@ class Treatment( BASE_SEARCH_VECTORS = [ SearchVectorConfig("treatment_types__label"), - SearchVectorConfig("treatment_state__label"), + SearchVectorConfig("treatment_status__label"), SearchVectorConfig("label"), SearchVectorConfig("goal", "local"), SearchVectorConfig("external_id"), @@ -294,10 +356,26 @@ class Treatment( ) treatment_state = models.ForeignKey( TreatmentState, - verbose_name=_("State"), - default=TreatmentState.get_default, + verbose_name=_("State - deprecated use input_status and treatment_status"), + blank=True, + null=True, on_delete=models.PROTECT, ) + input_status = models.ForeignKey( + TreatmentInputStatus, + verbose_name=_("Input status"), + default=TreatmentInputStatus.get_default, + blank=True, + null=True, + on_delete=models.SET_NULL, + ) + treatment_status = models.ForeignKey( + TreatmentStatus, + verbose_name=_("Treatment status"), + blank=True, + null=True, + on_delete=models.SET_NULL, + ) executed = models.BooleanField(_("Treatment have been executed"), default=False) location = models.ForeignKey( Warehouse, @@ -311,7 +389,7 @@ class Treatment( ) person = models.ForeignKey( Person, - verbose_name=_("Responsible"), + verbose_name=_("Responsible of the treatment"), blank=True, null=True, on_delete=models.SET_NULL, @@ -340,7 +418,7 @@ class Treatment( description = models.TextField(_("Description"), blank=True, default="") goal = models.TextField(_("Goal"), blank=True, default="") start_date = models.DateField(_("Start date"), blank=True, null=True) - end_date = models.DateField(_("Closing date"), blank=True, null=True) + end_date = models.DateField(_("End date"), blank=True, null=True) creation_date = models.DateTimeField(default=datetime.datetime.now) container = models.ForeignKey( Container, @@ -718,7 +796,7 @@ class Treatment( super().save(*args, **kwargs) # reinit cached values self.clean_cache() - to_be_executed = not self.executed and self.treatment_state.executed + to_be_executed = not self.executed and self.input_status.executed updated = [] # baskets @@ -1313,7 +1391,7 @@ class TreatmentFile( blank=True, null=True, ) - end_date = models.DateField(_("Closing date"), null=True, blank=True) + end_date = models.DateField(_("End date"), null=True, blank=True) creation_date = models.DateField( _("Creation date"), default=datetime.date.today, blank=True, null=True ) diff --git a/archaeological_finds/templates/ishtar/forms/exhibition_form.html b/archaeological_finds/templates/ishtar/forms/exhibition_form.html new file mode 100644 index 000000000..56259b265 --- /dev/null +++ b/archaeological_finds/templates/ishtar/forms/exhibition_form.html @@ -0,0 +1,3 @@ +{% extends "ishtar/forms/base_form.html" %} +{% load i18n inline_formset table_form from_dict %} + diff --git a/archaeological_finds/templates/ishtar/sheet_treatment.html b/archaeological_finds/templates/ishtar/sheet_treatment.html index ac11d9b70..7fa2097f0 100644 --- a/archaeological_finds/templates/ishtar/sheet_treatment.html +++ b/archaeological_finds/templates/ishtar/sheet_treatment.html @@ -66,11 +66,6 @@ <p class="window-refs">{{ item.year|unlocalize }} - {{ item.index|unlocalize }}</p> {% if item.external_id %} <p class="window-refs">{{ item.external_id }}</p>{% endif %} - {% if item.end_date %} - <p class="window-refs">{% trans "Closed" context "Treatment" %} ({{item.end_date}})</p> - {% else %} - <p class="window-refs">{% trans "Active" context "Treatment" %}</p> - {% endif %} </div> </div> </div> @@ -78,15 +73,15 @@ <div class="row"> {% field_flex_multiple_obj "Treatment type" item 'treatment_types' %} - {% field_flex "State" item.treatment_state %} + {% field_flex _("Status") item.treatment_status %} {% field_flex_detail "Associated request" item.file %} {% field_flex_detail "Location" item.location %} {% field_flex_detail "Container" item.container %} {% field_flex_detail "Scientific monitoring manager" item.scientific_monitoring_manager %} - {% field_flex_detail "Responsible" item.person %} + {% field_flex_detail _("Responsible of the treatment") item.person %} {% field_flex_detail "Organization" item.organization %} {% field_flex "Start date" item.start_date|date:"DATE_FORMAT" %} - {% field_flex "Closing date" item.end_date|date:"DATE_FORMAT" %} + {% field_flex _("End date") item.end_date|date:"DATE_FORMAT" %} {% field_flex "Estimated cost" item.estimated_cost|intcomma '' " "|add:CURRENCY %} {% field_flex "Quoted cost" item.quoted_cost|intcomma '' " "|add:CURRENCY %} {% field_flex "Realized cost" item.realized_cost|intcomma '' " "|add:CURRENCY %} @@ -102,6 +97,7 @@ {% if item.history_creator or item.last_edition_date or item.created %} <h3>{% trans "Sheet"%}</h3> <div class="row"> + {% field_flex _("Input status") item.input_status %} {% include "ishtar/blocks/sheet_creation_section.html" %} </div> {% endif %} diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index 6b5c1f1b1..2cf184561 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -573,20 +573,24 @@ class TreatmentWizardCreationTest(WizardTest, FindInit, TestCase): loan.save() self.form_datas[2].set("basetreatment", "treatment_type", loan.pk) - planned, __ = models.TreatmentState.objects.get_or_create( - txt_idx="planned", defaults={"executed": False, "label": "Planned"} + planned, __ = models.TreatmentInputStatus.objects.get_or_create( + txt_idx="draft", defaults={"executed": False, "label": "Draft"} ) planned.executed = False planned.save() - self.form_datas[0].set("basetreatment", "treatment_state", planned.pk) + self.form_datas[0].set("basetreatment", "input_status", planned.pk) - completed, created = models.TreatmentState.objects.get_or_create( - txt_idx="completed", defaults={"executed": True, "label": "Done"} + completed, created = models.TreatmentInputStatus.objects.get_or_create( + txt_idx="validated", defaults={"executed": True, "label": "Validated"} ) completed.executed = True completed.save() - self.form_datas[1].set("basetreatment", "treatment_state", completed.pk) - self.form_datas[2].set("basetreatment", "treatment_state", completed.pk) + self.form_datas[1].set("basetreatment", "input_status", completed.pk) + self.form_datas[2].set("basetreatment", "input_status", completed.pk) + + status = models.TreatmentStatus.objects.all()[0] + for form_data in self.form_datas: + form_data.set("basetreatment", "treatment_status", status.pk) self.treatment_number = models.Treatment.objects.count() @@ -2220,10 +2224,10 @@ class FindQATest(FindInit, TestCase): def test_duplicate(self): t1, __ = models.Treatment.objects.get_or_create( - label="Treatment 1", treatment_state=models.TreatmentState.objects.all()[0] + label="Treatment 1", input_status=models.TreatmentInputStatus.objects.all()[0] ) t2, __ = models.Treatment.objects.get_or_create( - label="Treatment 1", treatment_state=models.TreatmentState.objects.all()[0] + label="Treatment 2", input_status=models.TreatmentInputStatus.objects.all()[0] ) find = self.finds[0] default_desc = "Description for duplicate" @@ -2778,13 +2782,13 @@ class TreatmentTest(FindInit, TestCase): first_find = self.finds[0] - completed, created = models.TreatmentState.objects.get_or_create( - txt_idx="completed", defaults={"executed": True, "label": "Done"} + completed, created = models.TreatmentInputStatus.objects.get_or_create( + txt_idx="validated", defaults={"executed": True, "label": "Validated"} ) completed.executed = True completed.save() - treatment.treatment_state = completed + treatment.input_status = completed treatment.save( user=self.get_default_user(), items=self.basket, @@ -2833,13 +2837,13 @@ class TreatmentTest(FindInit, TestCase): treatment = models.Treatment() initial_find = self.finds[0] - completed, created = models.TreatmentState.objects.get_or_create( - txt_idx="completed", defaults={"executed": True, "label": "Done"} + completed, created = models.TreatmentInputStatus.objects.get_or_create( + txt_idx="validated", defaults={"executed": True, "label": "Validated"} ) completed.executed = True completed.save() - treatment.treatment_state = completed + treatment.input_status = completed treatment.save( user=self.get_default_user(), items=self.basket, @@ -2869,13 +2873,13 @@ class TreatmentTest(FindInit, TestCase): treatment = models.Treatment() initial_find = self.finds[0] - completed, created = models.TreatmentState.objects.get_or_create( - txt_idx="completed", defaults={"executed": True, "label": "Done"} + completed, created = models.TreatmentInputStatus.objects.get_or_create( + txt_idx="validated", defaults={"executed": True, "label": "Validated"} ) completed.executed = True completed.save() - treatment.treatment_state = completed + treatment.input_status = completed treatment.save( user=self.get_default_user(), items=self.basket, @@ -2909,13 +2913,13 @@ class TreatmentTest(FindInit, TestCase): treatment = models.Treatment() initial_find = self.finds[0] - completed, created = models.TreatmentState.objects.get_or_create( - txt_idx="completed", defaults={"executed": True, "label": "Done"} + completed, created = models.TreatmentInputStatus.objects.get_or_create( + txt_idx="validated", defaults={"executed": True, "label": "Validated"} ) completed.executed = True completed.save() - treatment.treatment_state = completed + treatment.input_status = completed treatment.save( user=self.get_default_user(), items=self.basket, @@ -2973,7 +2977,7 @@ class AutocompleteTest(AutocompleteTestBase, TestCase): def create_treatment(self, base_name): item, __ = models.Treatment.objects.get_or_create( - label=base_name, treatment_state=models.TreatmentState.objects.all()[0] + label=base_name, input_status=models.TreatmentInputStatus.objects.all()[0] ) return item, None diff --git a/archaeological_warehouse/forms.py b/archaeological_warehouse/forms.py index c681869ad..b100c72bc 100644 --- a/archaeological_warehouse/forms.py +++ b/archaeological_warehouse/forms.py @@ -52,7 +52,8 @@ from archaeological_finds.models import ( RemarkabilityType, TreatmentEmergencyType, Treatment, - TreatmentState, + TreatmentInputStatus, + TreatmentStatus, TreatmentType, ) from . import models @@ -874,10 +875,12 @@ class QAContainerMoveForm(QABasePackagingForm): if not self.cleaned_data.get("create_treatment", False): return treat_type = TreatmentType.objects.get(pk=self.cleaned_data['treatment_type']) - treat_state = TreatmentState.get_completed_state() + treat_input_status = TreatmentInputStatus.get_validated_state() + treat_status = TreatmentStatus.get_completed_state() try: - location = models.Warehouse.objects.get(pk=self.cleaned_data.get("qalocation", None)) + location = models.Warehouse.objects.get( + pk=self.cleaned_data.get("qalocation", None)) except models.Warehouse.DoesNotExist: location = None @@ -890,7 +893,8 @@ class QAContainerMoveForm(QABasePackagingForm): person_id=self.cleaned_data['person'], organization_id=self.cleaned_data['organization'], history_modifier=user, - treatment_state=treat_state + input_status=treat_input_status, + treatment_status=treat_status, ) t.treatment_types.add(treat_type) t.save() diff --git a/archaeological_warehouse/tests.py b/archaeological_warehouse/tests.py index d00156d90..896014c0d 100644 --- a/archaeological_warehouse/tests.py +++ b/archaeological_warehouse/tests.py @@ -41,9 +41,10 @@ from ishtar_common.tests import ( from ishtar_common.models import IshtarSiteProfile, ProfileType, UserProfile from archaeological_operations.models import Operation from archaeological_context_records.models import ContextRecord -from archaeological_finds.models import \ - Find, MaterialType, Treatment, TreatmentType, FindTreatment, TreatmentState - +from archaeological_finds.models import ( + Find, MaterialType, Treatment, TreatmentType, FindTreatment, + TreatmentStatus, TreatmentInputStatus +) from archaeological_warehouse import models, views, forms, serializers from archaeological_operations.serializers import operation_serialization @@ -947,13 +948,16 @@ class ContainerTest(FindInit, TestCase): self.assertEqual(find.container_fisrt_full_location, full_localisation) self.assertEqual(find.container_ref_fisrt_full_location, full_localisation) - treat_state = TreatmentState.get_completed_state() + treat_input_status = TreatmentInputStatus.get_validated_state() + treat_status = TreatmentStatus.get_completed_state() + t = Treatment.objects.create( container=container_2, year=2024, start_date=datetime.date(2024, 2, 29), location=self.main_warehouse, - treatment_state=treat_state + input_status=treat_input_status, + treatment_status=treat_status, ) treat_type = TreatmentType.objects.get(txt_idx="packaging") t.treatment_types.add(treat_type) diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index 0341d932a..5692c9863 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -2525,13 +2525,15 @@ class ShortMenuTest(TestCase): def _create_treatment(self): Treatment = apps.get_model("archaeological_finds", "Treatment") - TreatmentState = apps.get_model("archaeological_finds", "TreatmentState") - - completed, created = TreatmentState.objects.get_or_create( - txt_idx="completed", defaults={"executed": True, "label": "Done"} + TreatmentInputStatus = apps.get_model("archaeological_finds", "TreatmentInputStatus") + completed, created = TreatmentInputStatus.objects.get_or_create( + txt_idx="validated", defaults={"executed": True, "label": "Validated"} ) + completed.executed = True + completed.save() + return Treatment.objects.create( - label="My treatment", year=2052, treatment_state=completed + label="My treatment", year=2052, input_status=completed ) def test_treatment(self): |