Coverage for webapp/publisher/snaps/metrics_views.py: 81%
128 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-28 22:05 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-28 22:05 +0000
1# Standard library
2from json import loads
3from dateutil import relativedelta
4import math
6# Packages
7import flask
8import webapp.metrics.helper as metrics_helper
9import webapp.metrics.metrics as metrics
10from canonicalwebteam.store_api.dashboard import Dashboard
11from canonicalwebteam.store_api.devicegw import DeviceGW
13# Local
14from webapp.helpers import api_session
15from webapp.decorators import login_required
16from webapp.publisher.snaps import logic
19dashboard = Dashboard(api_session)
20device_gateway = DeviceGW("snap", api_session)
22downsample_data_limit = 500
23downsample_target_size = 10
26@login_required
27def get_account_snaps_metrics():
28 if not flask.request.data:
29 error = {"error": "Please provide a list of snaps"}
30 return flask.jsonify(error), 500
32 try:
33 metrics = {"buckets": [], "snaps": []}
35 snaps = loads(flask.request.data)
36 metrics_query = metrics_helper.build_snap_installs_metrics_query(snaps)
38 if metrics_query:
39 snap_metrics = dashboard.get_publisher_metrics(
40 flask.session, json=metrics_query
41 )
42 metrics = metrics_helper.transform_metrics(
43 metrics, snap_metrics, snaps
44 )
45 return flask.jsonify(metrics), 200
46 except Exception:
47 error = {"error": "An error occured while fetching metrics"}
48 return flask.jsonify(error), 500
51@login_required
52def get_measure_snap(snap_name):
53 return flask.redirect(
54 flask.url_for(".publisher_snap_metrics", snap_name=snap_name)
55 )
58@login_required
59def publisher_snap_metrics(snap_name):
60 """
61 A view to display the snap metrics page for specific snaps.
62 """
63 # If this fails, the page will 404
64 dashboard.get_snap_info(flask.session, snap_name)
66 context = {
67 # Data direct from details API
68 "snap_name": snap_name,
69 # pass snap id from here?
70 "is_linux": "Linux" in flask.request.headers["User-Agent"],
71 }
73 return flask.render_template("store/publisher.html", **context)
76@login_required
77def get_active_devices(snap_name):
78 snap_details = dashboard.get_snap_info(flask.session, snap_name)
80 snap_id = snap_details["snap_id"]
82 installed_base_metric = logic.verify_base_metrics(
83 flask.request.args.get("active-devices", default="version", type=str)
84 )
86 period = flask.request.args.get("period", default="30d", type=str)
87 active_device_period = logic.extract_metrics_period(period)
89 page = flask.request.args.get("page", default=1, type=int)
91 metric_requested_length = active_device_period["int"]
92 metric_requested_bucket = active_device_period["bucket"]
94 page_time_length = flask.request.args.get(
95 "page-length", default=3, type=int
96 )
97 total_page_num = 1
98 if metric_requested_bucket == "d" or (
99 metric_requested_bucket == "m"
100 and page_time_length >= metric_requested_length
101 ):
102 dates = metrics_helper.get_dates_for_metric(
103 metric_requested_length, metric_requested_bucket
104 )
105 start = dates["start"]
106 end = dates["end"]
107 else:
108 page_period_length = (
109 (metric_requested_length * 12)
110 if metric_requested_bucket == "y"
111 else metric_requested_length
112 )
113 total_page_num = math.floor(page_period_length / page_time_length)
115 end = metrics_helper.get_last_metrics_processed_date() + (
116 relativedelta.relativedelta(
117 months=-(page_time_length * (page - 1))
118 )
119 )
120 start = end + (relativedelta.relativedelta(months=-(page_time_length)))
122 # Decrease the date by a day to make sure
123 # there is no overlapping dates across the pages.
124 if page != 1:
125 end = end + relativedelta.relativedelta(days=-1)
127 installed_base = logic.get_installed_based_metric(installed_base_metric)
129 new_metrics_query = metrics_helper.build_active_device_metric_query(
130 snap_id=snap_id, installed_base=installed_base, end=end, start=start
131 )
133 metrics_response = dashboard.get_publisher_metrics(
134 flask.session, json=new_metrics_query
135 )
137 active_metrics = metrics_helper.find_metric(
138 metrics_response["metrics"], installed_base
139 )
141 metrics_data = active_metrics
142 buckets = metrics_data["buckets"]
143 series = metrics_data["series"]
144 metric_name = metrics_data["metric_name"]
145 # Add constants to a variable
146 if len(series) > downsample_data_limit:
147 (
148 downsampled_buckets,
149 downsampled_series,
150 ) = metrics_helper.downsample_series(
151 buckets, series, downsample_target_size
152 )
153 else:
154 downsampled_buckets = buckets
155 downsampled_series = series
157 series = downsampled_series
158 if metric_name == "weekly_installed_base_by_channel":
159 for s in series:
160 if "/" not in s["name"]:
161 s["name"] = f"latest/{s['name']}"
163 if installed_base_metric == "os":
164 for item in series:
165 item["name"] = metrics._capitalize_os_name(item["name"])
167 active_devices = metrics.ActiveDevices(
168 name=metric_name,
169 series=series,
170 buckets=downsampled_buckets,
171 status=metrics_data["status"],
172 )
174 latest_active = 0
175 if active_devices:
176 latest_active = active_devices.get_number_latest_active_devices()
178 return flask.jsonify(
179 {
180 "active_devices": dict(active_devices),
181 "latest_active_devices": latest_active,
182 "total_page_num": total_page_num,
183 }
184 )
187@login_required
188def get_latest_active_devices(snap_name):
189 snap_details = dashboard.get_snap_info(flask.session, snap_name)
190 snap_id = snap_details["snap_id"]
191 # get latest active devices
192 latest_day_period = logic.extract_metrics_period("1d")
193 latest_installed_base = logic.get_installed_based_metric("version")
194 latest_day_query_json = metrics_helper.build_metric_query_installed_base(
195 snap_id=snap_id,
196 installed_base=latest_installed_base,
197 metric_period=latest_day_period["int"],
198 metric_bucket=latest_day_period["bucket"],
199 )
201 latest_day_response = dashboard.get_publisher_metrics(
202 flask.session, json=latest_day_query_json
203 )
205 latest_active = 0
207 if latest_day_response:
208 latest_active_metrics = metrics_helper.find_metric(
209 latest_day_response["metrics"], latest_installed_base
210 )
211 if latest_active_metrics:
212 latest_active_devices = metrics.ActiveDevices(
213 name=latest_active_metrics["metric_name"],
214 series=latest_active_metrics["series"],
215 buckets=latest_active_metrics["buckets"],
216 status=latest_active_metrics["status"],
217 )
218 latest_active = (
219 latest_active_devices.get_number_latest_active_devices()
220 )
221 return flask.jsonify(
222 {
223 "latest_active_devices": latest_active,
224 }
225 )
228@login_required
229def get_metric_annotaion(snap_name):
230 details = dashboard.get_snap_info(flask.session, snap_name)
231 annotations = {"name": "annotations", "series": [], "buckets": []}
233 for category in details["categories"]["items"]:
234 date = category["since"].split("T")[0]
235 new_date = logic.convert_date(category["since"])
237 if date not in annotations["buckets"]:
238 annotations["buckets"].append(date)
240 index_of_date = annotations["buckets"].index(date)
242 single_series = {
243 "values": [0] * (len(annotations)),
244 "name": category["name"],
245 "display_name": category["name"].capitalize().replace("-", " "),
246 "display_date": new_date,
247 "date": date,
248 }
250 single_series["values"][index_of_date] = 1
252 annotations["series"].append(single_series)
254 annotations["series"] = sorted(
255 annotations["series"], key=lambda k: k["date"]
256 )
257 return flask.jsonify(annotations)
260@login_required
261def get_country_metric(snap_name):
262 snap_details = dashboard.get_snap_info(flask.session, snap_name)
263 snap_id = snap_details["snap_id"]
264 metrics_query_json = metrics_helper.build_metric_query_country(
265 snap_id=snap_id,
266 )
268 metrics_response = dashboard.get_publisher_metrics(
269 flask.session, json=metrics_query_json
270 )
272 country_metric = metrics_helper.find_metric(
273 metrics_response["metrics"], "weekly_installed_base_by_country"
274 )
275 country_devices = metrics.CountryDevices(
276 name=country_metric["metric_name"],
277 series=country_metric["series"],
278 buckets=country_metric["buckets"],
279 status=country_metric["status"],
280 private=True,
281 )
283 territories_total = 0
284 if country_devices:
285 territories_total = country_devices.get_number_territories()
287 return flask.jsonify(
288 {
289 "active_devices": country_devices.country_data,
290 "territories_total": territories_total,
291 }
292 )