whatdidyoudo 0.1.12__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.12
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.12"
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():
@@ -64,23 +64,71 @@
64
64
  <h4>Tips</h4>
65
65
  <ul>
66
66
  <li>Do you want to have a bookmark that always shows your numbers for the current day?
67
- Just use <code>/YOUR_USERNAME</code> as URL and omit the date!</li>
67
+ Just use <code>/YOUR_USERNAME</code> as URL and omit the date!
68
+ </li>
68
69
  <li>You can enter multiple usernames separated by commas to see their contributions at once.
69
- Try this after a mapwalk with friends!</li>
70
+ Try this after a mapwalk with friends!
71
+ </li>
70
72
  </ul>
71
73
  </div>
72
74
  <div class="disclaimer">
73
75
  <h4>Disclaimers</h4>
74
76
  <ul>
75
- <li>This is a very simple service. It does exactly one job and tries to do it good.</li>
76
77
  <li>Quality beats quantity. While it's nice to see big numbers here, please remember
77
- that just one high-effort change will help more that any amount of mediocre changes.</li>
78
+ that just one high-effort change will help more that any amount of mediocre changes.
79
+ </li>
78
80
  <li>This service is rate-limited to 10 OSM API requests per minute and caches results
79
- for 1 hour to reduce load on the API.</li>
81
+ for 1 hour (except for the current day) to reduce load on the API.
82
+ Too restrictive? Tell me!
83
+ </li>
84
+ <li>This is a very simple service. It does exactly one job and tries to do it good.
85
+ The data displayed here is shown by many other services, but I didn't find one
86
+ that answers my question in this way with just a simple bookmark.
87
+ Suggestions are welcome! Get in touch via the issue tracker on the project page.
88
+ </li>
80
89
  </ul>
81
90
  </div>
82
91
  {% endif %}
83
- <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>
84
132
  </div>
85
133
  </body>
86
134
  </html>
File without changes
File without changes