whatdidyoudo 0.1.13__tar.gz → 0.2.0__tar.gz

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 whatdidyoudo might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: whatdidyoudo
3
- Version: 0.1.13
3
+ Version: 0.2.0
4
4
  Summary: A minimal Flask app that shows the amount of OpenStreetMap changes made by a user on a day.
5
5
  Project-URL: Homepage, https://github.com/rompe/whatdidyoudo
6
6
  Project-URL: Issues, https://github.com/rompe/whatdidyoudo/issues
@@ -7,10 +7,10 @@ import requests
7
7
 
8
8
  from flask import Flask, render_template
9
9
  from flask_caching import Cache
10
- from flask_limiter import Limiter
10
+ from flask_limiter import Limiter, RateLimitExceeded
11
11
  from flask_limiter.util import get_remote_address
12
12
 
13
- __version__ = "0.1.13"
13
+ __version__ = "0.2.0"
14
14
 
15
15
  app = Flask(__name__)
16
16
  cache = Cache(app, config={"CACHE_TYPE": "SimpleCache",
@@ -32,10 +32,10 @@ def get_etree_from_url(url: str) -> ET.Element:
32
32
  return ET.fromstring(response.content)
33
33
 
34
34
 
35
- def get_changes(user: str, date: str) -> defaultdict[str, Changes]:
36
- """Return a {app: Changes} dictionary."""
37
- # {user: {app: Changes}}
35
+ def get_changes(user: str, date: str):
36
+ """Return a ({app: Changes} dictionary, [changeset_ids]) tuple."""
38
37
  changes: defaultdict[str, Changes] = defaultdict(Changes)
38
+ changeset_ids: list[str] = []
39
39
  datetime_date = datetime.date.fromisoformat(date)
40
40
  start_time = f"{datetime_date}T00:00:00Z"
41
41
  end_time = f"{datetime_date + datetime.timedelta(days=1)}T00:00:00Z"
@@ -46,6 +46,7 @@ def get_changes(user: str, date: str) -> defaultdict[str, Changes]:
46
46
 
47
47
  for cs in root.findall("changeset"):
48
48
  cs_id = cs.attrib["id"]
49
+ changeset_ids.append(cs_id)
49
50
 
50
51
  tags = {tag.attrib["k"]: tag.attrib["v"] for tag in cs.findall("tag")}
51
52
  editor = tags.get("created_by", "")
@@ -60,7 +61,7 @@ def get_changes(user: str, date: str) -> defaultdict[str, Changes]:
60
61
 
61
62
  for action in root:
62
63
  changes[editor].changes += len(action)
63
- return changes
64
+ return changes, changeset_ids
64
65
 
65
66
 
66
67
  @app.route('/')
@@ -76,25 +77,32 @@ def whatdidyoudo(user: str | None = None, date: str | None = None) -> str:
76
77
  today = datetime.date.today().isoformat()
77
78
  if not date:
78
79
  date = today
80
+
81
+ changeset_ids: list[str] = []
79
82
  for name in [item.strip() for item in (user or "").split(",")
80
83
  if item.strip()]:
81
84
  cache_key = f"changes_{name}_{date}"
82
- cur_changes = cache.get(cache_key) # type: ignore
83
- if not cur_changes:
85
+ cur_data = cache.get(cache_key) # type: ignore
86
+ if not cur_data:
84
87
  try:
85
88
  with limiter.limit("10 per minute"):
86
- cur_changes = get_changes(name, date)
89
+ cur_data = get_changes(name, date)
87
90
  if date != today:
88
- cache.set(cache_key, cur_changes) # type: ignore
91
+ cache.set(cache_key, cur_data) # type: ignore
89
92
  except requests.HTTPError:
90
93
  errors.append(
91
94
  f"Can't determine changes for user {name} on {date}.")
92
- if cur_changes:
95
+ except RateLimitExceeded as msg:
96
+ errors.append("Rate limit exceeded while processing user "
97
+ f"{name}: {msg}")
98
+ if cur_data:
99
+ cur_changes, cur_ids = cur_data
93
100
  changes[name] = cur_changes
101
+ changeset_ids.extend(cur_ids)
94
102
 
95
103
  return render_template('form.html', user=user, date=date, changes=changes,
96
104
  changesets=changesets, errors=errors,
97
- version=__version__)
105
+ version=__version__, changeset_ids=changeset_ids)
98
106
 
99
107
 
100
108
  def main():
@@ -89,7 +89,46 @@
89
89
  </ul>
90
90
  </div>
91
91
  {% endif %}
92
- <p class="footer">Powered by <a href="https://github.com/rompe/whatdidyoudo">whatdidyoudo {{ version }}</a></p>
92
+ {% if changeset_ids and changeset_ids|length > 0 %}
93
+ <div id="map" style="height: 300px; margin-top: 2em; border-radius: 12px; overflow: hidden;"></div>
94
+ <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
95
+ <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
96
+ <script>
97
+ const changesetIds = {{ changeset_ids|tojson }};
98
+ if (changesetIds.length > 0) {
99
+ const bounds = [];
100
+ Promise.all(
101
+ changesetIds.map(id =>
102
+ fetch(`https://api.openstreetmap.org/api/0.6/changeset/${id}`)
103
+ .then(r => r.text())
104
+ .then(xml => {
105
+ const parser = new DOMParser();
106
+ const doc = parser.parseFromString(xml, 'application/xml');
107
+ const cs = doc.querySelector('changeset');
108
+ if (cs) {
109
+ bounds.push([
110
+ [parseFloat(cs.getAttribute('min_lat')), parseFloat(cs.getAttribute('min_lon'))],
111
+ [parseFloat(cs.getAttribute('max_lat')), parseFloat(cs.getAttribute('max_lon'))]
112
+ ]);
113
+ }
114
+ })
115
+ )
116
+ ).then(() => {
117
+ if (bounds.length > 0) {
118
+ const map = L.map('map').setView([0, 0], 2);
119
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
120
+ maxZoom: 19,
121
+ attribution: '© OpenStreetMap contributors'
122
+ }).addTo(map);
123
+ const allBounds = L.latLngBounds(bounds.flat());
124
+ map.fitBounds(allBounds);
125
+ bounds.forEach(b => L.rectangle(b, {color: '#4f8cff', weight: 2}).addTo(map));
126
+ }
127
+ });
128
+ }
129
+ </script>
130
+ {% endif %}
131
+ <p class="footer">Powered by <a href="https://github.com/rompe/whatdidyoudo">whatdidyoudo {{ version }}</a></p>
93
132
  </div>
94
133
  </body>
95
134
  </html>
File without changes
File without changes