We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Hide certain fields in admin area
Several of our models' fields are designed to improve database operation, or to preserve an archive of past information, and should not be edited by users. These fields include `json_info` (present in several models - i.e. the response from OldCantus from when we last synced the data), `next_chant` (Chant, Sequence), `is_last_chant_in_feast` (Chant, Sequence) `number_of_chants` and `number_of_melodies` (Source); there may be others.

We can `exclude` these fields in our Admin configuration - this shouldn't be too hard to fix.
Hide certain fields in admin area
Several of our models' fields are designed to improve database operation, or to preserve an archive of past information, and should not be edited by users. These fields include `json_info` (present in several models - i.e. the response from OldCantus from when we last synced the data), `next_chant` (Chant, Sequence), `is_last_chant_in_feast` (Chant, Sequence) `number_of_chants` and `number_of_melodies` (Source); there may be others.

We can `exclude` these fields in our Admin configuration - this shouldn't be too hard to fix.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `django/cantusdb_project/main_app/models/base_chant.py`
Content:
```
1 from main_app.models import BaseModel
2 from django.db import models
3 from django.contrib.auth import get_user_model
4 from django.contrib.postgres.search import SearchVectorField
5 
6 
7 class BaseChant(BaseModel):
8     """
9     the Chant and Sequence models inherit from BaseChant.
10 
11     Both Chant and Sequence must have the same fields, otherwise errors will be raised
12     when a user searches for chants/sequences in the database. Thus, all fields,
13     properties and attributes should be declared in BaseChant in order to keep the two
14     models harmonized, even if only one of the two models uses a particular field.
15     """
16 
17     class Meta:
18         abstract = True
19 
20     # The "visible_status" field corresponds to the "status" field on old Cantus
21     visible_status = models.CharField(max_length=1, blank=True, null=True)
22     # For chants, the old Cantus "title" field (in json export) is used to populate the new Cantus "incipit" field,
23     # For sequences, the old Cantus "title" field is used to populate the new Cantus "title" field,
24     # and the old Cantus "incipit" field is used to populate the new Cantus "incipit" field.
25     title = models.CharField(blank=True, null=True, max_length=255)
26     incipit = models.CharField(blank=True, null=True, max_length=255)
27     siglum = models.CharField(blank=True, null=True, max_length=255)
28     folio = models.CharField(
29         help_text="Binding order", blank=True, null=True, max_length=255, db_index=True
30     )
31     # The "s_sequence" char field, used for Sequences, is used to indicate the relative positions of sequences on the page.
32     # It sometimes features non-numeric characters and leading zeroes, so it's a CharField.
33     s_sequence = models.CharField(blank=True, null=True, max_length=255)
34     # The "c_sequence" integer field, used for Chants, similarly indicates the relative positions of chants on the page
35     c_sequence = models.PositiveIntegerField(
36         help_text='Each folio starts with "1"', null=True, blank=True
37     )
38     genre = models.ForeignKey("Genre", blank=True, null=True, on_delete=models.PROTECT)
39     rubrics = models.CharField(blank=True, null=True, max_length=255)
40     analecta_hymnica = models.CharField(blank=True, null=True, max_length=255)
41     indexing_notes = models.TextField(blank=True, null=True)
42     date = models.CharField(blank=True, null=True, max_length=255)
43     col1 = models.CharField(blank=True, null=True, max_length=255)
44     col2 = models.CharField(blank=True, null=True, max_length=255)
45     col3 = models.CharField(blank=True, null=True, max_length=255)
46     ah_volume = models.CharField(blank=True, null=True, max_length=255)
47     # Note that some chants do not have a source
48     source = models.ForeignKey(
49         "Source", on_delete=models.CASCADE, null=True, blank=True
50     )  # PROTECT so that we can't delete a source with chants in it
51     cantus_id = models.CharField(blank=True, null=True, max_length=255, db_index=True)
52     image_link = models.URLField(blank=True, null=True)
53     json_info = models.JSONField(null=True, blank=True)
54     marginalia = models.CharField(max_length=63, null=True, blank=True)
55     office = models.ForeignKey(
56         "Office", on_delete=models.PROTECT, null=True, blank=True
57     )
58     position = models.CharField(max_length=63, null=True, blank=True)
59     feast = models.ForeignKey("Feast", on_delete=models.PROTECT, null=True, blank=True)
60     mode = models.CharField(max_length=63, null=True, blank=True)
61     differentia = models.CharField(blank=True, null=True, max_length=63)
62     differentia_new = models.CharField(blank=True, null=True, max_length=12)
63     finalis = models.CharField(blank=True, null=True, max_length=63)
64     extra = models.CharField(blank=True, null=True, max_length=63)
65     chant_range = models.CharField(
66         blank=True,
67         null=True,
68         help_text='Example: "1-c-k-4". Optional field',
69         max_length=255,
70     )
71     addendum = models.CharField(blank=True, null=True, max_length=255)
72     manuscript_full_text_std_spelling = models.TextField(
73         help_text="Manuscript full text with standardized spelling. Enter the words "
74         "according to the manuscript but normalize their spellings following "
75         "Classical Latin forms. Use upper-case letters for proper nouns, "
76         'the first word of each chant, and the first word after "Alleluia" for '
77         "Mass Alleluias. Punctuation is omitted.",
78         null=True,
79         blank=True,
80     )
81     manuscript_full_text_std_proofread = models.BooleanField(blank=True, null=True)
82     manuscript_full_text = models.TextField(
83         help_text="Enter the wording, word order and spellings as found in the manuscript"
84         ", with abbreviations resolved to standard words. Use upper-case letters as found"
85         " in the source. Retain “Xpistum” (Christum), “Ihc” (Jesus) and other instances of "
86         "Greek characters with their closest approximations of Latin letters. Some punctuation"
87         " signs and vertical dividing lines | are employed in this field. Repetenda and psalm "
88         "cues can also be recorded here.",
89         null=True,
90         blank=True,
91     )
92     manuscript_full_text_proofread = models.BooleanField(blank=True, null=True)
93     manuscript_syllabized_full_text = models.TextField(null=True, blank=True)
94     volpiano = models.TextField(null=True, blank=True)
95     volpiano_proofread = models.BooleanField(blank=True, null=True)
96     # The "volpiano_notes" and "volpiano_intervals" field are added in new Cantus to aid melody search.
97     # "volpiano_notes" is extracted from the "volpiano" field, by eliminating all non-note characters
98     # and removing consecutive repeated notes.
99     # "volpiano_intervals" is extracted from the "volpiano_notes" field.
100     # It records the intervals between any two adjacent volpiano notes.
101     volpiano_notes = models.TextField(null=True, blank=True)
102     volpiano_intervals = models.TextField(null=True, blank=True)
103 
104     # NB: the cao_concordances field should not be used in public-facing views, as it contains data that may be out-of-date.
105     # For more information, see https://github.com/DDMAL/CantusDB/wiki/BaseChant-Model
106     cao_concordances = models.CharField(
107         blank=True, null=True, max_length=63
108     )  # !! see lines immediately above
109     proofread_by = models.ManyToManyField(get_user_model(), blank=True)
110     melody_id = models.CharField(blank=True, null=True, max_length=63)
111     search_vector = SearchVectorField(null=True, editable=False)
112     content_structure = models.CharField(
113         blank=True,
114         null=True,
115         max_length=64,
116         help_text="Additional folio number field, if folio numbers appear on the leaves but are not in the 'binding order'.",
117     )
118     next_chant = models.OneToOneField(
119         "self",
120         related_name="prev_chant",
121         null=True,
122         blank=True,
123         on_delete=models.SET_NULL,
124     )
125     # prev_chant = ...  # prev_chant is created when next_chant is calculated
126 
127     # this field, populated by the populate_is_last_chant_in_feast script, exists in order to optimize .get_suggested_feasts() on the chant-create page
128     is_last_chant_in_feast = models.BooleanField(blank=True, null=True)
129 
130     # fragmentarium_id = models.CharField(blank=True, null=True, max_length=64)
131     # # Digital Analysis of Chant Transmission
132     # dact = models.CharField(blank=True, null=True, max_length=64)
133     # also a second differentia field
134 
135     def get_ci_url(self) -> str:
136         """Construct the url to the entry in Cantus Index correponding to the chant.
137 
138         Returns:
139             str: The url to the Cantus Index page
140         """
141         return f"http://cantusindex.org/id/{self.cantus_id}"
142 
143     def __str__(self):
144         incipit = ""
145         if self.incipit:
146             incipit = self.incipit
147         elif self.manuscript_full_text:
148             split_text = self.manuscript_full_text.split()
149             incipit = " ".join(split_text[:4])
150         return '"{incip}" ({id})'.format(incip=incipit, id=self.id)
151
```

Path: `django/cantusdb_project/main_app/admin.py`
Content:
```
1 from django.contrib import admin
2 from main_app.models import *
3 
4 # Register your models here.
5 
6 
7 class BaseModelAdmin(admin.ModelAdmin):
8     # these fields should not be editable
9     exclude = ("created_by", "last_updated_by")
10 
11     # if an object is created in the admin interface, assign the user to the created_by field
12     # else if an object is updated in the admin interface, assign the user to the last_updated_by field
13     def save_model(self, request, obj, form, change):
14         if change:
15             obj.last_updated_by = request.user
16         else:
17             obj.created_by = request.user
18         super().save_model(request, obj, form, change)
19 
20 
21 class CenturyAdmin(BaseModelAdmin):
22     pass
23 
24 
25 class ChantAdmin(BaseModelAdmin):
26     pass
27 
28 
29 class FeastAdmin(BaseModelAdmin):
30     pass
31 
32 
33 class GenreAdmin(BaseModelAdmin):
34     pass
35 
36 
37 class NotationAdmin(BaseModelAdmin):
38     pass
39 
40 
41 class OfficeAdmin(BaseModelAdmin):
42     pass
43 
44 
45 class ProvenanceAdmin(BaseModelAdmin):
46     pass
47 
48 
49 class RismSiglumAdmin(BaseModelAdmin):
50     pass
51 
52 
53 class SegmentAdmin(BaseModelAdmin):
54     pass
55 
56 
57 class SequenceAdmin(BaseModelAdmin):
58     pass
59 
60 
61 class SourceAdmin(BaseModelAdmin):
62     # from the Django docs:
63     # Adding a ManyToManyField to this list will instead use a nifty unobtrusive JavaScript “filter” interface
64     # that allows searching within the options. The unselected and selected options appear in two boxes side by side.
65     filter_horizontal = (
66         "century",
67         "notation",
68         "current_editors",
69         "inventoried_by",
70         "full_text_entered_by",
71         "melodies_entered_by",
72         "proofreaders",
73         "other_editors",
74     )
75 
76 
77 admin.site.register(Century, CenturyAdmin)
78 admin.site.register(Chant, ChantAdmin)
79 admin.site.register(Feast, FeastAdmin)
80 admin.site.register(Genre, GenreAdmin)
81 admin.site.register(Notation, NotationAdmin)
82 admin.site.register(Office, OfficeAdmin)
83 admin.site.register(Provenance, ProvenanceAdmin)
84 admin.site.register(RismSiglum, RismSiglumAdmin)
85 admin.site.register(Segment, SegmentAdmin)
86 admin.site.register(Sequence, SequenceAdmin)
87 admin.site.register(Source, SourceAdmin)
88
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/django/cantusdb_project/main_app/admin.py b/django/cantusdb_project/main_app/admin.py
--- a/django/cantusdb_project/main_app/admin.py
+++ b/django/cantusdb_project/main_app/admin.py
@@ -1,12 +1,11 @@
 from django.contrib import admin
 from main_app.models import *
 
-# Register your models here.
-
+# these fields should not be editable by all classes
+EXCLUDE = ("created_by", "last_updated_by", "json_info")
 
 class BaseModelAdmin(admin.ModelAdmin):
-    # these fields should not be editable
-    exclude = ("created_by", "last_updated_by")
+    exclude = EXCLUDE
 
     # if an object is created in the admin interface, assign the user to the created_by field
     # else if an object is updated in the admin interface, assign the user to the last_updated_by field
@@ -23,8 +22,7 @@
 
 
 class ChantAdmin(BaseModelAdmin):
-    pass
-
+    exclude = EXCLUDE + ("col1", "col2", "col3", "next_chant", "s_sequence", "is_last_chant_in_feast")
 
 class FeastAdmin(BaseModelAdmin):
     pass
@@ -55,8 +53,7 @@
 
 
 class SequenceAdmin(BaseModelAdmin):
-    pass
-
+    exclude = EXCLUDE + ("c_sequence", "next_chant", "is_last_chant_in_feast")
 
 class SourceAdmin(BaseModelAdmin):
     # from the Django docs:
diff --git a/django/cantusdb_project/main_app/models/base_chant.py b/django/cantusdb_project/main_app/models/base_chant.py
--- a/django/cantusdb_project/main_app/models/base_chant.py
+++ b/django/cantusdb_project/main_app/models/base_chant.py
@@ -30,9 +30,9 @@
     )
     # The "s_sequence" char field, used for Sequences, is used to indicate the relative positions of sequences on the page.
     # It sometimes features non-numeric characters and leading zeroes, so it's a CharField.
-    s_sequence = models.CharField(blank=True, null=True, max_length=255)
+    s_sequence = models.CharField("Sequence", blank=True, null=True, max_length=255)
     # The "c_sequence" integer field, used for Chants, similarly indicates the relative positions of chants on the page
-    c_sequence = models.PositiveIntegerField(
+    c_sequence = models.PositiveIntegerField("Sequence",
         help_text='Each folio starts with "1"', null=True, blank=True
     )
     genre = models.ForeignKey("Genre", blank=True, null=True, on_delete=models.PROTECT)

{"golden_diff": "diff --git a/django/cantusdb_project/main_app/admin.py b/django/cantusdb_project/main_app/admin.py\n--- a/django/cantusdb_project/main_app/admin.py\n+++ b/django/cantusdb_project/main_app/admin.py\n@@ -1,12 +1,11 @@\n from django.contrib import admin\n from main_app.models import *\n \n-# Register your models here.\n-\n+# these fields should not be editable by all classes\n+EXCLUDE = (\"created_by\", \"last_updated_by\", \"json_info\")\n \n class BaseModelAdmin(admin.ModelAdmin):\n-    # these fields should not be editable\n-    exclude = (\"created_by\", \"last_updated_by\")\n+    exclude = EXCLUDE\n \n     # if an object is created in the admin interface, assign the user to the created_by field\n     # else if an object is updated in the admin interface, assign the user to the last_updated_by field\n@@ -23,8 +22,7 @@\n \n \n class ChantAdmin(BaseModelAdmin):\n-    pass\n-\n+    exclude = EXCLUDE + (\"col1\", \"col2\", \"col3\", \"next_chant\", \"s_sequence\", \"is_last_chant_in_feast\")\n \n class FeastAdmin(BaseModelAdmin):\n     pass\n@@ -55,8 +53,7 @@\n \n \n class SequenceAdmin(BaseModelAdmin):\n-    pass\n-\n+    exclude = EXCLUDE + (\"c_sequence\", \"next_chant\", \"is_last_chant_in_feast\")\n \n class SourceAdmin(BaseModelAdmin):\n     # from the Django docs:\ndiff --git a/django/cantusdb_project/main_app/models/base_chant.py b/django/cantusdb_project/main_app/models/base_chant.py\n--- a/django/cantusdb_project/main_app/models/base_chant.py\n+++ b/django/cantusdb_project/main_app/models/base_chant.py\n@@ -30,9 +30,9 @@\n     )\n     # The \"s_sequence\" char field, used for Sequences, is used to indicate the relative positions of sequences on the page.\n     # It sometimes features non-numeric characters and leading zeroes, so it's a CharField.\n-    s_sequence = models.CharField(blank=True, null=True, max_length=255)\n+    s_sequence = models.CharField(\"Sequence\", blank=True, null=True, max_length=255)\n     # The \"c_sequence\" integer field, used for Chants, similarly indicates the relative positions of chants on the page\n-    c_sequence = models.PositiveIntegerField(\n+    c_sequence = models.PositiveIntegerField(\"Sequence\",\n         help_text='Each folio starts with \"1\"', null=True, blank=True\n     )\n     genre = models.ForeignKey(\"Genre\", blank=True, null=True, on_delete=models.PROTECT)\n", "issue": "Hide certain fields in admin area\nSeveral of our models' fields are designed to improve database operation, or to preserve an archive of past information, and should not be edited by users. These fields include `json_info` (present in several models - i.e. the response from OldCantus from when we last synced the data), `next_chant` (Chant, Sequence), `is_last_chant_in_feast` (Chant, Sequence) `number_of_chants` and `number_of_melodies` (Source); there may be others.\r\n\r\nWe can `exclude` these fields in our Admin configuration - this shouldn't be too hard to fix.\nHide certain fields in admin area\nSeveral of our models' fields are designed to improve database operation, or to preserve an archive of past information, and should not be edited by users. These fields include `json_info` (present in several models - i.e. the response from OldCantus from when we last synced the data), `next_chant` (Chant, Sequence), `is_last_chant_in_feast` (Chant, Sequence) `number_of_chants` and `number_of_melodies` (Source); there may be others.\r\n\r\nWe can `exclude` these fields in our Admin configuration - this shouldn't be too hard to fix.\n", "before_files": [{"content": "from main_app.models import BaseModel\nfrom django.db import models\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.postgres.search import SearchVectorField\n\n\nclass BaseChant(BaseModel):\n    \"\"\"\n    the Chant and Sequence models inherit from BaseChant.\n\n    Both Chant and Sequence must have the same fields, otherwise errors will be raised\n    when a user searches for chants/sequences in the database. Thus, all fields,\n    properties and attributes should be declared in BaseChant in order to keep the two\n    models harmonized, even if only one of the two models uses a particular field.\n    \"\"\"\n\n    class Meta:\n        abstract = True\n\n    # The \"visible_status\" field corresponds to the \"status\" field on old Cantus\n    visible_status = models.CharField(max_length=1, blank=True, null=True)\n    # For chants, the old Cantus \"title\" field (in json export) is used to populate the new Cantus \"incipit\" field,\n    # For sequences, the old Cantus \"title\" field is used to populate the new Cantus \"title\" field,\n    # and the old Cantus \"incipit\" field is used to populate the new Cantus \"incipit\" field.\n    title = models.CharField(blank=True, null=True, max_length=255)\n    incipit = models.CharField(blank=True, null=True, max_length=255)\n    siglum = models.CharField(blank=True, null=True, max_length=255)\n    folio = models.CharField(\n        help_text=\"Binding order\", blank=True, null=True, max_length=255, db_index=True\n    )\n    # The \"s_sequence\" char field, used for Sequences, is used to indicate the relative positions of sequences on the page.\n    # It sometimes features non-numeric characters and leading zeroes, so it's a CharField.\n    s_sequence = models.CharField(blank=True, null=True, max_length=255)\n    # The \"c_sequence\" integer field, used for Chants, similarly indicates the relative positions of chants on the page\n    c_sequence = models.PositiveIntegerField(\n        help_text='Each folio starts with \"1\"', null=True, blank=True\n    )\n    genre = models.ForeignKey(\"Genre\", blank=True, null=True, on_delete=models.PROTECT)\n    rubrics = models.CharField(blank=True, null=True, max_length=255)\n    analecta_hymnica = models.CharField(blank=True, null=True, max_length=255)\n    indexing_notes = models.TextField(blank=True, null=True)\n    date = models.CharField(blank=True, null=True, max_length=255)\n    col1 = models.CharField(blank=True, null=True, max_length=255)\n    col2 = models.CharField(blank=True, null=True, max_length=255)\n    col3 = models.CharField(blank=True, null=True, max_length=255)\n    ah_volume = models.CharField(blank=True, null=True, max_length=255)\n    # Note that some chants do not have a source\n    source = models.ForeignKey(\n        \"Source\", on_delete=models.CASCADE, null=True, blank=True\n    )  # PROTECT so that we can't delete a source with chants in it\n    cantus_id = models.CharField(blank=True, null=True, max_length=255, db_index=True)\n    image_link = models.URLField(blank=True, null=True)\n    json_info = models.JSONField(null=True, blank=True)\n    marginalia = models.CharField(max_length=63, null=True, blank=True)\n    office = models.ForeignKey(\n        \"Office\", on_delete=models.PROTECT, null=True, blank=True\n    )\n    position = models.CharField(max_length=63, null=True, blank=True)\n    feast = models.ForeignKey(\"Feast\", on_delete=models.PROTECT, null=True, blank=True)\n    mode = models.CharField(max_length=63, null=True, blank=True)\n    differentia = models.CharField(blank=True, null=True, max_length=63)\n    differentia_new = models.CharField(blank=True, null=True, max_length=12)\n    finalis = models.CharField(blank=True, null=True, max_length=63)\n    extra = models.CharField(blank=True, null=True, max_length=63)\n    chant_range = models.CharField(\n        blank=True,\n        null=True,\n        help_text='Example: \"1-c-k-4\". Optional field',\n        max_length=255,\n    )\n    addendum = models.CharField(blank=True, null=True, max_length=255)\n    manuscript_full_text_std_spelling = models.TextField(\n        help_text=\"Manuscript full text with standardized spelling. Enter the words \"\n        \"according to the manuscript but normalize their spellings following \"\n        \"Classical Latin forms. Use upper-case letters for proper nouns, \"\n        'the first word of each chant, and the first word after \"Alleluia\" for '\n        \"Mass Alleluias. Punctuation is omitted.\",\n        null=True,\n        blank=True,\n    )\n    manuscript_full_text_std_proofread = models.BooleanField(blank=True, null=True)\n    manuscript_full_text = models.TextField(\n        help_text=\"Enter the wording, word order and spellings as found in the manuscript\"\n        \", with abbreviations resolved to standard words. Use upper-case letters as found\"\n        \" in the source. Retain \u201cXpistum\u201d (Christum), \u201cIhc\u201d (Jesus) and other instances of \"\n        \"Greek characters with their closest approximations of Latin letters. Some punctuation\"\n        \" signs and vertical dividing lines | are employed in this field. Repetenda and psalm \"\n        \"cues can also be recorded here.\",\n        null=True,\n        blank=True,\n    )\n    manuscript_full_text_proofread = models.BooleanField(blank=True, null=True)\n    manuscript_syllabized_full_text = models.TextField(null=True, blank=True)\n    volpiano = models.TextField(null=True, blank=True)\n    volpiano_proofread = models.BooleanField(blank=True, null=True)\n    # The \"volpiano_notes\" and \"volpiano_intervals\" field are added in new Cantus to aid melody search.\n    # \"volpiano_notes\" is extracted from the \"volpiano\" field, by eliminating all non-note characters\n    # and removing consecutive repeated notes.\n    # \"volpiano_intervals\" is extracted from the \"volpiano_notes\" field.\n    # It records the intervals between any two adjacent volpiano notes.\n    volpiano_notes = models.TextField(null=True, blank=True)\n    volpiano_intervals = models.TextField(null=True, blank=True)\n\n    # NB: the cao_concordances field should not be used in public-facing views, as it contains data that may be out-of-date.\n    # For more information, see https://github.com/DDMAL/CantusDB/wiki/BaseChant-Model\n    cao_concordances = models.CharField(\n        blank=True, null=True, max_length=63\n    )  # !! see lines immediately above\n    proofread_by = models.ManyToManyField(get_user_model(), blank=True)\n    melody_id = models.CharField(blank=True, null=True, max_length=63)\n    search_vector = SearchVectorField(null=True, editable=False)\n    content_structure = models.CharField(\n        blank=True,\n        null=True,\n        max_length=64,\n        help_text=\"Additional folio number field, if folio numbers appear on the leaves but are not in the 'binding order'.\",\n    )\n    next_chant = models.OneToOneField(\n        \"self\",\n        related_name=\"prev_chant\",\n        null=True,\n        blank=True,\n        on_delete=models.SET_NULL,\n    )\n    # prev_chant = ...  # prev_chant is created when next_chant is calculated\n\n    # this field, populated by the populate_is_last_chant_in_feast script, exists in order to optimize .get_suggested_feasts() on the chant-create page\n    is_last_chant_in_feast = models.BooleanField(blank=True, null=True)\n\n    # fragmentarium_id = models.CharField(blank=True, null=True, max_length=64)\n    # # Digital Analysis of Chant Transmission\n    # dact = models.CharField(blank=True, null=True, max_length=64)\n    # also a second differentia field\n\n    def get_ci_url(self) -> str:\n        \"\"\"Construct the url to the entry in Cantus Index correponding to the chant.\n\n        Returns:\n            str: The url to the Cantus Index page\n        \"\"\"\n        return f\"http://cantusindex.org/id/{self.cantus_id}\"\n\n    def __str__(self):\n        incipit = \"\"\n        if self.incipit:\n            incipit = self.incipit\n        elif self.manuscript_full_text:\n            split_text = self.manuscript_full_text.split()\n            incipit = \" \".join(split_text[:4])\n        return '\"{incip}\" ({id})'.format(incip=incipit, id=self.id)\n", "path": "django/cantusdb_project/main_app/models/base_chant.py"}, {"content": "from django.contrib import admin\nfrom main_app.models import *\n\n# Register your models here.\n\n\nclass BaseModelAdmin(admin.ModelAdmin):\n    # these fields should not be editable\n    exclude = (\"created_by\", \"last_updated_by\")\n\n    # if an object is created in the admin interface, assign the user to the created_by field\n    # else if an object is updated in the admin interface, assign the user to the last_updated_by field\n    def save_model(self, request, obj, form, change):\n        if change:\n            obj.last_updated_by = request.user\n        else:\n            obj.created_by = request.user\n        super().save_model(request, obj, form, change)\n\n\nclass CenturyAdmin(BaseModelAdmin):\n    pass\n\n\nclass ChantAdmin(BaseModelAdmin):\n    pass\n\n\nclass FeastAdmin(BaseModelAdmin):\n    pass\n\n\nclass GenreAdmin(BaseModelAdmin):\n    pass\n\n\nclass NotationAdmin(BaseModelAdmin):\n    pass\n\n\nclass OfficeAdmin(BaseModelAdmin):\n    pass\n\n\nclass ProvenanceAdmin(BaseModelAdmin):\n    pass\n\n\nclass RismSiglumAdmin(BaseModelAdmin):\n    pass\n\n\nclass SegmentAdmin(BaseModelAdmin):\n    pass\n\n\nclass SequenceAdmin(BaseModelAdmin):\n    pass\n\n\nclass SourceAdmin(BaseModelAdmin):\n    # from the Django docs:\n    # Adding a ManyToManyField to this list will instead use a nifty unobtrusive JavaScript \u201cfilter\u201d interface\n    # that allows searching within the options. The unselected and selected options appear in two boxes side by side.\n    filter_horizontal = (\n        \"century\",\n        \"notation\",\n        \"current_editors\",\n        \"inventoried_by\",\n        \"full_text_entered_by\",\n        \"melodies_entered_by\",\n        \"proofreaders\",\n        \"other_editors\",\n    )\n\n\nadmin.site.register(Century, CenturyAdmin)\nadmin.site.register(Chant, ChantAdmin)\nadmin.site.register(Feast, FeastAdmin)\nadmin.site.register(Genre, GenreAdmin)\nadmin.site.register(Notation, NotationAdmin)\nadmin.site.register(Office, OfficeAdmin)\nadmin.site.register(Provenance, ProvenanceAdmin)\nadmin.site.register(RismSiglum, RismSiglumAdmin)\nadmin.site.register(Segment, SegmentAdmin)\nadmin.site.register(Sequence, SequenceAdmin)\nadmin.site.register(Source, SourceAdmin)\n", "path": "django/cantusdb_project/main_app/admin.py"}], "after_files": [{"content": "from main_app.models import BaseModel\nfrom django.db import models\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.postgres.search import SearchVectorField\n\n\nclass BaseChant(BaseModel):\n    \"\"\"\n    the Chant and Sequence models inherit from BaseChant.\n\n    Both Chant and Sequence must have the same fields, otherwise errors will be raised\n    when a user searches for chants/sequences in the database. Thus, all fields,\n    properties and attributes should be declared in BaseChant in order to keep the two\n    models harmonized, even if only one of the two models uses a particular field.\n    \"\"\"\n\n    class Meta:\n        abstract = True\n\n    # The \"visible_status\" field corresponds to the \"status\" field on old Cantus\n    visible_status = models.CharField(max_length=1, blank=True, null=True)\n    # For chants, the old Cantus \"title\" field (in json export) is used to populate the new Cantus \"incipit\" field,\n    # For sequences, the old Cantus \"title\" field is used to populate the new Cantus \"title\" field,\n    # and the old Cantus \"incipit\" field is used to populate the new Cantus \"incipit\" field.\n    title = models.CharField(blank=True, null=True, max_length=255)\n    incipit = models.CharField(blank=True, null=True, max_length=255)\n    siglum = models.CharField(blank=True, null=True, max_length=255)\n    folio = models.CharField(\n        help_text=\"Binding order\", blank=True, null=True, max_length=255, db_index=True\n    )\n    # The \"s_sequence\" char field, used for Sequences, is used to indicate the relative positions of sequences on the page.\n    # It sometimes features non-numeric characters and leading zeroes, so it's a CharField.\n    s_sequence = models.CharField(\"Sequence\", blank=True, null=True, max_length=255)\n    # The \"c_sequence\" integer field, used for Chants, similarly indicates the relative positions of chants on the page\n    c_sequence = models.PositiveIntegerField(\"Sequence\",\n        help_text='Each folio starts with \"1\"', null=True, blank=True\n    )\n    genre = models.ForeignKey(\"Genre\", blank=True, null=True, on_delete=models.PROTECT)\n    rubrics = models.CharField(blank=True, null=True, max_length=255)\n    analecta_hymnica = models.CharField(blank=True, null=True, max_length=255)\n    indexing_notes = models.TextField(blank=True, null=True)\n    date = models.CharField(blank=True, null=True, max_length=255)\n    col1 = models.CharField(blank=True, null=True, max_length=255)\n    col2 = models.CharField(blank=True, null=True, max_length=255)\n    col3 = models.CharField(blank=True, null=True, max_length=255)\n    ah_volume = models.CharField(blank=True, null=True, max_length=255)\n    # Note that some chants do not have a source\n    source = models.ForeignKey(\n        \"Source\", on_delete=models.CASCADE, null=True, blank=True\n    )  # PROTECT so that we can't delete a source with chants in it\n    cantus_id = models.CharField(blank=True, null=True, max_length=255, db_index=True)\n    image_link = models.URLField(blank=True, null=True)\n    json_info = models.JSONField(null=True, blank=True)\n    marginalia = models.CharField(max_length=63, null=True, blank=True)\n    office = models.ForeignKey(\n        \"Office\", on_delete=models.PROTECT, null=True, blank=True\n    )\n    position = models.CharField(max_length=63, null=True, blank=True)\n    feast = models.ForeignKey(\"Feast\", on_delete=models.PROTECT, null=True, blank=True)\n    mode = models.CharField(max_length=63, null=True, blank=True)\n    differentia = models.CharField(blank=True, null=True, max_length=63)\n    differentia_new = models.CharField(blank=True, null=True, max_length=12)\n    finalis = models.CharField(blank=True, null=True, max_length=63)\n    extra = models.CharField(blank=True, null=True, max_length=63)\n    chant_range = models.CharField(\n        blank=True,\n        null=True,\n        help_text='Example: \"1-c-k-4\". Optional field',\n        max_length=255,\n    )\n    addendum = models.CharField(blank=True, null=True, max_length=255)\n    manuscript_full_text_std_spelling = models.TextField(\n        help_text=\"Manuscript full text with standardized spelling. Enter the words \"\n        \"according to the manuscript but normalize their spellings following \"\n        \"Classical Latin forms. Use upper-case letters for proper nouns, \"\n        'the first word of each chant, and the first word after \"Alleluia\" for '\n        \"Mass Alleluias. Punctuation is omitted.\",\n        null=True,\n        blank=True,\n    )\n    manuscript_full_text_std_proofread = models.BooleanField(blank=True, null=True)\n    manuscript_full_text = models.TextField(\n        help_text=\"Enter the wording, word order and spellings as found in the manuscript\"\n        \", with abbreviations resolved to standard words. Use upper-case letters as found\"\n        \" in the source. Retain \u201cXpistum\u201d (Christum), \u201cIhc\u201d (Jesus) and other instances of \"\n        \"Greek characters with their closest approximations of Latin letters. Some punctuation\"\n        \" signs and vertical dividing lines | are employed in this field. Repetenda and psalm \"\n        \"cues can also be recorded here.\",\n        null=True,\n        blank=True,\n    )\n    manuscript_full_text_proofread = models.BooleanField(blank=True, null=True)\n    manuscript_syllabized_full_text = models.TextField(null=True, blank=True)\n    volpiano = models.TextField(null=True, blank=True)\n    volpiano_proofread = models.BooleanField(blank=True, null=True)\n    # The \"volpiano_notes\" and \"volpiano_intervals\" field are added in new Cantus to aid melody search.\n    # \"volpiano_notes\" is extracted from the \"volpiano\" field, by eliminating all non-note characters\n    # and removing consecutive repeated notes.\n    # \"volpiano_intervals\" is extracted from the \"volpiano_notes\" field.\n    # It records the intervals between any two adjacent volpiano notes.\n    volpiano_notes = models.TextField(null=True, blank=True)\n    volpiano_intervals = models.TextField(null=True, blank=True)\n\n    # NB: the cao_concordances field should not be used in public-facing views, as it contains data that may be out-of-date.\n    # For more information, see https://github.com/DDMAL/CantusDB/wiki/BaseChant-Model\n    cao_concordances = models.CharField(\n        blank=True, null=True, max_length=63\n    )  # !! see lines immediately above\n    proofread_by = models.ManyToManyField(get_user_model(), blank=True)\n    melody_id = models.CharField(blank=True, null=True, max_length=63)\n    search_vector = SearchVectorField(null=True, editable=False)\n    content_structure = models.CharField(\n        blank=True,\n        null=True,\n        max_length=64,\n        help_text=\"Additional folio number field, if folio numbers appear on the leaves but are not in the 'binding order'.\",\n    )\n    next_chant = models.OneToOneField(\n        \"self\",\n        related_name=\"prev_chant\",\n        null=True,\n        blank=True,\n        on_delete=models.SET_NULL,\n    )\n    # prev_chant = ...  # prev_chant is created when next_chant is calculated\n\n    # this field, populated by the populate_is_last_chant_in_feast script, exists in order to optimize .get_suggested_feasts() on the chant-create page\n    is_last_chant_in_feast = models.BooleanField(blank=True, null=True)\n\n    # fragmentarium_id = models.CharField(blank=True, null=True, max_length=64)\n    # # Digital Analysis of Chant Transmission\n    # dact = models.CharField(blank=True, null=True, max_length=64)\n    # also a second differentia field\n\n    def get_ci_url(self) -> str:\n        \"\"\"Construct the url to the entry in Cantus Index correponding to the chant.\n\n        Returns:\n            str: The url to the Cantus Index page\n        \"\"\"\n        return f\"http://cantusindex.org/id/{self.cantus_id}\"\n\n    def __str__(self):\n        incipit = \"\"\n        if self.incipit:\n            incipit = self.incipit\n        elif self.manuscript_full_text:\n            split_text = self.manuscript_full_text.split()\n            incipit = \" \".join(split_text[:4])\n        return '\"{incip}\" ({id})'.format(incip=incipit, id=self.id)\n", "path": "django/cantusdb_project/main_app/models/base_chant.py"}, {"content": "from django.contrib import admin\nfrom main_app.models import *\n\n# these fields should not be editable by all classes\nEXCLUDE = (\"created_by\", \"last_updated_by\", \"json_info\")\n\nclass BaseModelAdmin(admin.ModelAdmin):\n    exclude = EXCLUDE\n\n    # if an object is created in the admin interface, assign the user to the created_by field\n    # else if an object is updated in the admin interface, assign the user to the last_updated_by field\n    def save_model(self, request, obj, form, change):\n        if change:\n            obj.last_updated_by = request.user\n        else:\n            obj.created_by = request.user\n        super().save_model(request, obj, form, change)\n\n\nclass CenturyAdmin(BaseModelAdmin):\n    pass\n\n\nclass ChantAdmin(BaseModelAdmin):\n    exclude = EXCLUDE + (\"col1\", \"col2\", \"col3\", \"next_chant\", \"s_sequence\", \"is_last_chant_in_feast\")\n\nclass FeastAdmin(BaseModelAdmin):\n    pass\n\n\nclass GenreAdmin(BaseModelAdmin):\n    pass\n\n\nclass NotationAdmin(BaseModelAdmin):\n    pass\n\n\nclass OfficeAdmin(BaseModelAdmin):\n    pass\n\n\nclass ProvenanceAdmin(BaseModelAdmin):\n    pass\n\n\nclass RismSiglumAdmin(BaseModelAdmin):\n    pass\n\n\nclass SegmentAdmin(BaseModelAdmin):\n    pass\n\n\nclass SequenceAdmin(BaseModelAdmin):\n    exclude = EXCLUDE + (\"c_sequence\", \"next_chant\", \"is_last_chant_in_feast\")\n\nclass SourceAdmin(BaseModelAdmin):\n    # from the Django docs:\n    # Adding a ManyToManyField to this list will instead use a nifty unobtrusive JavaScript \u201cfilter\u201d interface\n    # that allows searching within the options. The unselected and selected options appear in two boxes side by side.\n    filter_horizontal = (\n        \"century\",\n        \"notation\",\n        \"current_editors\",\n        \"inventoried_by\",\n        \"full_text_entered_by\",\n        \"melodies_entered_by\",\n        \"proofreaders\",\n        \"other_editors\",\n    )\n\n\nadmin.site.register(Century, CenturyAdmin)\nadmin.site.register(Chant, ChantAdmin)\nadmin.site.register(Feast, FeastAdmin)\nadmin.site.register(Genre, GenreAdmin)\nadmin.site.register(Notation, NotationAdmin)\nadmin.site.register(Office, OfficeAdmin)\nadmin.site.register(Provenance, ProvenanceAdmin)\nadmin.site.register(RismSiglum, RismSiglumAdmin)\nadmin.site.register(Segment, SegmentAdmin)\nadmin.site.register(Sequence, SequenceAdmin)\nadmin.site.register(Source, SourceAdmin)\n", "path": "django/cantusdb_project/main_app/admin.py"}]}