From 43d37e37d76e3e0fed06f941d848118ccc84eb40 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:11:16 -0400 Subject: [PATCH 01/74] beginning work on API --- cl/favorites/api_serializers.py | 2 +- cl/favorites/api_views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cl/favorites/api_serializers.py b/cl/favorites/api_serializers.py index 7f0ed014df..85b7cb506c 100644 --- a/cl/favorites/api_serializers.py +++ b/cl/favorites/api_serializers.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.serializers import ModelSerializer -from cl.favorites.models import DocketTag, UserTag +from cl.favorites.models import DocketTag, Prayer, UserTag from cl.search.models import Docket diff --git a/cl/favorites/api_views.py b/cl/favorites/api_views.py index f53d5c4fb1..c201d77a2d 100644 --- a/cl/favorites/api_views.py +++ b/cl/favorites/api_views.py @@ -7,7 +7,7 @@ from cl.favorites.api_permissions import IsTagOwner from cl.favorites.api_serializers import DocketTagSerializer, UserTagSerializer from cl.favorites.filters import DocketTagFilter, UserTagFilter -from cl.favorites.models import DocketTag, UserTag +from cl.favorites.models import DocketTag, Prayer, UserTag class UserTagViewSet(ModelViewSet): From 6677f72b5d528e2626e3210104bc5d8ecdecf9fa Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:25:02 -0400 Subject: [PATCH 02/74] PrayerSerializer --- cl/favorites/api_serializers.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cl/favorites/api_serializers.py b/cl/favorites/api_serializers.py index 85b7cb506c..dd2cb6765b 100644 --- a/cl/favorites/api_serializers.py +++ b/cl/favorites/api_serializers.py @@ -37,3 +37,17 @@ class DocketTagSerializer(DynamicFieldsMixin, ModelSerializer): class Meta: model = DocketTag fields = "__all__" + +class PrayerSerializer(DynamicFieldsMixin, serializers.ModelSerializer): + user = serializers.HiddenField(default=serializers.CurrentUserDefault()) + + class Meta: + model = Prayer + fields = "__all__" + read_only_fields = ( + "date_created", + "user", + "recap_document", + "status", + ) + From 611b1379d3fc6804e395e0a285edfb04c42b04a4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:25:43 +0000 Subject: [PATCH 03/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/api_serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/api_serializers.py b/cl/favorites/api_serializers.py index dd2cb6765b..b05681bc23 100644 --- a/cl/favorites/api_serializers.py +++ b/cl/favorites/api_serializers.py @@ -38,6 +38,7 @@ class Meta: model = DocketTag fields = "__all__" + class PrayerSerializer(DynamicFieldsMixin, serializers.ModelSerializer): user = serializers.HiddenField(default=serializers.CurrentUserDefault()) @@ -50,4 +51,3 @@ class Meta: "recap_document", "status", ) - From 18498f63050a288253b53c045adb6db8171ffa6c Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:50:46 -0400 Subject: [PATCH 04/74] filters --- cl/favorites/filters.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cl/favorites/filters.py b/cl/favorites/filters.py index e0e2e0e724..0a104c458d 100644 --- a/cl/favorites/filters.py +++ b/cl/favorites/filters.py @@ -1,7 +1,7 @@ import rest_framework_filters as filters from cl.api.utils import BASIC_TEXT_LOOKUPS, BOOLEAN_LOOKUPS, NoEmptyFilterSet -from cl.favorites.models import DocketTag, UserTag +from cl.favorites.models import DocketTag, Prayer, UserTag class UserTagFilter(NoEmptyFilterSet): @@ -21,3 +21,12 @@ class DocketTagFilter(NoEmptyFilterSet): class Meta: model = DocketTag fields = {"id": ["exact"], "docket": ["exact"]} + +class PrayerFilter(NoEmptyFilterSet): + date_created = filters.DateFromToRangeFilter( + field_name="date_created", + help_text="Filter prayers by a date range (e.g., ?date_created_after=2024-09-01&date_created_before=2024-12-31)." +) + class Meta: + model = Prayer + fields = {"date_created": ["exact", "range"], "user": ["exact"], "recap_document": ["exact"], "status": ["exact", "in"]} \ No newline at end of file From 114bbf93ad4c01d090c97a7e251a60391671565e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:51:53 +0000 Subject: [PATCH 05/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/filters.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cl/favorites/filters.py b/cl/favorites/filters.py index 0a104c458d..cbd3c24a1f 100644 --- a/cl/favorites/filters.py +++ b/cl/favorites/filters.py @@ -22,11 +22,18 @@ class Meta: model = DocketTag fields = {"id": ["exact"], "docket": ["exact"]} + class PrayerFilter(NoEmptyFilterSet): date_created = filters.DateFromToRangeFilter( - field_name="date_created", - help_text="Filter prayers by a date range (e.g., ?date_created_after=2024-09-01&date_created_before=2024-12-31)." -) + field_name="date_created", + help_text="Filter prayers by a date range (e.g., ?date_created_after=2024-09-01&date_created_before=2024-12-31).", + ) + class Meta: model = Prayer - fields = {"date_created": ["exact", "range"], "user": ["exact"], "recap_document": ["exact"], "status": ["exact", "in"]} \ No newline at end of file + fields = { + "date_created": ["exact", "range"], + "user": ["exact"], + "recap_document": ["exact"], + "status": ["exact", "in"], + } From ea76f6c078919329089c425fc3d65b57ad6d64c4 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:53:04 -0400 Subject: [PATCH 06/74] views --- cl/favorites/api_views.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/cl/favorites/api_views.py b/cl/favorites/api_views.py index c201d77a2d..b04f1824fb 100644 --- a/cl/favorites/api_views.py +++ b/cl/favorites/api_views.py @@ -4,9 +4,10 @@ from rest_framework.viewsets import ModelViewSet from cl.api.pagination import MediumAdjustablePagination +from cl.api.utils import LoggingMixin from cl.favorites.api_permissions import IsTagOwner -from cl.favorites.api_serializers import DocketTagSerializer, UserTagSerializer -from cl.favorites.filters import DocketTagFilter, UserTagFilter +from cl.favorites.api_serializers import DocketTagSerializer, PrayerSerializer, UserTagSerializer +from cl.favorites.filters import DocketTagFilter, PrayerFilter, UserTagFilter from cl.favorites.models import DocketTag, Prayer, UserTag @@ -51,3 +52,26 @@ def get_queryset(self): return DocketTag.objects.filter( Q(tag__user=self.request.user) | Q(tag__published=True) ) + +class PrayerViewSet(LoggingMixin, ModelViewSet): + """A ModelViewset to handle CRUD operations for Prayer.""" + + permission_classes = [IsAuthenticated] + serializer_class = PrayerSerializer + pagination_class = MediumAdjustablePagination + filterset_class = PrayerFilter + ordering_fields = ( + "date_created", + ) + # Default cursor ordering key + ordering = "-date_created" + # Additional cursor ordering fields + cursor_ordering_fields = ["date_created"] + + def get_queryset(self): + """ + Return a list of all the open prayers + for the currently authenticated user. + """ + user = self.request.user + return Prayer.objects.filter(user=user, status=Prayer.WAITING).order_by("-date_created") From 2707ebd046c4a6d1a6a6a14b471862ca3d3cc953 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:54:12 +0000 Subject: [PATCH 07/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/api_views.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cl/favorites/api_views.py b/cl/favorites/api_views.py index b04f1824fb..36cc655f3b 100644 --- a/cl/favorites/api_views.py +++ b/cl/favorites/api_views.py @@ -6,7 +6,11 @@ from cl.api.pagination import MediumAdjustablePagination from cl.api.utils import LoggingMixin from cl.favorites.api_permissions import IsTagOwner -from cl.favorites.api_serializers import DocketTagSerializer, PrayerSerializer, UserTagSerializer +from cl.favorites.api_serializers import ( + DocketTagSerializer, + PrayerSerializer, + UserTagSerializer, +) from cl.favorites.filters import DocketTagFilter, PrayerFilter, UserTagFilter from cl.favorites.models import DocketTag, Prayer, UserTag @@ -53,6 +57,7 @@ def get_queryset(self): Q(tag__user=self.request.user) | Q(tag__published=True) ) + class PrayerViewSet(LoggingMixin, ModelViewSet): """A ModelViewset to handle CRUD operations for Prayer.""" @@ -60,9 +65,7 @@ class PrayerViewSet(LoggingMixin, ModelViewSet): serializer_class = PrayerSerializer pagination_class = MediumAdjustablePagination filterset_class = PrayerFilter - ordering_fields = ( - "date_created", - ) + ordering_fields = ("date_created",) # Default cursor ordering key ordering = "-date_created" # Additional cursor ordering fields @@ -74,4 +77,6 @@ def get_queryset(self): for the currently authenticated user. """ user = self.request.user - return Prayer.objects.filter(user=user, status=Prayer.WAITING).order_by("-date_created") + return Prayer.objects.filter( + user=user, status=Prayer.WAITING + ).order_by("-date_created") From 4586a36c25df77edc8b3662825efe19d30b7a0ca Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:07:28 -0400 Subject: [PATCH 08/74] fixing email test (single-digit dates don't have zero padding in the email). --- cl/favorites/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index 8ece116a5a..cd8ae0bf8d 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -924,7 +924,7 @@ async def test_prayers_integration(self) -> None: email_text_content, ) self.assertIn( - f"You requested it on {prayer_1.date_created.strftime("%b %d, %Y")}", + f"You requested it on {prayer_1.date_created.strftime("%b %-d, %Y")}", email_text_content, ) self.assertIn( @@ -945,7 +945,7 @@ async def test_prayers_integration(self) -> None: html_content, ) self.assertIn( - f"You requested it on {prayer_1.date_created.strftime("%b %d, %Y")}", + f"You requested it on {prayer_1.date_created.strftime("%b %-d, %Y")}", html_content, ) self.assertIn( From c687c40126f2ac2a6fb8e66f85683a2ee8940f86 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 18:20:44 +0000 Subject: [PATCH 09/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index a2c6160528..9af49eb0c2 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -947,7 +947,7 @@ async def test_prayers_integration(self) -> None: ) self.assertIn( f"You requested it on {template_date(prayer_1.date_created, 'M j, Y')}", - html_content, + html_content, ) self.assertIn( f"Somebody paid ${price(rd_6)}", From a31bcceca64d7712567f8ade5a088ac8ad0deb4a Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:37:50 -0400 Subject: [PATCH 10/74] route --- cl/api/urls.py | 3 +++ cl/favorites/api_permissions.py | 1 + 2 files changed, 4 insertions(+) diff --git a/cl/api/urls.py b/cl/api/urls.py index cf600790d9..85c4cd7691 100644 --- a/cl/api/urls.py +++ b/cl/api/urls.py @@ -96,6 +96,9 @@ r"docket-tags", favorite_views.DocketTagViewSet, basename="DocketTag" ) +# Prayers +router.register(r"prayers", favorite_views.PrayerViewSet, basename="Prayer") + # Visualizations router.register( r"visualizations/json", viz_views.JSONViewSet, basename="jsonversion" diff --git a/cl/favorites/api_permissions.py b/cl/favorites/api_permissions.py index 168b2fa6ce..d14fccb7b8 100644 --- a/cl/favorites/api_permissions.py +++ b/cl/favorites/api_permissions.py @@ -10,3 +10,4 @@ def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True return obj.tag.user == request.user + From ad42a89048d8ae9942baaf625ba71c7606d98bd4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 18:38:39 +0000 Subject: [PATCH 11/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/api_permissions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cl/favorites/api_permissions.py b/cl/favorites/api_permissions.py index d14fccb7b8..168b2fa6ce 100644 --- a/cl/favorites/api_permissions.py +++ b/cl/favorites/api_permissions.py @@ -10,4 +10,3 @@ def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True return obj.tag.user == request.user - From f32cfd518ce692665b5437e418e919e2542add8c Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 1 Oct 2024 18:52:52 -0400 Subject: [PATCH 12/74] adding link to open document requests to base.html --- cl/assets/templates/base.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cl/assets/templates/base.html b/cl/assets/templates/base.html index 33181f52e1..4a8f240880 100644 --- a/cl/assets/templates/base.html +++ b/cl/assets/templates/base.html @@ -231,17 +231,20 @@

You did not supply the "private" variable to your template.
  • Install RECAP
  • + {% comment %}
  • + Pray and Pay Project +
  • {% endcomment %}
  • - Get Case Alerts + Get Case Alerts
  • - About this Collection + About this Collection
  • - Bulk Data Service + Bulk Data Service
  • - API + API
  • From b2f276ba25dc365ea4c559fc7f5eb869281295e3 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:27:31 -0400 Subject: [PATCH 13/74] function to get how many open requests for a given document --- cl/favorites/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index dae437abad..2f873140d1 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -148,3 +148,8 @@ def send_prayer_emails(instance: RECAPDocument) -> None: messages.append(msg) connection = get_connection() connection.send_messages(messages) + +async def get_prayer_count( + recap_document: RECAPDocument +) -> int: + return await Prayer.objects.filter(recap_document=recap_document, status=Prayer.WAITING).acount() \ No newline at end of file From 0fb956e0747cdfc48870799eaba91bd6a9821ea8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:28:10 +0000 Subject: [PATCH 14/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 2f873140d1..b5b7d68cf9 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -149,7 +149,8 @@ def send_prayer_emails(instance: RECAPDocument) -> None: connection = get_connection() connection.send_messages(messages) -async def get_prayer_count( - recap_document: RECAPDocument -) -> int: - return await Prayer.objects.filter(recap_document=recap_document, status=Prayer.WAITING).acount() \ No newline at end of file + +async def get_prayer_count(recap_document: RECAPDocument) -> int: + return await Prayer.objects.filter( + recap_document=recap_document, status=Prayer.WAITING + ).acount() From 8bba545ad1a8a3b5d2dbdf8bf48be67f5cec1a31 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:55:14 -0400 Subject: [PATCH 15/74] function to check if user can request a given document --- cl/favorites/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index b5b7d68cf9..b1d83b6b96 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -154,3 +154,7 @@ async def get_prayer_count(recap_document: RECAPDocument) -> int: return await Prayer.objects.filter( recap_document=recap_document, status=Prayer.WAITING ).acount() + + +async def prayer_exists(user: User, recap_document: RECAPDocument) -> bool: + return await Prayer.objects.filter(user=user, recap_document=recap_document).aexists() \ No newline at end of file From d676edef885680b474c36061e29662054bd05341 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:56:20 +0000 Subject: [PATCH 16/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index b1d83b6b96..30450dd3ca 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -157,4 +157,6 @@ async def get_prayer_count(recap_document: RECAPDocument) -> int: async def prayer_exists(user: User, recap_document: RECAPDocument) -> bool: - return await Prayer.objects.filter(user=user, recap_document=recap_document).aexists() \ No newline at end of file + return await Prayer.objects.filter( + user=user, recap_document=recap_document + ).aexists() From 20f1ec60de9d1ce81e883a7e1b690d2d1313e271 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:26:32 -0400 Subject: [PATCH 17/74] showing how many open prayers on docket entry page --- cl/opinion_page/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cl/opinion_page/views.py b/cl/opinion_page/views.py index c96cc3af85..659bd210bc 100644 --- a/cl/opinion_page/views.py +++ b/cl/opinion_page/views.py @@ -44,6 +44,7 @@ from cl.custom_filters.templatetags.text_filters import best_case_name from cl.favorites.forms import NoteForm from cl.favorites.models import Note +from cl.favorites.utils import get_prayer_count from cl.lib.auth import group_required from cl.lib.bot_detector import is_og_bot from cl.lib.http import is_ajax @@ -377,6 +378,12 @@ async def view_docket( "-recap_sequence_number", "-entry_number" ) + for entry in await sync_to_async(list)(de_list): + if await sync_to_async(entry.recap_documents.count)() > 0: + for rd in await sync_to_async(list)(entry.recap_documents.all()): + if rd.pacer_url and not(rd.is_free_on_pacer or rd.is_sealed): + rd.prayer_count = await get_prayer_count(rd) + page = request.GET.get("page", 1) @sync_to_async From 1b1975cf20718095b2be2f503ab0109982c8a26a Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:27:53 -0400 Subject: [PATCH 18/74] adding prayer count to docket entry --- cl/opinion_page/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cl/opinion_page/views.py b/cl/opinion_page/views.py index 82c1c60efc..5b8e6f61d1 100644 --- a/cl/opinion_page/views.py +++ b/cl/opinion_page/views.py @@ -384,6 +384,7 @@ async def view_docket( for rd in await sync_to_async(list)(entry.recap_documents.all()): if rd.pacer_url and not(rd.is_free_on_pacer or rd.is_sealed): rd.prayer_count = await get_prayer_count(rd) + page = request.GET.get("page", 1) From 027edbf542fda8acdcaf25bb1f3dad375b2b2e0f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 15:28:16 +0000 Subject: [PATCH 19/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/opinion_page/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cl/opinion_page/views.py b/cl/opinion_page/views.py index 5b8e6f61d1..f6dcb4a5c0 100644 --- a/cl/opinion_page/views.py +++ b/cl/opinion_page/views.py @@ -382,9 +382,8 @@ async def view_docket( for entry in await sync_to_async(list)(de_list): if await sync_to_async(entry.recap_documents.count)() > 0: for rd in await sync_to_async(list)(entry.recap_documents.all()): - if rd.pacer_url and not(rd.is_free_on_pacer or rd.is_sealed): + if rd.pacer_url and not (rd.is_free_on_pacer or rd.is_sealed): rd.prayer_count = await get_prayer_count(rd) - page = request.GET.get("page", 1) From 7920547989650160df6dbcfc9fb93eec274c6e8c Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:40:30 -0400 Subject: [PATCH 20/74] url --- cl/favorites/urls.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cl/favorites/urls.py b/cl/favorites/urls.py index 65ef65544d..ca211d8cd7 100644 --- a/cl/favorites/urls.py +++ b/cl/favorites/urls.py @@ -1,6 +1,7 @@ from django.urls import path from cl.favorites.views import ( + create_prayer_view, delete_note, open_prayers, save_or_update_note, @@ -24,4 +25,5 @@ ), path("tags//", view_tags, name="tag_list"), path("prayers/top/", open_prayers, name="top_prayers"), + path("prayer/create/", create_prayer_view, name="create_prayer") ] From 725b3e910cc002dd0bf23a952403f1f34d1cf27d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 15:41:30 +0000 Subject: [PATCH 21/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/urls.py b/cl/favorites/urls.py index ca211d8cd7..be24a17cca 100644 --- a/cl/favorites/urls.py +++ b/cl/favorites/urls.py @@ -25,5 +25,5 @@ ), path("tags//", view_tags, name="tag_list"), path("prayers/top/", open_prayers, name="top_prayers"), - path("prayer/create/", create_prayer_view, name="create_prayer") + path("prayer/create/", create_prayer_view, name="create_prayer"), ] From d40f02672b61cd56cff399a1d1a17f3ab6b54ae0 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:15:35 -0400 Subject: [PATCH 22/74] rough work on html --- cl/favorites/views.py | 15 +++++++++++++++ cl/opinion_page/templates/includes/de_list.html | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/cl/favorites/views.py b/cl/favorites/views.py index 1cd5b8ba15..b1d7fe0b8d 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -14,9 +14,13 @@ from django.shortcuts import aget_object_or_404 from django.template.response import TemplateResponse from django.utils.datastructures import MultiValueDictKeyError +from django.http import HttpResponseRedirect +from django.urls import reverse from cl.favorites.forms import NoteForm from cl.favorites.models import DocketTag, Note, UserTag +from cl.favorites.utils import create_prayer +from cl.search.models import RECAPDocument from cl.favorites.utils import get_top_prayers from cl.lib.decorators import cache_page_ignore_params from cl.lib.http import is_ajax @@ -190,3 +194,14 @@ async def open_prayers(request: HttpRequest) -> HttpResponse: "private": True, # temporary to prevent Google indexing }, ) + +# this is a rough function just to assess the test cases. It needs a lot of work before it's ready for showtime. +@login_required +async def create_prayer_view(request: HttpRequest, recap_document: RECAPDocument) -> HttpResponse: + user = request.user + + # Call the create_prayer async function + new_prayer = await create_prayer(user, recap_document) + + # Redirect back to the referring page after creating the prayer + return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse('view_docket'))) \ No newline at end of file diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index 0c0776ef9f..6b10dab69f 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -171,6 +171,10 @@ target="_blank" rel="nofollow">Buy on PACER {% if rd.page_count %}(${{ rd|price }}){% endif %} + + {{rd.prayer_count}} 🙏 + {% endif %} {% endif %} {% endif %} From 703d6e76d2b37d39346ff625f35c847cde7cebe5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:18:31 +0000 Subject: [PATCH 23/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/views.py | 22 +++++++++++-------- .../templates/includes/de_list.html | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/cl/favorites/views.py b/cl/favorites/views.py index b1d7fe0b8d..646cf55bb7 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -9,22 +9,21 @@ HttpRequest, HttpResponse, HttpResponseNotAllowed, + HttpResponseRedirect, HttpResponseServerError, ) from django.shortcuts import aget_object_or_404 from django.template.response import TemplateResponse -from django.utils.datastructures import MultiValueDictKeyError -from django.http import HttpResponseRedirect from django.urls import reverse +from django.utils.datastructures import MultiValueDictKeyError from cl.favorites.forms import NoteForm from cl.favorites.models import DocketTag, Note, UserTag -from cl.favorites.utils import create_prayer -from cl.search.models import RECAPDocument -from cl.favorites.utils import get_top_prayers +from cl.favorites.utils import create_prayer, get_top_prayers from cl.lib.decorators import cache_page_ignore_params from cl.lib.http import is_ajax from cl.lib.view_utils import increment_view_count +from cl.search.models import RECAPDocument async def get_note(request: HttpRequest) -> HttpResponse: @@ -195,13 +194,18 @@ async def open_prayers(request: HttpRequest) -> HttpResponse: }, ) + # this is a rough function just to assess the test cases. It needs a lot of work before it's ready for showtime. @login_required -async def create_prayer_view(request: HttpRequest, recap_document: RECAPDocument) -> HttpResponse: +async def create_prayer_view( + request: HttpRequest, recap_document: RECAPDocument +) -> HttpResponse: user = request.user - + # Call the create_prayer async function new_prayer = await create_prayer(user, recap_document) - + # Redirect back to the referring page after creating the prayer - return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse('view_docket'))) \ No newline at end of file + return HttpResponseRedirect( + request.META.get("HTTP_REFERER", reverse("view_docket")) + ) diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index 6b10dab69f..a72cb90907 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -171,7 +171,7 @@ target="_blank" rel="nofollow">Buy on PACER {% if rd.page_count %}(${{ rd|price }}){% endif %} - {{rd.prayer_count}} 🙏 From 3a32ebcf0c26fa1fbf22707e8317a936fe9ac89d Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:13:44 -0400 Subject: [PATCH 24/74] grouping similar functions together --- cl/favorites/utils.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 30450dd3ca..a59974db12 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -35,6 +35,18 @@ async def prayer_eligible(user: User) -> bool: return prayer_count < allowed_prayer_count +async def get_prayer_count(recap_document: RECAPDocument) -> int: + return await Prayer.objects.filter( + recap_document=recap_document, status=Prayer.WAITING + ).acount() + + +async def prayer_exists(user: User, recap_document: RECAPDocument) -> bool: + return await Prayer.objects.filter( + user=user, recap_document=recap_document + ).aexists() + + async def create_prayer( user: User, recap_document: RECAPDocument ) -> Prayer | None: @@ -148,15 +160,3 @@ def send_prayer_emails(instance: RECAPDocument) -> None: messages.append(msg) connection = get_connection() connection.send_messages(messages) - - -async def get_prayer_count(recap_document: RECAPDocument) -> int: - return await Prayer.objects.filter( - recap_document=recap_document, status=Prayer.WAITING - ).acount() - - -async def prayer_exists(user: User, recap_document: RECAPDocument) -> bool: - return await Prayer.objects.filter( - user=user, recap_document=recap_document - ).aexists() From c1e7b9f40c8d532d7379f0b7f35cc7277ed4cb73 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:15:17 -0400 Subject: [PATCH 25/74] commenting out problematic function --- cl/favorites/views.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cl/favorites/views.py b/cl/favorites/views.py index 646cf55bb7..cc92d97aa0 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -195,17 +195,17 @@ async def open_prayers(request: HttpRequest) -> HttpResponse: ) -# this is a rough function just to assess the test cases. It needs a lot of work before it's ready for showtime. -@login_required -async def create_prayer_view( - request: HttpRequest, recap_document: RECAPDocument -) -> HttpResponse: - user = request.user - - # Call the create_prayer async function - new_prayer = await create_prayer(user, recap_document) - - # Redirect back to the referring page after creating the prayer - return HttpResponseRedirect( - request.META.get("HTTP_REFERER", reverse("view_docket")) - ) +# # this is a rough function just to assess the test cases. It needs a lot of work before it's ready for showtime. +# @login_required +# async def create_prayer_view( +# request: HttpRequest, recap_document: RECAPDocument +# ) -> HttpResponse: +# user = request.user + +# # Call the create_prayer async function +# new_prayer = await create_prayer(user, recap_document) + +# # Redirect back to the referring page after creating the prayer +# return HttpResponseRedirect( +# request.META.get("HTTP_REFERER", reverse("view_docket")) +# ) From 98b0ac2ba4170c3d7a74a7da1a7da85477478abe Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:21:41 -0400 Subject: [PATCH 26/74] comment out dead url path --- cl/favorites/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/urls.py b/cl/favorites/urls.py index be24a17cca..ca931d9a9a 100644 --- a/cl/favorites/urls.py +++ b/cl/favorites/urls.py @@ -25,5 +25,5 @@ ), path("tags//", view_tags, name="tag_list"), path("prayers/top/", open_prayers, name="top_prayers"), - path("prayer/create/", create_prayer_view, name="create_prayer"), + # path("prayer/create/", create_prayer_view, name="create_prayer"), ] From 2aabc991e4143270f78758e68037439dc692de14 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:27:03 -0400 Subject: [PATCH 27/74] fix --- cl/favorites/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/urls.py b/cl/favorites/urls.py index ca931d9a9a..1238b43abc 100644 --- a/cl/favorites/urls.py +++ b/cl/favorites/urls.py @@ -1,7 +1,7 @@ from django.urls import path from cl.favorites.views import ( - create_prayer_view, + # create_prayer_view, delete_note, open_prayers, save_or_update_note, From 4291b1b2c83371370eaf768ed02d8cc6468ab999 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 6 Oct 2024 21:27:43 +0000 Subject: [PATCH 28/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/urls.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cl/favorites/urls.py b/cl/favorites/urls.py index 1238b43abc..6196727103 100644 --- a/cl/favorites/urls.py +++ b/cl/favorites/urls.py @@ -1,7 +1,6 @@ from django.urls import path -from cl.favorites.views import ( - # create_prayer_view, +from cl.favorites.views import ( # create_prayer_view, delete_note, open_prayers, save_or_update_note, From e96d1462000fc44bd586df3b573ee6f19c4da5ad Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:29:44 -0400 Subject: [PATCH 29/74] grouping common functions together --- cl/favorites/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index a59974db12..2ba8914590 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -35,18 +35,18 @@ async def prayer_eligible(user: User) -> bool: return prayer_count < allowed_prayer_count -async def get_prayer_count(recap_document: RECAPDocument) -> int: - return await Prayer.objects.filter( - recap_document=recap_document, status=Prayer.WAITING - ).acount() - - async def prayer_exists(user: User, recap_document: RECAPDocument) -> bool: return await Prayer.objects.filter( user=user, recap_document=recap_document ).aexists() +async def get_prayer_count(recap_document: RECAPDocument) -> int: + return await Prayer.objects.filter( + recap_document=recap_document, status=Prayer.WAITING + ).acount() + + async def create_prayer( user: User, recap_document: RECAPDocument ) -> Prayer | None: From 479aef5ec54cd47249acc7f95d4f3aa0a7be7e6a Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:32:14 -0400 Subject: [PATCH 30/74] adding checks for whether user can request a particular document --- cl/opinion_page/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cl/opinion_page/views.py b/cl/opinion_page/views.py index f6dcb4a5c0..b6dbcb2ddb 100644 --- a/cl/opinion_page/views.py +++ b/cl/opinion_page/views.py @@ -44,7 +44,7 @@ from cl.custom_filters.templatetags.text_filters import best_case_name from cl.favorites.forms import NoteForm from cl.favorites.models import Note -from cl.favorites.utils import get_prayer_count +from cl.favorites.utils import get_prayer_count, prayer_eligible, prayer_exists from cl.lib.auth import group_required from cl.lib.bot_detector import is_og_bot from cl.lib.decorators import cache_page_ignore_params @@ -385,6 +385,10 @@ async def view_docket( if rd.pacer_url and not (rd.is_free_on_pacer or rd.is_sealed): rd.prayer_count = await get_prayer_count(rd) + if request.user.is_authenticated: + rd.prayer_exists = await prayer_exists(request.user, rd) + rd.prayer_eligible = await prayer_eligible(request.user) + page = request.GET.get("page", 1) @sync_to_async From 842389298628dabf08c950e2ed11a43a08e2e38e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:32:58 +0000 Subject: [PATCH 31/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/opinion_page/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cl/opinion_page/views.py b/cl/opinion_page/views.py index b6dbcb2ddb..4828468232 100644 --- a/cl/opinion_page/views.py +++ b/cl/opinion_page/views.py @@ -386,8 +386,12 @@ async def view_docket( rd.prayer_count = await get_prayer_count(rd) if request.user.is_authenticated: - rd.prayer_exists = await prayer_exists(request.user, rd) - rd.prayer_eligible = await prayer_eligible(request.user) + rd.prayer_exists = await prayer_exists( + request.user, rd + ) + rd.prayer_eligible = await prayer_eligible( + request.user + ) page = request.GET.get("page", 1) From 120ebad35db59dc5017f7c3983540647e634a0fe Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:06:11 -0400 Subject: [PATCH 32/74] this all seems to work. I'm not 100% happy with how it's written, but it is functional. --- cl/favorites/urls.py | 5 ++-- cl/favorites/views.py | 24 +++++++++---------- .../templates/includes/de_list.html | 24 +++++++++++++++---- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/cl/favorites/urls.py b/cl/favorites/urls.py index 6196727103..04160578ed 100644 --- a/cl/favorites/urls.py +++ b/cl/favorites/urls.py @@ -1,6 +1,7 @@ from django.urls import path -from cl.favorites.views import ( # create_prayer_view, +from cl.favorites.views import ( + create_prayer_view, delete_note, open_prayers, save_or_update_note, @@ -24,5 +25,5 @@ ), path("tags//", view_tags, name="tag_list"), path("prayers/top/", open_prayers, name="top_prayers"), - # path("prayer/create/", create_prayer_view, name="create_prayer"), + path("prayer/create//", create_prayer_view, name="create_prayer"), ] diff --git a/cl/favorites/views.py b/cl/favorites/views.py index cc92d97aa0..113b9bd910 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -196,16 +196,14 @@ async def open_prayers(request: HttpRequest) -> HttpResponse: # # this is a rough function just to assess the test cases. It needs a lot of work before it's ready for showtime. -# @login_required -# async def create_prayer_view( -# request: HttpRequest, recap_document: RECAPDocument -# ) -> HttpResponse: -# user = request.user - -# # Call the create_prayer async function -# new_prayer = await create_prayer(user, recap_document) - -# # Redirect back to the referring page after creating the prayer -# return HttpResponseRedirect( -# request.META.get("HTTP_REFERER", reverse("view_docket")) -# ) +@login_required +async def create_prayer_view( + request: HttpRequest, recap_document: int +) -> HttpResponse: + user = request.user + recap_document = await RECAPDocument.objects.aget(id=recap_document) + + # Call the create_prayer async function + new_prayer = await create_prayer(user, recap_document) + + return HttpResponse("It worked.") diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index a72cb90907..f9719fa46d 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -171,10 +171,26 @@ target="_blank" rel="nofollow">Buy on PACER {% if rd.page_count %}(${{ rd|price }}){% endif %} - - {{rd.prayer_count}} 🙏 - + {% if request.user.is_authenticated %} + + {{rd.prayer_count}} 🙏 + + {% else %} + + {{rd.prayer_count}} 🙏 + + {% endif %} {% endif %} {% endif %} {% endif %} From a0c95116c318719eb31b9c151f2c4a5a75d45b8a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:06:51 +0000 Subject: [PATCH 33/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/urls.py | 6 +++++- cl/favorites/views.py | 2 +- cl/opinion_page/templates/includes/de_list.html | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cl/favorites/urls.py b/cl/favorites/urls.py index 04160578ed..27547bcd32 100644 --- a/cl/favorites/urls.py +++ b/cl/favorites/urls.py @@ -25,5 +25,9 @@ ), path("tags//", view_tags, name="tag_list"), path("prayers/top/", open_prayers, name="top_prayers"), - path("prayer/create//", create_prayer_view, name="create_prayer"), + path( + "prayer/create//", + create_prayer_view, + name="create_prayer", + ), ] diff --git a/cl/favorites/views.py b/cl/favorites/views.py index 113b9bd910..337d8a31b1 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -201,7 +201,7 @@ async def create_prayer_view( request: HttpRequest, recap_document: int ) -> HttpResponse: user = request.user - recap_document = await RECAPDocument.objects.aget(id=recap_document) + recap_document = await RECAPDocument.objects.aget(id=recap_document) # Call the create_prayer async function new_prayer = await create_prayer(user, recap_document) diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index f9719fa46d..4c97d9d509 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -172,7 +172,7 @@ rel="nofollow">Buy on PACER {% if rd.page_count %}(${{ rd|price }}){% endif %} {% if request.user.is_authenticated %} - Date: Tue, 8 Oct 2024 12:17:05 -0400 Subject: [PATCH 34/74] Update .env.example adding new variable for CL API key --- .env.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.env.example b/.env.example index 7d111c1b3f..25cd8994a9 100644 --- a/.env.example +++ b/.env.example @@ -42,3 +42,6 @@ IA_ACCESS_KEY="" IA_SECRET_KEY="" FTM_KEY="" + +# CL API key for cloning data +CL_API_KEY="" From 2e2840dbe1417110bc885b39fc4dd913b342ce46 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:06:05 -0400 Subject: [PATCH 35/74] adding document cost to wishlist --- cl/favorites/templates/top_prayers.html | 12 +++++++++++- cl/favorites/utils.py | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/cl/favorites/templates/top_prayers.html b/cl/favorites/templates/top_prayers.html index a12a5edba3..9afe9dafce 100644 --- a/cl/favorites/templates/top_prayers.html +++ b/cl/favorites/templates/top_prayers.html @@ -2,7 +2,7 @@ {% load extras %} {% load text_filters %} {% load static %} - +{% load pacer %} {% block title %}RECAP Requests – CourtListener.com{% endblock %} {% block og_title %}RECAP Requests – CourtListener.com{% endblock %} @@ -21,6 +21,7 @@ Document Number PACER Doc ID Document Court + Buy on Pacer @@ -31,6 +32,15 @@ {{ prayer.document_number }} {{ prayer.pacer_doc_id }} {{ prayer.docket_entry.docket.court_id }} + Buy on PACER {% if prayer.page_count %}(${{ prayer|price }}){% endif %} {% empty %} diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 2ba8914590..79307c6645 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -81,6 +81,7 @@ async def get_top_prayers() -> list[RECAPDocument]: "document_number", "attachment_number", "pacer_doc_id", + "page_count", "description", "docket_entry__docket_id", "docket_entry__docket__slug", @@ -107,6 +108,7 @@ async def get_top_prayers() -> list[RECAPDocument]: ) .order_by("-geometric_mean")[:50] ) + return [doc async for doc in documents.aiterator()] From 385a943ef9800096cbe5f055ca42d92d4868d260 Mon Sep 17 00:00:00 2001 From: Alberto Islas Date: Wed, 9 Oct 2024 10:49:22 -0600 Subject: [PATCH 36/74] fix(favorites): Improved prayers frontend query performance and added tests - Added PrayerAPITests --- cl/api/urls.py | 2 +- cl/favorites/api_serializers.py | 25 +- cl/favorites/api_views.py | 2 + cl/favorites/templates/top_prayers.html | 6 + cl/favorites/tests.py | 237 +++++++++++++++++- cl/favorites/utils.py | 51 +++- .../templates/includes/de_list.html | 4 +- cl/opinion_page/views.py | 47 ++-- 8 files changed, 332 insertions(+), 42 deletions(-) diff --git a/cl/api/urls.py b/cl/api/urls.py index 85c4cd7691..e407d4b94a 100644 --- a/cl/api/urls.py +++ b/cl/api/urls.py @@ -97,7 +97,7 @@ ) # Prayers -router.register(r"prayers", favorite_views.PrayerViewSet, basename="Prayer") +router.register(r"prayers", favorite_views.PrayerViewSet, basename="prayer") # Visualizations router.register( diff --git a/cl/favorites/api_serializers.py b/cl/favorites/api_serializers.py index b05681bc23..a12bb4c101 100644 --- a/cl/favorites/api_serializers.py +++ b/cl/favorites/api_serializers.py @@ -1,8 +1,12 @@ +from asgiref.sync import async_to_sync +from django.conf import settings from drf_dynamic_fields import DynamicFieldsMixin from rest_framework import serializers +from rest_framework.exceptions import ValidationError from rest_framework.serializers import ModelSerializer from cl.favorites.models import DocketTag, Prayer, UserTag +from cl.favorites.utils import prayer_eligible from cl.search.models import Docket @@ -48,6 +52,23 @@ class Meta: read_only_fields = ( "date_created", "user", - "recap_document", - "status", ) + + def validate(self, data): + user = self.context["request"].user + recap_document = data.get("recap_document") + + # Check if a Prayer for the same user and recap_document already exists + if Prayer.objects.filter( + user=user, recap_document=recap_document + ).exists(): + raise ValidationError( + "A prayer for this recap document already exists." + ) + + # Check if the user is eligible to create a new prayer + if not async_to_sync(prayer_eligible)(user): + raise ValidationError( + f"You have reached the maximum number of prayers ({settings.ALLOWED_PRAYER_COUNT}) allowed in the last 24 hours." + ) + return data diff --git a/cl/favorites/api_views.py b/cl/favorites/api_views.py index 36cc655f3b..981c1d3ebb 100644 --- a/cl/favorites/api_views.py +++ b/cl/favorites/api_views.py @@ -70,6 +70,8 @@ class PrayerViewSet(LoggingMixin, ModelViewSet): ordering = "-date_created" # Additional cursor ordering fields cursor_ordering_fields = ["date_created"] + # Only allow these methods. Restricting PUT and PATCH. + http_method_names = ["get", "post", "delete", "head", "options"] def get_queryset(self): """ diff --git a/cl/favorites/templates/top_prayers.html b/cl/favorites/templates/top_prayers.html index 9afe9dafce..800f160d31 100644 --- a/cl/favorites/templates/top_prayers.html +++ b/cl/favorites/templates/top_prayers.html @@ -52,3 +52,9 @@ {% endblock %} + +{% block footer-scripts %} + + {% include "includes/buy_pacer_modal.html" %} +{% endblock %} diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index 9af49eb0c2..4fbb0d56a3 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -10,6 +10,7 @@ from django.template.defaultfilters import date as template_date from django.test import AsyncClient, override_settings from django.urls import reverse +from django.utils import timezone from django.utils.timezone import now from selenium.webdriver.common.by import By from timeout_decorator import timeout_decorator @@ -17,7 +18,13 @@ from cl.custom_filters.templatetags.pacer import price from cl.favorites.factories import NoteFactory, PrayerFactory from cl.favorites.models import DocketTag, Note, Prayer, UserTag -from cl.favorites.utils import create_prayer, get_top_prayers, prayer_eligible +from cl.favorites.utils import ( + create_prayer, + get_existing_prayers_in_bulk, + get_prayer_counts_in_bulk, + get_top_prayers, + prayer_eligible, +) from cl.lib.test_helpers import AudioTestCase, SimpleUserDataMixin from cl.search.factories import RECAPDocumentFactory from cl.search.views import get_homepage_stats @@ -887,6 +894,26 @@ async def test_prayers_integration(self) -> None: actual_top_prayers, expected_top_prayers, msg="Wrong top_prayers." ) + # Assert prayer_counts dict. + prayers_counts_dict = await get_prayer_counts_in_bulk( + [rd_6, self.rd_4] + ) + self.assertEqual({rd_6.pk: 2, self.rd_4.pk: 1}, prayers_counts_dict) + + # Assert existing_prayers dict for user + existing_prayers_dict = await get_existing_prayers_in_bulk( + self.user, [rd_6, self.rd_4] + ) + self.assertEqual( + {rd_6.pk: True, self.rd_4.pk: True}, existing_prayers_dict + ) + + # Assert existing_prayers dict for user_2 + existing_prayers_dict = await get_existing_prayers_in_bulk( + self.user_2, [rd_6, self.rd_4] + ) + self.assertEqual({rd_6.pk: True}, existing_prayers_dict) + # rd_6 is granted. rd_6.is_available = True await rd_6.asave() @@ -924,10 +951,13 @@ async def test_prayers_integration(self) -> None: f"https://www.courtlistener.com{rd_6.get_absolute_url()}", email_text_content, ) - self.assertIn( - f"You requested it on {template_date(prayer_1.date_created, 'M j, Y')}", - email_text_content, - ) + with timezone.override("America/Los_Angeles"): + localized_date = timezone.localtime(prayer_1.date_created) + formatted_date = template_date(localized_date, "M j, Y") + self.assertIn( + f"You requested it on {formatted_date}", + email_text_content, + ) self.assertIn( f"{len(actual_top_prayers)} people were also waiting for it.", email_text_content, @@ -945,10 +975,13 @@ async def test_prayers_integration(self) -> None: f"{len(actual_top_prayers)} people were also waiting for it.", html_content, ) - self.assertIn( - f"You requested it on {template_date(prayer_1.date_created, 'M j, Y')}", - html_content, - ) + with timezone.override("America/Los_Angeles"): + localized_date = timezone.localtime(prayer_1.date_created) + formatted_date = template_date(localized_date, "M j, Y") + self.assertIn( + f"You requested it on {formatted_date}", + html_content, + ) self.assertIn( f"Somebody paid ${price(rd_6)}", html_content, @@ -963,3 +996,189 @@ async def test_prayers_integration(self) -> None: self.assertEqual( top_prayers[0], self.rd_4, msg="The top prayer didn't match." ) + + +class PrayerAPITests(APITestCase): + """Check that Prayer API operations works as expected.""" + + @classmethod + def setUpTestData(cls) -> None: + cls.user_1 = UserFactory() + cls.user_2 = UserFactory() + + cls.rd_1 = RECAPDocumentFactory( + pacer_doc_id="98763421", + document_number="1", + is_available=True, + ) + cls.rd_2 = RECAPDocumentFactory( + pacer_doc_id="98763422", + document_number="2", + is_available=False, + ) + cls.rd_3 = RECAPDocumentFactory( + pacer_doc_id="98763423", + document_number="3", + is_available=False, + ) + + def setUp(self) -> None: + self.prayer_path = reverse("prayer-list", kwargs={"version": "v4"}) + self.client = make_client(self.user_1.pk) + self.client_2 = make_client(self.user_2.pk) + + async def make_a_prayer( + self, + client, + recap_doc_id, + ): + data = { + "recap_document": recap_doc_id, + } + return await client.post(self.prayer_path, data, format="json") + + async def test_make_a_prayer(self) -> None: + """Can we make a prayer?""" + + prayer = Prayer.objects.all() + response = await self.make_a_prayer(self.client, self.rd_1.pk) + prayer_first = await prayer.afirst() + self.assertIsNotNone(prayer_first) + self.assertEqual(await prayer.acount(), 1) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + + async def test_duplicate_prayer_fails(self) -> None: + """Ensure a user can't create multiple prayers for the same document + and user. + """ + await self.make_a_prayer(self.client, self.rd_1.pk) + response = await self.make_a_prayer(self.client, self.rd_1.pk) + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + + async def test_list_users_prayers(self) -> None: + """Can we list user's own prayers?""" + + # Make two prayers for user_1 + await self.make_a_prayer(self.client, self.rd_1.pk) + await self.make_a_prayer(self.client, self.rd_3.id) + + # Make one prayer for user_2 + await self.make_a_prayer(self.client_2, self.rd_1.pk) + + # Get the prayers for user_1, should be 2 + response = await self.client.get(self.prayer_path) + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(len(response.json()["results"]), 2) + + # Get the prayers for user_2, should be 1 + response_2 = await self.client_2.get(self.prayer_path) + self.assertEqual(response_2.status_code, HTTPStatus.OK) + self.assertEqual(len(response_2.json()["results"]), 1) + + async def test_delete_prayer(self) -> None: + """Can we delete a prayer? + Avoid users from deleting other users' prayers. + """ + + # Make two prayers for user_1 + prayer_1 = await self.make_a_prayer(self.client, self.rd_1.pk) + prayer_2 = await self.make_a_prayer(self.client, self.rd_3.id) + + prayer = Prayer.objects.all() + self.assertEqual(await prayer.acount(), 2) + + prayer_1_path_detail = reverse( + "prayer-detail", + kwargs={"pk": prayer_1.json()["id"], "version": "v4"}, + ) + + # Delete the prayer for user_1 + response = await self.client.delete(prayer_1_path_detail) + self.assertEqual(response.status_code, HTTPStatus.NO_CONTENT) + self.assertEqual(await prayer.acount(), 1) + + prayer_2_path_detail = reverse( + "prayer-detail", + kwargs={"pk": prayer_2.json()["id"], "version": "v3"}, + ) + + # user_2 tries to delete a user_1 prayer, it should fail + response = await self.client_2.delete(prayer_2_path_detail) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + self.assertEqual(await prayer.acount(), 1) + + async def test_prayer_detail(self) -> None: + """Can we get the detail for a prayer? Avoid users from getting other + users prayers. + """ + + # Make one prayer for user_1 + prayer_1 = await self.make_a_prayer(self.client, self.rd_1.pk) + prayer = Prayer.objects.all() + self.assertEqual(await prayer.acount(), 1) + prayer_1_path_detail = reverse( + "prayer-detail", + kwargs={"pk": prayer_1.json()["id"], "version": "v3"}, + ) + + # Get the prayer detail for user_1 + response = await self.client.get(prayer_1_path_detail) + self.assertEqual(response.status_code, HTTPStatus.OK) + + # user_2 tries to get user_1 prayer, it should fail + response = await self.client_2.get(prayer_1_path_detail) + self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + + async def test_prayer_update_fails(self) -> None: + """PUT AND PATCH methods are restricted.""" + + # Make one prayer for user_1 + prayer_1 = await self.make_a_prayer(self.client, self.rd_1.pk) + prayer_1_path_detail = reverse( + "prayer-detail", + kwargs={"pk": prayer_1.json()["id"], "version": "v3"}, + ) + # PATCH not allowed + data = {"status": 2} + response = await self.client.patch( + prayer_1_path_detail, data, format="json" + ) + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + + # PUT not allowed + data = {"status": 2, "recap_document": self.rd_1.pk} + response = await self.client.put( + prayer_1_path_detail, data, format="json" + ) + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + + @override_settings(ALLOWED_PRAYER_COUNT=2) + async def test_prayer_creation_eligibility(self): + """Test the prayer creation eligibility and limits in the API.""" + current_time = timezone.now() + prayers = Prayer.objects.all() + + with time_machine.travel(current_time, tick=False): + # First prayer succeed + response = await self.make_a_prayer(self.client, self.rd_1.pk) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + self.assertEqual(await prayers.acount(), 1) + + # Second prayer succeed + response = await self.make_a_prayer(self.client, self.rd_2.pk) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + self.assertEqual(await prayers.acount(), 2) + + # Third prayer fails due to limit + response = await self.make_a_prayer(self.client, self.rd_3.pk) + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + self.assertIn("maximum number of prayers", str(response.data)) + self.assertEqual(await prayers.acount(), 2) + + # After more than 24 hours the user is eligible to create more prays. + with time_machine.travel( + current_time + timedelta(hours=25), tick=False + ): + response = await self.make_a_prayer(self.client, self.rd_3.pk) + self.assertEqual(response.status_code, HTTPStatus.CREATED) + self.assertEqual(await prayers.acount(), 3) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 79307c6645..814585fbf3 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -35,18 +35,6 @@ async def prayer_eligible(user: User) -> bool: return prayer_count < allowed_prayer_count -async def prayer_exists(user: User, recap_document: RECAPDocument) -> bool: - return await Prayer.objects.filter( - user=user, recap_document=recap_document - ).aexists() - - -async def get_prayer_count(recap_document: RECAPDocument) -> int: - return await Prayer.objects.filter( - recap_document=recap_document, status=Prayer.WAITING - ).acount() - - async def create_prayer( user: User, recap_document: RECAPDocument ) -> Prayer | None: @@ -58,6 +46,45 @@ async def create_prayer( return None +async def get_prayer_counts_in_bulk( + recap_documents: list[RECAPDocument], +) -> dict[str, int]: + """Retrieve the count of prayers with a status of "WAITING" for a list of recap documents. + + :param recap_documents: A list of RECAPDocument instances to filter prayers. + :return: A dictionary where keys are RECAPDocument IDs and values are the + count of "WAITING" prayers for each document. + """ + + prayer_counts = ( + Prayer.objects.filter( + recap_document__in=recap_documents, status=Prayer.WAITING + ) + .values("recap_document") + .annotate(count=Count("id")) + ) + return { + prayer_count["recap_document"]: prayer_count["count"] + async for prayer_count in prayer_counts + } + + +async def get_existing_prayers_in_bulk( + user: User, recap_documents: list[RECAPDocument] +) -> dict[int, bool]: + """Check if prayers exist for a user and a list of recap documents. + + :param user: The user for whom to check prayer existence. + :param recap_documents: A list of RECAPDocument instances to check prayers. + :return: A dictionary where keys are RECAPDocument IDs and values are True + if a prayer exists for the user and RD. + """ + existing_prayers = Prayer.objects.filter( + user=user, recap_document__in=recap_documents + ).values_list("recap_document_id", flat=True) + return {rd_id: True async for rd_id in existing_prayers} + + async def get_top_prayers() -> list[RECAPDocument]: # Calculate the age of each prayer prayer_age = ExpressionWrapper( diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index 4c97d9d509..86913a4131 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -173,11 +173,11 @@ {% if request.user.is_authenticated %} diff --git a/cl/opinion_page/views.py b/cl/opinion_page/views.py index 4828468232..7c527529dd 100644 --- a/cl/opinion_page/views.py +++ b/cl/opinion_page/views.py @@ -44,7 +44,11 @@ from cl.custom_filters.templatetags.text_filters import best_case_name from cl.favorites.forms import NoteForm from cl.favorites.models import Note -from cl.favorites.utils import get_prayer_count, prayer_eligible, prayer_exists +from cl.favorites.utils import ( + get_existing_prayers_in_bulk, + get_prayer_counts_in_bulk, + prayer_eligible, +) from cl.lib.auth import group_required from cl.lib.bot_detector import is_og_bot from cl.lib.decorators import cache_page_ignore_params @@ -379,20 +383,6 @@ async def view_docket( "-recap_sequence_number", "-entry_number" ) - for entry in await sync_to_async(list)(de_list): - if await sync_to_async(entry.recap_documents.count)() > 0: - for rd in await sync_to_async(list)(entry.recap_documents.all()): - if rd.pacer_url and not (rd.is_free_on_pacer or rd.is_sealed): - rd.prayer_count = await get_prayer_count(rd) - - if request.user.is_authenticated: - rd.prayer_exists = await prayer_exists( - request.user, rd - ) - rd.prayer_eligible = await prayer_eligible( - request.user - ) - page = request.GET.get("page", 1) @sync_to_async @@ -405,15 +395,40 @@ def paginate_docket_entries(docket_entries, docket_page): except EmptyPage: return paginator.page(paginator.num_pages) + paginated_entries = await paginate_docket_entries(de_list, page) + + # Extract recap documents from the current page. + recap_documents = [ + rd + for entry in await sync_to_async(list)(paginated_entries) + async for rd in entry.recap_documents.all() + ] + # Get prayer counts in bulk. + prayer_counts = await get_prayer_counts_in_bulk(recap_documents) + existing_prayers = {} + prayer_is_eligible = False + if request.user.is_authenticated: + # Check prayer existence in bulk. + existing_prayers = await get_existing_prayers_in_bulk( + request.user, recap_documents + ) + prayer_is_eligible = await prayer_eligible(request.user) + + # Merge counts and existing prayer status to RECAPDocuments. + for rd in recap_documents: + rd.prayer_count = prayer_counts.get(rd.id, 0) + rd.prayer_exists = existing_prayers.get(rd.id, False) + context.update( { "parties": await docket.parties.aexists(), # Needed to show/hide parties tab. "authorities": await docket.ahas_authorities(), - "docket_entries": await paginate_docket_entries(de_list, page), + "docket_entries": paginated_entries, "sort_order_asc": sort_order_asc, "form": form, "get_string": make_get_string(request), + "prayer_eligible": prayer_is_eligible, } ) return TemplateResponse(request, "docket.html", context) From e4ebf01c29dfb19d6e4026680e46717b6a4a773f Mon Sep 17 00:00:00 2001 From: Alberto Islas Date: Wed, 9 Oct 2024 17:00:51 -0600 Subject: [PATCH 37/74] fix(favorites): Linked pray button to modal-logged-out --- cl/opinion_page/templates/includes/de_list.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index 86913a4131..fbdd7885d0 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -184,12 +184,14 @@ {{rd.prayer_count}} 🙏 {% else %} - {{rd.prayer_count}} 🙏 - + {% endif %} {% endif %} {% endif %} From 47ee93f22c1c5b7f8dcd48fa774ecdb237e3a469 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Wed, 9 Oct 2024 22:54:59 -0400 Subject: [PATCH 38/74] delete prayer function --- cl/favorites/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 814585fbf3..ad612b4765 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -46,6 +46,14 @@ async def create_prayer( return None +async def delete_prayer( + user: User, recap_document: RECAPDocument +) -> bool: + await Prayer.objects.filter.adelete(user=user, recap_document=recap_document) + + return await Prayer.objects.filter(user=user, recap_document=recap_document).aexists() + + async def get_prayer_counts_in_bulk( recap_documents: list[RECAPDocument], ) -> dict[str, int]: From e005d829d2d794cfed7384d8f46b75ab9c6d5453 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 02:55:39 +0000 Subject: [PATCH 39/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/utils.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index ad612b4765..07a29b0dab 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -46,12 +46,14 @@ async def create_prayer( return None -async def delete_prayer( - user: User, recap_document: RECAPDocument -) -> bool: - await Prayer.objects.filter.adelete(user=user, recap_document=recap_document) +async def delete_prayer(user: User, recap_document: RECAPDocument) -> bool: + await Prayer.objects.filter.adelete( + user=user, recap_document=recap_document + ) - return await Prayer.objects.filter(user=user, recap_document=recap_document).aexists() + return await Prayer.objects.filter( + user=user, recap_document=recap_document + ).aexists() async def get_prayer_counts_in_bulk( From f806fb34eb546a709dc130e83cf53fdeb06f0bee Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Wed, 9 Oct 2024 23:14:36 -0400 Subject: [PATCH 40/74] waffle flag "pray-and-pay" --- cl/opinion_page/templates/includes/de_list.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index fbdd7885d0..941d5202a6 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -1,6 +1,6 @@ {% load pacer %} {% load tz %} - +{% load waffle_tags %}
    @@ -171,7 +171,8 @@ target="_blank" rel="nofollow">Buy on PACER {% if rd.page_count %}(${{ rd|price }}){% endif %} - {% if request.user.is_authenticated %} + {% flag "pray-and-pay" %} + {% if request.user.is_authenticated %} {% endif %} + {% endflag %} {% endif %} {% endif %} {% endif %} From f617d8582c8e4fb785cf6ed9fffb578b07985ab0 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:42:46 -0400 Subject: [PATCH 41/74] work on frontend --- .../templates/includes/de_list.html | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index 941d5202a6..5c5a5dd7f0 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -173,17 +173,16 @@ {% flag "pray-and-pay" %} {% if request.user.is_authenticated %} - +
    + {% csrf_token %} + +
    {% else %} - - {% else %} - - {% endif %} + {% if request.user.is_authenticated %} +
    + {% csrf_token %} + +
    + {% else %} + + {% endif %} {% endflag %} {% endif %} {% endif %} From f0501b9400a07351870de0ef6765ed2cee7dff21 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:39:10 -0400 Subject: [PATCH 48/74] allowing users to see how many requests have been fulfilled over the lifetime --- cl/favorites/templates/top_prayers.html | 12 ++++++++++-- cl/favorites/utils.py | 11 +++++++++++ cl/favorites/views.py | 26 ++++++++++++++++--------- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/cl/favorites/templates/top_prayers.html b/cl/favorites/templates/top_prayers.html index 800f160d31..e6dcf7d99b 100644 --- a/cl/favorites/templates/top_prayers.html +++ b/cl/favorites/templates/top_prayers.html @@ -7,10 +7,18 @@ {% block title %}RECAP Requests – CourtListener.com{% endblock %} {% block og_title %}RECAP Requests – CourtListener.com{% endblock %} -{% block description %}Lorem Ipsum on CourtListener.{% endblock %} -{% block og_description %}Lorem Ipsum on CourtListener.{% endblock %} +{% block description %}RECAP Requests on CourtListener.{% endblock %} +{% block og_description %}RECAP Requests on CourtListener.{% endblock %} + {% block content %} +{% comment %}
    + {% if user.is_authenticated %} +
    +

    You have had {{ user_prayer_count }} document requests fulfilled, which others contributed ${{ user_prayer_cost }} to.

    +
    + {% endif %} {% endcomment %} +
    diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index ca7d43a4eb..5f17e57ec6 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -199,3 +199,14 @@ def send_prayer_emails(instance: RECAPDocument) -> None: messages.append(msg) connection = get_connection() connection.send_messages(messages) + +async def get_user_prayer_history(user: User): + filtered_list = Prayer.objects.filter(user=user, status=Prayer.GRANTED) + + count = await filtered_list.acount() + + total_cost = 0 + async for prayer in filtered_list: + total_cost += float(price(prayer.recap_document)) + + return count, total_cost diff --git a/cl/favorites/views.py b/cl/favorites/views.py index 69c288fa0d..573124656f 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -19,7 +19,7 @@ from cl.favorites.forms import NoteForm from cl.favorites.models import DocketTag, Note, UserTag -from cl.favorites.utils import create_prayer, delete_prayer, get_top_prayers +from cl.favorites.utils import create_prayer, delete_prayer, get_top_prayers, get_user_prayer_history from cl.lib.decorators import cache_page_ignore_params from cl.lib.http import is_ajax from cl.lib.view_utils import increment_view_count @@ -185,14 +185,22 @@ async def open_prayers(request: HttpRequest) -> HttpResponse: """Show the user top open prayer requests.""" top_prayers = await get_top_prayers() - return TemplateResponse( - request, - "top_prayers.html", - { - "top_prayers": top_prayers, - "private": True, # temporary to prevent Google indexing - }, - ) + + context = { + "top_prayers": top_prayers, + "private": True, # Temporary to prevent Google indexing + } + + if request.user.is_authenticated: + user_prayer_count, user_prayer_cost = await get_user_prayer_history(request.user) + + context.update({ + "user_prayer_count": user_prayer_count, + "user_prayer_cost": user_prayer_cost, + }) + + return TemplateResponse(request, "top_prayers.html", context) + @login_required From f28678ba3549723d0cd076ca30f934566acfb897 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:40:02 +0000 Subject: [PATCH 49/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/utils.py | 1 + cl/favorites/views.py | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 5f17e57ec6..7a19b76601 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -200,6 +200,7 @@ def send_prayer_emails(instance: RECAPDocument) -> None: connection = get_connection() connection.send_messages(messages) + async def get_user_prayer_history(user: User): filtered_list = Prayer.objects.filter(user=user, status=Prayer.GRANTED) diff --git a/cl/favorites/views.py b/cl/favorites/views.py index 573124656f..d23f6f6196 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -19,7 +19,12 @@ from cl.favorites.forms import NoteForm from cl.favorites.models import DocketTag, Note, UserTag -from cl.favorites.utils import create_prayer, delete_prayer, get_top_prayers, get_user_prayer_history +from cl.favorites.utils import ( + create_prayer, + delete_prayer, + get_top_prayers, + get_user_prayer_history, +) from cl.lib.decorators import cache_page_ignore_params from cl.lib.http import is_ajax from cl.lib.view_utils import increment_view_count @@ -192,15 +197,18 @@ async def open_prayers(request: HttpRequest) -> HttpResponse: } if request.user.is_authenticated: - user_prayer_count, user_prayer_cost = await get_user_prayer_history(request.user) - - context.update({ - "user_prayer_count": user_prayer_count, - "user_prayer_cost": user_prayer_cost, - }) + user_prayer_count, user_prayer_cost = await get_user_prayer_history( + request.user + ) - return TemplateResponse(request, "top_prayers.html", context) + context.update( + { + "user_prayer_count": user_prayer_count, + "user_prayer_cost": user_prayer_cost, + } + ) + return TemplateResponse(request, "top_prayers.html", context) @login_required From 531166bb118122fbcba5a7035955f36ff79f239a Mon Sep 17 00:00:00 2001 From: Vijay Anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:45:41 -0400 Subject: [PATCH 50/74] Update utils.py type hint for new function --- cl/favorites/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 7a19b76601..ae96f27602 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -201,7 +201,7 @@ def send_prayer_emails(instance: RECAPDocument) -> None: connection.send_messages(messages) -async def get_user_prayer_history(user: User): +async def get_user_prayer_history(user: User) -> tuple[int, float]: filtered_list = Prayer.objects.filter(user=user, status=Prayer.GRANTED) count = await filtered_list.acount() From ef4b21c409723d4b736526f5577238c4d10b9bfe Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:04:58 -0400 Subject: [PATCH 51/74] adding tests and adjusting logic of delete_prayer() function --- cl/favorites/tests.py | 39 +++++++++++++++++++++++++++++++++------ cl/favorites/utils.py | 8 +++----- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index a905f55420..f3c9cc79f8 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -20,6 +20,7 @@ from cl.favorites.models import DocketTag, Note, Prayer, UserTag from cl.favorites.utils import ( create_prayer, + delete_prayer, get_existing_prayers_in_bulk, get_prayer_counts_in_bulk, get_top_prayers, @@ -678,9 +679,15 @@ def setUpTestData(cls) -> None: is_available=False, ) + cls.rd_6 = RECAPDocumentFactory( + pacer_doc_id="98763426", + document_number="6", + is_available=False, + ) + @override_settings(ALLOWED_PRAYER_COUNT=2) async def test_prayer_eligible(self) -> None: - """Does the prayer_eligible method works properly?""" + """Does the prayer_eligible method work properly?""" current_time = timezone.now() with time_machine.travel(current_time, tick=False): @@ -720,7 +727,7 @@ async def test_prayer_eligible(self) -> None: self.assertTrue(user_is_eligible) async def test_create_prayer(self) -> None: - """Does the create_prayer method works properly?""" + """Does the create_prayer method work properly?""" # Prayer is not created if the document is already available. prayer_created = await create_prayer(self.user, self.rd_1) @@ -734,8 +741,28 @@ async def test_create_prayer(self) -> None: same_prayer_created = await create_prayer(self.user, self.rd_2) self.assertIsNone(same_prayer_created) + async def test_delete_prayer(self) -> None: + """Does the delete_prayer method work properly?""" + + # Prayer is added, then deleted successfully + prayer_created = await create_prayer(self.user, self.rd_2) + prayer_deleted = await delete_prayer(self.user, self.rd_2) + self.assertFalse(prayer_deleted) + + # Prayer is created, then document is made available to check that a user can't delete a prayer that has been granted + prayer_created = await create_prayer(self.user, self.rd_6) + self.rd_6.is_available = True + prayer_deleted = await delete_prayer(self.user, self.rd_6) + self.assertFalse(prayer_deleted) + + # Ensure that a user cannot delete the same prayer twice + prayer_created = await create_prayer(self.user, self.rd_2) + prayer_deleted = await delete_prayer(self.user, self.rd_2) + prayer_deleted = await delete_prayer(self.user, self.rd_2) + self.assertFalse(prayer_deleted) + async def test_get_top_prayers_by_number(self) -> None: - """Does the get_top_prayers method works properly?""" + """Does the get_top_prayers method work properly?""" # Test top documents based on prayers count. current_time = timezone.now() @@ -763,7 +790,7 @@ async def test_get_top_prayers_by_number(self) -> None: ) async def test_get_top_prayers_by_age(self) -> None: - """Does the get_top_prayers method works properly?""" + """Does the get_top_prayers method work properly?""" # Test top documents based on prayer age. current_time = timezone.now() @@ -794,7 +821,7 @@ async def test_get_top_prayers_by_age(self) -> None: ) async def test_get_top_prayers_by_number_and_age(self) -> None: - """Does the get_top_prayers method works properly?""" + """Does the get_top_prayers method work properly?""" # Create prayers with different counts and ages current_time = timezone.now() @@ -999,7 +1026,7 @@ async def test_prayers_integration(self) -> None: class PrayerAPITests(APITestCase): - """Check that Prayer API operations works as expected.""" + """Check that Prayer API operations work as expected.""" @classmethod def setUpTestData(cls) -> None: diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index ae96f27602..ef188f0102 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -47,13 +47,11 @@ async def create_prayer( async def delete_prayer(user: User, recap_document: RECAPDocument) -> bool: - await Prayer.objects.filter( - user=user, recap_document=recap_document + deleted, _ = await Prayer.objects.filter( + user=user, recap_document=recap_document, status=Prayer.WAITING ).adelete() - return await Prayer.objects.filter( - user=user, recap_document=recap_document - ).aexists() + return deleted > 0 async def get_prayer_counts_in_bulk( From e959b83cff831b988638a233ed47115e5af04c8c Mon Sep 17 00:00:00 2001 From: Vijay Anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:21:20 -0400 Subject: [PATCH 52/74] Update tests.py --- cl/favorites/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index f3c9cc79f8..39b5dc8c63 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -747,7 +747,7 @@ async def test_delete_prayer(self) -> None: # Prayer is added, then deleted successfully prayer_created = await create_prayer(self.user, self.rd_2) prayer_deleted = await delete_prayer(self.user, self.rd_2) - self.assertFalse(prayer_deleted) + self.assertTrue(prayer_deleted) # Prayer is created, then document is made available to check that a user can't delete a prayer that has been granted prayer_created = await create_prayer(self.user, self.rd_6) From ba2ce3c9cf37bb0078f070b50ef10f619b48d7e2 Mon Sep 17 00:00:00 2001 From: Vijay Anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:35:12 -0400 Subject: [PATCH 53/74] Update tests.py --- cl/favorites/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index 39b5dc8c63..ca82e058fb 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -752,6 +752,7 @@ async def test_delete_prayer(self) -> None: # Prayer is created, then document is made available to check that a user can't delete a prayer that has been granted prayer_created = await create_prayer(self.user, self.rd_6) self.rd_6.is_available = True + self.rd_6.save() prayer_deleted = await delete_prayer(self.user, self.rd_6) self.assertFalse(prayer_deleted) From fd5fb34a3ef6358930078f73ceb5d72d7fc161dc Mon Sep 17 00:00:00 2001 From: Vijay Anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:03:21 -0400 Subject: [PATCH 54/74] Update tests.py async --- cl/favorites/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index ca82e058fb..3a619828a5 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -752,7 +752,7 @@ async def test_delete_prayer(self) -> None: # Prayer is created, then document is made available to check that a user can't delete a prayer that has been granted prayer_created = await create_prayer(self.user, self.rd_6) self.rd_6.is_available = True - self.rd_6.save() + await sync_to_async(self.rd_6.save)() prayer_deleted = await delete_prayer(self.user, self.rd_6) self.assertFalse(prayer_deleted) From d21ecc052d0f033d32331d7e7f06bbade0ce7f68 Mon Sep 17 00:00:00 2001 From: Vijay Anne <69829523+v-anne@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:06:47 -0400 Subject: [PATCH 55/74] Update tests.py from django.utils import timezone --- cl/favorites/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cl/favorites/tests.py b/cl/favorites/tests.py index 36f2dea586..e2aa34ab56 100644 --- a/cl/favorites/tests.py +++ b/cl/favorites/tests.py @@ -10,6 +10,7 @@ from django.template.defaultfilters import date as template_date from django.test import AsyncClient, override_settings from django.urls import reverse +from django.utils import timezone from django.utils.timezone import make_naive, now from selenium.webdriver.common.by import By from timeout_decorator import timeout_decorator From 0132a43a7ad8e9b182add4383dab67937282fee8 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:03:30 -0400 Subject: [PATCH 56/74] new func --- cl/favorites/utils.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index ef188f0102..d29d9a1e1c 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -1,5 +1,7 @@ from datetime import timedelta +import asyncio + from django.conf import settings from django.contrib.auth.models import User from django.core.mail import EmailMultiAlternatives, get_connection @@ -204,8 +206,22 @@ async def get_user_prayer_history(user: User) -> tuple[int, float]: count = await filtered_list.acount() - total_cost = 0 - async for prayer in filtered_list: - total_cost += float(price(prayer.recap_document)) + prices = await asyncio.gather(*[price(prayer.recap_document) for prayer in await filtered_list.alist()]) + total_cost = sum(map(float, prices)) return count, total_cost + + +async def get_lifetime_prayer_stats() -> tuple[int, int, float]: + + filtered_list = Prayer.objects.filter(status=Prayer.GRANTED) + + count = await filtered_list.acount() + + distinct_documents = filtered_list.values_list('recap_document', flat=True).distinct() + num_distinct_purchases = len(distinct_documents) + + prices = await asyncio.gather(*[price(recap_document) for recap_document in distinct_documents]) + total_cost = sum(map(float, prices)) + + return count, num_distinct_purchases, total_cost From 841c521f88918f3ea1f6c420d6f876a2e76d5afc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:04:10 +0000 Subject: [PATCH 57/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/utils.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index d29d9a1e1c..b84f6114ff 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -1,6 +1,5 @@ -from datetime import timedelta - import asyncio +from datetime import timedelta from django.conf import settings from django.contrib.auth.models import User @@ -206,7 +205,12 @@ async def get_user_prayer_history(user: User) -> tuple[int, float]: count = await filtered_list.acount() - prices = await asyncio.gather(*[price(prayer.recap_document) for prayer in await filtered_list.alist()]) + prices = await asyncio.gather( + *[ + price(prayer.recap_document) + for prayer in await filtered_list.alist() + ] + ) total_cost = sum(map(float, prices)) return count, total_cost @@ -218,10 +222,14 @@ async def get_lifetime_prayer_stats() -> tuple[int, int, float]: count = await filtered_list.acount() - distinct_documents = filtered_list.values_list('recap_document', flat=True).distinct() + distinct_documents = filtered_list.values_list( + "recap_document", flat=True + ).distinct() num_distinct_purchases = len(distinct_documents) - prices = await asyncio.gather(*[price(recap_document) for recap_document in distinct_documents]) + prices = await asyncio.gather( + *[price(recap_document) for recap_document in distinct_documents] + ) total_cost = sum(map(float, prices)) return count, num_distinct_purchases, total_cost From 19b9306a23d0051133883ef024738ef1357852a2 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:46:04 -0400 Subject: [PATCH 58/74] fixing buggy functions --- cl/favorites/utils.py | 27 +++++++++++---------------- cl/favorites/views.py | 1 + 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index b84f6114ff..ba393c4ef5 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -205,13 +205,9 @@ async def get_user_prayer_history(user: User) -> tuple[int, float]: count = await filtered_list.acount() - prices = await asyncio.gather( - *[ - price(prayer.recap_document) - for prayer in await filtered_list.alist() - ] - ) - total_cost = sum(map(float, prices)) + total_cost = 0 + async for prayer in filtered_list: + total_cost += float(price(prayer.recap_document)) return count, total_cost @@ -222,14 +218,13 @@ async def get_lifetime_prayer_stats() -> tuple[int, int, float]: count = await filtered_list.acount() - distinct_documents = filtered_list.values_list( - "recap_document", flat=True - ).distinct() - num_distinct_purchases = len(distinct_documents) + total_cost = 0 + distinct_documents = set() - prices = await asyncio.gather( - *[price(recap_document) for recap_document in distinct_documents] - ) - total_cost = sum(map(float, prices)) + async for prayer in filtered_list: + distinct_documents.add(prayer.recap_document) + total_cost += float(await price(prayer.recap_document)) + + num_distinct_purchases = len(distinct_documents) - return count, num_distinct_purchases, total_cost + return count, num_distinct_purchases, total_cost \ No newline at end of file diff --git a/cl/favorites/views.py b/cl/favorites/views.py index d23f6f6196..75cdec5b53 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -22,6 +22,7 @@ from cl.favorites.utils import ( create_prayer, delete_prayer, + get_lifetime_prayer_stats, get_top_prayers, get_user_prayer_history, ) From be928aa35c555cd8474d97d693e5ccc32ff41e7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:47:02 +0000 Subject: [PATCH 59/74] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cl/favorites/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index ba393c4ef5..898607e607 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -227,4 +227,4 @@ async def get_lifetime_prayer_stats() -> tuple[int, int, float]: num_distinct_purchases = len(distinct_documents) - return count, num_distinct_purchases, total_cost \ No newline at end of file + return count, num_distinct_purchases, total_cost From c958ce56890b43775ac0f71165be19c95f5a0e09 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:44:33 -0400 Subject: [PATCH 60/74] removing unused imports --- cl/favorites/utils.py | 1 - cl/favorites/views.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/cl/favorites/utils.py b/cl/favorites/utils.py index 898607e607..bbe40309c3 100644 --- a/cl/favorites/utils.py +++ b/cl/favorites/utils.py @@ -1,4 +1,3 @@ -import asyncio from datetime import timedelta from django.conf import settings diff --git a/cl/favorites/views.py b/cl/favorites/views.py index 75cdec5b53..f18ef0494e 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -9,12 +9,10 @@ HttpRequest, HttpResponse, HttpResponseNotAllowed, - HttpResponseRedirect, HttpResponseServerError, ) from django.shortcuts import aget_object_or_404 from django.template.response import TemplateResponse -from django.urls import reverse from django.utils.datastructures import MultiValueDictKeyError from cl.favorites.forms import NoteForm From 3776784361262ce640c133f93462610d77629816 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:48:26 -0400 Subject: [PATCH 61/74] adding waffle flag to base.html --- cl/assets/templates/base.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cl/assets/templates/base.html b/cl/assets/templates/base.html index 4a8f240880..be91672e34 100644 --- a/cl/assets/templates/base.html +++ b/cl/assets/templates/base.html @@ -231,9 +231,11 @@

    You did not supply the "private" variable to your template.
  • Install RECAP
  • - {% comment %}
  • - Pray and Pay Project -
  • {% endcomment %} + {% flag "pray-and-pay" %} +
  • + Pray and Pay Project +
  • + {% endflag %}
  • Get Case Alerts
  • From 267d5dbd7846d0ee87b06883852dd84edfe0a9c2 Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:08:39 -0400 Subject: [PATCH 62/74] deleting commented out summary statistics --- cl/favorites/templates/top_prayers.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cl/favorites/templates/top_prayers.html b/cl/favorites/templates/top_prayers.html index e6dcf7d99b..980d6a76e8 100644 --- a/cl/favorites/templates/top_prayers.html +++ b/cl/favorites/templates/top_prayers.html @@ -12,12 +12,6 @@ {% block content %} -{% comment %}
    - {% if user.is_authenticated %} -
    -

    You have had {{ user_prayer_count }} document requests fulfilled, which others contributed ${{ user_prayer_cost }} to.

    -
    - {% endif %} {% endcomment %}
    From fa181a9d5f084cbec45b4e87c1d4822362355f4b Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:10:19 -0400 Subject: [PATCH 63/74] changing private to False --- cl/favorites/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/favorites/views.py b/cl/favorites/views.py index f18ef0494e..adf09d5ad1 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -192,7 +192,7 @@ async def open_prayers(request: HttpRequest) -> HttpResponse: context = { "top_prayers": top_prayers, - "private": True, # Temporary to prevent Google indexing + "private": False, } if request.user.is_authenticated: From 634842446413afc6090ec1677f8203513f0f52ab Mon Sep 17 00:00:00 2001 From: v_anne <69829523+v-anne@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:55:06 -0400 Subject: [PATCH 64/74] removing import of unused function --- cl/favorites/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cl/favorites/views.py b/cl/favorites/views.py index adf09d5ad1..2b61e95e7e 100644 --- a/cl/favorites/views.py +++ b/cl/favorites/views.py @@ -20,7 +20,6 @@ from cl.favorites.utils import ( create_prayer, delete_prayer, - get_lifetime_prayer_stats, get_top_prayers, get_user_prayer_history, ) From 7b217a8f678fed02b247c32035b433d48605f89d Mon Sep 17 00:00:00 2001 From: Eduardo Rosendo Date: Mon, 14 Oct 2024 14:50:45 -0400 Subject: [PATCH 65/74] feat(docket): Remove inline styles from pray button Replaces inline styles on the pray button with class-based styles in `override.css` for better maintainability and consistency. --- cl/assets/static-global/css/override.css | 8 ++++ .../templates/includes/de_list.html | 44 ++++++++++--------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/cl/assets/static-global/css/override.css b/cl/assets/static-global/css/override.css index 7a27e9f08f..3693518187 100644 --- a/cl/assets/static-global/css/override.css +++ b/cl/assets/static-global/css/override.css @@ -1723,3 +1723,11 @@ rect.series-segment { opacity 150ms 150ms ease-in; transform: translate3d(0, 0, 0); } + +.prayer-button[data-gap-size="small"] { + margin-left: 8px; +} + +.prayer-button[data-gap-size="large"]{ + margin-left: 44px; +} diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index fc5fcfdb61..66b6285b04 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -172,27 +172,29 @@ rel="nofollow">Buy on PACER {% if rd.page_count %}(${{ rd|price }}){% endif %} {% flag "pray-and-pay" %} - {% if request.user.is_authenticated %} -
    - {% csrf_token %} - - - {% else %} - - {% endif %} +
    + {% if request.user.is_authenticated %} +
    + {% csrf_token %} + + + {% else %} + + {% endif %} +
    {% endflag %} {% endif %} {% endif %} From 69494daeb819c1b4d51b030b632ad2b2a897b492 Mon Sep 17 00:00:00 2001 From: Eduardo Rosendo Date: Mon, 14 Oct 2024 14:54:27 -0400 Subject: [PATCH 66/74] refactor(docket): Extract pray button HTML into reusable fragment Creates a reusable HTML fragment for the pray button --- .../templates/includes/de_list.html | 22 +------------------ .../pray_and_pay_htmx/pray_button.html | 21 ++++++++++++++++++ 2 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html diff --git a/cl/opinion_page/templates/includes/de_list.html b/cl/opinion_page/templates/includes/de_list.html index 66b6285b04..5ab5f72e5c 100644 --- a/cl/opinion_page/templates/includes/de_list.html +++ b/cl/opinion_page/templates/includes/de_list.html @@ -173,27 +173,7 @@ {% flag "pray-and-pay" %}
    - {% if request.user.is_authenticated %} -
    - {% csrf_token %} - - - {% else %} - - {% endif %} + {% include "includes/pray_and_pay_htmx/pray_button.html" with prayer_exists=rd.prayer_exists document_id=rd.id count=rd.prayer_count%}
    {% endflag %} {% endif %} diff --git a/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html b/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html new file mode 100644 index 0000000000..db957784cf --- /dev/null +++ b/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html @@ -0,0 +1,21 @@ +{% if request.user.is_authenticated %} +
    + {% csrf_token %} + + +{% else %} + +{% endif %} From 09a9dd74be1e89a19a84926ad5c346817c5b2f4d Mon Sep 17 00:00:00 2001 From: Eduardo Rosendo Date: Mon, 14 Oct 2024 14:57:26 -0400 Subject: [PATCH 67/74] feat(docket): Integrate HTMX to the docket page --- cl/opinion_page/templates/docket.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cl/opinion_page/templates/docket.html b/cl/opinion_page/templates/docket.html index be857c1da5..df0b33363a 100644 --- a/cl/opinion_page/templates/docket.html +++ b/cl/opinion_page/templates/docket.html @@ -22,6 +22,15 @@ href="{% url "docket_feed" docket.pk %}" /> {% endblock %} +{% block footer-scripts %} + {% if DEBUG %} + + + {% else %} + + {% endif %} +{% endblock %} + {% block nav-de %}active{% endblock %} {% block tab-content %} {% if docket_entries.paginator.count %} From e99e61b4fa5b8a9c8ab5d0f678cd35a9ee57e564 Mon Sep 17 00:00:00 2001 From: Eduardo Rosendo Date: Mon, 14 Oct 2024 15:00:44 -0400 Subject: [PATCH 68/74] feat(pray button): Adds HTMX functionality to the pray button --- .../templates/includes/pray_and_pay_htmx/pray_button.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html b/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html index db957784cf..d2ddb4c67c 100644 --- a/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html +++ b/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html @@ -1,12 +1,13 @@ {% if request.user.is_authenticated %} -
    + {% csrf_token %} {% else %} From 7d479d5137b41da88e881214efaf6ce39fb47f09 Mon Sep 17 00:00:00 2001 From: Eduardo Rosendo Date: Mon, 14 Oct 2024 23:19:57 -0400 Subject: [PATCH 69/74] eat(pray_button): Add visual indicator for requested docs Updates the pray_button.html fragment to use the prayer_exists context variable. This provides a visual cue to users by changing the button's style when a document has already been requested. --- .../templates/includes/pray_and_pay_htmx/pray_button.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html b/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html index d2ddb4c67c..8a476dbf96 100644 --- a/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html +++ b/cl/opinion_page/templates/includes/pray_and_pay_htmx/pray_button.html @@ -1,9 +1,8 @@ {% if request.user.is_authenticated %}
    - {% csrf_token %} + {% if daily_limit_reached %} +   + + + + {% endif %} {% else %}