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

Add ol-django olposthog app #2211

Merged
merged 16 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ SENTRY_DSN=
MITX_ONLINE_BASE_URL=http://mitxonline.odl.local:8013
MITX_ONLINE_ADMIN_CLIENT_ID=refine-local-client-id
MITX_ONLINE_ADMIN_BASE_URL=http://mitxonline.odl.local:8016
POSTHOG_API_TOKEN=
POSTHOG_PROJECT_API_KEY=
POSTHOG_API_HOST=https://app.posthog.com/
HUBSPOT_HOME_PAGE_FORM_GUID=
HUBSPOT_PORTAL_ID=
4 changes: 2 additions & 2 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
"filename": "main/settings.py",
"hashed_secret": "09edaaba587f94f60fbb5cee2234507bcb883cc2",
"is_verified": false,
"line_number": 954
"line_number": 958
}
],
"pants": [
Expand Down Expand Up @@ -247,5 +247,5 @@
}
]
},
"generated_at": "2024-05-13T13:11:36Z"
"generated_at": "2024-05-15T14:13:55Z"
}
14 changes: 13 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,19 @@
"description": "API host for PostHog",
"required": false
},
"POSTHOG_API_TOKEN": {
"POSTHOG_ENABLED": {
"description": "API host for PostHog",
"required": false
},
"POSTHOG_FEATURE_FLAG_REQUEST_TIMEOUT_MS": {
"description": "Timeout(MS) for PostHog feature flag requests.",
"required": false
},
"POSTHOG_MAX_RETRIES": {
"description": "Number of times that requests to PostHog should be retried after failing.",
"required": false
},
"POSTHOG_PROJECT_API_KEY": {
"description": "API token to communicate with PostHog",
"required": false
},
Expand Down
9 changes: 5 additions & 4 deletions cms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from django.utils.functional import cached_property
from django.utils.text import slugify
from mitol.common.utils.datetime import now_in_utc
from mitol.olposthog.features import is_enabled
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import FieldPanel, InlinePanel, PageChooserPanel
from wagtail.blocks import PageChooserBlock, StreamBlock
Expand Down Expand Up @@ -826,22 +827,22 @@ def get_context(self, request, *args, **kwargs): # noqa: ARG002
request.session["anonymous_session_id"] = str(uuid.uuid4())
user = request.session["anonymous_session_id"]

show_new_featured_carousel = features.is_enabled(
show_new_featured_carousel = is_enabled(
features.ENABLE_NEW_HOME_PAGE_FEATURED,
False, # noqa: FBT003
user,
)
show_new_design_hero = features.is_enabled(
show_new_design_hero = is_enabled(
features.ENABLE_NEW_HOME_PAGE_HERO,
False, # noqa: FBT003
user,
)
show_home_page_video_component = features.is_enabled(
show_home_page_video_component = is_enabled(
features.ENABLE_NEW_HOME_PAGE_VIDEO,
False, # noqa: FBT003
user,
)
show_home_page_contact_form = features.is_enabled(
show_home_page_contact_form = is_enabled(
features.ENABLE_NEW_HOME_PAGE_CONTACT_FORM,
False, # noqa: FBT003
user,
Expand Down
11 changes: 7 additions & 4 deletions cms/models_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.urls import resolve
from mitol.common.factories import UserFactory
from mitol.common.utils.datetime import now_in_utc
from mitol.olposthog.features import is_enabled

from cms.constants import CMS_EDITORS_GROUP_NAME
from cms.factories import (
Expand Down Expand Up @@ -44,7 +45,6 @@
from flexiblepricing.constants import FlexiblePriceStatus
from flexiblepricing.factories import FlexiblePriceFactory, FlexiblePriceTierFactory
from flexiblepricing.models import FlexiblePrice
from main import features

pytestmark = [pytest.mark.django_db]

Expand All @@ -57,7 +57,10 @@ def test_resource_page_site_name(settings, mocker):
"""
settings.SITE_NAME = "a site's name"
page = ResourcePageFactory.create()
assert page.get_context(mocker.Mock())["site_name"] == settings.SITE_NAME
rf = RequestFactory()
request = rf.get("/")
mocker.patch("cms.models.get_base_context")
assert page.get_context(request)["site_name"] == settings.SITE_NAME


def test_custom_detail_page_urls(fully_configured_wagtail):
Expand Down Expand Up @@ -168,12 +171,12 @@ def test_course_page_context( # noqa: PLR0913
member.linked_instructor_page
for member in course_page.linked_instructors.order_by("order").all()
],
"new_design": features.is_enabled(
"new_design": is_enabled(
"mitxonline-new-product-page",
False, # noqa: FBT003
request.user.id if request.user.is_authenticated else "anonymousUser",
),
"new_footer": features.is_enabled(
"new_footer": is_enabled(
"mitxonline-new-footer",
False, # noqa: FBT003
request.user.id if request.user.is_authenticated else "anonymousUser",
Expand Down
3 changes: 2 additions & 1 deletion courses/serializers/v1/courses.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from mitol.olposthog.features import is_enabled
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

Expand Down Expand Up @@ -146,7 +147,7 @@ def create(self, validated_data):
successful_enrollments, edx_request_success = create_run_enrollments(
user,
[run],
keep_failed_enrollments=features.is_enabled(features.IGNORE_EDX_FAILURES),
keep_failed_enrollments=is_enabled(features.IGNORE_EDX_FAILURES),
)
return successful_enrollments

Expand Down
3 changes: 2 additions & 1 deletion courses/serializers/v2/courses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import logging

from mitol.olposthog.features import is_enabled
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

Expand Down Expand Up @@ -158,7 +159,7 @@ def create(self, validated_data):
successful_enrollments, edx_request_success = create_run_enrollments(
user,
[run],
keep_failed_enrollments=features.is_enabled(features.IGNORE_EDX_FAILURES),
keep_failed_enrollments=is_enabled(features.IGNORE_EDX_FAILURES),
)
return successful_enrollments

Expand Down
9 changes: 5 additions & 4 deletions courses/views/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django_filters.rest_framework import DjangoFilterBackend
from mitol.olposthog.features import is_enabled
from requests import ConnectionError as RequestsConnectionError
from requests.exceptions import HTTPError
from rest_framework import mixins, status, viewsets
Expand Down Expand Up @@ -277,7 +278,7 @@ def create_enrollment_view(request):
_, edx_request_success = create_run_enrollments(
user=user,
runs=[run],
keep_failed_enrollments=features.is_enabled(features.IGNORE_EDX_FAILURES),
keep_failed_enrollments=is_enabled(features.IGNORE_EDX_FAILURES),
)

def respond(data, status=True): # noqa: FBT002
Expand All @@ -290,7 +291,7 @@ def respond(data, status=True): # noqa: FBT002

return HttpResponseRedirect(data)

if edx_request_success or features.is_enabled(features.IGNORE_EDX_FAILURES):
if edx_request_success or is_enabled(features.IGNORE_EDX_FAILURES):
resp = respond(reverse("user-dashboard"))
cookie_value = {
"type": USER_MSG_TYPE_ENROLLED,
Expand Down Expand Up @@ -362,7 +363,7 @@ def get_serializer_context(self):
return {"user": self.request.user}

def list(self, request, *args, **kwargs):
if features.is_enabled(features.SYNC_ON_DASHBOARD_LOAD):
if is_enabled(features.SYNC_ON_DASHBOARD_LOAD):
try:
sync_enrollments_with_edx(self.request.user)
except Exception: # pylint: disable=broad-except
Expand All @@ -374,7 +375,7 @@ def destroy(self, request, *args, **kwargs): # noqa: ARG002
deactivated_enrollment = deactivate_run_enrollment(
enrollment,
change_status=ENROLL_CHANGE_STATUS_UNENROLLED,
keep_failed_enrollments=features.is_enabled(features.IGNORE_EDX_FAILURES),
keep_failed_enrollments=is_enabled(features.IGNORE_EDX_FAILURES),
)
if deactivated_enrollment is None:
return Response(status=status.HTTP_400_BAD_REQUEST)
Expand Down
1 change: 1 addition & 0 deletions courses/views/v1/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ def test_user_enrollments_list_sync(
If the appropriate feature flag is turned on, the enrollments list API call should sync enrollments with
"""
settings.FEATURES[features.SYNC_ON_DASHBOARD_LOAD] = sync_dashboard_flag
settings.POSTHOG_ENABLED = False
patched_sync = mocker.patch(
"courses.views.v1.sync_enrollments_with_edx",
)
Expand Down
2 changes: 2 additions & 0 deletions main/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ class RootConfig(AppConfig):

def ready(self):
from mitol.common import envs
from mitol.olposthog.features import configure

envs.validate()
configure()
63 changes: 0 additions & 63 deletions main/features.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
"""MITxOnline feature flags"""

import os
from functools import wraps

from django.conf import settings

IGNORE_EDX_FAILURES = "IGNORE_EDX_FAILURES"
SYNC_ON_DASHBOARD_LOAD = "SYNC_ON_DASHBOARD_LOAD"
ENABLE_NEW_DESIGN = "mitxonline-new-product-page"
Expand All @@ -15,61 +10,3 @@
ENABLE_NEW_HOME_PAGE_CONTACT_FORM = "mitxonline-new-home-page-contact-form"
ENABLE_NEW_FOOTER = "mitxonline-new-footer"
ENABLE_AUTO_DAILY_FEATURED_ITEMS = "mitxonline-auto-daily-featured-items"


def is_enabled(name, default=None, unique_id=settings.HOSTNAME):
"""
Returns True if the feature flag is enabled

Args:
name (str): feature flag name
default (bool): default value if not set in settings
unique_id (str): person identifier passed back to posthog which is the display value for person. I recommend
this be user.id for logged-in users to allow for more readable user flags as well as more clear
troubleshooting. For anonymous users, a persistent ID will help with troubleshooting and
tracking efforts.

Returns:
bool: True if the feature flag is enabled
"""

if "IN_TEST_SUITE" not in os.environ:
import posthog
else:
posthog = None
return (
posthog
and posthog.get_feature_flag(
name,
unique_id,
person_properties={
"environment": settings.ENVIRONMENT,
"user_id": unique_id,
},
)
) or settings.FEATURES.get(name, default or settings.FEATURES_DEFAULT)


def if_feature_enabled(name, default=None):
"""
Wrapper that results in a no-op if the given feature isn't enabled, and otherwise
runs the wrapped function as normal.

Args:
name (str): Feature flag name
default (bool): default value if not set in settings
"""

def if_feature_enabled_inner(func): # pylint: disable=missing-docstring
@wraps(func)
def wrapped_func(*args, **kwargs): # pylint: disable=missing-docstring
if not is_enabled(name, default):
# If the given feature name is not enabled, do nothing (no-op).
return None
else:
# If the given feature name is enabled, call the function and return as normal.
return func(*args, **kwargs)

return wrapped_func

return if_feature_enabled_inner
30 changes: 24 additions & 6 deletions main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import cssutils
import dj_database_url
import posthog
from celery.schedules import crontab
from django.core.exceptions import ImproperlyConfigured
from mitol.common.envs import (
Expand Down Expand Up @@ -217,6 +216,7 @@
"mitol.mail.apps.MailApp",
"mitol.authentication.apps.TransitionalAuthenticationApp",
"mitol.payment_gateway.apps.PaymentGatewayApp",
"mitol.olposthog.apps.OlPosthog",
# "mitol.oauth_toolkit_extensions.apps.OAuthToolkitExtensionsApp",
)
# Only include the seed data app if this isn't running in prod
Expand Down Expand Up @@ -896,6 +896,10 @@
"LOCATION": CELERY_BROKER_URL,
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
},
"durable": {
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
"LOCATION": "durable_cache",
},
}

AUTHENTICATION_BACKENDS = (
Expand Down Expand Up @@ -1153,8 +1157,8 @@
)

# PostHog related settings
POSTHOG_API_TOKEN = get_string(
name="POSTHOG_API_TOKEN",
POSTHOG_PROJECT_API_KEY = get_string(
name="POSTHOG_PROJECT_API_KEY",
default="",
description="API token to communicate with PostHog",
)
Expand All @@ -1164,9 +1168,23 @@
default="",
description="API host for PostHog",
)
if "IN_TEST_SUITE" not in os.environ:
posthog.api_key = POSTHOG_API_TOKEN
posthog.host = POSTHOG_API_HOST
POSTHOG_FEATURE_FLAG_REQUEST_TIMEOUT_MS = get_int(
name="POSTHOG_FEATURE_FLAG_REQUEST_TIMEOUT_MS",
default=3000,
description="Timeout(MS) for PostHog feature flag requests.",
)

POSTHOG_MAX_RETRIES = get_int(
name="POSTHOG_MAX_RETRIES",
default=3,
description="Number of times that requests to PostHog should be retried after failing.",
)

POSTHOG_ENABLED = get_bool(
name="POSTHOG_ENABLED",
default=False,
description="API host for PostHog",
)

# HomePage Hubspot Form Settings
HUBSPOT_HOME_PAGE_FORM_GUID = get_string(
Expand Down
2 changes: 1 addition & 1 deletion main/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def get_js_settings(request: HttpRequest): # noqa: ARG001
"support_email": settings.EMAIL_SUPPORT,
"site_name": settings.SITE_NAME,
"features": {},
"posthog_api_token": settings.POSTHOG_API_TOKEN,
"posthog_api_token": settings.POSTHOG_PROJECT_API_KEY,
"posthog_api_host": settings.POSTHOG_API_HOST,
}

Expand Down
2 changes: 1 addition & 1 deletion main/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_get_js_settings(settings, rf):
"support_email": settings.EMAIL_SUPPORT,
"site_name": settings.SITE_NAME,
"features": {},
"posthog_api_token": settings.POSTHOG_API_TOKEN,
"posthog_api_token": settings.POSTHOG_PROJECT_API_KEY,
"posthog_api_host": settings.POSTHOG_API_HOST,
}

Expand Down
5 changes: 3 additions & 2 deletions main/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.template.loader import render_to_string
from django.urls import reverse
from django.views.decorators.cache import never_cache
from mitol.olposthog.features import is_enabled
from rest_framework.pagination import LimitOffsetPagination

from main import features
Expand All @@ -19,12 +20,12 @@ def get_base_context(request):
Returns the template context key/values needed for the base template and all templates that extend it
"""
context = {
"new_design": features.is_enabled(
"new_design": is_enabled(
features.ENABLE_NEW_DESIGN,
False, # noqa: FBT003
request.user.id if request.user.is_authenticated else "anonymousUser",
),
"new_footer": features.is_enabled(
"new_footer": is_enabled(
features.ENABLE_NEW_FOOTER,
False, # noqa: FBT003
request.user.id if request.user.is_authenticated else "anonymousUser",
Expand Down
Loading
Loading