diff --git a/django/cantusdb_project/main_app/forms.py b/django/cantusdb_project/main_app/forms.py index 4f65fa883..698cc8359 100644 --- a/django/cantusdb_project/main_app/forms.py +++ b/django/cantusdb_project/main_app/forms.py @@ -71,6 +71,15 @@ def label_from_instance(self, obj): widget = CheckboxSelectMultiple() +class StyledChoiceField(forms.ChoiceField): + """ + A custom ChoiceField that uses the custom SelectWidget defined in widgets.py + as its widget (for styling). + """ + + widget = SelectWidget() + + class ChantCreateForm(forms.ModelForm): class Meta: model = Chant @@ -191,6 +200,7 @@ class Meta: # "siglum", "holding_institution", "shelfmark", + "name", "segment_m2m", "provenance", "provenance_notes", @@ -212,11 +222,15 @@ class Meta: "fragmentarium_id", "dact_id", "indexing_notes", + "production_method", + "source_completeness", ] widgets = { # "title": TextInputWidget(), # "siglum": TextInputWidget(), + "shelfmark": TextInputWidget(), "provenance": autocomplete.ModelSelect2(url="provenance-autocomplete"), + "name": TextInputWidget(), "provenance_notes": TextInputWidget(), "date": TextInputWidget(), "cursus": SelectWidget(), @@ -246,6 +260,8 @@ class Meta: "other_editors": autocomplete.ModelSelect2Multiple( url="all-users-autocomplete" ), + "production_method": SelectWidget(), + "source_completeness": SelectWidget(), } field_classes = { "segment_m2m": CheckboxNameModelMultipleChoiceField, @@ -253,32 +269,15 @@ class Meta: holding_institution = forms.ModelChoiceField( queryset=Institution.objects.all(), - required=True, widget=autocomplete.ModelSelect2(url="holding-autocomplete"), + required=False, ) - shelfmark = forms.CharField( - required=True, - widget=TextInputWidget, - ) - - TRUE_FALSE_CHOICES_SOURCE = ( - (True, "Full source"), - (False, "Fragment or Fragmented"), - ) - - full_source = forms.ChoiceField(choices=TRUE_FALSE_CHOICES_SOURCE, required=False) - full_source.widget.attrs.update( - {"class": "form-control custom-select custom-select-sm"} - ) TRUE_FALSE_CHOICES_INVEN = ((True, "Complete"), (False, "Incomplete")) - complete_inventory = forms.ChoiceField( + complete_inventory = StyledChoiceField( choices=TRUE_FALSE_CHOICES_INVEN, required=False ) - complete_inventory.widget.attrs.update( - {"class": "form-control custom-select custom-select-sm"} - ) class ChantEditForm(forms.ModelForm): @@ -391,6 +390,7 @@ class Meta: # "siglum", "holding_institution", "shelfmark", + "name", "segment_m2m", "provenance", "provenance_notes", @@ -413,12 +413,17 @@ class Meta: "full_text_entered_by", "proofreaders", "other_editors", + "production_method", + "source_completeness", ] widgets = { + "shelfmark": TextInputWidget(), "segment_m2m": CheckboxSelectMultiple(), + "name": TextInputWidget(), "provenance": autocomplete.ModelSelect2(url="provenance-autocomplete"), "provenance_notes": TextInputWidget(), "date": TextInputWidget(), + "cursus": SelectWidget(), "summary": TextAreaWidget(), "liturgical_occasions": TextAreaWidget(), "description": TextAreaWidget(), @@ -446,48 +451,24 @@ class Meta: "other_editors": autocomplete.ModelSelect2Multiple( url="all-users-autocomplete" ), + "production_method": SelectWidget(), + "source_completeness": SelectWidget(), } field_classes = { "segment_m2m": CheckboxNameModelMultipleChoiceField, } - shelfmark = forms.CharField( - required=True, - widget=TextInputWidget, - ) - holding_institution = forms.ModelChoiceField( queryset=Institution.objects.all(), - required=True, widget=autocomplete.ModelSelect2(url="holding-autocomplete"), + required=False, ) - CHOICES_FULL_SOURCE = ( - (None, "None"), - (True, "Full source"), - (False, "Fragment or Fragmented"), - ) - full_source = forms.ChoiceField(choices=CHOICES_FULL_SOURCE, required=False) - full_source.widget.attrs.update( - {"class": "form-control custom-select custom-select-sm"} - ) - - CHOICES_CURSUS = ( - (None, "None"), - ("Monastic", "Monastic"), - ("Secular", "Secular"), - ) - cursus = forms.ChoiceField(choices=CHOICES_CURSUS, required=False) - cursus.widget.attrs.update({"class": "form-control custom-select custom-select-sm"}) - CHOICES_COMPLETE_INV = ( (True, "complete inventory"), (False, "partial inventory"), ) - complete_inventory = forms.ChoiceField(choices=CHOICES_COMPLETE_INV, required=False) - complete_inventory.widget.attrs.update( - {"class": "form-control custom-select custom-select-sm"} - ) + complete_inventory = StyledChoiceField(choices=CHOICES_COMPLETE_INV, required=False) class SequenceEditForm(forms.ModelForm): @@ -733,19 +714,9 @@ class Meta: # help_text="RISM-style siglum + Shelf-mark (e.g. GB-Ob 202).", # ) - shelfmark = forms.CharField( - required=True, - widget=TextInputWidget, - ) - - name = forms.CharField( - required=False, - widget=TextInputWidget - ) - holding_institution = forms.ModelChoiceField( - queryset=Institution.objects.all().order_by("name"), - required=True, + queryset=Institution.objects.all().order_by("city", "name"), + required=False, ) provenance = forms.ModelChoiceField( diff --git a/django/cantusdb_project/main_app/management/commands/migrate_records.py b/django/cantusdb_project/main_app/management/commands/migrate_records.py index bfda4e0a6..bc3f26325 100644 --- a/django/cantusdb_project/main_app/management/commands/migrate_records.py +++ b/django/cantusdb_project/main_app/management/commands/migrate_records.py @@ -163,7 +163,7 @@ def handle(self, *args, **options): ) ) institution = print_inst - elif siglum in created_institutions: + elif siglum in created_institutions and source.id not in bad_siglum: print( self.style.SUCCESS( f"Re-using the pre-created institution for {siglum}" @@ -185,7 +185,7 @@ def handle(self, *args, **options): institution.alternate_names = "\n".join(list(deduped_names)) institution.save() - elif siglum not in created_institutions: + elif siglum not in created_institutions and source.id not in bad_siglum: print(self.style.SUCCESS(f"Creating institution record for {siglum}")) iobj = { @@ -229,6 +229,8 @@ def handle(self, *args, **options): created_institutions[siglum] = institution else: + source.shelfmark = shelfmark.strip() + source.save() print( self.style.ERROR( f"Could not determine the holding institution for {source}" diff --git a/django/cantusdb_project/main_app/management/commands/populate_source_completeness.py b/django/cantusdb_project/main_app/management/commands/populate_source_completeness.py new file mode 100644 index 000000000..fe7d9e279 --- /dev/null +++ b/django/cantusdb_project/main_app/management/commands/populate_source_completeness.py @@ -0,0 +1,22 @@ +""" +A temporary command to populate the source_completeness field in the Source model, +based on the full_source field. This command will be removed once the source_completeness +is initially populated. +""" + +from django.core.management.base import BaseCommand +from main_app.models import Source + + +class Command(BaseCommand): + def handle(self, *args, **options): + sources = Source.objects.all() + for source in sources: + if source.full_source: + source.source_completeness = ( + source.SourceCompletenessChoices.FULL_SOURCE + ) + else: + source.source_completeness = source.SourceCompletenessChoices.FRAGMENT + source.save() + self.stdout.write(self.style.SUCCESS("Source completeness populated")) diff --git a/django/cantusdb_project/main_app/migrations/0031_alter_source_holding_institution_and_more.py b/django/cantusdb_project/main_app/migrations/0031_alter_source_holding_institution_and_more.py deleted file mode 100644 index e793e3991..000000000 --- a/django/cantusdb_project/main_app/migrations/0031_alter_source_holding_institution_and_more.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 4.2.14 on 2024-08-30 13:58 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("main_app", "0030_institution_is_private_collection"), - ] - - operations = [ - migrations.AlterField( - model_name="source", - name="holding_institution", - field=models.ForeignKey( - default=19, - on_delete=django.db.models.deletion.PROTECT, - to="main_app.institution", - ), - preserve_default=False, - ), - migrations.AlterField( - model_name="source", - name="shelfmark", - field=models.CharField( - default="XX-NN", - max_length=255, - ), - ), - ] diff --git a/django/cantusdb_project/main_app/migrations/0031_source_name_source_production_method_and_more.py b/django/cantusdb_project/main_app/migrations/0031_source_name_source_production_method_and_more.py new file mode 100644 index 000000000..b0440b7f8 --- /dev/null +++ b/django/cantusdb_project/main_app/migrations/0031_source_name_source_production_method_and_more.py @@ -0,0 +1,104 @@ +# Generated by Django 4.2.14 on 2024-10-02 17:01 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("main_app", "0030_institution_is_private_collection"), + ] + + operations = [ + migrations.AddField( + model_name="source", + name="name", + field=models.CharField( + blank=True, + help_text="A colloquial or commonly-used name for the source", + max_length=255, + null=True, + ), + ), + migrations.AddField( + model_name="source", + name="production_method", + field=models.IntegerField( + choices=[(1, "Manuscript"), (2, "Printed")], + default=1, + verbose_name="Manuscript/Printed", + ), + ), + migrations.AddField( + model_name="source", + name="source_completeness", + field=models.IntegerField( + choices=[ + (1, "Full source"), + (2, "Fragment/Fragmented"), + (3, "Reconstruction"), + ], + default=1, + verbose_name="Full Source/Fragment", + ), + ), + migrations.AlterField( + model_name="institution", + name="country", + field=models.CharField(default="[No Country]", max_length=64), + ), + migrations.AlterField( + model_name="institution", + name="name", + field=models.CharField(default="[No Name]", max_length=255), + ), + migrations.AlterField( + model_name="source", + name="shelfmark", + field=models.CharField( + default="[No Shelfmark]", + help_text="Primary Cantus Database identifier for the source (e.g. library shelfmark, DACT ID, etc.)", + max_length=255, + ), + ), + migrations.CreateModel( + name="SourceIdentifier", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("identifier", models.CharField(max_length=255)), + ( + "type", + models.IntegerField( + choices=[ + (1, "Other catalogues"), + (2, "olim (Former shelfmark)"), + (3, "Alternative names"), + (4, "RISM Online"), + ] + ), + ), + ("note", models.TextField(blank=True, null=True)), + ( + "source", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="identifiers", + to="main_app.source", + ), + ), + ], + options={ + "verbose_name": "Source Identifier", + "ordering": ("type",), + }, + ), + ] diff --git a/django/cantusdb_project/main_app/migrations/0032_source_name_alter_source_shelfmark.py b/django/cantusdb_project/main_app/migrations/0032_source_name_alter_source_shelfmark.py deleted file mode 100644 index 12a3e2327..000000000 --- a/django/cantusdb_project/main_app/migrations/0032_source_name_alter_source_shelfmark.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.2.14 on 2024-09-13 14:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("main_app", "0031_alter_source_holding_institution_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="source", - name="name", - field=models.CharField( - blank=True, - help_text="A colloquial or commonly-used name for the source", - max_length=255, - null=True, - ), - ), - migrations.AlterField( - model_name="source", - name="shelfmark", - field=models.CharField(max_length=255), - ), - ] diff --git a/django/cantusdb_project/main_app/migrations/0033_sourceidentifier.py b/django/cantusdb_project/main_app/migrations/0033_sourceidentifier.py deleted file mode 100644 index d17e23cb0..000000000 --- a/django/cantusdb_project/main_app/migrations/0033_sourceidentifier.py +++ /dev/null @@ -1,53 +0,0 @@ -# Generated by Django 4.2.14 on 2024-09-13 14:34 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("main_app", "0032_source_name_alter_source_shelfmark"), - ] - - operations = [ - migrations.CreateModel( - name="SourceIdentifier", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("identifier", models.CharField(max_length=255)), - ( - "type", - models.IntegerField( - choices=[ - (1, "Other catalogues"), - (2, "olim (Former shelfmark)"), - (3, "Alternative names"), - (4, "RISM Online"), - ] - ), - ), - ("note", models.TextField(blank=True, null=True)), - ( - "source", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="identifiers", - to="main_app.source", - ), - ), - ], - options={ - "verbose_name": "Source Identifier", - "ordering": ("type",), - }, - ), - ] diff --git a/django/cantusdb_project/main_app/models/institution.py b/django/cantusdb_project/main_app/models/institution.py index e38450d90..a5d966f1b 100644 --- a/django/cantusdb_project/main_app/models/institution.py +++ b/django/cantusdb_project/main_app/models/institution.py @@ -28,7 +28,7 @@ class Meta: ), ] - name = models.CharField(max_length=255, default="s.n.") + name = models.CharField(max_length=255, default="[No Name]") siglum = models.CharField( verbose_name="RISM Siglum", max_length=32, @@ -50,7 +50,7 @@ class Meta: region = models.CharField( max_length=64, blank=True, null=True, help_text=region_help_text ) - country = models.CharField(max_length=64, default="s.l.") + country = models.CharField(max_length=64, default="[No Country]") alternate_names = models.TextField( blank=True, null=True, help_text="Enter alternate names on separate lines." ) diff --git a/django/cantusdb_project/main_app/models/source.py b/django/cantusdb_project/main_app/models/source.py index 98caddc06..2887b95f6 100644 --- a/django/cantusdb_project/main_app/models/source.py +++ b/django/cantusdb_project/main_app/models/source.py @@ -42,19 +42,24 @@ class Source(BaseModel): holding_institution = models.ForeignKey( "Institution", on_delete=models.PROTECT, - null=False, - blank=False, + null=True, + blank=True, ) shelfmark = models.CharField( max_length=255, blank=False, null=False, + help_text=( + "Primary Cantus Database identifier for the source " + "(e.g. library shelfmark, DACT ID, etc.)" + ), + default="[No Shelfmark]", ) name = models.CharField( max_length=255, blank=True, null=True, - help_text="A colloquial or commonly-used name for the source" + help_text="A colloquial or commonly-used name for the source", ) provenance = models.ForeignKey( "Provenance", @@ -71,6 +76,18 @@ class Source(BaseModel): null=True, help_text="More exact indication of the provenance (if necessary)", ) + + class SourceCompletenessChoices(models.IntegerChoices): + FULL_SOURCE = 1, "Full source" + FRAGMENT = 2, "Fragment/Fragmented" + RECONSTRUCTION = 3, "Reconstruction" + + source_completeness = models.IntegerField( + choices=SourceCompletenessChoices.choices, + default=SourceCompletenessChoices.FULL_SOURCE, + verbose_name="Full Source/Fragment", + ) + full_source = models.BooleanField(blank=True, null=True) date = models.CharField( blank=True, @@ -140,6 +157,16 @@ class Source(BaseModel): blank=False, null=False, default=False ) + class ProductionMethodChoices(models.IntegerChoices): + MANUSCRIPT = 1, "Manuscript" + PRINTED = 2, "Printed" + + production_method = models.IntegerField( + default=ProductionMethodChoices.MANUSCRIPT, + choices=ProductionMethodChoices.choices, + verbose_name="Manuscript/Printed", + ) + # number_of_chants and number_of_melodies are used for rendering the source-list page (perhaps among other places) # they are automatically recalculated in main_app.signals.update_source_chant_count and # main_app.signals.update_source_melody_count every time a chant or sequence is saved or deleted @@ -163,10 +190,16 @@ def heading(self) -> str: city = f"{holdinst.city}," if holdinst.city else "" title.append(city) title.append(f"{holdinst.name},") + else: + title.append("Cantus") + + title.append(self.shelfmark) - tt = self.shelfmark if self.shelfmark else self.title + if self.source_completeness == self.SourceCompletenessChoices.FRAGMENT: + title.append("(fragment)") - title.append(tt) + if self.name: + title.append(f'("{self.name}")') return " ".join(title) @@ -176,13 +209,14 @@ def short_heading(self) -> str: if holdinst := self.holding_institution: if holdinst.siglum and holdinst.siglum != "XX-NN": title.append(f"{holdinst.siglum}") - elif holdinst.is_private_collector: + else: title.append("Cantus") + else: + title.append("Cantus") - tt = self.shelfmark if self.shelfmark else self.title - title.append(tt) + title.append(self.shelfmark) - if not self.full_source: + if self.source_completeness == self.SourceCompletenessChoices.FRAGMENT: title.append("(fragment)") return " ".join(title) diff --git a/django/cantusdb_project/main_app/templates/source_create.html b/django/cantusdb_project/main_app/templates/source_create.html index 62a8d50a3..395a373fa 100644 --- a/django/cantusdb_project/main_app/templates/source_create.html +++ b/django/cantusdb_project/main_app/templates/source_create.html @@ -46,12 +46,9 @@
- {{ form.holding_institution.help_text }} -
- {{ form.shelfmark.help_text }} +
+ {{ form.shelfmark.help_text|safe }} +
++ {{ form.name.help_text|safe }}
{{ form.full_source.help_text }}
+ {{ form.source_completeness.label_tag }} + {{ form.source_completeness }}- {{ form.holding_institution.help_text }} +
+ {{ form.holding_institution.help_text }}
- {{ form.shelfmark.help_text }} +
+ {{ form.shelfmark.help_text }} +
++ {{ form.name.help_text|safe }}