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_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
- def geocode_order_task(self, order_pk: int, nominatim_user_agent: str | None = None):
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 the Nominatim User-Agent as an argument.
29
- Activates django-scopes organizer scope before querying scoped models.
29
+ Accepts organizer_pk and Nominatim User-Agent as arguments.
30
+ Fetches Organizer first, then activates scope.
30
31
  """
31
- order = None # Initialize order to None
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
- order = Order.objects.select_related('event__organizer', 'invoice_address').get(pk=order_pk)
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"Order with PK {order_pk} not found in geocode_order_task.")
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
- relation_name = 'geocode_data' # Ensure this matches your model
44
- # Check existence within scope (safer)
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 here ---
100
+ # --- Scope deactivated automatically ---
81
101
 
82
- # Keep outer exception handling
102
+ # --- Outer exception handling ---
83
103
  except ObjectDoesNotExist:
84
- # This case is now handled earlier, but keep for safety
85
- logger.error(f"Order with PK {order_pk} not found outside scope handling.")
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
- logger.exception(
88
- f"Unexpected error in geocode_order_task for Order PK {order_pk} (potentially outside scope): {e}")
89
- # Retry on unexpected errors before scope activation
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" %} {# Use Pretix base template #}
1
+ {% extends "pretixcontrol/event/settings_base.html" %}
2
2
  {% load i18n %}
3
- {% load static %} {# To load static files like CSS/JS later #}
4
- {% load eventurl %} {# Needed for the url template tag #}
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
- <div class="form-group">
12
- <button id="view-toggle-btn" class="btn btn-default">Switch to Heatmap View</button>
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
- {# The container where the map will be rendered #}
19
- {# Ensure this has height/width via CSS #}
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
- {# --- End Container Wrapper --- #}
55
+ {# -- End Map Area -- #}
56
+
35
57
 
36
- {# === LEAFLET PLUGINS (Loaded via staticfiles) === #}
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