Skip to content

Commit

Permalink
refactor metrics_summaries to support any type of label
Browse files Browse the repository at this point in the history
These functions can be made more generic to support 'purpose' or 'replaced_mode' rather than just 'mode'
  • Loading branch information
JGreenlee committed May 17, 2024
1 parent aa17c6a commit c388fe9
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 15 deletions.
54 changes: 39 additions & 15 deletions src/emcommon/metrics/metrics_summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,39 @@


# @memoize
def labeled_mode_for_trip(composite_trip: dict, trip_labels_map: dict[str, any]) -> str:
def label_for_trip(composite_trip: dict, label_key: str, trip_labels_map: dict[str, any] = None) -> str:
"""
:param composite_trip: composite trip
:param label_key: which type of label to get ('mode', 'purpose', or 'replaced_mode')
:param trip_labels_map: trip labels map
:return: labeled mode for the trip, derived from the trip's user_input if available, or the trip_labels_map if available, or 'unlabeled' otherwise
:return: the label for the trip, derived from the trip's user_input if available, or the trip_labels_map if available, or 'unlabeled' otherwise
"""
label_key = label_key.upper()
label_key_confirm = label_key.lower() + '_confirm'
UNLABELED = 'unlabeled'
if not composite_trip:
return UNLABELED
if 'user_input' in composite_trip and 'mode_confirm' in composite_trip['user_input']:
return composite_trip['user_input']['mode_confirm']
if 'user_input' in composite_trip and label_key_confirm in composite_trip['user_input']:
return composite_trip['user_input'][label_key_confirm]
if trip_labels_map and composite_trip['_id']['$oid'] in trip_labels_map:
if 'MODE' in trip_labels_map[composite_trip['_id']['$oid']]:
return trip_labels_map[composite_trip['_id']['$oid']]['MODE']['data']['label']
if label_key_upper in trip_labels_map[composite_trip['_id']['$oid']]:
return trip_labels_map[composite_trip['_id']['$oid']][label_key_upper]['data']['label']
return UNLABELED

def labeled_purpose_for_trip(composite_trip: dict, trip_labels_map: dict[str, any] = None) -> str:
"""
:param composite_trip: composite trip
:param trip_labels_map: trip labels map
:return: labeled purpose for the trip, derived from the trip's user_input if available, or the trip_labels_map if available, or 'unlabeled' otherwise
"""
UNLABELED = 'unlabeled'
if not composite_trip:
return UNLABELED
if 'user_input' in composite_trip and 'purpose_confirm' in composite_trip['user_input']:
return composite_trip['user_input']['purpose_confirm']
if trip_labels_map and composite_trip['_id']['$oid'] in trip_labels_map:
if 'PURPOSE' in trip_labels_map[composite_trip['_id']['$oid']]:
return trip_labels_map[composite_trip['_id']['$oid']]['PURPOSE']['data']['label']
return UNLABELED


Expand All @@ -26,7 +45,7 @@ def generate_summaries(metrics: list[str], composite_trips: list, trip_labels_ma
return {metric: get_summary_for_metric(metric, composite_trips, trip_labels_map) for metric in metrics}


def value_of_metric_for_trip(metric: str, trip: dict, trip_labels_map: dict[str, any]):
def value_of_metric_for_trip(metric: str, trip: dict):
if metric == 'distance':
return trip['distance']
elif metric == 'count':
Expand All @@ -36,7 +55,7 @@ def value_of_metric_for_trip(metric: str, trip: dict, trip_labels_map: dict[str,
return None


def get_summary_for_metric(metric: str, composite_trips: list, trip_labels_map: dict[str, any]):
def get_summary_for_metric(metric: str, composite_trips: list, trip_labels_map: dict[str, any] = None):
days_of_metrics_data = {}
for trip in composite_trips:
date = trip['start_fmt_time'].split('T')[0]
Expand All @@ -54,19 +73,24 @@ def get_summary_for_metric(metric: str, composite_trips: list, trip_labels_map:
days_summaries.append(summary_for_day)
return days_summaries


def metric_summary_by_mode(metric: str, composite_trips: list, trip_labels_map: dict[str, any]):
def metric_summary_by_mode(metric: str, composite_trips: list, trip_labels_map = None):
"""
:param composite_trips: list of composite trips
:return: a dict of mode keys to the metric total for that mode
"""
grouping_fields = {
'mode_confirm': lambda trip: label_for_trip(trip, 'mode', trip_labels_map),
'purpose_confirm': lambda trip: label_for_trip(trip, 'purpose', trip_labels_map),
'replaced_mode_confirm': lambda trip: label_for_trip(trip, 'replaced_mode', trip_labels_map),
}

mode_to_metric_map = {}
if not composite_trips:
return mode_to_metric_map
for trip in composite_trips:
mode_key = 'mode_' + labeled_mode_for_trip(trip, trip_labels_map)
if mode_key not in mode_to_metric_map:
mode_to_metric_map[mode_key] = 0
mode_to_metric_map[mode_key] += value_of_metric_for_trip(
metric, trip, trip_labels_map)
for grouping_field, field_for_trip_fn in grouping_fields.items():
grouping_key = grouping_field + '_' + field_for_trip_fn(trip)
if grouping_key not in mode_to_metric_map:
mode_to_metric_map[grouping_key] = 0
mode_to_metric_map[grouping_key] += value_of_metric_for_trip(metric, trip)
return mode_to_metric_map
15 changes: 15 additions & 0 deletions src/emcommon/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,18 @@ def wrapper(*args, **kwargs):
return wrapper

# __pragma__('nokwargs')


# e-mission-phone www/js/diary/timelineHelper.ts unpackServerData()
def flatten_db_entry(entry: dict) -> dict:
'''
DB entries retrieved from the server have '_id', 'metadata', and 'data' fields.
This function returns a shallow copy of the obj, which flattens the 'data' field into the top
level, while also including '_id', 'metadata.key', and 'metadata.origin_key'.
'''
return {
**entry['data'],
'_id': entry['_id'],
'key': entry['metadata']['key'],
'origin_key': entry['metadata']['origin_key'] if 'origin_key' in entry['metadata'] else entry['metadata']['key']
}

0 comments on commit c388fe9

Please sign in to comment.