pretix-map 0.1.4__py3-none-any.whl → 0.1.5__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.
Files changed (33) hide show
  1. pretix_map-0.1.5.dist-info/METADATA +88 -0
  2. {pretix_map-0.1.4.dist-info → pretix_map-0.1.5.dist-info}/RECORD +32 -30
  3. {pretix_map-0.1.4.dist-info → pretix_map-0.1.5.dist-info}/WHEEL +1 -1
  4. {pretix_map-0.1.4.dist-info → pretix_map-0.1.5.dist-info}/licenses/LICENSE +15 -15
  5. pretix_mapplugin/__init__.py +1 -1
  6. pretix_mapplugin/apps.py +28 -28
  7. pretix_mapplugin/geocoding.py +162 -102
  8. pretix_mapplugin/locale/de/LC_MESSAGES/django.po +12 -12
  9. pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po +12 -12
  10. pretix_mapplugin/management/commands/geocode_existing_orders.py +271 -271
  11. pretix_mapplugin/migrations/0001_initial.py +27 -27
  12. pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py +32 -32
  13. pretix_mapplugin/migrations/0003_mapmilestone.py +27 -0
  14. pretix_mapplugin/models.py +71 -47
  15. pretix_mapplugin/signals.py +77 -92
  16. pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +51 -51
  17. pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +342 -452
  18. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css +59 -59
  19. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css +14 -14
  20. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js +10 -10
  21. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js +14419 -14419
  22. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js +14512 -14512
  23. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css +661 -661
  24. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js +5 -5
  25. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js +2 -2
  26. pretix_mapplugin/tasks.py +144 -113
  27. pretix_mapplugin/templates/pretix_mapplugin/map_page.html +154 -88
  28. pretix_mapplugin/templates/pretix_mapplugin/milestones.html +53 -0
  29. pretix_mapplugin/urls.py +38 -21
  30. pretix_mapplugin/views.py +272 -163
  31. pretix_map-0.1.4.dist-info/METADATA +0 -195
  32. {pretix_map-0.1.4.dist-info → pretix_map-0.1.5.dist-info}/entry_points.txt +0 -0
  33. {pretix_map-0.1.4.dist-info → pretix_map-0.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: pretix-map
3
+ Version: 0.1.5
4
+ Summary: An overview map of the catchment area of previous orders. Measured by postcode
5
+ Author-email: MarkenJaden <jjsch1410@gmail.com>
6
+ Maintainer-email: MarkenJaden <jjsch1410@gmail.com>
7
+ License-Expression: Apache-2.0
8
+ Project-URL: homepage, https://github.com/MarkenJaden/pretix-map
9
+ Project-URL: repository, https://github.com/MarkenJaden/pretix-map
10
+ Keywords: pretix
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: geopy
14
+ Dynamic: license-file
15
+
16
+ # Pretix Sales Map Plugin
17
+
18
+ A powerful geographic analytics and visualization plugin for [pretix](https://github.com/pretix/pretix). This plugin allows you to visualize where your attendees are coming from, track sales growth over time, and analyze regional market penetration.
19
+
20
+ ## ✨ Features
21
+
22
+ - **📍 Automatic Geocoding:** Automatically converts attendee addresses to map coordinates upon payment.
23
+ - **🗺️ Interactive Map:** Switch between **Pin View** (with clustering), **Heatmap**, and **Density Grid**.
24
+ - **📈 Sales Analytics:** Track total revenue, average travel distance, and top regions (cities/items).
25
+ - **⏱️ Timeline Animation:** Play back the history of your sales to see geographic growth over time.
26
+ - **★ Marketing Milestones:** Define important dates (e.g., newsletters) in the UI to see their impact on the timeline.
27
+ - **🔄 Event Comparison:** Overlay data from previous events to compare reach and performance.
28
+ - **⚠️ Quality Control:** Dedicated view for orders with failed geocoding to manage data issues.
29
+ - **🌙 Nightly Sync:** Automated background task to retry failed geocoding attempts.
30
+ - **🔘 Manual Trigger:** Admin button to re-run geocoding for all orders of an event.
31
+
32
+ ## 🚀 Installation & Setup
33
+
34
+ To use this plugin with your local pretix instance (`C:\Users\SCJA03\Desktop\Programmieren\pretix`):
35
+
36
+ ### 1. Register the Plugin
37
+ Open your terminal, navigate to this directory, and ensure your pretix virtual environment is active.
38
+ ```bash
39
+ python setup.py develop
40
+ ```
41
+
42
+ ### 2. Run Migrations
43
+ Apply the database changes for geodata and milestones:
44
+ ```bash
45
+ # From your pretix/src directory
46
+ python manage.py migrate
47
+ ```
48
+
49
+ ### 3. Configuration
50
+ Add a User-Agent for the geocoding service in your `pretix.cfg`:
51
+ ```ini
52
+ [pretix_mapplugin]
53
+ nominatim_user_agent = YourProjectName/1.0 (contact@yourdomain.com)
54
+ ```
55
+
56
+ ### 4. Enable the Plugin
57
+ 1. Log in to your Pretix Control Panel.
58
+ 2. Go to **Organizer Settings > Plugins** and enable **Map-Plugin**.
59
+ 3. In your specific **Event > Settings > Plugins**, also enable **Map-Plugin**.
60
+
61
+ ## 🛠️ Usage
62
+
63
+ - **Map View:** Navigate to **Sales Map > Map View** in the event sidebar.
64
+ - **Milestones:** Go to **Sales Map > Milestones** to add marketing dates.
65
+ - **Revenue Weighting:** Use the toggle button on the map to see "where the money comes from" instead of just "where the people are".
66
+
67
+ ## 🚀 Starting the Application (Development)
68
+
69
+ 1. **Pretix Server:**
70
+ ```bash
71
+ cd C:\Users\SCJA03\Desktop\Programmieren\pretix\src
72
+ python manage.py runserver
73
+ ```
74
+ 2. **Celery Worker:**
75
+ ```bash
76
+ cd C:\Users\SCJA03\Desktop\Programmieren\pretix\src
77
+ celery -A pretix.celery_app worker -l info
78
+ ```
79
+ *Note: If you don't have a broker like Redis or RabbitMQ running, the plugin will automatically fall back to synchronous geocoding (eager mode) if configured in `pretix.cfg`.*
80
+
81
+ ## 🛡️ Requirements
82
+
83
+ - pretix >= 2.7
84
+ - geopy
85
+ - A running Celery worker (essential for background geocoding)
86
+
87
+ ---
88
+ Developed by MarkenJaden. Released under the Apache License 2.0.
@@ -1,37 +1,38 @@
1
- pretix_map-0.1.4.dist-info/licenses/LICENSE,sha256=RhQ89ePNDClBzEROahhwjDrBSEb5Zpx6XewZfGlY4Ss,569
2
- pretix_mapplugin/__init__.py,sha256=wQL21SKqJmZ-NSboyC8tgIR3EmyN1XDjwrN7CAHZpGQ,23
3
- pretix_mapplugin/apps.py,sha256=AnThwyRw2AAz5f-kmXZ8hm85OmKnlDkRosVoQOBgPzE,830
4
- pretix_mapplugin/geocoding.py,sha256=lBmwMvmE_cPyOHxWE8H3Se2P-2Eq0UjDTCv9gUs97Fo,4018
5
- pretix_mapplugin/models.py,sha256=klKrgMu1bmiPBwucTdOAZGWtv4WAEKcnoeqPlZgFR1A,2091
6
- pretix_mapplugin/signals.py,sha256=pSkucUPU6XgR0KLw4bKYEUW2Bqs5vfQXhO5YILQ2wps,3928
7
- pretix_mapplugin/tasks.py,sha256=F_c36RwyTQzUJ9kBBos_-2zth1UXw_kpcQpUcE90BNM,5428
8
- pretix_mapplugin/urls.py,sha256=o5407vULF4S-bUihU7AeRxUcMyazg2lPjbvqRflsGxE,838
9
- pretix_mapplugin/views.py,sha256=7WgmNZeqwmOesT6PrkAIRC8fNfAcWGm-j9-2YqF5egI,7146
1
+ pretix_map-0.1.5.dist-info/licenses/LICENSE,sha256=MNMHjIJIjeQzQboYGsgFSRTBUHctF603DDa8MgCNyAg,554
2
+ pretix_mapplugin/__init__.py,sha256=rPSfWgIeq2YWVPyESOAwCBt8vftsTpIkuLAGDEzyRQc,22
3
+ pretix_mapplugin/apps.py,sha256=QOo53Z6zX0yB-Rz6I92tAu051sP38hM2iJlP9sk2JEg,802
4
+ pretix_mapplugin/geocoding.py,sha256=N_JyjYU_JBouwE3oaL3G1wI5jAyezZzS5IBMgI_GdWQ,6053
5
+ pretix_mapplugin/models.py,sha256=Ofx8S1UlpQVFt-pC9-_LKecF-Hv2uI2rDq5zJ5S_UJQ,2494
6
+ pretix_mapplugin/signals.py,sha256=_SRT-3TllYzD9kUf0JG3PnuWa6cPHkFFSWInCDGm_j0,3155
7
+ pretix_mapplugin/tasks.py,sha256=HKDFC-U1TtYDNrN26QVx133hn0K0BdBUm4qCCYHcgOY,6367
8
+ pretix_mapplugin/urls.py,sha256=gZC74OUSZI2ZUWQXm1pbz8taIV8jA9D2h7FvojRX9Ws,1458
9
+ pretix_mapplugin/views.py,sha256=MByLWX2_U7gqs1AV59ZICIZDcEi2nIPPOfFxZgUhCvI,11591
10
10
  pretix_mapplugin/locale/de/LC_MESSAGES/django.mo,sha256=6VVRAqa0ixL-lDA1QwoVvG0wd5ZBwYjaR4P8T73hxhU,269
11
- pretix_mapplugin/locale/de/LC_MESSAGES/django.po,sha256=tIFKw9KOdGTjGq8bHV6tquRZe_MOn8TT4MJjdTRhId8,323
11
+ pretix_mapplugin/locale/de/LC_MESSAGES/django.po,sha256=-HtJt_qb8k7C30pVXRuYeh5CIB_ISVt2HEAXGn2rVnw,311
12
12
  pretix_mapplugin/locale/de_Informal/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.mo,sha256=6VVRAqa0ixL-lDA1QwoVvG0wd5ZBwYjaR4P8T73hxhU,269
14
- pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po,sha256=tIFKw9KOdGTjGq8bHV6tquRZe_MOn8TT4MJjdTRhId8,323
14
+ pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po,sha256=-HtJt_qb8k7C30pVXRuYeh5CIB_ISVt2HEAXGn2rVnw,311
15
15
  pretix_mapplugin/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  pretix_mapplugin/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- pretix_mapplugin/management/commands/geocode_existing_orders.py,sha256=zD8OD7c1ZXPCR1KNOc-gWY8ZlIk_IbMKtagFinG6qf0,14099
18
- pretix_mapplugin/migrations/0001_initial.py,sha256=KAl1Egxptv1bpregGbsh8wUbr4Yh5A_zazVSAQdmoHM,1020
19
- pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py,sha256=dXmZRdqrND0pxiPRuitlDdg-Q2JBqYG-sRPJxr6Urpk,889
17
+ pretix_mapplugin/management/commands/geocode_existing_orders.py,sha256=ACDCH17WdNY6cQLVNj8oW0vlqfzlM3-oqA-sKDPtnFg,13828
18
+ pretix_mapplugin/migrations/0001_initial.py,sha256=i33QeU7ioVpk5HpSShdF-ievDUHsjTzKf2OWO9oSkl8,993
19
+ pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py,sha256=U0xDF4KcLqb1QTuDJvilr9NEECVorM7W8ll86RDWi-4,857
20
+ pretix_mapplugin/migrations/0003_mapmilestone.py,sha256=2fcuMOSWcyF-wReVuGs53_a8pLpYLfNoCIIAekdRSUQ,991
20
21
  pretix_mapplugin/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
22
  pretix_mapplugin/static/pretix_mapplugin/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css,sha256=9t2grYB2nWk90Q8h7XjDrlMw9UvcwYS4lcXM1KFidqI,964
23
- pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js,sha256=ObXC0E0UOJMIiS5DobuGkTVjNzxDjODs8WgxbKaryWE,18591
24
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css,sha256=LWhzWaQGZRsWFrrJxg-6Zn8TT84k0_trtiHBc6qcGpY,1346
25
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css,sha256=-bdWuWOXMFkX0v9Cvr3OWClPiYefDQz9GGZP_7xZxdc,886
26
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js,sha256=aPb_2lnWKnXsUc1_-aT9-kbtr4CV3c85jH9xC1e5QDI,5168
27
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js,sha256=i7N_sDD-OoSdbNWYx4paYveaHkkprfXimr67-kGDy_M,430058
23
+ pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css,sha256=l-PfqoW8pGYAI30l6KYkmdxRDQdtz84vN_6Qbvo3kNU,913
24
+ pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js,sha256=1zJKkHL1WnB8u3MkUJOR_33dGkaiz5ylpJbNg4Tr-Iw,19897
25
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css,sha256=YSWCMtmNZNwqex4CEw1nQhvFub2lmU7vcCKP-XVwwXA,1287
26
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css,sha256=YU3qCpj_P06tdPBJGPax0bm6Q1wltfwjsho5TR4-TYc,872
27
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js,sha256=65UqrlgGoRAnKfKRuriH3eeDrOhZgZo1SCenduc-SGo,5158
28
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js,sha256=Ffst8WrY9mwnR_UnN1iW4n9cHbbYDtWt9_3psXENFCY,415639
28
29
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js.map,sha256=lxY34ye1PfsolFQ8pTsEtlgBJ4tW7panCBRsm35HNbs,866200
29
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js,sha256=Y0Ki5d8X0X1H2YiS590G4GgQTUnf6kP26s5XsJ2w76Q,455791
30
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js,sha256=IV8Omcuoc8Y1mhT0Sv-yu2T-CxccDjkT2w8Qdb7SJ6Y,441279
30
31
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js.map,sha256=l7cBxd_w6YZOXEWKixXC7DN4ejJsXed7kI-eoTvi1Wo,866292
31
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css,sha256=p4NxAoJBhIIN-hmNHrzRCf9tD_miZyoHS5obTRR9BMY,14806
32
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js,sha256=MQS1JlBNDWH9MJmkUh6H9zLMwxdN7FTgjea6i94-Ff8,147557
32
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css,sha256=M3v8pcq9A7OYFbJwD-vis7ft9VkhxZzUn4jssyghIwM,14145
33
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js,sha256=20nQCchB9co0qIjJZRGuk2_Z9VM-kNiyxNV1lvTlZBo,147552
33
34
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js.map,sha256=YAoQ3FzREN4GmVENMir8vgHHypC0xfSK3CAxTHCqx1M,225544
34
- pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js,sha256=WxJnsgS9OQGt16B_czxdDIl7Am9bwYkpj0eYwRE-Cy4,33726
35
+ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js,sha256=WL6HHfYfbFEkZOFdsJQeY7lJG_E5airjvqbznghUzRw,33724
35
36
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js.map,sha256=R8juz3AGnE2aOl3MIx1ram0tNMMiec7EPYwDbqb2ycc,27636
36
37
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers-2x.png,sha256=Bm2sqFDY_77wB68AsG6sABVyje4nnFHzy2xxbffELt8,1259
37
38
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers.png,sha256=Hbvp0CjikvNvy6j4s6KNXokydU_CIVuaxp5M3s9RB8Y,696
@@ -39,9 +40,10 @@ pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-ic
39
40
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon.png,sha256=V0w6XMqF9BFAhbaEFZbWLwDXyJLHsD8oy_owHesdxDc,1466
40
41
  pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-shadow.png,sha256=Jk9cZAM58ELdcpBiz8BMF_jqDymIK1OOOEjtjxDttNo,618
41
42
  pretix_mapplugin/templates/pretix_mapplugin/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
- pretix_mapplugin/templates/pretix_mapplugin/map_page.html,sha256=wrUFxvtlmBhgADoRqB7sL3QsGETNC_DgYjWboHvDYFw,4827
43
- pretix_map-0.1.4.dist-info/METADATA,sha256=BkU4P9Kyz6cXKpmquqrTwkVXTd3ng0ygN_kmbP3YBuU,9518
44
- pretix_map-0.1.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
45
- pretix_map-0.1.4.dist-info/entry_points.txt,sha256=C3NAjeZHoCekafkLMCJynPcABRTK8AUprtQv7sUNDZs,137
46
- pretix_map-0.1.4.dist-info/top_level.txt,sha256=CAtEnkgA73zE9Gadm5mjt1SpXHBPOS-QWP0dQVoNToE,17
47
- pretix_map-0.1.4.dist-info/RECORD,,
43
+ pretix_mapplugin/templates/pretix_mapplugin/map_page.html,sha256=_lUjaaRNf9bBc4aepek4Sp2w7QE1coRLPSF9mdfIFiw,9824
44
+ pretix_mapplugin/templates/pretix_mapplugin/milestones.html,sha256=2cdcmuA9XxDC3BFZuVL9tO-Nsz7whKasqLM9en3x5cw,2032
45
+ pretix_map-0.1.5.dist-info/METADATA,sha256=ls_bUm29Qxk256wtUPHplJOrWxywvFN8J7LPecu2HN0,3575
46
+ pretix_map-0.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
47
+ pretix_map-0.1.5.dist-info/entry_points.txt,sha256=C3NAjeZHoCekafkLMCJynPcABRTK8AUprtQv7sUNDZs,137
48
+ pretix_map-0.1.5.dist-info/top_level.txt,sha256=CAtEnkgA73zE9Gadm5mjt1SpXHBPOS-QWP0dQVoNToE,17
49
+ pretix_map-0.1.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,15 +1,15 @@
1
-
2
- Copyright 2025 MarkenJaden
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License");
5
- you may not use this file except in compliance with the License.
6
- You may obtain a copy of the License at
7
-
8
- http://www.apache.org/licenses/LICENSE-2.0
9
-
10
- Unless required by applicable law or agreed to in writing, software
11
- distributed under the License is distributed on an "AS IS" BASIS,
12
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- See the License for the specific language governing permissions and
14
- limitations under the License.
15
-
1
+
2
+ Copyright 2025 MarkenJaden
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
@@ -1 +1 @@
1
- __version__ = "0.1.4"
1
+ __version__ = "0.1.5"
pretix_mapplugin/apps.py CHANGED
@@ -1,28 +1,28 @@
1
- from django.utils.translation import gettext_lazy
2
-
3
- from . import __version__
4
-
5
- try:
6
- from pretix.base.plugins import PluginConfig
7
- except ImportError:
8
- raise RuntimeError("Please use pretix 2.7 or above to run this plugin!")
9
-
10
-
11
- class PluginApp(PluginConfig):
12
- default = True
13
- name = "pretix_mapplugin"
14
- verbose_name = "Map-Plugin"
15
-
16
- class PretixPluginMeta:
17
- name = gettext_lazy("Map-Plugin")
18
- author = "MarkenJaden"
19
- description = gettext_lazy("An overview map of the catchment area of previous orders. Measured by postcode")
20
- visible = True
21
- version = __version__
22
- category = "FEATURE"
23
- compatibility = "pretix>=2.7.0"
24
- settings_links = []
25
- navigation_links = []
26
-
27
- def ready(self):
28
- from . import signals # NOQA
1
+ from django.utils.translation import gettext_lazy
2
+
3
+ from . import __version__
4
+
5
+ try:
6
+ from pretix.base.plugins import PluginConfig
7
+ except ImportError:
8
+ raise RuntimeError("Please use pretix 2.7 or above to run this plugin!")
9
+
10
+
11
+ class PluginApp(PluginConfig):
12
+ default = True
13
+ name = "pretix_mapplugin"
14
+ verbose_name = "Map-Plugin"
15
+
16
+ class PretixPluginMeta:
17
+ name = gettext_lazy("Map-Plugin")
18
+ author = "MarkenJaden"
19
+ description = gettext_lazy("An overview map of the catchment area of previous orders. Measured by postcode")
20
+ visible = True
21
+ version = __version__
22
+ category = "FEATURE"
23
+ compatibility = "pretix>=2.7.0"
24
+ settings_links = []
25
+ navigation_links = []
26
+
27
+ def ready(self):
28
+ from . import signals # NOQA
@@ -1,102 +1,162 @@
1
- import logging
2
- from geopy.geocoders import Nominatim
3
- from geopy.exc import GeocoderTimedOut, GeocoderServiceError
4
- from time import sleep
5
-
6
- # DO NOT import settings here, as it won't work reliably in Celery
7
-
8
- logger = logging.getLogger(__name__)
9
-
10
- # Define a default/fallback User-Agent. Users *should* override this in pretix.cfg.
11
- DEFAULT_NOMINATIM_USER_AGENT = "pretix-map-plugin/unknown (Please configure nominatim_user_agent in pretix.cfg)"
12
-
13
-
14
- # --- Geocoding Function (Accepts user_agent) ---
15
- def geocode_address(address_string: str, nominatim_user_agent: str | None = None) -> tuple[float, float] | None:
16
- """
17
- Tries to geocode a given address string using Nominatim, using the
18
- provided User-Agent string.
19
-
20
- Args:
21
- address_string: A single string representing the address.
22
- nominatim_user_agent: The User-Agent string to use for Nominatim.
23
-
24
- Returns:
25
- A tuple (latitude, longitude) if successful, otherwise None.
26
- """
27
- # Use the provided User-Agent or the default
28
- user_agent = nominatim_user_agent or DEFAULT_NOMINATIM_USER_AGENT
29
-
30
- if user_agent == DEFAULT_NOMINATIM_USER_AGENT:
31
- # Log warning if default is used - admins should configure this
32
- logger.warning(
33
- "Using default Nominatim User-Agent. Please set a specific "
34
- "'nominatim_user_agent' under [pretix_mapplugin] in your "
35
- "pretix.cfg according to Nominatim's usage policy."
36
- )
37
-
38
- # Initialize the geolocator with the determined user_agent
39
- geolocator = Nominatim(user_agent=user_agent)
40
-
41
- try:
42
- # Add a 1-second delay to respect Nominatim's usage policy (1 req/sec)
43
- sleep(1)
44
-
45
- # Perform geocoding
46
- location = geolocator.geocode(address_string, timeout=10) # 10-second timeout
47
-
48
- if location:
49
- logger.debug(
50
- f"Geocoded '{address_string}' to ({location.latitude}, {location.longitude}) using User-Agent: {user_agent}"
51
- )
52
- return (location.latitude, location.longitude)
53
- else:
54
- logger.warning(f"Could not geocode address: {address_string} (Address not found by Nominatim)")
55
- return None
56
-
57
- except GeocoderTimedOut:
58
- logger.error(f"Geocoding timed out for address: {address_string}")
59
- return None
60
- except GeocoderServiceError as e:
61
- # Log specific service errors (e.g., API limits, server issues)
62
- logger.error(f"Geocoding service error for address '{address_string}': {e}")
63
- return None
64
- except Exception as e:
65
- # Catch any other unexpected exceptions during geocoding
66
- logger.exception(f"An unexpected error occurred during geocoding for address '{address_string}': {e}")
67
- return None
68
-
69
-
70
- # --- Helper to Format Address from Pretix Order ---
71
- def get_formatted_address_from_order(order) -> str | None:
72
- """
73
- Creates a formatted address string from a Pretix order's invoice address.
74
-
75
- Args:
76
- order: A Pretix `Order` object.
77
-
78
- Returns:
79
- A formatted address string suitable for geocoding, or None if no address.
80
- """
81
- # Ensure order and invoice_address exist
82
- if not order or not order.invoice_address:
83
- return None
84
-
85
- parts = []
86
- addr = order.invoice_address # Shortcut
87
-
88
- # Add components in a likely useful order for geocoding
89
- if addr.street: parts.append(addr.street)
90
- if addr.city: parts.append(addr.city)
91
- if addr.zipcode: parts.append(addr.zipcode)
92
- if addr.state: parts.append(addr.state)
93
- # Use the full country name if possible, geocoders often prefer it
94
- if addr.country: parts.append(str(addr.country.name))
95
-
96
- # Only return an address if we have useful parts
97
- if not parts:
98
- return None
99
-
100
- # Join parts with commas. Geocoders are usually good at parsing this.
101
- full_address = ", ".join(filter(None, parts)) # filter(None,...) removes empty strings
102
- return full_address
1
+ import logging
2
+ from geopy.geocoders import Nominatim
3
+ from geopy.exc import GeocoderTimedOut, GeocoderServiceError
4
+ from geopy.distance import distance as geopy_distance
5
+ from time import sleep
6
+
7
+ # DO NOT import settings here, as it won't work reliably in Celery
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ # Define a default/fallback User-Agent. Users *should* override this in pretix.cfg.
12
+ DEFAULT_NOMINATIM_USER_AGENT = "pretix-map-plugin/unknown (Please configure nominatim_user_agent in pretix.cfg)"
13
+
14
+
15
+ # --- Geocoding Function (Accepts user_agent) ---
16
+ def geocode_address(address_string: str, nominatim_user_agent: str | None = None) -> tuple[float, float] | None:
17
+ """
18
+ Tries to geocode a given address string using Nominatim, using the
19
+ provided User-Agent string.
20
+
21
+ Args:
22
+ address_string: A single string representing the address.
23
+ nominatim_user_agent: The User-Agent string to use for Nominatim.
24
+
25
+ Returns:
26
+ A tuple (latitude, longitude) if successful, otherwise None.
27
+ """
28
+ # Use the provided User-Agent or the default
29
+ user_agent = nominatim_user_agent or DEFAULT_NOMINATIM_USER_AGENT
30
+
31
+ if user_agent == DEFAULT_NOMINATIM_USER_AGENT:
32
+ # Log warning if default is used - admins should configure this
33
+ logger.warning(
34
+ "Using default Nominatim User-Agent. Please set a specific "
35
+ "'nominatim_user_agent' under [pretix_mapplugin] in your "
36
+ "pretix.cfg according to Nominatim's usage policy."
37
+ )
38
+
39
+ # Initialize the geolocator with the determined user_agent
40
+ geolocator = Nominatim(user_agent=user_agent)
41
+
42
+ try:
43
+ # Add a 1-second delay to respect Nominatim's usage policy (1 req/sec)
44
+ sleep(1)
45
+
46
+ # Perform geocoding
47
+ location = geolocator.geocode(address_string, timeout=10) # 10-second timeout
48
+
49
+ if location:
50
+ logger.debug(
51
+ f"Geocoded '{address_string}' to ({location.latitude}, {location.longitude}) using User-Agent: {user_agent}"
52
+ )
53
+ return (location.latitude, location.longitude)
54
+ else:
55
+ logger.warning(f"Could not geocode address: {address_string} (Address not found by Nominatim)")
56
+ return None
57
+
58
+ except GeocoderTimedOut:
59
+ logger.error(f"Geocoding timed out for address: {address_string}")
60
+ return None
61
+ except GeocoderServiceError as e:
62
+ # Log specific service errors (e.g., API limits, server issues)
63
+ logger.error(f"Geocoding service error for address '{address_string}': {e}")
64
+ return None
65
+ except Exception as e:
66
+ # Catch any other unexpected exceptions during geocoding
67
+ logger.exception(f"An unexpected error occurred during geocoding for address '{address_string}': {e}")
68
+ return None
69
+
70
+
71
+ # --- Helper to Format Address Candidates from Pretix Order ---
72
+ def get_address_candidates_from_order(order) -> list[str]:
73
+ """
74
+ Creates a list of formatted address strings from a Pretix order's invoice address,
75
+ ordered from most specific to least specific (fallback).
76
+
77
+ Args:
78
+ order: A Pretix `Order` object.
79
+
80
+ Returns:
81
+ A list of address strings (e.g., ["Street, City, Zip, Country", "Zip City, Country"]).
82
+ """
83
+ # Ensure order and invoice_address exist
84
+ if not order or not order.invoice_address:
85
+ return []
86
+
87
+ candidates = []
88
+ addr = order.invoice_address # Shortcut
89
+ country_name = str(addr.country.name) if addr.country else ""
90
+
91
+ # Strategy 1: Full Address (Street, Zip City, State, Country)
92
+ parts_full = []
93
+ if addr.street: parts_full.append(addr.street)
94
+ if addr.zipcode and addr.city:
95
+ parts_full.append(f"{addr.zipcode} {addr.city}")
96
+ elif addr.city:
97
+ parts_full.append(addr.city)
98
+ elif addr.zipcode:
99
+ parts_full.append(addr.zipcode)
100
+
101
+ if addr.state: parts_full.append(addr.state)
102
+ if country_name: parts_full.append(country_name)
103
+
104
+ full_address = ", ".join(filter(None, parts_full))
105
+ if full_address:
106
+ candidates.append(full_address)
107
+
108
+ # Strategy 2: Zip + City + Country (No Street) - Good for privacy or if street is weird
109
+ parts_zip_city = []
110
+ if addr.zipcode and addr.city:
111
+ parts_zip_city.append(f"{addr.zipcode} {addr.city}")
112
+ elif addr.city: # Fallback if only city
113
+ parts_zip_city.append(addr.city)
114
+
115
+ if country_name: parts_zip_city.append(country_name)
116
+
117
+ zip_city_address = ", ".join(filter(None, parts_zip_city))
118
+ if zip_city_address and zip_city_address not in candidates:
119
+ candidates.append(zip_city_address)
120
+
121
+ # Strategy 3: Just Zip + Country (Coarse location)
122
+ parts_zip = []
123
+ if addr.zipcode: parts_zip.append(addr.zipcode)
124
+ if country_name: parts_zip.append(country_name)
125
+
126
+ zip_address = ", ".join(filter(None, parts_zip))
127
+ if zip_address and zip_address not in candidates:
128
+ candidates.append(zip_address)
129
+
130
+ return candidates
131
+
132
+
133
+ def get_best_address_string(order) -> str | None:
134
+ """Returns the most specific address string for an order."""
135
+ candidates = get_address_candidates_from_order(order)
136
+ return candidates[0] if candidates else None
137
+
138
+
139
+ def geocode_event_location(event, nominatim_user_agent: str | None = None) -> tuple[float, float] | None:
140
+ """
141
+ Tries to geocode the event's location field.
142
+ """
143
+ if not event or not event.location:
144
+ return None
145
+
146
+ location_str = str(event.location)
147
+ logger.info(f"Geocoding event location: {location_str}")
148
+ return geocode_address(location_str, nominatim_user_agent=nominatim_user_agent)
149
+
150
+
151
+ def calculate_distance(coord1: tuple[float, float], coord2: tuple[float, float]) -> float | None:
152
+ """
153
+ Calculates the distance in kilometers between two coordinates (lat, lon).
154
+ Returns None if coordinates are invalid.
155
+ """
156
+ try:
157
+ if not coord1 or not coord2:
158
+ return None
159
+ return geopy_distance(coord1, coord2).km
160
+ except Exception as e:
161
+ logger.warning(f"Error calculating distance: {e}")
162
+ return None
@@ -1,12 +1,12 @@
1
- msgid ""
2
- msgstr ""
3
- "Project-Id-Version: \n"
4
- "Report-Msgid-Bugs-To: \n"
5
- "POT-Creation-Date: 2017-03-07 19:01+0100\n"
6
- "PO-Revision-Date: \n"
7
- "Last-Translator: MarkenJaden\n"
8
- "Language-Team: \n"
9
- "Language: de\n"
10
- "MIME-Version: 1.0\n"
11
- "Content-Type: text/plain; charset=UTF-8\n"
12
- "Content-Transfer-Encoding: 8bit\n"
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: \n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: 2017-03-07 19:01+0100\n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: MarkenJaden\n"
8
+ "Language-Team: \n"
9
+ "Language: de\n"
10
+ "MIME-Version: 1.0\n"
11
+ "Content-Type: text/plain; charset=UTF-8\n"
12
+ "Content-Transfer-Encoding: 8bit\n"
@@ -1,12 +1,12 @@
1
- msgid ""
2
- msgstr ""
3
- "Project-Id-Version: \n"
4
- "Report-Msgid-Bugs-To: \n"
5
- "POT-Creation-Date: 2017-03-07 19:01+0100\n"
6
- "PO-Revision-Date: \n"
7
- "Last-Translator: MarkenJaden\n"
8
- "Language-Team: \n"
9
- "Language: de\n"
10
- "MIME-Version: 1.0\n"
11
- "Content-Type: text/plain; charset=UTF-8\n"
12
- "Content-Transfer-Encoding: 8bit\n"
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: \n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: 2017-03-07 19:01+0100\n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: MarkenJaden\n"
8
+ "Language-Team: \n"
9
+ "Language: de\n"
10
+ "MIME-Version: 1.0\n"
11
+ "Content-Type: text/plain; charset=UTF-8\n"
12
+ "Content-Transfer-Encoding: 8bit\n"