from django.urls import reverse
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
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.core.paginator import Paginator
from django.contrib.auth.views import LogoutView, LoginView
from django.contrib import messages
from extra_views import SearchableListMixin
from django.http import HttpResponseRedirect
from django.core.exceptions import PermissionDenied


class UserDetailView(DetailView):
    """Detail view for User model

    Accessed by /users/<pk>
    """

    model = get_user_model()
    context_object_name = "user"
    template_name = "user_detail.html"

    def get_context_data(self, **kwargs):
        user = self.get_object()
        # to begin, if the person viewing the site is not logged in,
        # they should only be able to view the detail pages of indexers,
        # and not the detail pages of run-of-the-mill users
        viewing_user = self.request.user
        if not (viewing_user.is_authenticated or user.is_indexer):
            raise PermissionDenied()

        context = super().get_context_data(**kwargs)
        display_unpublished = viewing_user.is_authenticated
        sort_by_siglum = lambda source: source.siglum
        if display_unpublished:
            context["inventoried_sources"] = sorted(
                user.inventoried_sources.all(), key=sort_by_siglum
            )
            context["full_text_sources"] = sorted(
                user.entered_full_text_for_sources.all(), key=sort_by_siglum
            )
            context["melody_sources"] = sorted(
                user.entered_melody_for_sources.all(), key=sort_by_siglum
            )
            context["proofread_sources"] = sorted(
                user.proofread_sources.all(), key=sort_by_siglum
            )
            context["edited_sources"] = sorted(
                user.edited_sources.all(), key=sort_by_siglum
            )
        else:
            context["inventoried_sources"] = sorted(
                user.inventoried_sources.all().filter(published=True),
                key=sort_by_siglum,
            )
            context["full_text_sources"] = sorted(
                user.entered_full_text_for_sources.all().filter(published=True),
                key=sort_by_siglum,
            )
            context["melody_sources"] = sorted(
                user.entered_melody_for_sources.all().filter(published=True),
                key=sort_by_siglum,
            )
            context["proofread_sources"] = sorted(
                user.proofread_sources.all().filter(published=True), key=sort_by_siglum
            )
            context["edited_sources"] = sorted(
                user.edited_sources.all().filter(published=True), key=sort_by_siglum
            )

        return context


class UserSourceListView(LoginRequiredMixin, ListView):
    model = Source
    context_object_name = "sources"
    template_name = "user_source_list.html"
    paginate_by = 100

    def get_queryset(self):
        return (
            Source.objects.filter(
                Q(current_editors=self.request.user)
                | Q(created_by=self.request.user)
                # | Q(inventoried_by=self.request.user)
                # | Q(full_text_entered_by=self.request.user)
                # | Q(melodies_entered_by=self.request.user)
                # | Q(proofreaders=self.request.user)
                # | Q(other_editors=self.request.user)
            )
            .order_by("-date_created")
            .distinct()
        )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        user_created_sources = (
            Source.objects.filter(created_by=self.request.user)
            .order_by("-date_created")
            .distinct()
        )
        paginator = Paginator(user_created_sources, 10)
        page_number = self.request.GET.get("page2")
        page_obj = paginator.get_page(page_number)

        context["user_created_sources_page_obj"] = page_obj
        return context


class CustomLogoutView(LogoutView):
    def get_next_page(self):
        next_page = super().get_next_page()
        messages.success(self.request, "You have successfully logged out!")
        return next_page


class UserListView(LoginRequiredMixin, SearchableListMixin, ListView):
    """A list of all User objects

    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=<query>` 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"
    search_fields = ["full_name", "institution", "city", "country"]
    paginate_by = 100
    template_name = "user_list.html"
    context_object_name = "users"


class IndexerListView(SearchableListMixin, ListView):
    """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=<query>` argument in the GET request, it will filter users
    based on the fields defined in `search_fields` with the `icontains` lookup.

    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"

    def get_queryset(self):
        all_users = super().get_queryset()
        indexers = all_users.filter(is_indexer=True)
        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)

django/cantusdb_project/main_app/views/user.py
from django.urls import reverse
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
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.core.paginator import Paginator
from django.contrib.auth.views import LogoutView, LoginView
from django.contrib import messages
from extra_views import SearchableListMixin
from django.http import HttpResponseRedirect
from django.core.exceptions import PermissionDenied


class UserDetailView(DetailView):
    """Detail view for User model

    Accessed by /users/<pk>
    """

    model = get_user_model()
    context_object_name = "user"
    template_name = "user_detail.html"

    def get_context_data(self, **kwargs):
        user = self.get_object()
        # to begin, if the person viewing the site is not logged in,
        # they should only be able to view the detail pages of indexers,
        # and not the detail pages of run-of-the-mill users
        viewing_user = self.request.user
        if not (viewing_user.is_authenticated or user.is_indexer):
            raise PermissionDenied()

        context = super().get_context_data(**kwargs)
        display_unpublished = viewing_user.is_authenticated
        sort_by_siglum = lambda source: source.siglum
        if display_unpublished:
            context["inventoried_sources"] = sorted(
                user.inventoried_sources.all(), key=sort_by_siglum
            )
            context["full_text_sources"] = sorted(
                user.entered_full_text_for_sources.all(), key=sort_by_siglum
            )
            context["melody_sources"] = sorted(
                user.entered_melody_for_sources.all(), key=sort_by_siglum
            )
            context["proofread_sources"] = sorted(
                user.proofread_sources.all(), key=sort_by_siglum
            )
            context["edited_sources"] = sorted(
                user.edited_sources.all(), key=sort_by_siglum
            )
        else:
            context["inventoried_sources"] = sorted(
                user.inventoried_sources.all().filter(published=True),
                key=sort_by_siglum,
            )
            context["full_text_sources"] = sorted(
                user.entered_full_text_for_sources.all().filter(published=True),
                key=sort_by_siglum,
            )
            context["melody_sources"] = sorted(
                user.entered_melody_for_sources.all().filter(published=True),
                key=sort_by_siglum,
            )
            context["proofread_sources"] = sorted(
                user.proofread_sources.all().filter(published=True), key=sort_by_siglum
            )
            context["edited_sources"] = sorted(
                user.edited_sources.all().filter(published=True), key=sort_by_siglum
            )

        return context


class UserSourceListView(LoginRequiredMixin, ListView):
    model = Source
    context_object_name = "sources"
    template_name = "user_source_list.html"
    paginate_by = 100

    def get_queryset(self):
        return (
            Source.objects.filter(
                Q(current_editors=self.request.user)
                | Q(created_by=self.request.user)
                # | Q(inventoried_by=self.request.user)
                # | Q(full_text_entered_by=self.request.user)
                # | Q(melodies_entered_by=self.request.user)
                # | Q(proofreaders=self.request.user)
                # | Q(other_editors=self.request.user)
            )
            .order_by("-date_created")
            .distinct()
        )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        user_created_sources = (
            Source.objects.filter(created_by=self.request.user)
            .order_by("-date_created")
            .distinct()
        )
        paginator = Paginator(user_created_sources, 6)
        page_number = self.request.GET.get("page2")
        page_obj = paginator.get_page(page_number)

        context["user_created_sources_page_obj"] = page_obj
        return context


class CustomLogoutView(LogoutView):
    def get_next_page(self):
        next_page = super().get_next_page()
        messages.success(self.request, "You have successfully logged out!")
        return next_page


class UserListView(LoginRequiredMixin, SearchableListMixin, ListView):
    """A list of all User objects

    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=<query>` 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"
    search_fields = ["full_name", "institution", "city", "country"]
    paginate_by = 100
    template_name = "user_list.html"
    context_object_name = "users"


class IndexerListView(SearchableListMixin, ListView):
    """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=<query>` argument in the GET request, it will filter users
    based on the fields defined in `search_fields` with the `icontains` lookup.

    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"

    def get_queryset(self):
        all_users = super().get_queryset()
        indexers = all_users.filter(is_indexer=True)
        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)

django/cantusdb_project/main_app/views/user.py
diff --git a/django/cantusdb_project/main_app/templates/user_source_list.html b/django/cantusdb_project/main_app/templates/user_source_list.html
index f4e00cb54..7dac86167 100644
--- a/django/cantusdb_project/main_app/templates/user_source_list.html
+++ b/django/cantusdb_project/main_app/templates/user_source_list.html
@@ -52,7 +52,7 @@ <h4>Sources created by user</h4>
                 <div class="card-body">
                     <small><a href="{% url "source-create" %}"><b>+ Add new source</b></a></small>
                     <ul>
-                        {% for my_source in page_obj %}
+                        {% for my_source in user_created_sources_page_obj %}
                             <li>
                                 <a href="{% url "source-detail" my_source.pk %}">
                                     <b> {{ my_source.siglum }}</b>
diff --git a/django/cantusdb_project/main_app/views/user.py b/django/cantusdb_project/main_app/views/user.py
index 7c8cfd22f..e6ba2f51b 100644
--- a/django/cantusdb_project/main_app/views/user.py
+++ b/django/cantusdb_project/main_app/views/user.py
@@ -104,7 +104,7 @@ def get_context_data(self, **kwargs):
             .order_by("-date_created")
             .distinct()
         )
-        paginator = Paginator(user_created_sources, 10)
+        paginator = Paginator(user_created_sources, 6)
         page_number = self.request.GET.get("page2")
         page_obj = paginator.get_page(page_number)
 

"my sources"" page side panel
http://206.12.88.113/my-sources/ has a "created by" side panel. This is
1. not paginated
3. includes all the sources and not just the created ones (so it's both extra- long and also redundant).
Staging:
<img width="1106" alt="image" src="https://github.com/DDMAL/CantusDB/assets/67451875/3d11789e-6027-4358-8595-328e95e89d7b">
on production this only has the sources created on production, so it only has one source (I'm assuming the others will show up once we sort out the "Created by" info from OldCantus?
<img width="1160" alt="image" src="https://github.com/DDMAL/CantusDB/assets/67451875/f6e98d78-0f66-421c-aad9-2ede47400d88">
On OldCantus it looks like this:

<img width="981" alt="image" src="https://github.com/DDMAL/CantusDB/assets/67451875/15f4b995-d930-4645-9ca4-3befce6a868d">


