We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`created_by` and `last_updated_by` fields should be read only on source admin change page
They should be readonly fields instead. I don't recall seeing them at the top of the page like they are now. Maybe previously they were hidden and now they are visible.

--- 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/admin.py`
Content:
```
1 from django.contrib import admin
2 from main_app.models import *
3 from main_app.forms import (
4     AdminCenturyForm,
5     AdminChantForm,
6     AdminFeastForm,
7     AdminGenreForm,
8     AdminNotationForm,
9     AdminOfficeForm,
10     AdminProvenanceForm,
11     AdminRismSiglumForm,
12     AdminSegmentForm,
13     AdminSequenceForm,
14     AdminSourceForm,
15 )
16 
17 # these fields should not be editable by all classes
18 EXCLUDE = (
19     "created_by",
20     "last_updated_by",
21     "json_info",
22 )
23 
24 
25 class BaseModelAdmin(admin.ModelAdmin):
26     exclude = EXCLUDE
27 
28     # if an object is created in the admin interface, assign the user to the created_by field
29     # else if an object is updated in the admin interface, assign the user to the last_updated_by field
30     def save_model(self, request, obj, form, change):
31         if change:
32             obj.last_updated_by = request.user
33         else:
34             obj.created_by = request.user
35         super().save_model(request, obj, form, change)
36 
37 
38 class CenturyAdmin(BaseModelAdmin):
39     search_fields = ("name",)
40     form = AdminCenturyForm
41 
42 
43 class ChantAdmin(BaseModelAdmin):
44     @admin.display(description="Source Siglum")
45     def get_source_siglum(self, obj):
46         if obj.source:
47             return obj.source.siglum
48 
49     list_display = (
50         "incipit",
51         "get_source_siglum",
52         "genre",
53     )
54     search_fields = (
55         "title",
56         "incipit",
57         "cantus_id",
58         "id",
59     )
60 
61     readonly_fields = (
62         "date_created",
63         "date_updated",
64     )
65 
66     list_filter = (
67         "genre",
68         "office",
69     )
70     exclude = EXCLUDE + (
71         "col1",
72         "col2",
73         "col3",
74         "next_chant",
75         "s_sequence",
76         "is_last_chant_in_feast",
77         "visible_status",
78         "date",
79         "volpiano_notes",
80         "volpiano_intervals",
81         "title",
82         "differentiae_database",
83     )
84     form = AdminChantForm
85     raw_id_fields = (
86         "source",
87         "feast",
88     )
89     ordering = ("source__siglum",)
90 
91 
92 class DifferentiaAdmin(BaseModelAdmin):
93     search_fields = (
94         "differentia_id",
95         "id",
96     )
97 
98 
99 class FeastAdmin(BaseModelAdmin):
100     search_fields = (
101         "name",
102         "feast_code",
103     )
104     list_display = (
105         "name",
106         "month",
107         "day",
108         "feast_code",
109     )
110     form = AdminFeastForm
111 
112 
113 class GenreAdmin(BaseModelAdmin):
114     search_fields = ("name",)
115     form = AdminGenreForm
116 
117 
118 class NotationAdmin(BaseModelAdmin):
119     search_fields = ("name",)
120     form = AdminNotationForm
121 
122 
123 class OfficeAdmin(BaseModelAdmin):
124     search_fields = ("name",)
125     form = AdminOfficeForm
126 
127 
128 class ProvenanceAdmin(BaseModelAdmin):
129     search_fields = ("name",)
130     form = AdminProvenanceForm
131 
132 
133 class RismSiglumAdmin(BaseModelAdmin):
134     search_fields = ("name",)
135     form = AdminRismSiglumForm
136 
137 
138 class SegmentAdmin(BaseModelAdmin):
139     search_fields = ("name",)
140     form = AdminSegmentForm
141 
142 
143 class SequenceAdmin(BaseModelAdmin):
144     @admin.display(description="Source Siglum")
145     def get_source_siglum(self, obj):
146         if obj.source:
147             return obj.source.siglum
148 
149     search_fields = (
150         "title",
151         "incipit",
152         "cantus_id",
153         "id",
154     )
155     exclude = EXCLUDE + (
156         "c_sequence",
157         "next_chant",
158         "is_last_chant_in_feast",
159         "visible_status",
160     )
161     list_display = ("incipit", "get_source_siglum", "genre")
162     list_filter = (
163         "genre",
164         "office",
165     )
166     raw_id_fields = (
167         "source",
168         "feast",
169     )
170     ordering = ("source__siglum",)
171     form = AdminSequenceForm
172 
173 
174 class SourceAdmin(BaseModelAdmin):
175     exclude = ("source_status",)
176 
177     # These search fields are also available on the user-source inline relationship in the user admin page
178     search_fields = (
179         "siglum",
180         "title",
181         "id",
182     )
183     readonly_fields = (
184         "number_of_chants",
185         "number_of_melodies",
186         "date_created",
187         "date_updated",
188     )
189     # from the Django docs:
190     # Adding a ManyToManyField to this list will instead use a nifty unobtrusive JavaScript “filter” interface
191     # that allows searching within the options. The unselected and selected options appear in two boxes side by side.
192     filter_horizontal = (
193         "century",
194         "notation",
195         "current_editors",
196         "inventoried_by",
197         "full_text_entered_by",
198         "melodies_entered_by",
199         "proofreaders",
200         "other_editors",
201     )
202 
203     list_display = (
204         "title",
205         "siglum",
206         "id",
207     )
208 
209     list_filter = (
210         "full_source",
211         "segment",
212         "source_status",
213         "published",
214         "century",
215     )
216 
217     ordering = ("siglum",)
218 
219     form = AdminSourceForm
220 
221 
222 admin.site.register(Century, CenturyAdmin)
223 admin.site.register(Chant, ChantAdmin)
224 admin.site.register(Differentia, DifferentiaAdmin)
225 admin.site.register(Feast, FeastAdmin)
226 admin.site.register(Genre, GenreAdmin)
227 admin.site.register(Notation, NotationAdmin)
228 admin.site.register(Office, OfficeAdmin)
229 admin.site.register(Provenance, ProvenanceAdmin)
230 admin.site.register(RismSiglum, RismSiglumAdmin)
231 admin.site.register(Segment, SegmentAdmin)
232 admin.site.register(Sequence, SequenceAdmin)
233 admin.site.register(Source, SourceAdmin)
234
```
--- 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
@@ -16,14 +16,17 @@
 
 # these fields should not be editable by all classes
 EXCLUDE = (
-    "created_by",
-    "last_updated_by",
     "json_info",
 )
 
+READ_ONLY = (
+    "created_by",
+    "last_updated_by",
+)
 
 class BaseModelAdmin(admin.ModelAdmin):
     exclude = EXCLUDE
+    readonly_fields = READ_ONLY
 
     # 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
@@ -58,7 +61,7 @@
         "id",
     )
 
-    readonly_fields = (
+    readonly_fields = READ_ONLY + (
         "date_created",
         "date_updated",
     )
@@ -172,7 +175,7 @@
 
 
 class SourceAdmin(BaseModelAdmin):
-    exclude = ("source_status",)
+    exclude = EXCLUDE + ("source_status",)
 
     # These search fields are also available on the user-source inline relationship in the user admin page
     search_fields = (
@@ -180,7 +183,7 @@
         "title",
         "id",
     )
-    readonly_fields = (
+    readonly_fields = READ_ONLY + (
         "number_of_chants",
         "number_of_melodies",
         "date_created",

{"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@@ -16,14 +16,17 @@\n \n # these fields should not be editable by all classes\n EXCLUDE = (\n-    \"created_by\",\n-    \"last_updated_by\",\n     \"json_info\",\n )\n \n+READ_ONLY = (\n+    \"created_by\",\n+    \"last_updated_by\",\n+)\n \n class BaseModelAdmin(admin.ModelAdmin):\n     exclude = EXCLUDE\n+    readonly_fields = READ_ONLY\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@@ -58,7 +61,7 @@\n         \"id\",\n     )\n \n-    readonly_fields = (\n+    readonly_fields = READ_ONLY + (\n         \"date_created\",\n         \"date_updated\",\n     )\n@@ -172,7 +175,7 @@\n \n \n class SourceAdmin(BaseModelAdmin):\n-    exclude = (\"source_status\",)\n+    exclude = EXCLUDE + (\"source_status\",)\n \n     # These search fields are also available on the user-source inline relationship in the user admin page\n     search_fields = (\n@@ -180,7 +183,7 @@\n         \"title\",\n         \"id\",\n     )\n-    readonly_fields = (\n+    readonly_fields = READ_ONLY + (\n         \"number_of_chants\",\n         \"number_of_melodies\",\n         \"date_created\",\n", "issue": "`created_by` and `last_updated_by` fields should be read only on source admin change page\nThey should be readonly fields instead. I don't recall seeing them at the top of the page like they are now. Maybe previously they were hidden and now they are visible.\n", "before_files": [{"content": "from django.contrib import admin\nfrom main_app.models import *\nfrom main_app.forms import (\n    AdminCenturyForm,\n    AdminChantForm,\n    AdminFeastForm,\n    AdminGenreForm,\n    AdminNotationForm,\n    AdminOfficeForm,\n    AdminProvenanceForm,\n    AdminRismSiglumForm,\n    AdminSegmentForm,\n    AdminSequenceForm,\n    AdminSourceForm,\n)\n\n# these fields should not be editable by all classes\nEXCLUDE = (\n    \"created_by\",\n    \"last_updated_by\",\n    \"json_info\",\n)\n\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    search_fields = (\"name\",)\n    form = AdminCenturyForm\n\n\nclass ChantAdmin(BaseModelAdmin):\n    @admin.display(description=\"Source Siglum\")\n    def get_source_siglum(self, obj):\n        if obj.source:\n            return obj.source.siglum\n\n    list_display = (\n        \"incipit\",\n        \"get_source_siglum\",\n        \"genre\",\n    )\n    search_fields = (\n        \"title\",\n        \"incipit\",\n        \"cantus_id\",\n        \"id\",\n    )\n\n    readonly_fields = (\n        \"date_created\",\n        \"date_updated\",\n    )\n\n    list_filter = (\n        \"genre\",\n        \"office\",\n    )\n    exclude = EXCLUDE + (\n        \"col1\",\n        \"col2\",\n        \"col3\",\n        \"next_chant\",\n        \"s_sequence\",\n        \"is_last_chant_in_feast\",\n        \"visible_status\",\n        \"date\",\n        \"volpiano_notes\",\n        \"volpiano_intervals\",\n        \"title\",\n        \"differentiae_database\",\n    )\n    form = AdminChantForm\n    raw_id_fields = (\n        \"source\",\n        \"feast\",\n    )\n    ordering = (\"source__siglum\",)\n\n\nclass DifferentiaAdmin(BaseModelAdmin):\n    search_fields = (\n        \"differentia_id\",\n        \"id\",\n    )\n\n\nclass FeastAdmin(BaseModelAdmin):\n    search_fields = (\n        \"name\",\n        \"feast_code\",\n    )\n    list_display = (\n        \"name\",\n        \"month\",\n        \"day\",\n        \"feast_code\",\n    )\n    form = AdminFeastForm\n\n\nclass GenreAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminGenreForm\n\n\nclass NotationAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminNotationForm\n\n\nclass OfficeAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminOfficeForm\n\n\nclass ProvenanceAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminProvenanceForm\n\n\nclass RismSiglumAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminRismSiglumForm\n\n\nclass SegmentAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminSegmentForm\n\n\nclass SequenceAdmin(BaseModelAdmin):\n    @admin.display(description=\"Source Siglum\")\n    def get_source_siglum(self, obj):\n        if obj.source:\n            return obj.source.siglum\n\n    search_fields = (\n        \"title\",\n        \"incipit\",\n        \"cantus_id\",\n        \"id\",\n    )\n    exclude = EXCLUDE + (\n        \"c_sequence\",\n        \"next_chant\",\n        \"is_last_chant_in_feast\",\n        \"visible_status\",\n    )\n    list_display = (\"incipit\", \"get_source_siglum\", \"genre\")\n    list_filter = (\n        \"genre\",\n        \"office\",\n    )\n    raw_id_fields = (\n        \"source\",\n        \"feast\",\n    )\n    ordering = (\"source__siglum\",)\n    form = AdminSequenceForm\n\n\nclass SourceAdmin(BaseModelAdmin):\n    exclude = (\"source_status\",)\n\n    # These search fields are also available on the user-source inline relationship in the user admin page\n    search_fields = (\n        \"siglum\",\n        \"title\",\n        \"id\",\n    )\n    readonly_fields = (\n        \"number_of_chants\",\n        \"number_of_melodies\",\n        \"date_created\",\n        \"date_updated\",\n    )\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    list_display = (\n        \"title\",\n        \"siglum\",\n        \"id\",\n    )\n\n    list_filter = (\n        \"full_source\",\n        \"segment\",\n        \"source_status\",\n        \"published\",\n        \"century\",\n    )\n\n    ordering = (\"siglum\",)\n\n    form = AdminSourceForm\n\n\nadmin.site.register(Century, CenturyAdmin)\nadmin.site.register(Chant, ChantAdmin)\nadmin.site.register(Differentia, DifferentiaAdmin)\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 django.contrib import admin\nfrom main_app.models import *\nfrom main_app.forms import (\n    AdminCenturyForm,\n    AdminChantForm,\n    AdminFeastForm,\n    AdminGenreForm,\n    AdminNotationForm,\n    AdminOfficeForm,\n    AdminProvenanceForm,\n    AdminRismSiglumForm,\n    AdminSegmentForm,\n    AdminSequenceForm,\n    AdminSourceForm,\n)\n\n# these fields should not be editable by all classes\nEXCLUDE = (\n    \"json_info\",\n)\n\nREAD_ONLY = (\n    \"created_by\",\n    \"last_updated_by\",\n)\n\nclass BaseModelAdmin(admin.ModelAdmin):\n    exclude = EXCLUDE\n    readonly_fields = READ_ONLY\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    search_fields = (\"name\",)\n    form = AdminCenturyForm\n\n\nclass ChantAdmin(BaseModelAdmin):\n    @admin.display(description=\"Source Siglum\")\n    def get_source_siglum(self, obj):\n        if obj.source:\n            return obj.source.siglum\n\n    list_display = (\n        \"incipit\",\n        \"get_source_siglum\",\n        \"genre\",\n    )\n    search_fields = (\n        \"title\",\n        \"incipit\",\n        \"cantus_id\",\n        \"id\",\n    )\n\n    readonly_fields = READ_ONLY + (\n        \"date_created\",\n        \"date_updated\",\n    )\n\n    list_filter = (\n        \"genre\",\n        \"office\",\n    )\n    exclude = EXCLUDE + (\n        \"col1\",\n        \"col2\",\n        \"col3\",\n        \"next_chant\",\n        \"s_sequence\",\n        \"is_last_chant_in_feast\",\n        \"visible_status\",\n        \"date\",\n        \"volpiano_notes\",\n        \"volpiano_intervals\",\n        \"title\",\n        \"differentiae_database\",\n    )\n    form = AdminChantForm\n    raw_id_fields = (\n        \"source\",\n        \"feast\",\n    )\n    ordering = (\"source__siglum\",)\n\n\nclass DifferentiaAdmin(BaseModelAdmin):\n    search_fields = (\n        \"differentia_id\",\n        \"id\",\n    )\n\n\nclass FeastAdmin(BaseModelAdmin):\n    search_fields = (\n        \"name\",\n        \"feast_code\",\n    )\n    list_display = (\n        \"name\",\n        \"month\",\n        \"day\",\n        \"feast_code\",\n    )\n    form = AdminFeastForm\n\n\nclass GenreAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminGenreForm\n\n\nclass NotationAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminNotationForm\n\n\nclass OfficeAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminOfficeForm\n\n\nclass ProvenanceAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminProvenanceForm\n\n\nclass RismSiglumAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminRismSiglumForm\n\n\nclass SegmentAdmin(BaseModelAdmin):\n    search_fields = (\"name\",)\n    form = AdminSegmentForm\n\n\nclass SequenceAdmin(BaseModelAdmin):\n    @admin.display(description=\"Source Siglum\")\n    def get_source_siglum(self, obj):\n        if obj.source:\n            return obj.source.siglum\n\n    search_fields = (\n        \"title\",\n        \"incipit\",\n        \"cantus_id\",\n        \"id\",\n    )\n    exclude = EXCLUDE + (\n        \"c_sequence\",\n        \"next_chant\",\n        \"is_last_chant_in_feast\",\n        \"visible_status\",\n    )\n    list_display = (\"incipit\", \"get_source_siglum\", \"genre\")\n    list_filter = (\n        \"genre\",\n        \"office\",\n    )\n    raw_id_fields = (\n        \"source\",\n        \"feast\",\n    )\n    ordering = (\"source__siglum\",)\n    form = AdminSequenceForm\n\n\nclass SourceAdmin(BaseModelAdmin):\n    exclude = EXCLUDE + (\"source_status\",)\n\n    # These search fields are also available on the user-source inline relationship in the user admin page\n    search_fields = (\n        \"siglum\",\n        \"title\",\n        \"id\",\n    )\n    readonly_fields = READ_ONLY + (\n        \"number_of_chants\",\n        \"number_of_melodies\",\n        \"date_created\",\n        \"date_updated\",\n    )\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    list_display = (\n        \"title\",\n        \"siglum\",\n        \"id\",\n    )\n\n    list_filter = (\n        \"full_source\",\n        \"segment\",\n        \"source_status\",\n        \"published\",\n        \"century\",\n    )\n\n    ordering = (\"siglum\",)\n\n    form = AdminSourceForm\n\n\nadmin.site.register(Century, CenturyAdmin)\nadmin.site.register(Chant, ChantAdmin)\nadmin.site.register(Differentia, DifferentiaAdmin)\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"}]}