diff --git a/bin/push/push_remind.py b/bin/push/push_remind.py index cda1cdcc1..6182757b5 100644 --- a/bin/push/push_remind.py +++ b/bin/push/push_remind.py @@ -1,13 +1,59 @@ +import arrow import json import logging import os import requests +import sys +import emission.core.get_database as edb +import emission.storage.decorations.analysis_timeseries_queries as esda import emission.storage.decorations.user_queries as esdu +import emission.storage.timeseries.timequery as estt import emission.net.ext_service.push.notify_usage as pnu STUDY_CONFIG = os.getenv('STUDY_CONFIG', "stage-program") + +def users_without_recent_user_input(uuid_list, recent_user_input_threshold=None): + if recent_user_input_threshold is None: + logging.debug("No recent_user_input_threshold provided, returning all users") + return uuid_list + now = arrow.now() + tq = estt.TimeQuery( + "data.start_ts", + now.shift(days=-recent_user_input_threshold).int_timestamp, + now.int_timestamp + ) + filtered_uuids = [] + for user_id in uuid_list: + trips = esda.get_entries(esda.CONFIRMED_TRIP_KEY, user_id, tq) + for trip in trips: + # If the trip's user_input is blank, it will be an empty dict {} which is falsy. + # A slight caveat to this is that if the trip is partially labeled (i.e. they + # labeled 'Mode' but not 'Purpose'), it will be non-empty and will be considered + # the same as if it was fully labeled. + # I think this is fine because if a user has partially labeled a trip, they have + # already seen it and bugging them again is not likely to help. + if not trip['data']['user_input']: # empty user_input is {} which is falsy + logging.debug(f"User {user_id} has trip with no user input: {trip['_id']}") + filtered_uuids.append(user_id) + break + return filtered_uuids + + +def bin_users_by_lang(uuid_list, langs, lang_key='phone_lang'): + uuids_by_lang = {lang: [] for lang in langs} + for user_id in uuid_list: + user_profile = edb.get_profile_db().find_one({'user_id': user_id}) + user_lang = user_profile.get(lang_key) if user_profile else None + logging.debug(f"User {user_id} has phone language {user_lang}") + if user_lang not in uuids_by_lang: + logging.debug(f"{user_lang} was not one of the provided langs, defaulting to en") + user_lang = "en" + uuids_by_lang[user_lang].append(user_id) + return uuids_by_lang + + if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) logging.debug(f"STUDY_CONFIG is {STUDY_CONFIG}") @@ -20,23 +66,48 @@ if r.status_code != 200: logging.debug(f"Unable to download study config, status code: {r.status_code}") sys.exit(1) - else: - dynamic_config = json.loads(r.text) - logging.debug(f"Successfully downloaded config with version {dynamic_config['version']} "\ - f"for {dynamic_config['intro']['translated_text']['en']['deployment_name']} "\ - f"and data collection URL {dynamic_config['server']['connectUrl']}") - - if "reminderSchemes" in dynamic_config: - logging.debug("Found flexible notification configuration, skipping server-side push") - else: - uuid_list = esdu.get_all_uuids() - json_data = { - "title": "Trip labels requested", - "message": "Please label your trips for the day" - } - response = pnu.send_visible_notification_to_users(uuid_list, - json_data["title"], - json_data["message"], - json_data, - dev = False) + + dynamic_config = json.loads(r.text) + logging.debug(f"Successfully downloaded config with version {dynamic_config['version']} "\ + f"for {dynamic_config['intro']['translated_text']['en']['deployment_name']} "\ + f"and data collection URL {dynamic_config['server']['connectUrl']}") + + if "reminderSchemes" in dynamic_config: + logging.debug("Found flexible notification configuration, skipping server-side push") + sys.exit(0) + + # get push notification config (if not present in dynamic_config, use default) + push_config = dynamic_config.get('push_notifications', { + "title": { + "en": "Trip labels requested", + "es": "Etiquetas de viaje solicitadas", + }, + "message": { + "en": "Please label your recent trips", + "es": "Por favor etiquete sus viajes recientes", + }, + "recent_user_input_threshold": 7, # past week + }) + + # filter users based on recent user input and bin by language + filtered_uuids = users_without_recent_user_input( + esdu.get_all_uuids(), + push_config.get('recent_user_input_threshold') + ) + filtered_uuids_by_lang = bin_users_by_lang(filtered_uuids, push_config['title'].keys()) + # for each language, send a push notification to the selected users in that language + for lang, uuids_to_notify in filtered_uuids_by_lang.items(): + if len(uuids_to_notify) == 0: + logging.debug(f"No users to notify in lang {lang}") + continue + logging.debug(f"Sending push notifications to {len(uuids_to_notify)} users in lang {lang}") + json_data = { + "title": push_config["title"][lang], + "message": push_config["message"][lang], + } + response = pnu.send_visible_notification_to_users(uuids_to_notify, + json_data["title"], + json_data["message"], + json_data, + dev = False)