From 41b22b29638e491cb7b4aa53c214cdba0fd72987 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 7 Sep 2022 03:45:08 +0000 Subject: [PATCH 01/20] Merge user and indexer lists into one --- .../management/commands/merge_indexer_user.py | 54 +++++++++++++++++++ .../main_app/templates/user_list.html | 4 +- .../cantusdb_project/main_app/views/user.py | 6 +-- django/cantusdb_project/users/models.py | 4 ++ 4 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 django/cantusdb_project/main_app/management/commands/merge_indexer_user.py diff --git a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py new file mode 100644 index 000000000..bb4fbf9e7 --- /dev/null +++ b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py @@ -0,0 +1,54 @@ +from operator import index +from django.core.management.base import BaseCommand +from main_app.models import Indexer +from users.models import User +from faker import Faker + +class Command(BaseCommand): + def add_arguments(self, parser): + pass + + def handle(self, *args, **options): + faker = Faker() + users = User.objects.all() + indexers = Indexer.objects.all() + + for user in users: + # set all users to be hidden first + # those listed as indexers on old Cantus will have this adjusted to True + user.show_in_list = False + # do not use the existing "name" property, + # because for some reason, the first/last names can have trailing spaces + # TODO: populate the full_name field and remove the "name" property + user.full_name = f"{user.first_name.strip()} {user.last_name.strip()}" + user.save() + + for indexer in indexers: + indexer_full_name = f"{indexer.given_name} {indexer.family_name}" + print(indexer_full_name) + homonymous_users = User.objects.filter(full_name__iexact=indexer_full_name) + # if the indexer also exists as a user + if homonymous_users: + assert homonymous_users.count() == 1 + homonymous_user = homonymous_users.get() + print(f"homonymous: {homonymous_user.full_name}") + # keep the user as it is (merge the indexer into existing user) + # and store the ID of its indexer object + homonymous_user.old_indexer_id = indexer.id + homonymous_user.show_in_list = True + homonymous_user.save() + # if the indexer doesn't exist as a user + else: + # create a new user with the indexer information + User.objects.create( + institution=indexer.institution, + city=indexer.city, + country=indexer.country, + full_name=indexer_full_name, + # assign random email to dummy users + email=f"{faker.bothify('????????')}@fakeemail.com", + # leave the password empty for dummy users + # the password can't be empty in login form, so they can't log in + password="", + show_in_list=True, + ) diff --git a/django/cantusdb_project/main_app/templates/user_list.html b/django/cantusdb_project/main_app/templates/user_list.html index e1442b4af..aef0dadd1 100644 --- a/django/cantusdb_project/main_app/templates/user_list.html +++ b/django/cantusdb_project/main_app/templates/user_list.html @@ -26,19 +26,17 @@

List of Users

Institution City Country - {% comment %} Sources {% endcomment %} {% for user in users %} - {{ user.first_name }} {{ user.last_name }} + {{ user.full_name|default:"" }} {{ user.institution|default:"" }} {{ user.city|default:"" }} {{ user.country|default:"" }} - {% comment %} {{ user.source_count }} source{{ user.source_count|pluralize }} {% endcomment %} {% endfor %} diff --git a/django/cantusdb_project/main_app/views/user.py b/django/cantusdb_project/main_app/views/user.py index 24d99b6d2..6b2a2203a 100644 --- a/django/cantusdb_project/main_app/views/user.py +++ b/django/cantusdb_project/main_app/views/user.py @@ -59,7 +59,7 @@ def get_next_page(self): ) return next_page -class UserListView(LoginRequiredMixin, SearchableListMixin, ListView): +class UserListView(SearchableListMixin, ListView): """Searchable List view for User model Accessed by /users/ @@ -67,9 +67,9 @@ class UserListView(LoginRequiredMixin, SearchableListMixin, ListView): When passed a ``?q=`` argument in the GET request, it will filter users based on the fields defined in ``search_fields`` with the ``icontains`` lookup """ - model = get_user_model() - search_fields = ["first_name", "last_name", "institution", "city", "country"] + ordering = "full_name" + search_fields = ["full_name", "institution", "city", "country"] paginate_by = 100 template_name = "user_list.html" context_object_name = "users" diff --git a/django/cantusdb_project/users/models.py b/django/cantusdb_project/users/models.py index 615abc00c..74e578d06 100644 --- a/django/cantusdb_project/users/models.py +++ b/django/cantusdb_project/users/models.py @@ -15,6 +15,10 @@ class User(AbstractUser): email = models.EmailField(unique=True) # will be used to check if the user has changed the password assigned to them changed_initial_password = models.BooleanField(default=False) + # whether to display the user in the user-list page + show_in_list = models.BooleanField(default=False) + # if the user has a associated indexer object on old Cantus, save its ID + old_indexer_id = models.IntegerField(blank=True, null=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] From dd2d57baab866d1717df6654aadb7b2b3d16ae4d Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Sun, 11 Sep 2022 21:05:48 +0000 Subject: [PATCH 02/20] Replace indexer views --- django/cantusdb_project/main_app/urls.py | 3 +- .../main_app/views/indexer.py | 48 ------------------- .../cantusdb_project/main_app/views/user.py | 36 +++++++++++--- 3 files changed, 31 insertions(+), 56 deletions(-) delete mode 100644 django/cantusdb_project/main_app/views/indexer.py diff --git a/django/cantusdb_project/main_app/urls.py b/django/cantusdb_project/main_app/urls.py index 6d6c6ae4f..0a7e568d6 100644 --- a/django/cantusdb_project/main_app/urls.py +++ b/django/cantusdb_project/main_app/urls.py @@ -1,4 +1,4 @@ -from django.urls import path, include +from django.urls import path from main_app.views import * from main_app.views import views from main_app.views.sequence import SequenceEditView @@ -50,7 +50,6 @@ path("genre/", GenreDetailView.as_view(), name="genre-detail"), # indexer path("indexers/", IndexerListView.as_view(), name="indexer-list"), - path("indexer/", IndexerDetailView.as_view(), name="indexer-detail"), # office path("offices/", OfficeListView.as_view(), name="office-list"), path("office/", OfficeDetailView.as_view(), name="office-detail"), diff --git a/django/cantusdb_project/main_app/views/indexer.py b/django/cantusdb_project/main_app/views/indexer.py deleted file mode 100644 index 057a167e7..000000000 --- a/django/cantusdb_project/main_app/views/indexer.py +++ /dev/null @@ -1,48 +0,0 @@ -from django.db.models.aggregates import Count -from django.db.models.query_utils import Q -from django.views.generic import DetailView, ListView -from main_app.models import Indexer -from extra_views import SearchableListMixin -from django.db.models.functions import Lower - - -class IndexerDetailView(DetailView): - """Detail view for Indexer model - - Accessed by /indexers/ - """ - - model = Indexer - context_object_name = "indexer" - template_name = "indexer_detail.html" - - -class IndexerListView(SearchableListMixin, ListView): - """Searchable List view for Indexer model - - Accessed by /indexers/ - - When passed a ``?q=`` argument in the GET request, it will filter indexers - based on the fields defined in ``search_fields`` with the ``icontains`` lookup - """ - - model = Indexer - search_fields = ["given_name", "family_name", "institution", "city", "country"] - paginate_by = 100 - template_name = "indexer_list.html" - context_object_name = "indexers" - - def get_queryset(self): - # sources_inventoried is the related name for the Source model in its many-to-many relationship with the Indexer model - queryset = ( - super() - .get_queryset() - .annotate( - source_count=Count( - "sources_inventoried", filter=Q(sources_inventoried__published=True) - ) - ) - .exclude(source_count=0) - .order_by(Lower("family_name")) - ) - return queryset diff --git a/django/cantusdb_project/main_app/views/user.py b/django/cantusdb_project/main_app/views/user.py index 6b2a2203a..8b0bdc245 100644 --- a/django/cantusdb_project/main_app/views/user.py +++ b/django/cantusdb_project/main_app/views/user.py @@ -1,5 +1,5 @@ from django.urls import reverse -from urllib import request +from django.db.models.aggregates import Count from django.views.generic import DetailView from django.contrib.auth import get_user_model, login as auth_login from main_app.models import Source @@ -60,12 +60,14 @@ def get_next_page(self): return next_page class UserListView(SearchableListMixin, ListView): - """Searchable List view for User model + """A list of all User objects - Accessed by /users/ - - When passed a ``?q=`` argument in the GET request, it will filter users - based on the fields defined in ``search_fields`` with the ``icontains`` lookup + This view is equivalent to the user list view on the old Cantus. + This includes all User objects on the old Cantus. + When passed a `?q=` argument in the GET request, it will filter users + based on the fields defined in `search_fields` with the `icontains` lookup. + + Accessed by /users/ """ model = get_user_model() ordering = "full_name" @@ -74,6 +76,28 @@ class UserListView(SearchableListMixin, ListView): template_name = "user_list.html" context_object_name = "users" +class IndexerListView(UserListView): + """A list of User objects shown to the public + + This view replaces the indexer list view on the old Cantus. + The indexers are considered a subset of all User objects, the subset shown to the public. + This includes the User objects corresponding to Indexer objects on the old Cantus. + When passed a `?q=` argument in the GET request, it will filter users + based on the fields defined in `search_fields` with the `icontains` lookup. + + Accessed by /indexers/ + """ + template_name = "indexer_list.html" + context_object_name = "indexers" + + def get_queryset(self): + all_users = super().get_queryset() + indexers = all_users.filter(show_in_list=True) + # indexers = indexers.annotate( + # source_count=Count("inventoried_sources", filter=Q(sources_inventoried__published=True)) + # ) + return indexers + class CustomLoginView(LoginView): def form_valid(self, form): auth_login(self.request, form.get_user()) From 89021907202462505b70d66d78fd26c06a3fcf1b Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Sun, 11 Sep 2022 21:07:18 +0000 Subject: [PATCH 03/20] Replace IndexerDetailView with UserDetailView --- django/cantusdb_project/main_app/views/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/cantusdb_project/main_app/views/__init__.py b/django/cantusdb_project/main_app/views/__init__.py index 38a8cebad..91e0a4287 100644 --- a/django/cantusdb_project/main_app/views/__init__.py +++ b/django/cantusdb_project/main_app/views/__init__.py @@ -12,7 +12,7 @@ ) from main_app.views.feast import FeastDetailView, FeastListView from main_app.views.genre import GenreDetailView, GenreListView -from main_app.views.indexer import IndexerDetailView, IndexerListView +from main_app.views.user import IndexerListView from main_app.views.office import OfficeDetailView, OfficeListView from main_app.views.sequence import SequenceDetailView, SequenceListView from main_app.views.source import SourceDetailView, SourceListView From 364a9d57d6ff40e0223c91b8c07f35af6c6c655b Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Sun, 11 Sep 2022 21:09:20 +0000 Subject: [PATCH 04/20] Replace name-related properties with full_name --- .../main_app/management/commands/merge_indexer_user.py | 6 +++++- .../cantusdb_project/main_app/templates/indexer_list.html | 2 +- django/cantusdb_project/main_app/templates/user_detail.html | 4 ++-- django/cantusdb_project/main_app/templates/user_list.html | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py index bb4fbf9e7..a297e22dc 100644 --- a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py +++ b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py @@ -20,7 +20,10 @@ def handle(self, *args, **options): # do not use the existing "name" property, # because for some reason, the first/last names can have trailing spaces # TODO: populate the full_name field and remove the "name" property - user.full_name = f"{user.first_name.strip()} {user.last_name.strip()}" + if user.first_name or user.last_name: + user.full_name = f"{user.first_name.strip()} {user.last_name.strip()}" + else: + user.full_name = "Anonymous User" user.save() for indexer in indexers: @@ -50,5 +53,6 @@ def handle(self, *args, **options): # leave the password empty for dummy users # the password can't be empty in login form, so they can't log in password="", + old_indexer_id = indexer.id, show_in_list=True, ) diff --git a/django/cantusdb_project/main_app/templates/indexer_list.html b/django/cantusdb_project/main_app/templates/indexer_list.html index cc253eb3e..68eeccb3e 100644 --- a/django/cantusdb_project/main_app/templates/indexer_list.html +++ b/django/cantusdb_project/main_app/templates/indexer_list.html @@ -33,7 +33,7 @@

List of Indexers

{% for indexer in indexers %} - {{ indexer.given_name }} {{ indexer.family_name }} + {{ indexer.full_name }} {{ indexer.institution|default:"" }} {{ indexer.city|default:"" }} diff --git a/django/cantusdb_project/main_app/templates/user_detail.html b/django/cantusdb_project/main_app/templates/user_detail.html index 7260342f1..10b9e2a84 100644 --- a/django/cantusdb_project/main_app/templates/user_detail.html +++ b/django/cantusdb_project/main_app/templates/user_detail.html @@ -1,13 +1,13 @@ {% extends "base.html" %} {% load helper_tags %} {% block content %} -{{ user.first_name }} {{ user.last_name }} | Cantus Manuscript Database +{{ user.full_name }} | Cantus Manuscript Database
{% include "global_search_bar.html" %} -

{{ user.first_name }} {{ user.last_name }}

+

{{ user.full_name }}

{% if user.institution %}
Institution
diff --git a/django/cantusdb_project/main_app/templates/user_list.html b/django/cantusdb_project/main_app/templates/user_list.html index aef0dadd1..0cb24e002 100644 --- a/django/cantusdb_project/main_app/templates/user_list.html +++ b/django/cantusdb_project/main_app/templates/user_list.html @@ -1,13 +1,13 @@ {% extends "base.html" %} {% load helper_tags %} {% block content %} -List of Users | Cantus Manuscript Database +All Users | Cantus Manuscript Database
{% include "global_search_bar.html" %} -

List of Users

+

All Users

Displaying {{ page_obj.start_index }}-{{ page_obj.end_index }} of {{ page_obj.paginator.count }}
From 57eddd997e231828e106634cb97ddb0e61316aa5 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Tue, 13 Sep 2022 01:29:51 +0000 Subject: [PATCH 05/20] Replace indexer fields with user fields --- .../commands/replace_indexer_fields.py | 40 +++++++++++++++++++ .../main_app/models/source.py | 33 +++++++++++---- .../main_app/templates/source_detail.html | 36 ++++++++--------- 3 files changed, 82 insertions(+), 27 deletions(-) create mode 100644 django/cantusdb_project/main_app/management/commands/replace_indexer_fields.py diff --git a/django/cantusdb_project/main_app/management/commands/replace_indexer_fields.py b/django/cantusdb_project/main_app/management/commands/replace_indexer_fields.py new file mode 100644 index 000000000..09e245e56 --- /dev/null +++ b/django/cantusdb_project/main_app/management/commands/replace_indexer_fields.py @@ -0,0 +1,40 @@ +from main_app.models import Source +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand + +# This command is intended as a temporary approach to +# populate the user-related fields using the indexer-related fields. +# The goal is to eventually remove the Indexer model and point all fields to User. +# After running this command, the indexer-related fields can be removed from the Source model. +# The user-related fields can then be renamed as needed. +# Run with `python manage.py replace_indexer_fields`. +class Command(BaseCommand): + def handle(self, *args, **options): + sources = Source.objects.all() + for source in sources: + print(source.id) + + inventoried_by = source.inventoried_by.all() + for indexer in inventoried_by: + user = get_user_model().objects.get(old_indexer_id=indexer.id) + source.inventoried_by_u.add(user) + + full_text_entered_by = source.full_text_entered_by.all() + for indexer in full_text_entered_by: + user = get_user_model().objects.get(old_indexer_id=indexer.id) + source.full_text_entered_by_u.add(user) + + melodies_entered_by = source.melodies_entered_by.all() + for indexer in melodies_entered_by: + user = get_user_model().objects.get(old_indexer_id=indexer.id) + source.melodies_entered_by_u.add(user) + + proofreaders = source.proofreaders.all() + for indexer in proofreaders: + user = get_user_model().objects.get(old_indexer_id=indexer.id) + source.proofreaders_u.add(user) + + other_editors = source.other_editors.all() + for indexer in other_editors: + user = get_user_model().objects.get(old_indexer_id=indexer.id) + source.other_editors_u.add(user) diff --git a/django/cantusdb_project/main_app/models/source.py b/django/cantusdb_project/main_app/models/source.py index ee05d531a..f3eb60ce7 100644 --- a/django/cantusdb_project/main_app/models/source.py +++ b/django/cantusdb_project/main_app/models/source.py @@ -62,23 +62,40 @@ class Source(BaseModel): max_length=63, help_text='Date of the manuscript (e.g. "1200s", "1300-1350", etc.)', ) - century = models.ManyToManyField("Century", related_name="sources", blank=True, null=True,) - notation = models.ManyToManyField("Notation", related_name="sources", blank=True, null=True,) + century = models.ManyToManyField("Century", related_name="sources", blank=True) + notation = models.ManyToManyField("Notation", related_name="sources", blank=True) cursus = models.CharField( blank=True, null=True, choices=cursus_choices, max_length=63 ) - current_editors = models.ManyToManyField(get_user_model(), related_name="sources_user_can_edit", blank=True, null=True,) + current_editors = models.ManyToManyField(get_user_model(), related_name="sources_user_can_edit", blank=True) + + # indexer-related fields inventoried_by = models.ManyToManyField( - "Indexer", related_name="sources_inventoried", blank=True, null=True, + "Indexer", related_name="sources_inventoried", blank=True ) full_text_entered_by = models.ManyToManyField( - "Indexer", related_name="entered_full_text_for_sources", blank=True, null=True, + "Indexer", related_name="entered_full_text_for_sources", blank=True ) melodies_entered_by = models.ManyToManyField( - "Indexer", related_name="entered_melody_for_sources", blank=True, null=True, + "Indexer", related_name="entered_melody_for_sources", blank=True ) - proofreaders = models.ManyToManyField("Indexer", related_name="proofread_sources", blank=True, null=True,) - other_editors = models.ManyToManyField("Indexer", related_name="edited_sources", blank=True, null=True,) + proofreaders = models.ManyToManyField("Indexer", related_name="proofread_sources", blank=True) + other_editors = models.ManyToManyField("Indexer", related_name="edited_sources", blank=True) + + # replace indexer-related fields with the following user-related fields + inventoried_by_u = models.ManyToManyField( + get_user_model(), related_name="sources_inventoried", blank=True + ) + full_text_entered_by_u = models.ManyToManyField( + get_user_model(), related_name="entered_full_text_for_sources", blank=True + ) + melodies_entered_by_u = models.ManyToManyField( + get_user_model(), related_name="entered_melody_for_sources", blank=True + ) + proofreaders_u = models.ManyToManyField(get_user_model(), related_name="proofread_sources", blank=True) + other_editors_u = models.ManyToManyField(get_user_model(), related_name="edited_sources", blank=True) + + segment = models.ForeignKey( "Segment", on_delete=models.PROTECT, blank=True, null=True ) diff --git a/django/cantusdb_project/main_app/templates/source_detail.html b/django/cantusdb_project/main_app/templates/source_detail.html index d0d696ba5..6a8ae5cbb 100644 --- a/django/cantusdb_project/main_app/templates/source_detail.html +++ b/django/cantusdb_project/main_app/templates/source_detail.html @@ -45,29 +45,29 @@

{{ source.title }}

{{ source.indexing_notes|safe }}
{% endif %} - {% if source.other_editors.all %} + {% if source.other_editors_u.all %}
Other Editors
- {% for editor in source.other_editors.all %} - {{ editor.given_name }} {{ editor.family_name }}
+ {% for editor in source.other_editors_u.all %} + {{ editor.full_name }}
{% endfor %}
{% endif %} - {% if source.full_text_entered_by.all %} + {% if source.full_text_entered_by_u.all %}
Full Text Entered by
- {% for editor in source.full_text_entered_by.all %} - {{ editor.given_name }} {{ editor.family_name }}
+ {% for editor in source.full_text_entered_by_u.all %} + {{ editor.full_name }}
{% endfor %}
{% endif %} - {% if source.melodies_entered_by.all %} + {% if source.melodies_entered_by_u.all %}
Melodies Entered by
- {% for editor in source.melodies_entered_by.all %} - {{ editor.given_name }} {{ editor.family_name }}
+ {% for editor in source.melodies_entered_by_u.all %} + {{ editor.full_name }}
{% endfor %}
{% endif %} @@ -210,33 +210,31 @@

{{ source.siglum }}

Notation: {{ source.notation.all.first.name }}
{% endif %} - {% if source.inventoried_by.all %} + {% if source.inventoried_by_u.all %} Inventoried by: {% endif %} - {% if source.proofreaders.all %} - Proofreader{{ source.proofreaders.all|pluralize }}: + {% if source.proofreaders_u.all %} + Proofreader{{ source.proofreaders_u.all|pluralize }}:
{% endif %} {{ source.indexing_notes|default_if_none:"" }}
- Contributor: CANTUS Database Administrator + Contributor:
From df513dd1785d089cdb353c214b997b4964d1a8b8 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Tue, 13 Sep 2022 17:05:15 +0000 Subject: [PATCH 06/20] Update source count on indexer list page --- .../cantusdb_project/main_app/models/source.py | 4 ++-- .../main_app/templates/indexer_list.html | 10 ++++++---- django/cantusdb_project/main_app/views/user.py | 18 ++++++++++++++---- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/django/cantusdb_project/main_app/models/source.py b/django/cantusdb_project/main_app/models/source.py index f3eb60ce7..10a82275e 100644 --- a/django/cantusdb_project/main_app/models/source.py +++ b/django/cantusdb_project/main_app/models/source.py @@ -71,7 +71,7 @@ class Source(BaseModel): # indexer-related fields inventoried_by = models.ManyToManyField( - "Indexer", related_name="sources_inventoried", blank=True + "Indexer", related_name="inventoried_sources", blank=True ) full_text_entered_by = models.ManyToManyField( "Indexer", related_name="entered_full_text_for_sources", blank=True @@ -84,7 +84,7 @@ class Source(BaseModel): # replace indexer-related fields with the following user-related fields inventoried_by_u = models.ManyToManyField( - get_user_model(), related_name="sources_inventoried", blank=True + get_user_model(), related_name="inventoried_sources", blank=True ) full_text_entered_by_u = models.ManyToManyField( get_user_model(), related_name="entered_full_text_for_sources", blank=True diff --git a/django/cantusdb_project/main_app/templates/indexer_list.html b/django/cantusdb_project/main_app/templates/indexer_list.html index 68eeccb3e..bd9d5ec5f 100644 --- a/django/cantusdb_project/main_app/templates/indexer_list.html +++ b/django/cantusdb_project/main_app/templates/indexer_list.html @@ -35,10 +35,12 @@

List of Indexers

{{ indexer.full_name }} - {{ indexer.institution|default:"" }} - {{ indexer.city|default:"" }} - {{ indexer.country|default:"" }} - {{ indexer.source_count }} source{{ indexer.source_count|pluralize }} + {{ indexer.institution|default:"" }} + {{ indexer.city|default:"" }} + {{ indexer.country|default:"" }} + + {{ indexer.source_count }} source{{ indexer.source_count|pluralize }} + {% endfor %} diff --git a/django/cantusdb_project/main_app/views/user.py b/django/cantusdb_project/main_app/views/user.py index 8b0bdc245..06a21dad8 100644 --- a/django/cantusdb_project/main_app/views/user.py +++ b/django/cantusdb_project/main_app/views/user.py @@ -93,10 +93,20 @@ class IndexerListView(UserListView): def get_queryset(self): all_users = super().get_queryset() indexers = all_users.filter(show_in_list=True) - # indexers = indexers.annotate( - # source_count=Count("inventoried_sources", filter=Q(sources_inventoried__published=True)) - # ) - return indexers + display_unpublished = self.request.user.is_authenticated + if display_unpublished: + indexers = indexers.annotate( + source_count=Count("inventoried_sources") + ) + # display those who have at least one source + return indexers.filter(source_count__gte=1) + else: + indexers = indexers.annotate( + source_count=Count("inventoried_sources", filter=Q(inventoried_sources__published=True)) + ) + # display those who have at least one published source + return indexers.filter(source_count__gte=1) + class CustomLoginView(LoginView): def form_valid(self, form): From d5d0980ae26be020030943626badf22ba0acc645 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Tue, 13 Sep 2022 17:13:23 +0000 Subject: [PATCH 07/20] Remove unused imports in chant model --- django/cantusdb_project/main_app/models/chant.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/django/cantusdb_project/main_app/models/chant.py b/django/cantusdb_project/main_app/models/chant.py index cbaed228d..c9df60917 100644 --- a/django/cantusdb_project/main_app/models/chant.py +++ b/django/cantusdb_project/main_app/models/chant.py @@ -1,9 +1,5 @@ -from django.contrib.postgres.search import SearchVectorField -from django.db import models from django.db.models.query import QuerySet from main_app.models.base_chant import BaseChant -from users.models import User - class Chant(BaseChant): """The model for chants @@ -13,8 +9,6 @@ class Chant(BaseChant): properties and attributes should be declared in BaseChant in order to keep the two models harmonized, even if only one of the two models uses a particular field. """ - - def get_ci_url(self) -> str: """Construct the url to the entry in Cantus Index correponding to the chant. From 37655bac02f473d45d1c43d04328f87115d96b7a Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 00:33:10 +0000 Subject: [PATCH 08/20] Set UserListView to login-required --- django/cantusdb_project/main_app/views/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/cantusdb_project/main_app/views/user.py b/django/cantusdb_project/main_app/views/user.py index 06a21dad8..7888490ba 100644 --- a/django/cantusdb_project/main_app/views/user.py +++ b/django/cantusdb_project/main_app/views/user.py @@ -59,7 +59,7 @@ def get_next_page(self): ) return next_page -class UserListView(SearchableListMixin, ListView): +class UserListView(LoginRequiredMixin, SearchableListMixin, ListView): """A list of all User objects This view is equivalent to the user list view on the old Cantus. From fc62e6b0ee53ad348c9efc55a42962350b24a8ab Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 00:34:51 +0000 Subject: [PATCH 09/20] Update tests --- .../main_app/tests/test_models.py | 65 +------- .../main_app/tests/test_views.py | 145 ------------------ 2 files changed, 2 insertions(+), 208 deletions(-) diff --git a/django/cantusdb_project/main_app/tests/test_models.py b/django/cantusdb_project/main_app/tests/test_models.py index eb33682f3..d7093cdc1 100644 --- a/django/cantusdb_project/main_app/tests/test_models.py +++ b/django/cantusdb_project/main_app/tests/test_models.py @@ -134,7 +134,7 @@ def setUpTestData(cls): def test_object_name(self): genre = Genre.objects.first() - self.assertEqual(str(genre), genre.name) + self.assertEqual(str(genre), f"[{genre.name}] {genre.description}") def test_display_name(self): genre = Genre.objects.first() @@ -148,67 +148,6 @@ def test_absolute_url(self): self.assertEqual(genre.get_absolute_url(), absolute_url) -class IndexerModelTest(TestCase): - @classmethod - def setUpTestData(cls): - make_fake_indexer() - - def test_given_name_label(self): - indexer = Indexer.objects.first() - field_label = indexer._meta.get_field("given_name").verbose_name - self.assertEqual(field_label, "given name") - - def test_given_name_max_length(self): - indexer = Indexer.objects.first() - max_length = indexer._meta.get_field("given_name").max_length - self.assertEqual(max_length, 50) - - def test_family_name_label(self): - indexer = Indexer.objects.first() - field_label = indexer._meta.get_field("family_name").verbose_name - self.assertEqual(field_label, "family name") - - def test_family_name_max_length(self): - indexer = Indexer.objects.first() - max_length = indexer._meta.get_field("family_name").max_length - self.assertEqual(max_length, 50) - - def test_insitution_label(self): - indexer = Indexer.objects.first() - field_label = indexer._meta.get_field("institution").verbose_name - self.assertEqual(field_label, "institution") - - def test_institution_max_length(self): - indexer = Indexer.objects.first() - max_length = indexer._meta.get_field("institution").max_length - self.assertEqual(max_length, 255) - - def test_city_label(self): - indexer = Indexer.objects.first() - field_label = indexer._meta.get_field("city").verbose_name - self.assertEqual(field_label, "city") - - def test_city_max_length(self): - indexer = Indexer.objects.first() - max_length = indexer._meta.get_field("city").max_length - self.assertEqual(max_length, 255) - - def test_country_label(self): - indexer = Indexer.objects.first() - field_label = indexer._meta.get_field("country").verbose_name - self.assertEqual(field_label, "country") - - def test_country_max_length(self): - indexer = Indexer.objects.first() - max_length = indexer._meta.get_field("country").max_length - self.assertEqual(max_length, 255) - - def test_absolute_url(self): - indexer = Indexer.objects.first() - absolute_url = reverse("indexer-detail", args=[str(indexer.id)]) - self.assertEqual(indexer.get_absolute_url(), absolute_url) - - class OfficeModelTest(TestCase): @classmethod def setUpTestData(cls): @@ -216,7 +155,7 @@ def setUpTestData(cls): def test_object_name(self): office = Office.objects.first() - self.assertEqual(str(office), office.name) + self.assertEqual(str(office), f"[{office.name}] {office.description}") def test_display_name(self): office = Office.objects.first() diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index e369f7c26..6f91e3e39 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -1735,151 +1735,6 @@ def test_search_incipit(self): self.assertEqual(len(response.context["object_list"]), 0) -class IndexerListViewTest(TestCase): - def setUp(self): - # unless a segment is specified when a source is created, the source is automatically assigned - # to the segment with the name "CANTUS Database" - to prevent errors, we must make sure that - # such a segment exists - Segment.objects.create(name="CANTUS Database") - - def test_view_url_path(self): - response = self.client.get("/indexers/") - self.assertEqual(response.status_code, 200) - - def test_view_url_reverse_name(self): - response = self.client.get(reverse("indexer-list")) - self.assertEqual(response.status_code, 200) - - def test_url_and_templates(self): - """Test the url and templates used""" - response = self.client.get(reverse("indexer-list")) - self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, "base.html") - self.assertTemplateUsed(response, "indexer_list.html") - - def test_only_public_indexer_visible(self): - """In the indexer list view, only public indexers (those who have at least one published source) should be visible""" - # generate some indexers - indexer_with_published_source = make_fake_indexer() - indexer_with_unpublished_source = make_fake_indexer() - indexer_with_no_source = make_fake_indexer() - - # generate published/unpublished sources and assign indexers to them - unpublished_source = Source.objects.create(title="unpublished source", published=False) - unpublished_source.inventoried_by.set([indexer_with_unpublished_source]) - - published_source = Source.objects.create(title="published source", published=True) - published_source.inventoried_by.set([indexer_with_published_source]) - - source_with_multiple_indexers = Source.objects.create( - title="unpublished source with multiple indexers", published=False, - ) - source_with_multiple_indexers.inventoried_by.set( - [indexer_with_published_source, indexer_with_unpublished_source] - ) - - # access the page context, only the public indexer should be in the context - response = self.client.get(reverse("indexer-list")) - self.assertEqual(response.status_code, 200) - self.assertIn(indexer_with_published_source, response.context["indexers"]) - self.assertNotIn(indexer_with_unpublished_source, response.context["indexers"]) - self.assertNotIn(indexer_with_no_source, response.context["indexers"]) - - def test_search_given_name(self): - """ - Indexer can be searched by passing a `q` parameter to the url \\ - Search fields include first name, family name, country, city, and institution \\ - Only public indexers should appear in the results - """ - indexer_with_published_source = make_fake_indexer() - published_source = Source.objects.create(title="published source", published=True) - published_source.inventoried_by.set([indexer_with_published_source]) - - # search with a random slice of first name - target = indexer_with_published_source.given_name - search_term = get_random_search_term(target) - response = self.client.get(reverse("indexer-list"), {"q": search_term}) - self.assertEqual(response.status_code, 200) - self.assertIn(indexer_with_published_source, response.context["indexers"]) - - def test_search_family_name(self): - indexer_with_published_source = make_fake_indexer() - published_source = Source.objects.create(title="published source", published=True) - published_source.inventoried_by.set([indexer_with_published_source]) - - target = indexer_with_published_source.family_name - search_term = get_random_search_term(target) - response = self.client.get(reverse("indexer-list"), {"q": search_term}) - self.assertEqual(response.status_code, 200) - self.assertIn(indexer_with_published_source, response.context["indexers"]) - - def test_search_country(self): - indexer_with_published_source = make_fake_indexer() - published_source = Source.objects.create(title="published source", published=True) - published_source.inventoried_by.set([indexer_with_published_source]) - - target = indexer_with_published_source.country - search_term = get_random_search_term(target) - response = self.client.get(reverse("indexer-list"), {"q": search_term}) - self.assertEqual(response.status_code, 200) - self.assertIn(indexer_with_published_source, response.context["indexers"]) - - def test_search_city(self): - indexer_with_published_source = make_fake_indexer() - published_source = Source.objects.create(title="published source", published=True) - published_source.inventoried_by.set([indexer_with_published_source]) - - target = indexer_with_published_source.city - search_term = get_random_search_term(target) - response = self.client.get(reverse("indexer-list"), {"q": search_term}) - self.assertEqual(response.status_code, 200) - self.assertIn(indexer_with_published_source, response.context["indexers"]) - - def test_search_institution(self): - indexer_with_published_source = make_fake_indexer() - published_source = Source.objects.create(title="published source", published=True) - published_source.inventoried_by.set([indexer_with_published_source]) - - target = indexer_with_published_source.institution - search_term = get_random_search_term(target) - response = self.client.get(reverse("indexer-list"), {"q": search_term}) - self.assertEqual(response.status_code, 200) - self.assertIn(indexer_with_published_source, response.context["indexers"]) - - -class IndexerDetailViewTest(TestCase): - NUM_INDEXERS = 10 - - @classmethod - def setUpTestData(cls): - for i in range(cls.NUM_INDEXERS): - make_fake_indexer() - - def test_view_url_path(self): - for indexer in Indexer.objects.all(): - response = self.client.get(f"/indexer/{indexer.id}") - self.assertEqual(response.status_code, 200) - - def test_view_url_reverse_name(self): - for indexer in Indexer.objects.all(): - response = self.client.get(reverse("indexer-detail", args=[indexer.id])) - self.assertEqual(response.status_code, 200) - - def test_url_and_templates(self): - """Test the url and templates used""" - indexer = make_fake_indexer() - response = self.client.get(reverse("indexer-detail", args=[indexer.id])) - self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, "base.html") - self.assertTemplateUsed(response, "indexer_detail.html") - - def test_context(self): - indexer = make_fake_indexer() - response = self.client.get(reverse("indexer-detail", args=[indexer.id])) - self.assertEqual(response.status_code, 200) - self.assertEqual(indexer, response.context["indexer"]) - - class OfficeListViewTest(TestCase): OFFICE_COUNT = 10 From e2739387a2408094fd38fe9cd548f5120879939e Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 00:39:26 +0000 Subject: [PATCH 10/20] Update user-field references on chant-detail page --- .../main_app/templates/chant_detail.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/django/cantusdb_project/main_app/templates/chant_detail.html b/django/cantusdb_project/main_app/templates/chant_detail.html index 9794ea65c..118765624 100644 --- a/django/cantusdb_project/main_app/templates/chant_detail.html +++ b/django/cantusdb_project/main_app/templates/chant_detail.html @@ -346,27 +346,27 @@

List of melodies


{% endif %} - {% if source.inventoried_by.all %} + {% if source.inventoried_by_u.all %} Inventoried by: {% endif %} - {% if source.proofreaders.all %} - Proofreader{{ source.proofreaders.all|pluralize }}: + {% if source.proofreaders_u.all %} + Proofreader{{ source.proofreaders_u.all|pluralize }}:
@@ -374,7 +374,7 @@

List of melodies

{{ source.indexing_notes|default_if_none:"" }}
- Contributor: CANTUS Database Administrator + Contributor:
From 3ace84d7c29e44b7f3dbe65e2ad8dfa6f1f5d286 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 01:43:04 +0000 Subject: [PATCH 11/20] Remove indexer fields and rename user fields --- .../main_app/models/source.py | 22 ++++--------------- .../main_app/templates/chant_detail.html | 10 ++++----- .../main_app/templates/source_detail.html | 22 +++++++++---------- 3 files changed, 20 insertions(+), 34 deletions(-) diff --git a/django/cantusdb_project/main_app/models/source.py b/django/cantusdb_project/main_app/models/source.py index 10a82275e..20c4ec60c 100644 --- a/django/cantusdb_project/main_app/models/source.py +++ b/django/cantusdb_project/main_app/models/source.py @@ -69,31 +69,17 @@ class Source(BaseModel): ) current_editors = models.ManyToManyField(get_user_model(), related_name="sources_user_can_edit", blank=True) - # indexer-related fields inventoried_by = models.ManyToManyField( - "Indexer", related_name="inventoried_sources", blank=True - ) - full_text_entered_by = models.ManyToManyField( - "Indexer", related_name="entered_full_text_for_sources", blank=True - ) - melodies_entered_by = models.ManyToManyField( - "Indexer", related_name="entered_melody_for_sources", blank=True - ) - proofreaders = models.ManyToManyField("Indexer", related_name="proofread_sources", blank=True) - other_editors = models.ManyToManyField("Indexer", related_name="edited_sources", blank=True) - - # replace indexer-related fields with the following user-related fields - inventoried_by_u = models.ManyToManyField( get_user_model(), related_name="inventoried_sources", blank=True ) - full_text_entered_by_u = models.ManyToManyField( + full_text_entered_by = models.ManyToManyField( get_user_model(), related_name="entered_full_text_for_sources", blank=True ) - melodies_entered_by_u = models.ManyToManyField( + melodies_entered_by = models.ManyToManyField( get_user_model(), related_name="entered_melody_for_sources", blank=True ) - proofreaders_u = models.ManyToManyField(get_user_model(), related_name="proofread_sources", blank=True) - other_editors_u = models.ManyToManyField(get_user_model(), related_name="edited_sources", blank=True) + proofreaders = models.ManyToManyField(get_user_model(), related_name="proofread_sources", blank=True) + other_editors = models.ManyToManyField(get_user_model(), related_name="edited_sources", blank=True) segment = models.ForeignKey( diff --git a/django/cantusdb_project/main_app/templates/chant_detail.html b/django/cantusdb_project/main_app/templates/chant_detail.html index 118765624..1ada2ba4b 100644 --- a/django/cantusdb_project/main_app/templates/chant_detail.html +++ b/django/cantusdb_project/main_app/templates/chant_detail.html @@ -346,10 +346,10 @@

List of melodies


{% endif %} - {% if source.inventoried_by_u.all %} + {% if source.inventoried_by.all %} Inventoried by:
    - {% for editor in source.inventoried_by_u.all %} + {% for editor in source.inventoried_by.all %}
  • {{ editor.full_name }}
    @@ -359,11 +359,11 @@

    List of melodies

{% endif %} - {% if source.proofreaders_u.all %} - Proofreader{{ source.proofreaders_u.all|pluralize }}: + {% if source.proofreaders.all %} + Proofreader{{ source.proofreaders.all|pluralize }}:
    - {% for editor in source.proofreaders_u.all %} + {% for editor in source.proofreaders.all %}
  • {{ editor.full_name }}
    diff --git a/django/cantusdb_project/main_app/templates/source_detail.html b/django/cantusdb_project/main_app/templates/source_detail.html index 6a8ae5cbb..0786be687 100644 --- a/django/cantusdb_project/main_app/templates/source_detail.html +++ b/django/cantusdb_project/main_app/templates/source_detail.html @@ -45,28 +45,28 @@

    {{ source.title }}

    {{ source.indexing_notes|safe }}
    {% endif %} - {% if source.other_editors_u.all %} + {% if source.other_editors.all %}
    Other Editors
    - {% for editor in source.other_editors_u.all %} + {% for editor in source.other_editors.all %} {{ editor.full_name }}
    {% endfor %}
    {% endif %} - {% if source.full_text_entered_by_u.all %} + {% if source.full_text_entered_by.all %}
    Full Text Entered by
    - {% for editor in source.full_text_entered_by_u.all %} + {% for editor in source.full_text_entered_by.all %} {{ editor.full_name }}
    {% endfor %}
    {% endif %} - {% if source.melodies_entered_by_u.all %} + {% if source.melodies_entered_by.all %}
    Melodies Entered by
    - {% for editor in source.melodies_entered_by_u.all %} + {% for editor in source.melodies_entered_by.all %} {{ editor.full_name }}
    {% endfor %}
    @@ -210,10 +210,10 @@

    {{ source.siglum }}

    Notation: {{ source.notation.all.first.name }}
    {% endif %} - {% if source.inventoried_by_u.all %} + {% if source.inventoried_by.all %} Inventoried by:
      - {% for editor in source.inventoried_by_u.all %} + {% for editor in source.inventoried_by.all %}
    • {{ editor.full_name }}
      {{ editor.institution|default_if_none:"" }} @@ -221,11 +221,11 @@

      {{ source.siglum }}

      {% endfor %}
    {% endif %} - {% if source.proofreaders_u.all %} - Proofreader{{ source.proofreaders_u.all|pluralize }}: + {% if source.proofreaders.all %} + Proofreader{{ source.proofreaders.all|pluralize }}:
      - {% for editor in source.proofreaders_u.all %} + {% for editor in source.proofreaders.all %}
    • {{ editor.full_name }}
    • From 71fb46f0fdff70b504d92a03f8089f7f279adfe0 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 01:45:21 +0000 Subject: [PATCH 12/20] Remove indexer model and its imports --- django/cantusdb_project/main_app/admin.py | 4 --- django/cantusdb_project/main_app/forms.py | 6 ++--- .../main_app/models/__init__.py | 1 - .../main_app/models/indexer.py | 13 ---------- .../main_app/templates/indexer_detail.html | 26 ------------------- .../main_app/tests/test_views.py | 24 +---------------- .../cantusdb_project/main_app/views/views.py | 5 ---- django/cantusdb_project/templates/base.html | 3 --- 8 files changed, 4 insertions(+), 78 deletions(-) delete mode 100644 django/cantusdb_project/main_app/models/indexer.py delete mode 100644 django/cantusdb_project/main_app/templates/indexer_detail.html diff --git a/django/cantusdb_project/main_app/admin.py b/django/cantusdb_project/main_app/admin.py index e575431e2..4bee5b289 100644 --- a/django/cantusdb_project/main_app/admin.py +++ b/django/cantusdb_project/main_app/admin.py @@ -28,9 +28,6 @@ class FeastAdmin(BaseModelAdmin): class GenreAdmin(BaseModelAdmin): pass -class IndexerAdmin(BaseModelAdmin): - pass - class NotationAdmin(BaseModelAdmin): pass @@ -59,7 +56,6 @@ class SourceAdmin(BaseModelAdmin): admin.site.register(Chant, ChantAdmin) admin.site.register(Feast, FeastAdmin) admin.site.register(Genre, GenreAdmin) -admin.site.register(Indexer, IndexerAdmin) admin.site.register(Notation, NotationAdmin) admin.site.register(Office, OfficeAdmin) admin.site.register(Provenance, ProvenanceAdmin) diff --git a/django/cantusdb_project/main_app/forms.py b/django/cantusdb_project/main_app/forms.py index f5e39522d..362c877d9 100644 --- a/django/cantusdb_project/main_app/forms.py +++ b/django/cantusdb_project/main_app/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import Chant, Office, Genre, Feast, Source, RismSiglum, Provenance, Century, Indexer, Sequence +from .models import Chant, Office, Genre, Feast, Source, RismSiglum, Provenance, Century, Sequence from .widgets import (TextInputWidget, VolpianoInputWidget, TextAreaWidget, @@ -216,7 +216,7 @@ class Meta: current_editors.widget.attrs.update({"class": "form-control custom-select custom-select-sm"}) melodies_entered_by = forms.ModelMultipleChoiceField( - queryset=Indexer.objects.all().order_by("family_name"), required=False + queryset=get_user_model().objects.all().order_by("full_name"), required=False ) melodies_entered_by.widget.attrs.update({"class": "form-control custom-select custom-select-sm"}) @@ -439,7 +439,7 @@ class Meta: current_editors.widget.attrs.update({"class": "form-control custom-select custom-select-sm"}) melodies_entered_by = forms.ModelMultipleChoiceField( - queryset=Indexer.objects.all().order_by("family_name"), required=False + queryset=get_user_model().objects.all().order_by("full_name"), required=False ) melodies_entered_by.widget.attrs.update({"class": "form-control custom-select custom-select-sm"}) diff --git a/django/cantusdb_project/main_app/models/__init__.py b/django/cantusdb_project/main_app/models/__init__.py index bcaf42c8e..f94d5428d 100644 --- a/django/cantusdb_project/main_app/models/__init__.py +++ b/django/cantusdb_project/main_app/models/__init__.py @@ -3,7 +3,6 @@ from main_app.models.chant import Chant from main_app.models.feast import Feast from main_app.models.genre import Genre -from main_app.models.indexer import Indexer from main_app.models.notation import Notation from main_app.models.office import Office from main_app.models.provenance import Provenance diff --git a/django/cantusdb_project/main_app/models/indexer.py b/django/cantusdb_project/main_app/models/indexer.py deleted file mode 100644 index 4a6d0024d..000000000 --- a/django/cantusdb_project/main_app/models/indexer.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.db import models -from main_app.models import BaseModel - - -class Indexer(BaseModel): - given_name = models.CharField(max_length=50) - family_name = models.CharField(max_length=50) - institution = models.CharField(max_length=255, blank=True, null=True) - city = models.CharField(max_length=255, blank=True, null=True) - country = models.CharField(max_length=255, blank=True, null=True) - - def __str__(self): - return f'{self.given_name} {self.family_name}' \ No newline at end of file diff --git a/django/cantusdb_project/main_app/templates/indexer_detail.html b/django/cantusdb_project/main_app/templates/indexer_detail.html deleted file mode 100644 index d96f49be1..000000000 --- a/django/cantusdb_project/main_app/templates/indexer_detail.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "base.html" %} -{% load helper_tags %} -{% block content %} -{{ indexer.given_name }} {{ indexer.family_name }} | Cantus Manuscript Database -
      - - - {% include "global_search_bar.html" %} - -

      {{ indexer.given_name }} {{ indexer.family_name }}

      -
      - {% if indexer.institution %} -
      Institution
      -
      {{ indexer.institution }}
      - {% endif %} - {% if indexer.city %} -
      City
      -
      {{ indexer.city }}
      - {% endif %} - {% if indexer.country %} -
      Country
      -
      {{ indexer.country }}
      - {% endif %} -
      -
      -{% endblock %} diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index 6f91e3e39..177190544 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -1,8 +1,6 @@ -import unittest import random from django.urls import reverse from django.test import TestCase -from django.http import HttpResponseNotFound from main_app.views.feast import FeastListView from django.http.response import JsonResponse import json @@ -10,8 +8,6 @@ from django.contrib.auth.models import Group from django.test import Client from django.db.models import Q -from abc import abstractmethod -from abc import ABC import csv from faker import Faker @@ -20,7 +16,6 @@ make_fake_chant, make_fake_feast, make_fake_genre, - make_fake_indexer, make_fake_notation, make_fake_office, make_fake_provenance, @@ -28,13 +23,13 @@ make_fake_segment, make_fake_sequence, make_fake_source, + make_fake_user, ) from main_app.models import Century from main_app.models import Chant from main_app.models import Feast from main_app.models import Genre -from main_app.models import Indexer from main_app.models import Notation from main_app.models import Office from main_app.models import Provenance @@ -2446,23 +2441,6 @@ def test_json_node_for_source(self): self.assertIsInstance(response_id, int) self.assertEqual(response_id, id) - def test_json_node_for_indexer(self): - indexer = make_fake_indexer() - id = indexer.id - - response = self.client.get(reverse("json-node-export", args=[id])) - self.assertIsInstance(response, JsonResponse) - - unpacked_response = json.loads(response.content) - - response_name = unpacked_response['given_name'] - self.assertIsInstance(response_name, str) - self.assertEqual(response_name, indexer.given_name) - - response_id = unpacked_response['id'] - self.assertIsInstance(response_id, int) - self.assertEqual(response_id, id) - def test_json_node_published_vs_unpublished(self): source = make_fake_source(published=True) chant = make_fake_chant(source=source) diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py index 0e0a8d8bd..baee66cce 100644 --- a/django/cantusdb_project/main_app/views/views.py +++ b/django/cantusdb_project/main_app/views/views.py @@ -8,7 +8,6 @@ Chant, Feast, Genre, - Indexer, Notation, Office, Provenance, @@ -546,7 +545,6 @@ def json_node_export(request, id): chant = Chant.objects.filter(id=id) sequence = Sequence.objects.filter(id=id) source = Source.objects.filter(id=id) - indexer = Indexer.objects.filter(id=id) if chant: if not chant.first().source.published: @@ -560,8 +558,6 @@ def json_node_export(request, id): if not source.first().published: return HttpResponseNotFound() requested_item = source - elif indexer: - requested_item = indexer else: # id does not correspond to a chant, sequence, source or indexer return HttpResponseNotFound() @@ -646,7 +642,6 @@ def content_overview(request): Chant, Feast, Genre, - Indexer, Notation, Office, Provenance, diff --git a/django/cantusdb_project/templates/base.html b/django/cantusdb_project/templates/base.html index e46036841..516196bf5 100644 --- a/django/cantusdb_project/templates/base.html +++ b/django/cantusdb_project/templates/base.html @@ -357,9 +357,6 @@
      Admin Navigation
    • Add Feast
    • -
    • - Add Indexer -
    • List of Indexers
    • From c4461cbe7c008eb7140ec304e11bec840ace2b2e Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 01:46:56 +0000 Subject: [PATCH 13/20] Replace remaining family/first name with full name --- .../main_app/templates/content_overview.html | 4 ++-- .../main_app/tests/make_fakes.py | 24 +++++++++---------- .../cantusdb_project/main_app/views/source.py | 18 +++++--------- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/django/cantusdb_project/main_app/templates/content_overview.html b/django/cantusdb_project/main_app/templates/content_overview.html index c9934b29b..073927b5b 100644 --- a/django/cantusdb_project/main_app/templates/content_overview.html +++ b/django/cantusdb_project/main_app/templates/content_overview.html @@ -38,9 +38,9 @@

      Content Overview

      {{ object.name|truncatechars:30 }} {% endif %} - {% elif object.first_name %} + {% elif object.full_name %} - {{ object.first_name }} {{ object.family_name }} + {{ object.full_name }} {% else %} diff --git a/django/cantusdb_project/main_app/tests/make_fakes.py b/django/cantusdb_project/main_app/tests/make_fakes.py index a03419948..d859b8534 100644 --- a/django/cantusdb_project/main_app/tests/make_fakes.py +++ b/django/cantusdb_project/main_app/tests/make_fakes.py @@ -6,7 +6,6 @@ from main_app.models import Chant from main_app.models import Feast from main_app.models import Genre -from main_app.models import Indexer from main_app.models import Notation from main_app.models import Office from main_app.models import Provenance @@ -14,6 +13,7 @@ from main_app.models import Segment from main_app.models import Sequence from main_app.models import Source +from django.contrib.auth import get_user_model # Max values for two types of Char fields LONG_CHAR_FIELD_MAX = 255 @@ -172,16 +172,16 @@ def make_fake_genre() -> Genre: return genre -def make_fake_indexer() -> Indexer: - """Generates a fake Indexer object.""" - indexer = Indexer.objects.create( - given_name=faker.first_name(), - family_name=faker.last_name(), +def make_fake_user() -> get_user_model(): + """Generates a fake User object.""" + user = get_user_model().objects.create( + full_name=f"{faker.first_name()} {faker.last_name()}", institution=faker.company(), city=faker.city(), country=faker.country(), + email=f"{faker.lexify('????????')}@fakeemail.com", ) - return indexer + return user def make_fake_notation() -> Notation: @@ -308,9 +308,9 @@ def make_fake_source(published=True, title=None, segment_name=None) -> Source: ) source.century.set([make_fake_century()]) source.notation.set([make_fake_notation()]) - source.inventoried_by.set([make_fake_indexer()]) - source.full_text_entered_by.set([make_fake_indexer()]) - source.melodies_entered_by.set([make_fake_indexer()]) - source.proofreaders.set([make_fake_indexer()]) - source.other_editors.set([make_fake_indexer()]) + source.inventoried_by.set([make_fake_user()]) + source.full_text_entered_by.set([make_fake_user()]) + source.melodies_entered_by.set([make_fake_user()]) + source.proofreaders.set([make_fake_user()]) + source.other_editors.set([make_fake_user()]) return source diff --git a/django/cantusdb_project/main_app/views/source.py b/django/cantusdb_project/main_app/views/source.py index d8a2cb0d4..c2497f287 100644 --- a/django/cantusdb_project/main_app/views/source.py +++ b/django/cantusdb_project/main_app/views/source.py @@ -194,21 +194,15 @@ def get_queryset(self): # field, allowing for a more flexible search, and a field needs # to match only one of the terms for term in indexing_search_terms: - inventoried_by_q |= Q(inventoried_by__given_name__icontains=term) | Q( - inventoried_by__family_name__icontains=term - ) + inventoried_by_q |= Q(inventoried_by__full_name__icontains=term) full_text_entered_by_q |= Q( - full_text_entered_by__given_name__icontains=term - ) | Q(full_text_entered_by__family_name__icontains=term) - melodies_entered_by_q |= Q( - melodies_entered_by__given_name__icontains=term - ) | Q(melodies_entered_by__family_name__icontains=term) - proofreaders_q |= Q(proofreaders__given_name__icontains=term) | Q( - proofreaders__family_name__icontains=term + full_text_entered_by__full_name__icontains=term ) - other_editors_q |= Q(other_editors__given_name__icontains=term) | Q( - other_editors__family_name__icontains=term + melodies_entered_by_q |= Q( + melodies_entered_by__full_name__icontains=term ) + proofreaders_q |= Q(proofreaders__full_name__icontains=term) + other_editors_q |= Q(other_editors__full_name__icontains=term) indexing_notes_q |= Q(indexing_notes__icontains=term) # All the Q objects are put together with OR. # The end result is that at least one term has to match in at least one From a7d4bc708b9afa146f05a8a4fdabb9e39819b5da Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 01:47:47 +0000 Subject: [PATCH 14/20] Make indexer list public --- django/cantusdb_project/main_app/views/user.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/django/cantusdb_project/main_app/views/user.py b/django/cantusdb_project/main_app/views/user.py index 7888490ba..506c15b7c 100644 --- a/django/cantusdb_project/main_app/views/user.py +++ b/django/cantusdb_project/main_app/views/user.py @@ -76,7 +76,7 @@ class UserListView(LoginRequiredMixin, SearchableListMixin, ListView): template_name = "user_list.html" context_object_name = "users" -class IndexerListView(UserListView): +class IndexerListView(SearchableListMixin, ListView): """A list of User objects shown to the public This view replaces the indexer list view on the old Cantus. @@ -87,6 +87,10 @@ class IndexerListView(UserListView): Accessed by /indexers/ """ + model = get_user_model() + ordering = "full_name" + search_fields = ["full_name", "institution", "city", "country"] + paginate_by = 100 template_name = "indexer_list.html" context_object_name = "indexers" From a985535bf143e95388d5cb6ea6246109ba3d94c1 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Wed, 14 Sep 2022 02:08:40 +0000 Subject: [PATCH 15/20] Replace indexers with users in articles app --- django/cantusdb_project/articles/models.py | 11 ++-------- .../articles/tests/test_articles.py | 22 ++++--------------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/django/cantusdb_project/articles/models.py b/django/cantusdb_project/articles/models.py index 02c424357..9713cf4dc 100644 --- a/django/cantusdb_project/articles/models.py +++ b/django/cantusdb_project/articles/models.py @@ -1,22 +1,15 @@ -from django.conf import settings +from django.contrib.auth import get_user_model from django.db import models from django.urls import reverse -from main_app.models import Indexer from django_quill.fields import QuillField class Article(models.Model): title = models.CharField(max_length=255) body = QuillField() # rich text field - author = models.ForeignKey( - Indexer, + get_user_model(), on_delete=models.CASCADE, ) - created_by = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - ) - date_created = models.DateTimeField( help_text="The date this article was created", null=True, diff --git a/django/cantusdb_project/articles/tests/test_articles.py b/django/cantusdb_project/articles/tests/test_articles.py index 3de8c6b63..b669698b3 100644 --- a/django/cantusdb_project/articles/tests/test_articles.py +++ b/django/cantusdb_project/articles/tests/test_articles.py @@ -1,40 +1,26 @@ from django.test import TestCase -from django.contrib.auth import get_user_model from django.urls import reverse - from articles.models import Article -from users.models import User -from main_app.models import Indexer from main_app.tests.make_fakes import ( make_fake_text, - make_fake_indexer, + make_fake_user, ) -# run with `python -Wa manage.py test articles.tests.test_articles.py` +# run with `python -Wa manage.py test articles.tests.test_articles` # the -Wa flag tells Python to display deprecation warnings -def make_fake_user(): - fake_email = "{f}@{g}.com".format( - f=make_fake_text(12), - g=make_fake_text(12), - ) - user = get_user_model().objects.create(email=fake_email) - return user - def make_fake_article(user=None): if user is None: - user=make_fake_user() + user = make_fake_user() article = Article.objects.create( title=make_fake_text(max_size=12), - author=make_fake_indexer(), - created_by=user, + author=make_fake_user(), ) return article class ArticleListViewTest(TestCase): def setUp(self): - fake_user = make_fake_user() for i in range(10): make_fake_article() From fd64ae8e13d281d6c890b9d9dfe7b07d47c71845 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Sun, 18 Sep 2022 14:25:52 +0000 Subject: [PATCH 16/20] Move indexer-related tests to the users app --- django/cantusdb_project/users/tests.py | 104 +++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/django/cantusdb_project/users/tests.py b/django/cantusdb_project/users/tests.py index 30e0dfc83..e59fd514b 100644 --- a/django/cantusdb_project/users/tests.py +++ b/django/cantusdb_project/users/tests.py @@ -2,6 +2,9 @@ from django.test import Client from django.contrib.auth import get_user_model from django.urls import reverse +from main_app.models import Segment +from main_app.tests.make_fakes import * +from main_app.tests.test_views import get_random_search_term # run with `python -Wa manage.py test users.tests` # the -Wa flag tells Python to display deprecation warnings @@ -29,6 +32,107 @@ def test_view(self): self.assertEqual(len(response.context["users"]), 6) +class IndexerListViewTest(TestCase): + def setUp(self): + # unless a segment is specified when a source is created, the source is automatically assigned + # to the segment with the name "CANTUS Database" - to prevent errors, we must make sure that + # such a segment exists + Segment.objects.create(name="CANTUS Database") + + def test_view_url_path(self): + response = self.client.get("/indexers/") + self.assertEqual(response.status_code, 200) + + def test_view_url_reverse_name(self): + response = self.client.get(reverse("indexer-list")) + self.assertEqual(response.status_code, 200) + + def test_url_and_templates(self): + """Test the url and templates used""" + response = self.client.get(reverse("indexer-list")) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "base.html") + self.assertTemplateUsed(response, "indexer_list.html") + + def test_only_public_indexer_visible(self): + """In the indexer list view, only public indexers (those who have at least one published source) should be visible""" + # generate some indexers + indexer_with_published_source = make_fake_user() + indexer_with_unpublished_source = make_fake_user() + indexer_with_no_source = make_fake_user() + + # generate published/unpublished sources and assign indexers to them + unpublished_source = Source.objects.create(title="unpublished source", published=False) + unpublished_source.inventoried_by.set([indexer_with_unpublished_source]) + + published_source = Source.objects.create(title="published source", published=True) + published_source.inventoried_by.set([indexer_with_published_source]) + + source_with_multiple_indexers = Source.objects.create( + title="unpublished source with multiple indexers", published=False, + ) + source_with_multiple_indexers.inventoried_by.set( + [indexer_with_published_source, indexer_with_unpublished_source] + ) + + # access the page context, only the public indexer should be in the context + response = self.client.get(reverse("indexer-list")) + self.assertEqual(response.status_code, 200) + self.assertIn(indexer_with_published_source, response.context["indexers"]) + self.assertNotIn(indexer_with_unpublished_source, response.context["indexers"]) + self.assertNotIn(indexer_with_no_source, response.context["indexers"]) + + def test_search_full_name(self): + """ + Indexer can be searched by passing a `q` parameter to the url \\ + Search fields include full name, country, city, and institution \\ + Only public indexers should appear in the results + """ + indexer_with_published_source = make_fake_user() + published_source = Source.objects.create(title="published source", published=True) + published_source.inventoried_by.set([indexer_with_published_source]) + + # search with a random slice of first name + target = indexer_with_published_source.full_name + search_term = get_random_search_term(target) + response = self.client.get(reverse("indexer-list"), {"q": search_term}) + self.assertEqual(response.status_code, 200) + self.assertIn(indexer_with_published_source, response.context["indexers"]) + + def test_search_country(self): + indexer_with_published_source = make_fake_user() + published_source = Source.objects.create(title="published source", published=True) + published_source.inventoried_by.set([indexer_with_published_source]) + + target = indexer_with_published_source.country + search_term = get_random_search_term(target) + response = self.client.get(reverse("indexer-list"), {"q": search_term}) + self.assertEqual(response.status_code, 200) + self.assertIn(indexer_with_published_source, response.context["indexers"]) + + def test_search_city(self): + indexer_with_published_source = make_fake_user() + published_source = Source.objects.create(title="published source", published=True) + published_source.inventoried_by.set([indexer_with_published_source]) + + target = indexer_with_published_source.city + search_term = get_random_search_term(target) + response = self.client.get(reverse("indexer-list"), {"q": search_term}) + self.assertEqual(response.status_code, 200) + self.assertIn(indexer_with_published_source, response.context["indexers"]) + + def test_search_institution(self): + indexer_with_published_source = make_fake_user() + published_source = Source.objects.create(title="published source", published=True) + published_source.inventoried_by.set([indexer_with_published_source]) + + target = indexer_with_published_source.institution + search_term = get_random_search_term(target) + response = self.client.get(reverse("indexer-list"), {"q": search_term}) + self.assertEqual(response.status_code, 200) + self.assertIn(indexer_with_published_source, response.context["indexers"]) + + class UserDetailViewTest(TestCase): @classmethod def setUpTestData(cls): From 87cc29b5d8758c8f14622ed0f56045eb5cae7e07 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Sun, 18 Sep 2022 14:26:45 +0000 Subject: [PATCH 17/20] Add show_in_list option when creating fake users --- django/cantusdb_project/main_app/tests/make_fakes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/django/cantusdb_project/main_app/tests/make_fakes.py b/django/cantusdb_project/main_app/tests/make_fakes.py index d859b8534..72a5a18f5 100644 --- a/django/cantusdb_project/main_app/tests/make_fakes.py +++ b/django/cantusdb_project/main_app/tests/make_fakes.py @@ -172,13 +172,14 @@ def make_fake_genre() -> Genre: return genre -def make_fake_user() -> get_user_model(): +def make_fake_user(show_in_list=True) -> get_user_model(): """Generates a fake User object.""" user = get_user_model().objects.create( full_name=f"{faker.first_name()} {faker.last_name()}", institution=faker.company(), city=faker.city(), country=faker.country(), + show_in_list=show_in_list, email=f"{faker.lexify('????????')}@fakeemail.com", ) return user From bc9c945228557358f060be94c8878e7db3dc94a3 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Sun, 18 Sep 2022 15:21:32 +0000 Subject: [PATCH 18/20] Update data migration scripts merge indexer/user --- .../management/commands/merge_indexer_user.py | 2 +- .../management/commands/sync_indexers.py | 71 ++++++++++--------- .../management/commands/sync_sources.py | 6 +- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py index a297e22dc..ec2e58d36 100644 --- a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py +++ b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py @@ -49,7 +49,7 @@ def handle(self, *args, **options): country=indexer.country, full_name=indexer_full_name, # assign random email to dummy users - email=f"{faker.bothify('????????')}@fakeemail.com", + email=f"{faker.lexify('????????')}@fakeemail.com", # leave the password empty for dummy users # the password can't be empty in login form, so they can't log in password="", diff --git a/django/cantusdb_project/main_app/management/commands/sync_indexers.py b/django/cantusdb_project/main_app/management/commands/sync_indexers.py index 78c1b6d16..99407b89a 100644 --- a/django/cantusdb_project/main_app/management/commands/sync_indexers.py +++ b/django/cantusdb_project/main_app/management/commands/sync_indexers.py @@ -1,10 +1,10 @@ -from main_app.models import Indexer from django.core.management.base import BaseCommand +from django.contrib.auth import get_user_model import requests, json +from faker import Faker INDEXER_ID_FILE = "indexer_list.txt" - def get_id_list(file_path): indexer_list = [] file = open(file_path, "r") @@ -14,8 +14,8 @@ def get_id_list(file_path): file.close() return indexer_list - def get_new_indexer(indexer_id): + # use json-export to get indexer information url = f"http://cantus.uwaterloo.ca/json-node/{indexer_id}" response = requests.get(url) json_response = json.loads(response.content) @@ -40,44 +40,45 @@ def get_new_indexer(indexer_id): else: country = None - indexer, created = Indexer.objects.update_or_create( - id=indexer_id, - defaults={ - "given_name": first_name, - "family_name": family_name, - "institution": institution, - "city": city, - "country": country, - }, - ) - if created: - print(f"created indexer {indexer_id}") - - -def remove_extra(): - waterloo_ids = get_id_list(INDEXER_ID_FILE) - our_ids = list(Indexer.objects.all().values_list("id", flat=True)) - our_ids = [str(id) for id in our_ids] - waterloo_ids = set(waterloo_ids) - print(f"Our count: {len(our_ids)}") - print(f"Waterloo count: {len(waterloo_ids)}") - extra_ids = [id for id in our_ids if id not in waterloo_ids] - for id in extra_ids: - Indexer.objects.get(id=id).delete() - print(f"Extra item removed: {id}") + # check whether the current indexer has a user entry of the same name + indexer_full_name = f"{first_name} {family_name}" + print(f"{indexer_id} {indexer_full_name}") + homonymous_users = get_user_model().objects.filter(full_name__iexact=indexer_full_name) + # if the indexer also exists as a user + if homonymous_users: + assert homonymous_users.count() == 1 + homonymous_user = homonymous_users.get() + print(f"homonymous: {homonymous_user.full_name}") + # keep the user as it is (merge the indexer into existing user) + # and store the ID of its indexer object + homonymous_user.old_indexer_id = indexer_id + homonymous_user.show_in_list = True + homonymous_user.save() + # if the indexer doesn't exist as a user + else: + faker = Faker() + # create a new user with the indexer information + get_user_model().objects.create( + institution=institution, + city=city, + country=country, + full_name=indexer_full_name, + # assign random email to dummy users + email=f"{faker.lexify('????????')}@fakeemail.com", + # leave the password empty for dummy users + # the password can't be empty in login form, so they can't log in + password="", + old_indexer_id = indexer_id, + show_in_list=True, + ) class Command(BaseCommand): def add_arguments(self, parser): - parser.add_argument( - "--remove_extra", - action="store_true", - help="add this flag to remove the indexers in our database that are no longer present in waterloo database", - ) + pass def handle(self, *args, **options): indexer_list = get_id_list(INDEXER_ID_FILE) for id in indexer_list: get_new_indexer(id) - if options["remove_extra"]: - remove_extra() + \ No newline at end of file diff --git a/django/cantusdb_project/main_app/management/commands/sync_sources.py b/django/cantusdb_project/main_app/management/commands/sync_sources.py index 9ebace368..d270e49c3 100644 --- a/django/cantusdb_project/main_app/management/commands/sync_sources.py +++ b/django/cantusdb_project/main_app/management/commands/sync_sources.py @@ -1,7 +1,6 @@ from main_app.models import ( Source, Century, - Indexer, Notation, Provenance, RismSiglum, @@ -40,7 +39,7 @@ def get_indexers(json_response, field_name): if json_response[field_name]: for und in json_response[field_name]["und"]: indexer_id = und["nid"] - indexer = Indexer.objects.get(id=indexer_id) + indexer = get_user_model().objects.get(old_indexer_id=indexer_id) indexers.append(indexer) return indexers @@ -268,7 +267,8 @@ def get_new_source(source_id): # True for published, False for unpublished published = status_published and source_status_published - # current editors are User, the other "people" fields are Indexer + # current editors are User, the other "people" fields were originally Indexer, + # but we harmonize them to User anyways current_editors = [] try: current_editors_entries = json_response["field_editors"]["und"] From 68a61298d6bd72baa8dd46c04ab8b0b3ab40372a Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Tue, 20 Sep 2022 14:53:48 +0000 Subject: [PATCH 19/20] Rename `show_in_list` to `is_indexer` --- .../main_app/management/commands/merge_indexer_user.py | 8 ++++---- .../main_app/management/commands/sync_indexers.py | 4 ++-- django/cantusdb_project/main_app/tests/make_fakes.py | 4 ++-- django/cantusdb_project/main_app/views/user.py | 2 +- django/cantusdb_project/users/models.py | 7 ++++--- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py index ec2e58d36..435506bd3 100644 --- a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py +++ b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py @@ -14,9 +14,9 @@ def handle(self, *args, **options): indexers = Indexer.objects.all() for user in users: - # set all users to be hidden first + # set all users to be non-indexer first # those listed as indexers on old Cantus will have this adjusted to True - user.show_in_list = False + user.is_indexer = False # do not use the existing "name" property, # because for some reason, the first/last names can have trailing spaces # TODO: populate the full_name field and remove the "name" property @@ -38,7 +38,7 @@ def handle(self, *args, **options): # keep the user as it is (merge the indexer into existing user) # and store the ID of its indexer object homonymous_user.old_indexer_id = indexer.id - homonymous_user.show_in_list = True + homonymous_user.is_indexer = True homonymous_user.save() # if the indexer doesn't exist as a user else: @@ -54,5 +54,5 @@ def handle(self, *args, **options): # the password can't be empty in login form, so they can't log in password="", old_indexer_id = indexer.id, - show_in_list=True, + is_indexer=True, ) diff --git a/django/cantusdb_project/main_app/management/commands/sync_indexers.py b/django/cantusdb_project/main_app/management/commands/sync_indexers.py index 99407b89a..d589db0e2 100644 --- a/django/cantusdb_project/main_app/management/commands/sync_indexers.py +++ b/django/cantusdb_project/main_app/management/commands/sync_indexers.py @@ -52,7 +52,7 @@ def get_new_indexer(indexer_id): # keep the user as it is (merge the indexer into existing user) # and store the ID of its indexer object homonymous_user.old_indexer_id = indexer_id - homonymous_user.show_in_list = True + homonymous_user.is_indexer = True homonymous_user.save() # if the indexer doesn't exist as a user else: @@ -69,7 +69,7 @@ def get_new_indexer(indexer_id): # the password can't be empty in login form, so they can't log in password="", old_indexer_id = indexer_id, - show_in_list=True, + is_indexer=True, ) diff --git a/django/cantusdb_project/main_app/tests/make_fakes.py b/django/cantusdb_project/main_app/tests/make_fakes.py index 38dcbe140..b374688a1 100644 --- a/django/cantusdb_project/main_app/tests/make_fakes.py +++ b/django/cantusdb_project/main_app/tests/make_fakes.py @@ -170,14 +170,14 @@ def make_fake_genre() -> Genre: return genre -def make_fake_user(show_in_list=True) -> get_user_model(): +def make_fake_user(is_indexer=True) -> get_user_model(): """Generates a fake User object.""" user = get_user_model().objects.create( full_name=f"{faker.first_name()} {faker.last_name()}", institution=faker.company(), city=faker.city(), country=faker.country(), - show_in_list=show_in_list, + is_indexer=is_indexer, email=f"{faker.lexify('????????')}@fakeemail.com", ) return user diff --git a/django/cantusdb_project/main_app/views/user.py b/django/cantusdb_project/main_app/views/user.py index 506c15b7c..172aa6a4f 100644 --- a/django/cantusdb_project/main_app/views/user.py +++ b/django/cantusdb_project/main_app/views/user.py @@ -96,7 +96,7 @@ class IndexerListView(SearchableListMixin, ListView): def get_queryset(self): all_users = super().get_queryset() - indexers = all_users.filter(show_in_list=True) + indexers = all_users.filter(is_indexer=True) display_unpublished = self.request.user.is_authenticated if display_unpublished: indexers = indexers.annotate( diff --git a/django/cantusdb_project/users/models.py b/django/cantusdb_project/users/models.py index 74e578d06..560b62286 100644 --- a/django/cantusdb_project/users/models.py +++ b/django/cantusdb_project/users/models.py @@ -15,9 +15,10 @@ class User(AbstractUser): email = models.EmailField(unique=True) # will be used to check if the user has changed the password assigned to them changed_initial_password = models.BooleanField(default=False) - # whether to display the user in the user-list page - show_in_list = models.BooleanField(default=False) - # if the user has a associated indexer object on old Cantus, save its ID + # whether the user has an associated indexer object on old Cantus + # if True, list the user in indexer-list page + is_indexer = models.BooleanField(default=False) + # if the user has an associated indexer object on old Cantus, save its ID old_indexer_id = models.IntegerField(blank=True, null=True) USERNAME_FIELD = 'email' From af53eaf9ca365456c5ec1b078b63fa7462e560b1 Mon Sep 17 00:00:00 2001 From: Junhao Wang Date: Tue, 20 Sep 2022 15:14:35 +0000 Subject: [PATCH 20/20] Replace `name` property with `full_name` field --- .../main_app/management/commands/merge_indexer_user.py | 3 --- django/cantusdb_project/users/models.py | 9 +-------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py index 435506bd3..4a70a8962 100644 --- a/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py +++ b/django/cantusdb_project/main_app/management/commands/merge_indexer_user.py @@ -17,9 +17,6 @@ def handle(self, *args, **options): # set all users to be non-indexer first # those listed as indexers on old Cantus will have this adjusted to True user.is_indexer = False - # do not use the existing "name" property, - # because for some reason, the first/last names can have trailing spaces - # TODO: populate the full_name field and remove the "name" property if user.first_name or user.last_name: user.full_name = f"{user.first_name.strip()} {user.last_name.strip()}" else: diff --git a/django/cantusdb_project/users/models.py b/django/cantusdb_project/users/models.py index 560b62286..78df1cf2e 100644 --- a/django/cantusdb_project/users/models.py +++ b/django/cantusdb_project/users/models.py @@ -26,16 +26,9 @@ class User(AbstractUser): objects = CustomUserManager() - @property - def name(self): + def __str__(self): if self.full_name: return self.full_name - elif self.first_name and self.last_name: - return f'{self.first_name} {self.last_name}' - - def __str__(self): - if self.name: - return self.name else: return self.email