pretix-map 0.1.1__py3-none-any.whl → 0.1.3__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.
- {pretix_map-0.1.1.dist-info → pretix_map-0.1.3.dist-info}/METADATA +1 -1
- {pretix_map-0.1.1.dist-info → pretix_map-0.1.3.dist-info}/RECORD +15 -14
- pretix_mapplugin/__init__.py +1 -1
- pretix_mapplugin/management/commands/geocode_existing_orders.py +149 -70
- pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py +32 -0
- pretix_mapplugin/models.py +30 -10
- pretix_mapplugin/signals.py +27 -22
- pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +40 -7
- pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +321 -257
- pretix_mapplugin/tasks.py +41 -18
- pretix_mapplugin/templates/pretix_mapplugin/map_page.html +40 -17
- {pretix_map-0.1.1.dist-info → pretix_map-0.1.3.dist-info}/WHEEL +0 -0
- {pretix_map-0.1.1.dist-info → pretix_map-0.1.3.dist-info}/entry_points.txt +0 -0
- {pretix_map-0.1.1.dist-info → pretix_map-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {pretix_map-0.1.1.dist-info → pretix_map-0.1.3.dist-info}/top_level.txt +0 -0
pretix_mapplugin/tasks.py
CHANGED
@@ -22,26 +22,48 @@ logger = logging.getLogger(__name__)
|
|
22
22
|
|
23
23
|
|
24
24
|
@app.task(bind=True, max_retries=3, default_retry_delay=60, ignore_result=True)
|
25
|
-
|
25
|
+
# --- Accept organizer_pk as kwarg ---
|
26
|
+
def geocode_order_task(self, order_pk: int, organizer_pk: int | None = None, nominatim_user_agent: str | None = None):
|
26
27
|
"""
|
27
28
|
Celery task to geocode the address for a given order PK.
|
28
|
-
Accepts
|
29
|
-
|
29
|
+
Accepts organizer_pk and Nominatim User-Agent as arguments.
|
30
|
+
Fetches Organizer first, then activates scope.
|
30
31
|
"""
|
31
|
-
|
32
|
+
organizer = None
|
33
|
+
order = None
|
32
34
|
try:
|
35
|
+
# --- Step 1: Fetch Organizer (unscoped) ---
|
36
|
+
if organizer_pk is None:
|
37
|
+
# This should ideally not happen if called correctly, but handle defensively
|
38
|
+
logger.error(f"organizer_pk not provided for Order PK {order_pk}. Cannot activate scope.")
|
39
|
+
# Depending on policy, you might retry, skip, or raise an error.
|
40
|
+
# Skipping for now.
|
41
|
+
return
|
42
|
+
|
33
43
|
try:
|
34
|
-
|
35
|
-
organizer = order.event.organizer # Get organizer from the fetched order
|
44
|
+
organizer = Organizer.objects.get(pk=organizer_pk)
|
36
45
|
except ObjectDoesNotExist:
|
37
|
-
logger.error(f"
|
46
|
+
logger.error(f"Organizer with PK {organizer_pk} not found (for Order PK {order_pk}).")
|
47
|
+
# Don't retry if organizer doesn't exist
|
38
48
|
return
|
49
|
+
|
50
|
+
# --- Step 2: Activate Scope ---
|
39
51
|
with scope(organizer=organizer):
|
52
|
+
# --- Step 3: Fetch Order (now within scope) ---
|
53
|
+
try:
|
54
|
+
order = Order.objects.select_related(
|
55
|
+
'invoice_address' # Only need this direct relation now
|
56
|
+
).get(pk=order_pk)
|
57
|
+
except ObjectDoesNotExist:
|
58
|
+
logger.error(f"Order with PK {order_pk} not found within scope of Org {organizer_pk}.")
|
59
|
+
# Don't retry if order doesn't exist in this scope
|
60
|
+
return
|
61
|
+
|
40
62
|
logger.info(
|
41
63
|
f"Starting geocoding task for Order {order.code} (PK: {order_pk}) within scope of Organizer '{organizer.slug}'")
|
42
64
|
|
43
|
-
|
44
|
-
|
65
|
+
# --- Rest of the logic runs within scope ---
|
66
|
+
relation_name = 'geocode_data'
|
45
67
|
if OrderGeocodeData.objects.filter(order_id=order_pk).exists():
|
46
68
|
logger.info(f"Geocode data already exists for Order {order.code} (checked within scope). Skipping.")
|
47
69
|
return
|
@@ -49,7 +71,6 @@ def geocode_order_task(self, order_pk: int, nominatim_user_agent: str | None = N
|
|
49
71
|
address_str = get_formatted_address_from_order(order)
|
50
72
|
if not address_str:
|
51
73
|
logger.info(f"Order {order.code} has no address suitable for geocoding. Storing null coordinates.")
|
52
|
-
# Store null within scope
|
53
74
|
with transaction.atomic():
|
54
75
|
OrderGeocodeData.objects.update_or_create(
|
55
76
|
order=order, defaults={'latitude': None, 'longitude': None}
|
@@ -59,7 +80,6 @@ def geocode_order_task(self, order_pk: int, nominatim_user_agent: str | None = N
|
|
59
80
|
logger.debug(f"Attempting to geocode address for Order {order.code}: '{address_str}'")
|
60
81
|
coordinates = geocode_address(address_str, nominatim_user_agent=nominatim_user_agent)
|
61
82
|
|
62
|
-
# Store result within scope
|
63
83
|
with transaction.atomic():
|
64
84
|
if coordinates:
|
65
85
|
latitude, longitude = coordinates
|
@@ -77,14 +97,17 @@ def geocode_order_task(self, order_pk: int, nominatim_user_agent: str | None = N
|
|
77
97
|
log_level = logging.INFO if created else logging.DEBUG
|
78
98
|
logger.log(log_level,
|
79
99
|
f"Saved{' new' if created else ' updated'} null geocode data for Order {order.code} after failed attempt.")
|
80
|
-
# --- Scope deactivated automatically
|
100
|
+
# --- Scope deactivated automatically ---
|
81
101
|
|
82
|
-
#
|
102
|
+
# --- Outer exception handling ---
|
83
103
|
except ObjectDoesNotExist:
|
84
|
-
#
|
85
|
-
|
104
|
+
# Should be caught earlier now, but keep for safety
|
105
|
+
obj_type = "Organizer" if organizer is None else "Order"
|
106
|
+
obj_pk = organizer_pk if organizer is None else order_pk
|
107
|
+
logger.error(f"{obj_type} with PK {obj_pk} not found.")
|
86
108
|
except Exception as e:
|
87
|
-
|
88
|
-
|
89
|
-
|
109
|
+
org_info = f" (Org PK: {organizer_pk})" if organizer_pk else ""
|
110
|
+
order_info = f" (Order PK: {order_pk})" if order_pk else ""
|
111
|
+
logger.exception(f"Unexpected error in geocode_order_task{org_info}{order_info}: {e}")
|
112
|
+
# Retry on potentially temporary errors
|
90
113
|
raise self.retry(exc=e)
|
@@ -1,44 +1,67 @@
|
|
1
|
-
{% extends "pretixcontrol/event/settings_base.html" %}
|
1
|
+
{% extends "pretixcontrol/event/settings_base.html" %}
|
2
2
|
{% load i18n %}
|
3
|
-
{% load static %}
|
4
|
-
{% load eventurl %}
|
3
|
+
{% load static %}
|
4
|
+
{% load eventurl %}
|
5
5
|
|
6
6
|
{% block title %}{% trans "Ticket Sales Map" %}{% endblock %}
|
7
7
|
|
8
8
|
{% block inside %}
|
9
9
|
<h1>{% trans "Ticket Sales Map" %}</h1>
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
{# -- Controls Row -- #}
|
12
|
+
<div class="form-inline" style="margin-bottom: 1em;">
|
13
|
+
<div class="form-group" style="margin-right: 10px;">
|
14
|
+
<button id="view-toggle-btn" class="btn btn-default">Switch to Heatmap View</button>
|
15
|
+
</div>
|
16
|
+
{# -- NEW Cluster Toggle Button -- #}
|
17
|
+
<div class="form-group" style="margin-right: 10px;">
|
18
|
+
<button id="cluster-toggle-btn" class="btn btn-default" disabled>Disable Clustering</button>
|
19
|
+
</div>
|
20
|
+
{# -- End Cluster Toggle Button -- #}
|
21
|
+
</div>
|
22
|
+
{# -- End Controls Row -- #}
|
23
|
+
|
24
|
+
{# -- NEW Heatmap Options Panel (Initially hidden for pins view) -- #}
|
25
|
+
<div id="heatmap-options-panel" class="panel panel-default"
|
26
|
+
style="display: none; padding: 15px; margin-bottom: 1em; max-width: 400px;">
|
27
|
+
<h4>{% trans "Heatmap Options" %}</h4>
|
28
|
+
<div class="form-group">
|
29
|
+
<label for="heatmap-radius">Radius: <span id="radius-value">25</span></label>
|
30
|
+
<input type="range" id="heatmap-radius" class="form-control" min="1" max="100" value="25" step="1" disabled>
|
31
|
+
</div>
|
32
|
+
<div class="form-group">
|
33
|
+
<label for="heatmap-blur">Blur: <span id="blur-value">15</span></label>
|
34
|
+
<input type="range" id="heatmap-blur" class="form-control" min="1" max="50" value="15" step="1" disabled>
|
35
|
+
</div>
|
36
|
+
<div class="form-group">
|
37
|
+
<label for="heatmap-maxZoom">Max Zoom: <span id="maxzoom-value">18</span></label>
|
38
|
+
<input type="range" id="heatmap-maxZoom" class="form-control" min="1" max="18" value="18" step="1" disabled>
|
39
|
+
</div>
|
40
|
+
{# Add more options like gradient if desired later #}
|
13
41
|
</div>
|
42
|
+
{# -- End Heatmap Options Panel -- #}
|
14
43
|
|
15
|
-
{# --- Container for Map and Status Overlay --- #}
|
16
|
-
<div style="position: relative; border: 1px solid #ccc;"> {# Wrapper for positioning #}
|
17
44
|
|
18
|
-
|
19
|
-
|
45
|
+
{# -- Map Area -- #}
|
46
|
+
<div style="position: relative; border: 1px solid #ccc;">
|
20
47
|
<div id="sales-map-container"
|
21
48
|
data-data-url="{% url 'plugins:pretix_mapplugin:event.settings.salesmap.data' organizer=request.organizer.slug event=request.event.slug %}">
|
22
|
-
{# Content inside here will be managed by Leaflet #}
|
23
49
|
</div>
|
24
|
-
|
25
|
-
{# --- Status Overlay Element --- #}
|
26
|
-
{# Initially visible, hidden by JS when map loads/errors occur #}
|
27
50
|
<div id="map-status-overlay"
|
28
51
|
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); z-index: 1000; display: flex; justify-content: center; align-items: center; text-align: center;">
|
29
52
|
<p>Loading map data...</p>
|
30
53
|
</div>
|
31
|
-
{# --- End Status Overlay --- #}
|
32
|
-
|
33
54
|
</div>
|
34
|
-
{#
|
55
|
+
{# -- End Map Area -- #}
|
56
|
+
|
35
57
|
|
36
|
-
{# ===
|
58
|
+
{# === Leaflet & Plugins (Keep as is) === #}
|
37
59
|
<script src="{% static 'pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js' %}"></script>
|
38
60
|
<link rel="stylesheet" href="{% static 'pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css' %}"/>
|
39
61
|
<link rel="stylesheet" href="{% static 'pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css' %}"/>
|
40
62
|
<script src="{% static 'pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js' %}"></script>
|
41
63
|
|
64
|
+
{# === Your JS/CSS (Keep as is) === #}
|
42
65
|
<script src="{% static 'pretix_mapplugin/js/salesmap.js' %}"></script>
|
43
66
|
<link rel="stylesheet" href="{% static 'pretix_mapplugin/css/salesmap.css' %}"/>
|
44
67
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|