Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: reactive schedules nudge emails filter #56

Merged
merged 25 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5c8100a
chore: upgrade requirements based on redwood
BryanttV Dec 4, 2024
4d2dea1
feat: create filter definition for schedule nudge emails
BryanttV Dec 4, 2024
e196ae7
feat: add pipeline for new filter
BryanttV Dec 4, 2024
d91a2ff
refactor: update filter definition
BryanttV Dec 5, 2024
cabfa56
feat: add init method in filter exception class
BryanttV Dec 5, 2024
7c89909
feat: update pipeline for raise exception
BryanttV Dec 5, 2024
7a3ff64
chore: add allow_newsletter field as a visible field in account settings
BryanttV Dec 5, 2024
d48cb3b
chore: allow show boolean fields in account fields
BryanttV Dec 5, 2024
cc358fb
refactor: use FieldError instead AttributeError
BryanttV Dec 6, 2024
1762743
chore: update filter definition
BryanttV Dec 9, 2024
8a4d8bc
chore: use latest versions of openedx-filters and openedx-events
BryanttV Dec 9, 2024
1aef6f0
fix: solve pylint errors
BryanttV Dec 9, 2024
02e352d
chore: remove changes of allow_newsletter field
BryanttV Dec 9, 2024
a9b0386
chore: remove filter definition
BryanttV Dec 12, 2024
e8b2934
chore: remove try-except when running the filter
BryanttV Dec 12, 2024
d10bb12
chore: upgrade openedx-filters to v1.12.0
BryanttV Dec 12, 2024
ec6cff9
chore: update translations
BryanttV Dec 12, 2024
673057c
test: add FilterUserWithAllowedNewsletter unit tests
BryanttV Dec 13, 2024
814a9b5
test: add unit test for new filter
BryanttV Dec 16, 2024
73bcb94
chore: add django_mock_queries in test requirements
BryanttV Dec 16, 2024
78144fb
docs: update pipeline docstring
BryanttV Dec 17, 2024
78b4bb0
feat: add boolean field in the extended profile fields
BryanttV Dec 11, 2024
11b21ee
feat: add allow_newsletter field into account settings
BryanttV Dec 12, 2024
569cd4b
chore: fix pylint errors
BryanttV Dec 12, 2024
c279fd7
fix: correct expiration date calculation for ID verification
igobranco Jan 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions nau_openedx_extensions/filters/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from fnmatch import fnmatch

from django.conf import settings
from django.db.models.query import QuerySet
from django.utils.translation import gettext as _
from openedx_filters import PipelineStep
from openedx_filters.learning.filters import CourseEnrollmentStarted
Expand Down Expand Up @@ -77,3 +78,39 @@ def _is_user_email_allowed(user, domains_allowed):
if user_domain == domain or fnmatch(user_domain, f"*.{domain}"):
return True
return False


class FilterUsersWithAllowedNewsletter(PipelineStep):
"""
Filter the Schedules QuerySet to only keep those whose associated user has
the `allow_newsletter` field set to `True`. If the user does not have the
`allow_newsletter` field set to `True`, or if the field does not exist, the
Schedule will be filtered out.

Example usage:

Add the following configurations to your configuration file:

```
OPEN_EDX_FILTERS_CONFIG = {
"org.openedx.learning.schedule.queryset.requested.v1": {
"fail_silently": False,
"pipeline": [
"nau_openedx_extensions.filters.pipeline.FilterUsersWithAllowedNewsletter",
],
},
}
```
"""

def run_filter(self, schedules: QuerySet) -> dict: # pylint: disable=arguments-differ
"""
Execute filter that filters users with allowed newsletter.

Arguments:
schedules (QuerySet): Queryset of schedules to be sent.

Returns:
dict: Dictionary with the filtered schedules.
"""
return {"schedules": schedules.filter(enrollment__user__nauuserextendedmodel__allow_newsletter=True)}
Binary file modified nau_openedx_extensions/locale/en/LC_MESSAGES/django.mo
Binary file not shown.
8 changes: 4 additions & 4 deletions nau_openedx_extensions/locale/en/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: [email protected]\n"
"POT-Creation-Date: 2024-10-02 13:56+0100\n"
"POT-Creation-Date: 2024-12-12 12:58-0500\n"
"PO-Revision-Date: 2021-02-15 15:56+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
Expand Down Expand Up @@ -108,18 +108,18 @@ msgstr ""
"href='https://www.nau.edu.pt/legal/consentimento-para-o-envio-de-"
"newsletters/'>consent to the sending of newsletters</a>"

#: nau_openedx_extensions/filters/pipeline.py:50
#: nau_openedx_extensions/filters/pipeline.py:51
msgid ""
"You need to activate your account before you can enroll in the course. "
"Check your {email} inbox for an account activation link from "
"{platform_name}."
msgstr ""

#: nau_openedx_extensions/filters/pipeline.py:62
#: nau_openedx_extensions/filters/pipeline.py:63
msgid "If you think this is an error, contact the course support."
msgstr ""

#: nau_openedx_extensions/filters/pipeline.py:63
#: nau_openedx_extensions/filters/pipeline.py:64
#, python-format
msgid ""
"You can't enroll on this course because your email domain is not allowed."
Expand Down
Binary file modified nau_openedx_extensions/locale/pt_PT/LC_MESSAGES/django.mo
Binary file not shown.
8 changes: 4 additions & 4 deletions nau_openedx_extensions/locale/pt_PT/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: [email protected]\n"
"POT-Creation-Date: 2024-10-02 13:56+0100\n"
"POT-Creation-Date: 2024-12-12 12:58-0500\n"
"PO-Revision-Date: 2021-02-15 15:56+0000\n"
"Last-Translator: Ivo Branco <[email protected]>\n"
"Language: pt_PT\n"
Expand Down Expand Up @@ -110,7 +110,7 @@ msgstr ""
"href='https://www.nau.edu.pt/legal/consentimento-para-o-envio-de-"
"newsletters/'>consentimento para o envio de newsletters</a>"

#: nau_openedx_extensions/filters/pipeline.py:50
#: nau_openedx_extensions/filters/pipeline.py:51
msgid ""
"You need to activate your account before you can enroll in the course. "
"Check your {email} inbox for an account activation link from "
Expand All @@ -120,11 +120,11 @@ msgstr ""
"Verifique no seu e-mail {email} o link de ativação da conta "
"{platform_name}."

#: nau_openedx_extensions/filters/pipeline.py:62
#: nau_openedx_extensions/filters/pipeline.py:63
msgid "If you think this is an error, contact the course support."
msgstr "Se achar que se trata de um erro, contacte o suporte."

#: nau_openedx_extensions/filters/pipeline.py:63
#: nau_openedx_extensions/filters/pipeline.py:64
#, python-format
msgid ""
"You can't enroll on this course because your email domain is not allowed."
Expand Down
38 changes: 37 additions & 1 deletion nau_openedx_extensions/tests/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

from django.test import TestCase
from django.test.utils import override_settings
from django_mock_queries.query import MockModel, MockSet
from opaque_keys.edx.keys import CourseKey
from openedx_filters.learning.filters import CourseEnrollmentStarted

from nau_openedx_extensions.filters.pipeline import FilterEnrollmentByDomain
from nau_openedx_extensions.filters.pipeline import FilterEnrollmentByDomain, FilterUsersWithAllowedNewsletter


class FilterEnrollmentByDomainTest(TestCase):
Expand Down Expand Up @@ -246,3 +247,38 @@ def test_inactive_user_with_email_not_in_allowed_domains(self):
"You need to activate your account before you can enroll in the course. "
"Check your [email protected] inbox for an account activation link from NAU."
))


class TestFilterUsersWithAllowedNewsletter(TestCase):
"""
Test the FilterUsersWithAllowedNewsletter class that filters users who have allowed newsletters.
"""

def test_run_filter(self):
"""
Test that the filter returns only schedules for users who have allowed newsletters.

Expected result:
- The filter returns a dictionary with the key schedules and a queryset of schedules.
- The schedules queryset has only one schedule that has a user with allow_newsletter=True.
- The other schedules that have a user with allow_newsletter=False or without allow_newsletter
are not in the queryset.
"""
mock_schedules = MockSet(
MockModel(
mock_name="allow_newsletter_true",
enrollment=MockModel(user=MockModel(nauuserextendedmodel=MockModel(allow_newsletter=True))),
),
MockModel(
mock_name="allow_newsletter_false",
enrollment=MockModel(user=MockModel(nauuserextendedmodel=MockModel(allow_newsletter=False))),
),
MockModel(mock_name="without_allow_newsletter", enrollment=MockModel(user=MockModel())),
)

result = FilterUsersWithAllowedNewsletter.run_filter(self, mock_schedules)

self.assertIsInstance(result, dict)
self.assertIn("schedules", result)
self.assertEqual(len(result["schedules"]), 1)
self.assertEqual(result["schedules"][0].mock_name, "allow_newsletter_true")
4 changes: 2 additions & 2 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ edx-opaque-keys[django]
six
future; python_version < "3.0"
web-fragments
openedx-filters==1.8.1
openedx-events==9.10.0
openedx-filters==1.12.0
openedx-events==9.15.0
14 changes: 7 additions & 7 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
#
# make upgrade
#
amqp==2.6.1 # via kombu
billiard==3.6.4.0 # via celery
celery==4.4.7 # via -c requirements/constraints.txt, -r requirements/base.in
amqp==5.2.0 # via kombu
billiard==4.2.0 # via celery
celery==5.4.0 # via -c requirements/constraints.txt, -r requirements/base.in
django==2.2.25 # via -c requirements/constraints.txt, edx-opaque-keys, openedx-filters
edx-opaque-keys[django]==2.2.0 # via -c requirements/constraints.txt, -r requirements/base.in
kombu==4.6.11 # via celery
openedx-filters==1.8.1 # via -c requirements/constraints.txt, -r requirements/base.in
openedx-events==9.10.0
kombu==5.3.4 # via celery
openedx-filters==1.12.0 # via -c requirements/constraints.txt, -r requirements/base.in
openedx-events==9.15.0
pbr==5.10.0 # via stevedore
pymongo==4.2.0 # via edx-opaque-keys
pytz==2022.2.1 # via celery, django
six==1.16.0 # via -r requirements/base.in
sqlparse==0.4.2 # via django
stevedore==4.0.0 # via edx-opaque-keys
vine==1.3.0 # via amqp, celery
vine==5.1.0 # via amqp, celery
web-fragments==2.0.0 # via -r requirements/base.in
4 changes: 2 additions & 2 deletions requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
celery<5.0
Django==2.2.25
edx-opaque-keys[django]==2.2.0
openedx-filters==1.8.1
openedx-events==9.10.0
openedx-filters==1.12.0
openedx-events==9.15.0
pip-tools<5.4
click==7.1.2
2 changes: 1 addition & 1 deletion requirements/django.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
celery==4.4.7 # via -c requirements/constraints.txt, -r requirements/base.txt
celery==5.4.0 # via -c requirements/constraints.txt, -r requirements/base.txt
django==2.2.25 # via -c requirements/constraints.txt, -r requirements/base.txt, edx-opaque-keys, openedx-filters
1 change: 1 addition & 0 deletions requirements/test.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ pycodestyle
pylint
pytest
coverage
django_mock_queries
14 changes: 8 additions & 6 deletions requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#
# make upgrade
#
amqp==2.6.1 # via -r requirements/base.txt, kombu
amqp==5.2.0 # via -r requirements/base.txt, kombu
astroid==2.12.9 # via pylint, pylint-celery
attrs==22.1.0 # via pytest
billiard==3.6.4.0 # via -r requirements/base.txt, celery
billiard==4.2.0 # via -r requirements/base.txt, celery
click-log==0.4.0 # via edx-lint
click==7.1.2 # via -c requirements/constraints.txt, click-log, code-annotations, edx-lint
code-annotations==1.3.0 # via edx-lint
Expand All @@ -18,12 +18,12 @@ edx-opaque-keys[django]==2.2.0 # via -c requirements/constraints.txt, -r requir
iniconfig==1.1.1 # via pytest
isort==5.10.1 # via pylint
jinja2==3.1.2 # via code-annotations
kombu==4.6.11 # via -r requirements/base.txt, celery
kombu==5.3.4 # via -r requirements/base.txt, celery
lazy-object-proxy==1.7.1 # via astroid
markupsafe==2.1.1 # via jinja2
mccabe==0.7.0 # via pylint
openedx-filters==1.8.1 # via -c requirements/constraints.txt, -r requirements/base.txt
openedx-events==9.10.0
openedx-filters==1.12.0 # via -c requirements/constraints.txt, -r requirements/base.txt
openedx-events==9.15.0
packaging==21.3 # via pytest
pbr==5.10.0 # via -r requirements/base.txt, stevedore
platformdirs==2.5.2 # via pylint
Expand All @@ -47,6 +47,8 @@ text-unidecode==1.3 # via python-slugify
tomli==2.0.1 # via pylint, pytest
tomlkit==0.11.4 # via pylint
typing-extensions==4.3.0 # via astroid, pylint
vine==1.3.0 # via -r requirements/base.txt, amqp, celery
vine==5.1.0 # via -r requirements/base.txt, amqp, celery
web-fragments==2.0.0 # via -r requirements/base.txt
wrapt==1.14.1 # via astroid
django_mock_queries==2.3.0 # via -r requirements/test.in
model-bakery==1.5.0 # via django_mock_queries
Loading