Skip to content

Commit

Permalink
Merge pull request #33852 from proteusvacuum/fr/yearly-active-mobile-…
Browse files Browse the repository at this point in the history
…users

New Metric for CommCareHQ Calculated Properties: 365 Active Mobile Workers
  • Loading branch information
snopoke authored Dec 15, 2023
2 parents a15eda2 + 6fc82f4 commit f77ae77
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 20 deletions.
46 changes: 28 additions & 18 deletions corehq/apps/domain/calculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from dateutil.relativedelta import relativedelta

from corehq.apps.users.role_utils import get_custom_roles_for_domain
from couchforms.analytics import (
domain_has_submission_in_last_30_days,
get_first_form_submission_received,
Expand Down Expand Up @@ -42,6 +41,7 @@
get_web_user_count,
)
from corehq.apps.users.models import CouchUser, UserRole
from corehq.apps.users.role_utils import get_custom_roles_for_domain
from corehq.apps.users.util import WEIRD_USER_IDS
from corehq.messaging.scheduling.util import domain_has_reminders
from corehq.motech.repeaters.models import Repeater
Expand All @@ -60,13 +60,20 @@ def num_mobile_users(domain, *args):
DISPLAY_DATE_FORMAT = '%Y/%m/%d %H:%M:%S'


def active_mobile_users(domain, *args):
def active_mobile_users(domain, days=30):
return _mobile_users(domain, int(days), inactive=False)


def inactive_mobile_users(domain, days=30):
return _mobile_users(domain, int(days), inactive=True)


def _mobile_users(domain, days=30, inactive=False):
"""
Returns the number of mobile users who have submitted a form or SMS in the
last 30 days
Returns the number of mobile users who have submitted a form or SMS
"""
now = datetime.utcnow()
then = (now - timedelta(days=30))
then = (now - timedelta(days=days))

user_ids = get_mobile_users(domain)

Expand Down Expand Up @@ -94,7 +101,7 @@ def active_mobile_users(domain, *args):
)

num_users = len(form_users | sms_users)
return num_users if 'inactive' not in args else len(user_ids) - num_users
return num_users if not inactive else len(user_ids) - num_users


def cases(domain, *args):
Expand Down Expand Up @@ -243,13 +250,13 @@ def not_implemented(domain, *args):

CALC_ORDER = [
'num_web_users', 'num_mobile_users', 'forms', 'cases',
'mobile_users--active', 'mobile_users--inactive', 'active_cases',
'cases_in_last--30', 'cases_in_last--60', 'cases_in_last--90',
'cases_in_last--120', 'active', 'first_form_submission',
'last_form_submission', 'has_app', 'web_users', 'active_apps',
'uses_reminders', 'sms--I', 'sms--O', 'sms_in_last', 'sms_in_last--30',
'sms_in_last_bool', 'sms_in_last_bool--30', 'sms_in_in_last--30',
'sms_out_in_last--30',
'active_mobile_users', 'inactive_mobile_users', 'active_mobile_users--365',
'active_cases', 'cases_in_last--30', 'cases_in_last--60',
'cases_in_last--90', 'cases_in_last--120', 'active',
'first_form_submission', 'last_form_submission', 'has_app', 'web_users',
'active_apps', 'uses_reminders', 'sms--I', 'sms--O', 'sms_in_last',
'sms_in_last--30', 'sms_in_last_bool', 'sms_in_last_bool--30',
'sms_in_in_last--30', 'sms_out_in_last--30',
]

CALCS = {
Expand All @@ -265,8 +272,9 @@ def not_implemented(domain, *args):
'sms_in_in_last--30': "# incoming SMS in last 30 days",
'sms_out_in_last--30': "# outgoing SMS in last 30 days",
'cases': "# cases",
'mobile_users--active': "# active mobile users",
'mobile_users--inactive': "# inactive mobile users",
'active_mobile_users': "# active mobile users in last 30 days",
'inactive_mobile_users': "# inactive mobile users in last 30 days",
'active_mobile_users--365': "# active mobile users in last 365 days",
'active_cases': "# active cases",
'cases_in_last--30': "# cases seen last 30 days",
'cases_in_last--60': "# cases seen last 60 days",
Expand All @@ -278,7 +286,7 @@ def not_implemented(domain, *args):
'has_app': "Has App",
'web_users': "list of web users",
'active_apps': "list of active apps",
'uses_reminders': "uses reminders"
'uses_reminders': "uses reminders",
}

CALC_FNS = {
Expand All @@ -293,7 +301,8 @@ def not_implemented(domain, *args):
"sms_in_in_last": sms_in_in_last,
"sms_out_in_last": sms_out_in_last,
"cases": cases,
"mobile_users": active_mobile_users,
"active_mobile_users": active_mobile_users,
"inactive_mobile_users": inactive_mobile_users,
"active_cases": not_implemented,
"cases_in_last": cases_in_last,
"inactive_cases_in_last": inactive_cases_in_last,
Expand Down Expand Up @@ -342,7 +351,8 @@ def calced_props(domain_obj, id, all_stats):
return {
"_id": id,
"cp_n_web_users": int(all_stats["web_users"].get(dom, 0)),
"cp_n_active_cc_users": int(CALC_FNS["mobile_users"](dom)),
"cp_n_active_cc_users": int(CALC_FNS["active_mobile_users"](dom)),
"cp_n_active_cc_users_365_days": int(CALC_FNS["active_mobile_users"](dom, 365)),
"cp_n_cc_users": int(all_stats["commcare_users"].get(dom, 0)),
"cp_n_active_cases": int(CALC_FNS["cases_in_last"](dom, 120)),
"cp_n_users_submitted_form": total_distinct_users(dom),
Expand Down
28 changes: 26 additions & 2 deletions corehq/apps/domain/tests/test_domain_calculated_properties.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import datetime
import json
import uuid
from datetime import datetime, timedelta

from django.test import TestCase

from dimagi.utils.parsing import json_format_datetime

from corehq.apps.domain.calculations import (
active_mobile_users,
all_domain_stats,
calced_props,
get_sms_count,
Expand All @@ -18,6 +20,8 @@
from corehq.apps.es.tests.utils import es_test
from corehq.apps.es.users import user_adapter
from corehq.apps.sms.models import INCOMING, OUTGOING
from corehq.apps.users.models import CommCareUser
from corehq.form_processor.tests.utils import create_form_for_test


@es_test(requires=[case_adapter, form_adapter, sms_adapter, user_adapter])
Expand All @@ -36,7 +40,7 @@ def setUp(self):
'_id': 'some_sms_id',
'domain': self.domain.name,
'direction': INCOMING,
'date': json_format_datetime(datetime.datetime.utcnow()),
'date': json_format_datetime(datetime.utcnow()),
}
sms_adapter.index(sms_doc, refresh=True)

Expand Down Expand Up @@ -64,3 +68,23 @@ def test_sms_count(self):
def test_days_as_str_is_valid(self):
count = get_sms_count(self.domain.name, days='30')
self.assertEqual(count, 1)


class MobileWorkerCountTest(BaseCalculatedPropertiesTest):

def test_yearly_mobile_worker_counts(self):
yesterday = datetime.utcnow() - timedelta(days=1)
last_year = datetime.utcnow() - timedelta(days=364)

for date in [yesterday, last_year]:
for user_number in range(5):
user = CommCareUser.create(self.domain.name, str(uuid.uuid4()), "123", None, None)
form = create_form_for_test(self.domain.name, received_on=date, user_id=user.user_id).to_json()
form['form']['meta']['userID'] = user.user_id
form['form']['meta']['username'] = user.username

user_adapter.index(user, refresh=True)
form_adapter.index(form, refresh=True)

self.assertEqual(active_mobile_users(self.domain.name), 5)
self.assertEqual(active_mobile_users(self.domain.name, '365'), 10)

0 comments on commit f77ae77

Please sign in to comment.