Skip to content
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 devsite/requirements/ginkgo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ django-webpack-loader==0.4.1
# appsembler/gingko/master users 0.4.1

django-model-utils==2.3.1

django-celery==3.2.1
jsonfield==1.0.3 # Version used in Ginkgo. Hawthorn uses version 2.0.2

##
Expand Down
38 changes: 34 additions & 4 deletions figures/mau.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
This module provides MAU metrics retrieval functionality
"""

from datetime import datetime
from datetime import datetime, timedelta
from django.utils.timezone import utc

from figures.compat import RELEASE_LINE

from figures.models import CourseMauMetrics, SiteMauMetrics
from figures.sites import (
Expand Down Expand Up @@ -84,11 +87,38 @@ def mau_1g_for_month_as_of_day(sm_queryset, date_for):
This function queries `courseware.models.StudentModule` to identify users
who are active in the site

Retrieves records based on date of the `StudentModule.modified` field
Returns a queryset of distinct user ids
"""
month_sm = sm_queryset.filter(modified__year=date_for.year,
modified__month=date_for.month,
modified__day__lte=date_for.day)

# TODO: Remove this `if` branch after dropping Ginkgo support.
if RELEASE_LINE == 'ginkgo':
# Django 1.8 appears not to support 'lte' on the 'day' of a datetime
# Therefore we have to get records within a range
start_date = datetime(year=date_for.year,
month=date_for.month,
day=1).replace(tzinfo=utc)
# We do this in case 'date_for' is at the end of the month and get
# the 'day_after' as midnight of the next day so we can use '__lt'. If
# we simply used '__lte', then we exclude any events that happened on
# the 'date_for' in hours after the 'date_for' hours.

# temporary var as we don't know
day_after_temp = date_for + timedelta(days=1)
day_after = datetime(year=day_after_temp.year,
month=day_after_temp.month,
day=day_after_temp.day).replace(tzinfo=utc)

# We don't use 'dict(modified__range=[start_date, date_for])' because
# doing "__lt" for 0:00 hour tne next day means we don't have to worry
# about fractions of a second on the last second of the last day
filter_args = dict(modified__gte=start_date, modified__lt=day_after)
else:
filter_args = dict(modified__year=date_for.year,
modified__month=date_for.month,
modified__day__lte=date_for.day)

month_sm = sm_queryset.filter(**filter_args)
return month_sm.values('student__id').distinct()


Expand Down
41 changes: 20 additions & 21 deletions tests/test_mau.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@

from datetime import datetime
from freezegun import freeze_time
from dateutil.relativedelta import relativedelta
import pytest

from django.utils.timezone import utc

from courseware.models import StudentModule

from figures.helpers import as_datetime, as_date
from figures.sites import (
get_student_modules_for_site,
get_student_modules_for_course_in_site,
Expand All @@ -21,7 +19,6 @@
)

from tests.factories import StudentModuleFactory
from tests.helpers import OPENEDX_RELEASE, GINKGO


def test_get_mau_from_site_course(sm_test_data):
Expand Down Expand Up @@ -56,8 +53,6 @@ def test_get_mau_from_sm_for_site(sm_test_data):
assert set(users) == set(sm_check)


@pytest.mark.skipif(OPENEDX_RELEASE == GINKGO,
reason='Broken test. Apparent Django 1.8 incompatibility')
@pytest.mark.django_db
def test_mau_1g_for_month_as_of_day_first_day_next_month(db):
"""
Expand All @@ -68,31 +63,35 @@ def test_mau_1g_for_month_as_of_day_first_day_next_month(db):
We want to make sure we get the right records when the query happens on the
first day of the next month. So we do the following

* Add a StudentModule record for two months before
* Add at least one StudentModule record for the month we want
* Add at least one StudentModule record for after the month we want
* Add StudentModule records for the month before we want to capture records
* Add StudentModule records for the month we want to capture records
* Add StudentModule records for the month after we want to capture records

This sets up the scenario that we run the daily pipeline to capture MAU
"as of" yesterday (the last day of the previous month) to capture MAU for
the previous month
the previous month and not capture any records before the previous month,
nor capture records for the "current month"
"""
mock_today = datetime(year=2020, month=4, day=1).replace(tzinfo=utc)
month_before = datetime(year=2020, month=2, day=2).replace(tzinfo=utc)
in_dates = [datetime(year=2020, month=3, day=1).replace(tzinfo=utc),
datetime(year=2020, month=3, day=15).replace(tzinfo=utc),
datetime(year=2020, month=3, day=31).replace(tzinfo=utc)]
date_for = mock_today.date() - relativedelta(days=1)

# Create a student module in the month before, and in month after
StudentModuleFactory(created=month_before, modified=month_before)
StudentModuleFactory(created=mock_today, modified=mock_today)
month_before = [as_datetime('2020-02-02'), as_datetime('2020-02-29')]
month_after = [as_datetime('2020-04-01'), as_datetime('2020-04-01 12:00')]
in_month = [as_datetime('2020-03-01'),
as_datetime('2020-03-15'),
as_datetime('2020-03-31'),
as_datetime('2020-03-31 12:00')]
date_for = as_date('2020-03-31')

# Create student modules for the month before, month after, and in the
# month for which we want to retrieve records
[StudentModuleFactory(created=dt, modified=dt) for dt in month_before]
[StudentModuleFactory(created=dt, modified=dt) for dt in month_after]
sm_in = [StudentModuleFactory(created=rec,
modified=rec) for rec in in_dates]
modified=rec) for rec in in_month]
expected_user_ids = [obj.student_id for obj in sm_in]

sm_queryset = StudentModule.objects.all()
user_ids = mau_1g_for_month_as_of_day(sm_queryset=sm_queryset,
date_for=date_for)
assert len(user_ids) == len(in_month)
assert set([rec['student__id'] for rec in user_ids]) == set(expected_user_ids)


Expand Down