udata 10.4.2.dev35441__py2.py3-none-any.whl → 10.4.2.dev35451__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

Files changed (27) hide show
  1. udata/core/metrics/commands.py +1 -0
  2. udata/core/metrics/helpers.py +102 -0
  3. udata/core/metrics/tasks.py +1 -0
  4. udata/core/site/models.py +33 -0
  5. udata/settings.py +4 -0
  6. udata/static/chunks/{10.471164b2a9fe15614797.js → 10.8ca60413647062717b1e.js} +3 -3
  7. udata/static/chunks/{10.471164b2a9fe15614797.js.map → 10.8ca60413647062717b1e.js.map} +1 -1
  8. udata/static/chunks/{11.0f04e49a40a0a381bcce.js → 11.51d706fb9521c16976bc.js} +3 -3
  9. udata/static/chunks/{11.0f04e49a40a0a381bcce.js.map → 11.51d706fb9521c16976bc.js.map} +1 -1
  10. udata/static/chunks/{13.2d06442dd9a05d9777b5.js → 13.d9c1735d14038b94c17e.js} +2 -2
  11. udata/static/chunks/{13.2d06442dd9a05d9777b5.js.map → 13.d9c1735d14038b94c17e.js.map} +1 -1
  12. udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js → 17.81c57c0dedf812e43013.js} +2 -2
  13. udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js.map → 17.81c57c0dedf812e43013.js.map} +1 -1
  14. udata/static/chunks/{19.df16abde17a42033a7f8.js → 19.a348a5fff8fe2801e52a.js} +3 -3
  15. udata/static/chunks/{19.df16abde17a42033a7f8.js.map → 19.a348a5fff8fe2801e52a.js.map} +1 -1
  16. udata/static/chunks/{8.0f42630e6d8ff782928e.js → 8.462bb3029de008497675.js} +2 -2
  17. udata/static/chunks/{8.0f42630e6d8ff782928e.js.map → 8.462bb3029de008497675.js.map} +1 -1
  18. udata/static/chunks/{9.07515e5187f475bce828.js → 9.033d7e190ca9e226a5d0.js} +3 -3
  19. udata/static/chunks/{9.07515e5187f475bce828.js.map → 9.033d7e190ca9e226a5d0.js.map} +1 -1
  20. udata/static/common.js +1 -1
  21. udata/static/common.js.map +1 -1
  22. {udata-10.4.2.dev35441.dist-info → udata-10.4.2.dev35451.dist-info}/METADATA +2 -1
  23. {udata-10.4.2.dev35441.dist-info → udata-10.4.2.dev35451.dist-info}/RECORD +27 -26
  24. {udata-10.4.2.dev35441.dist-info → udata-10.4.2.dev35451.dist-info}/LICENSE +0 -0
  25. {udata-10.4.2.dev35441.dist-info → udata-10.4.2.dev35451.dist-info}/WHEEL +0 -0
  26. {udata-10.4.2.dev35441.dist-info → udata-10.4.2.dev35451.dist-info}/entry_points.txt +0 -0
  27. {udata-10.4.2.dev35441.dist-info → udata-10.4.2.dev35451.dist-info}/top_level.txt +0 -0
@@ -60,6 +60,7 @@ def update(
60
60
  site.count_max_org_followers()
61
61
  site.count_max_org_reuses()
62
62
  site.count_max_org_datasets()
63
+ site.count_stock_metrics()
63
64
  except Exception as e:
64
65
  log.info(f"Error during update: {e}")
65
66
 
@@ -0,0 +1,102 @@
1
+ import logging
2
+ from collections import OrderedDict
3
+ from datetime import datetime, timedelta
4
+ from typing import Dict, List, Union
5
+
6
+ import requests
7
+ from bson import ObjectId
8
+ from dateutil.rrule import MONTHLY, rrule
9
+ from flask import current_app
10
+ from mongoengine import QuerySet
11
+ from pymongo.command_cursor import CommandCursor
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+
16
+ def get_last_13_months() -> List[str]:
17
+ dstart = datetime.today().replace(day=1) - timedelta(days=365)
18
+ months = rrule(freq=MONTHLY, count=13, dtstart=dstart)
19
+ return [month.strftime("%Y-%m") for month in months]
20
+
21
+
22
+ def compute_monthly_metrics(metrics_data: List[Dict], metrics_labels: List[str]) -> OrderedDict:
23
+ # Initialize default monthly_metrics
24
+ monthly_metrics = OrderedDict(
25
+ (month, {label: 0 for label in metrics_labels}) for month in get_last_13_months()
26
+ )
27
+ # Update monthly_metrics with metrics_data values
28
+ for entry in metrics_data:
29
+ entry_month = entry["metric_month"]
30
+ if entry_month in monthly_metrics:
31
+ for metric_label in metrics_labels:
32
+ label = f"monthly_{metric_label}"
33
+ monthly_metrics[entry_month][metric_label] = entry.get(label) or 0
34
+ return monthly_metrics
35
+
36
+
37
+ def metrics_by_label(monthly_metrics: Dict, metrics_labels: List[str]) -> List[OrderedDict]:
38
+ metrics_by_label = []
39
+ for label in metrics_labels:
40
+ metrics_by_label.append(
41
+ OrderedDict((month, monthly_metrics[month][label]) for month in monthly_metrics)
42
+ )
43
+ return metrics_by_label
44
+
45
+
46
+ def get_metrics_for_model(
47
+ model: str, id: Union[str, ObjectId, None], metrics_labels: List[str]
48
+ ) -> List[OrderedDict]:
49
+ """
50
+ Get distant metrics for a particular model object
51
+ """
52
+ if not current_app.config["METRICS_API"]:
53
+ # TODO: How to best deal with no METRICS_API, prevent calling or return empty?
54
+ # raise ValueError("missing config METRICS_API to use this function")
55
+ return [{} for _ in range(len(metrics_labels))]
56
+ models = model + "s" if id else model # TODO: not clean of a hack
57
+ model_metrics_api = f"{current_app.config['METRICS_API']}/{models}/data/"
58
+ try:
59
+ params = {"metric_month__sort": "desc"}
60
+ if id:
61
+ params[f"{model}_id__exact"] = id
62
+ res = requests.get(model_metrics_api, params)
63
+ res.raise_for_status()
64
+ monthly_metrics = compute_monthly_metrics(res.json()["data"], metrics_labels)
65
+ return metrics_by_label(monthly_metrics, metrics_labels)
66
+ except requests.exceptions.RequestException as e:
67
+ log.exception(f"Error while getting metrics for {model}({id}): {e}")
68
+ return [{} for _ in range(len(metrics_labels))]
69
+
70
+
71
+ def compute_monthly_aggregated_metrics(aggregation_res: CommandCursor) -> OrderedDict:
72
+ monthly_metrics = OrderedDict((month, 0) for month in get_last_13_months())
73
+ for monthly_count in aggregation_res:
74
+ year, month = monthly_count["_id"].split("-")
75
+ monthly_label = year + "-" + month.zfill(2)
76
+ if monthly_label in monthly_metrics:
77
+ monthly_metrics[monthly_label] = monthly_count["count"]
78
+ return monthly_metrics
79
+
80
+
81
+ def get_stock_metrics(objects: QuerySet, date_label: str = "created_at") -> OrderedDict:
82
+ """
83
+ Get stock metrics for a particular model object
84
+ """
85
+ pipeline = [
86
+ {"$match": {date_label: {"$gte": datetime.now() - timedelta(days=365)}}},
87
+ {
88
+ "$group": {
89
+ "_id": {
90
+ "$concat": [
91
+ {"$substr": [{"$year": f"${date_label}"}, 0, 4]},
92
+ "-",
93
+ {"$substr": [{"$month": f"${date_label}"}, 0, 12]},
94
+ ]
95
+ },
96
+ "count": {"$sum": 1},
97
+ }
98
+ },
99
+ ]
100
+ aggregation_res = objects.aggregate(*pipeline)
101
+
102
+ return compute_monthly_aggregated_metrics(aggregation_res)
@@ -24,5 +24,6 @@ def compute_site_metrics(self):
24
24
  site.count_max_org_followers()
25
25
  site.count_max_org_reuses()
26
26
  site.count_max_org_datasets()
27
+ site.count_stock_metrics()
27
28
  # Sending signal
28
29
  on_site_metrics_computed.send(site)
udata/core/site/models.py CHANGED
@@ -3,6 +3,7 @@ from werkzeug.local import LocalProxy
3
3
 
4
4
  from udata.core.dataservices.models import Dataservice
5
5
  from udata.core.dataset.models import Dataset
6
+ from udata.core.metrics.helpers import get_metrics_for_model, get_stock_metrics
6
7
  from udata.core.organization.models import Organization
7
8
  from udata.core.reuse.models import Reuse
8
9
  from udata.models import WithMetrics, db
@@ -36,15 +37,23 @@ class Site(WithMetrics, db.Document):
36
37
  "max_org_reuses",
37
38
  "max_org_datasets",
38
39
  "datasets",
40
+ "datasets_visits_by_months",
39
41
  "discussions",
40
42
  "followers",
41
43
  "organizations",
42
44
  "public-service",
43
45
  "resources",
46
+ "resources_downloads_by_months",
44
47
  "reuses",
45
48
  "dataservices",
46
49
  "users",
47
50
  "harvesters",
51
+ "users_by_months",
52
+ "datasets_by_months",
53
+ "harvesters_by_months",
54
+ "reuses_by_months",
55
+ "organizations_by_months",
56
+ "discussions_by_months",
48
57
  ]
49
58
 
50
59
  def __str__(self):
@@ -72,6 +81,9 @@ class Site(WithMetrics, db.Document):
72
81
  from udata.models import Dataset
73
82
 
74
83
  self.metrics["datasets"] = Dataset.objects.visible().count()
84
+ self.metrics["datasets_visits_by_months"] = get_metrics_for_model(
85
+ "site", None, ["visit_dataset"]
86
+ )[0]
75
87
  self.save()
76
88
 
77
89
  def count_resources(self):
@@ -83,6 +95,9 @@ class Site(WithMetrics, db.Document):
83
95
  ),
84
96
  {},
85
97
  ).get("count", 0)
98
+ self.metrics["resources_downloads_by_months"] = get_metrics_for_model(
99
+ "site", None, ["download_resource"]
100
+ )[0]
86
101
  self.save()
87
102
 
88
103
  def count_reuses(self):
@@ -172,6 +187,24 @@ class Site(WithMetrics, db.Document):
172
187
  self.metrics["max_org_datasets"] = org.metrics["datasets"] if org else 0
173
188
  self.save()
174
189
 
190
+ def count_stock_metrics(self):
191
+ from udata.harvest.models import HarvestSource
192
+ from udata.models import Discussion, User
193
+
194
+ self.metrics["users_by_months"] = get_stock_metrics(User.objects())
195
+ self.metrics["datasets_by_months"] = get_stock_metrics(
196
+ Dataset.objects().visible(), date_label="created_at_internal"
197
+ )
198
+ self.metrics["harvesters_by_months"] = get_stock_metrics(HarvestSource.objects())
199
+ self.metrics["reuses_by_months"] = get_stock_metrics(Reuse.objects().visible())
200
+ self.metrics["organizations_by_months"] = get_stock_metrics(
201
+ Organization.objects().visible()
202
+ )
203
+ self.metrics["discussions_by_months"] = get_stock_metrics(
204
+ Discussion.objects(), date_label="created"
205
+ )
206
+ self.save()
207
+
175
208
 
176
209
  def get_current_site():
177
210
  if getattr(g, "site", None) is None:
udata/settings.py CHANGED
@@ -574,6 +574,10 @@ class Defaults(object):
574
574
  ###########################################################################
575
575
  MAX_RESOURCES_IN_JSON_LD = 20
576
576
 
577
+ # Metrics settings
578
+ ###########################################################################
579
+ METRICS_API = None
580
+
577
581
 
578
582
  class Testing(object):
579
583
  """Sane values for testing. Should be applied as override"""