|
1 | 1 | from collections import defaultdict |
| 2 | +from functools import partial |
2 | 3 | from typing import Any, Iterable |
3 | 4 |
|
4 | 5 | from django.http import HttpRequest |
5 | 6 | from influxdb_client.client.flux_table import FluxTable |
6 | 7 | from pydantic import Field, create_model |
7 | 8 | from pydantic.type_adapter import TypeAdapter |
8 | 9 |
|
9 | | -from app_analytics.constants import TRACK_HEADERS |
| 10 | +from app_analytics.constants import LABELS, TRACK_HEADERS |
10 | 11 | from app_analytics.dataclasses import FeatureEvaluationData, UsageData |
11 | 12 | from app_analytics.models import FeatureEvaluationRaw, Resource |
12 | 13 | from app_analytics.types import ( |
|
29 | 30 | ) |
30 | 31 | _labels_type_adapter: TypeAdapter[Labels] = TypeAdapter(Labels) |
31 | 32 |
|
| 33 | +map_influx_record_values_to_labels = partial( |
| 34 | + _labels_type_adapter.dump_python, |
| 35 | + include=set(LABELS), |
| 36 | +) |
| 37 | + |
32 | 38 |
|
33 | 39 | def map_annotated_api_usage_buckets_to_usage_data( |
34 | 40 | api_usage_buckets: Iterable[AnnotatedAPIUsageBucket], |
@@ -63,25 +69,43 @@ def map_annotated_api_usage_buckets_to_usage_data( |
63 | 69 | def map_flux_tables_to_usage_data( |
64 | 70 | flux_tables: list[FluxTable], |
65 | 71 | ) -> list[UsageData]: |
66 | | - return [ |
67 | | - UsageData( |
68 | | - day=(values := record.values)["_time"].strftime("%Y-%m-%d"), |
69 | | - labels=_labels_type_adapter.validate_python(values), |
70 | | - **{values["resource"]: values["_value"]}, |
71 | | - ) |
72 | | - for flux_table in flux_tables |
73 | | - for record in flux_table.records |
74 | | - ] |
| 72 | + """ |
| 73 | + Aggregates API usage data buckets by date and labels. |
| 74 | + Each resulting `UsageData` object contains the total count for each resource |
| 75 | + for that date and labels combination. |
| 76 | + """ |
| 77 | + data_by_key: dict[AnnotatedAPIUsageKey, UsageData] = {} |
| 78 | + for flux_table in flux_tables: |
| 79 | + for record in flux_table.records: |
| 80 | + values = record.values |
| 81 | + date = values["_time"].date() |
| 82 | + labels: Labels = map_influx_record_values_to_labels(values) |
| 83 | + key = AnnotatedAPIUsageKey( |
| 84 | + date=date, |
| 85 | + labels=tuple(labels.items()), |
| 86 | + ) |
| 87 | + if key not in data_by_key: |
| 88 | + data_by_key[key] = UsageData( |
| 89 | + day=date, |
| 90 | + labels=labels, |
| 91 | + ) |
| 92 | + if resource := values.get("resource"): |
| 93 | + setattr( |
| 94 | + data_by_key[key], |
| 95 | + resource, |
| 96 | + values["_value"], |
| 97 | + ) |
| 98 | + return list(data_by_key.values()) |
75 | 99 |
|
76 | 100 |
|
77 | 101 | def map_flux_tables_to_feature_evaluation_data( |
78 | 102 | flux_tables: list[FluxTable], |
79 | 103 | ) -> list[FeatureEvaluationData]: |
80 | 104 | return [ |
81 | 105 | FeatureEvaluationData( |
82 | | - day=(values := record.values)["_time"].strftime("%Y-%m-%d"), |
| 106 | + day=(values := record.values)["_time"].date(), |
83 | 107 | count=values["_value"], |
84 | | - labels=_labels_type_adapter.validate_python(values), |
| 108 | + labels=map_influx_record_values_to_labels(values), |
85 | 109 | ) |
86 | 110 | for flux_table in flux_tables |
87 | 111 | for record in flux_table.records |
|
0 commit comments