pretix-map 0.1.1__tar.gz → 0.1.2__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.
Files changed (55) hide show
  1. {pretix_map-0.1.1/pretix_map.egg-info → pretix_map-0.1.2}/PKG-INFO +1 -1
  2. {pretix_map-0.1.1 → pretix_map-0.1.2/pretix_map.egg-info}/PKG-INFO +1 -1
  3. pretix_map-0.1.2/pretix_mapplugin/__init__.py +1 -0
  4. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/management/commands/geocode_existing_orders.py +69 -36
  5. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/signals.py +27 -22
  6. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/tasks.py +41 -18
  7. pretix_map-0.1.1/pretix_mapplugin/__init__.py +0 -1
  8. {pretix_map-0.1.1 → pretix_map-0.1.2}/LICENSE +0 -0
  9. {pretix_map-0.1.1 → pretix_map-0.1.2}/MANIFEST.in +0 -0
  10. {pretix_map-0.1.1 → pretix_map-0.1.2}/README.rst +0 -0
  11. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_map.egg-info/SOURCES.txt +0 -0
  12. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_map.egg-info/dependency_links.txt +0 -0
  13. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_map.egg-info/entry_points.txt +0 -0
  14. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_map.egg-info/requires.txt +0 -0
  15. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_map.egg-info/top_level.txt +0 -0
  16. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/apps.py +0 -0
  17. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/geocoding.py +0 -0
  18. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/locale/de/LC_MESSAGES/django.mo +0 -0
  19. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/locale/de/LC_MESSAGES/django.po +0 -0
  20. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/locale/de_Informal/.gitkeep +0 -0
  21. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.mo +0 -0
  22. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po +0 -0
  23. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/management/__init__.py +0 -0
  24. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/management/commands/__init__.py +0 -0
  25. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/migrations/0001_initial.py +0 -0
  26. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/migrations/__init__.py +0 -0
  27. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/models.py +0 -0
  28. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/.gitkeep +0 -0
  29. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +0 -0
  30. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +0 -0
  31. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css +0 -0
  32. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css +0 -0
  33. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers-2x.png +0 -0
  34. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers.png +0 -0
  35. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon-2x.png +0 -0
  36. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon.png +0 -0
  37. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-shadow.png +0 -0
  38. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js +0 -0
  39. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js +0 -0
  40. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js.map +0 -0
  41. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js +0 -0
  42. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js.map +0 -0
  43. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css +0 -0
  44. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js +0 -0
  45. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js.map +0 -0
  46. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js +0 -0
  47. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js.map +0 -0
  48. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/templates/pretix_mapplugin/.gitkeep +0 -0
  49. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/templates/pretix_mapplugin/map_page.html +0 -0
  50. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/urls.py +0 -0
  51. {pretix_map-0.1.1 → pretix_map-0.1.2}/pretix_mapplugin/views.py +0 -0
  52. {pretix_map-0.1.1 → pretix_map-0.1.2}/pyproject.toml +0 -0
  53. {pretix_map-0.1.1 → pretix_map-0.1.2}/setup.cfg +0 -0
  54. {pretix_map-0.1.1 → pretix_map-0.1.2}/setup.py +0 -0
  55. {pretix_map-0.1.1 → pretix_map-0.1.2}/tests/test_main.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pretix-map
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: An overview map of the catchment area of previous orders. Measured by postcode
5
5
  Author-email: MarkenJaden <jjsch1410@gmail.com>
6
6
  Maintainer-email: MarkenJaden <jjsch1410@gmail.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pretix-map
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: An overview map of the catchment area of previous orders. Measured by postcode
5
5
  Author-email: MarkenJaden <jjsch1410@gmail.com>
6
6
  Maintainer-email: MarkenJaden <jjsch1410@gmail.com>
@@ -0,0 +1 @@
1
+ __version__ = "0.1.2"
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from django.core.management.base import BaseCommand, CommandError
3
- from django.core.exceptions import FieldDoesNotExist
3
+ from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
4
4
 
5
5
  # --- Import Pretix Global Settings accessor ---
6
6
  from django_scopes import scope
@@ -8,6 +8,7 @@ from django_scopes import scope
8
8
  # Check if Pretix version uses AbstractSettingsHolder or GlobalSettingsObject
9
9
  # Adjust import based on Pretix version if needed. Assume AbstractSettingsHolder for newer Pretix.
10
10
  try:
11
+ # Newer Pretix often uses this pattern via AbstractSettingsHolder
11
12
  from pretix.base.settings import GlobalSettingsObject as SettingsProxy
12
13
  except ImportError:
13
14
  try:
@@ -21,7 +22,11 @@ except ImportError:
21
22
  except ImportError:
22
23
  # Fallback or raise error if neither is found
23
24
  logger.error("Could not determine Pretix settings accessor for management command.")
24
- raise ImportError("Cannot find Pretix settings accessor.")
25
+
26
+
27
+ # This will likely cause the command to fail later, but allows it to start
28
+ class SettingsProxy:
29
+ def __init__(self): self.settings = {} # Empty dict to avoid errors later
25
30
 
26
31
  # --- Import necessary Pretix models ---
27
32
  from pretix.base.models import Order, Event, Organizer
@@ -40,16 +45,23 @@ class Command(BaseCommand):
40
45
 
41
46
  def add_arguments(self, parser):
42
47
  parser.add_argument(
43
- '--organizer', type=str, help='Slug of a specific organizer to process orders for.',
48
+ '--organizer',
49
+ type=str,
50
+ help='Slug of a specific organizer to process orders for.',
44
51
  )
45
52
  parser.add_argument(
46
- '--event', type=str, help='Slug of a specific event to process orders for. Requires --organizer.',
53
+ '--event',
54
+ type=str,
55
+ help='Slug of a specific event to process orders for. Requires --organizer.',
47
56
  )
48
57
  parser.add_argument(
49
- '--dry-run', action='store_true', help='Simulate the process without actually queuing tasks.',
58
+ '--dry-run',
59
+ action='store_true',
60
+ help='Simulate the process without actually queuing tasks.',
50
61
  )
51
62
  parser.add_argument(
52
- '--force-recode', action='store_true',
63
+ '--force-recode',
64
+ action='store_true',
53
65
  help='Queue geocoding even for orders that already have geocode data.',
54
66
  )
55
67
 
@@ -65,13 +77,12 @@ class Command(BaseCommand):
65
77
  # --- Read User-Agent using Pretix Settings accessor ---
66
78
  user_agent = DEFAULT_NOMINATIM_USER_AGENT
67
79
  try:
68
- # Get settings holder instance
69
80
  gs = SettingsProxy()
70
81
  # Construct the setting key specific to plugins
71
- # Format might be 'plugin:plugin_name:setting_name' or just 'plugin_name_setting_name'
72
- # Check Pretix docs or experiment if needed. Assuming the former.
82
+ # The format 'plugin:plugin_name:setting_name' is common
73
83
  setting_key = 'plugin:pretix_mapplugin:nominatim_user_agent'
74
- user_agent = gs.settings.get(setting_key, DEFAULT_NOMINATIM_USER_AGENT)
84
+ # Use .get() which is safer for dictionaries possibly returned by load_config
85
+ user_agent = getattr(gs, 'settings', {}).get(setting_key, DEFAULT_NOMINATIM_USER_AGENT)
75
86
 
76
87
  if user_agent == DEFAULT_NOMINATIM_USER_AGENT:
77
88
  self.stdout.write(self.style.WARNING(
@@ -88,12 +99,14 @@ class Command(BaseCommand):
88
99
  organizers_to_process = []
89
100
  if organizer_slug:
90
101
  try:
102
+ # Fetch specific organizer (outside scope)
91
103
  organizer = Organizer.objects.get(slug=organizer_slug)
92
104
  organizers_to_process.append(organizer)
93
105
  self.stdout.write(f"Processing specified organizer: {organizer.name} ({organizer_slug})")
94
106
  except Organizer.DoesNotExist:
95
107
  raise CommandError(f"Organizer with slug '{organizer_slug}' not found.")
96
108
  else:
109
+ # Fetch all organizers (outside scope)
97
110
  organizers_to_process = list(Organizer.objects.all())
98
111
  self.stdout.write(f"Processing all {len(organizers_to_process)} organizers...")
99
112
 
@@ -105,9 +118,10 @@ class Command(BaseCommand):
105
118
  # --- Iterate through organizers and activate scope ---
106
119
  for organizer in organizers_to_process:
107
120
  self.stdout.write(f"\n--- Processing Organizer: {organizer.name} ({organizer.slug}) ---")
121
+ current_organizer_pk = organizer.pk # Get the PK needed for the task kwarg
108
122
 
109
123
  with scope(organizer=organizer):
110
- # --- Get orders ---
124
+ # --- Get orders within scope ---
111
125
  orders_qs = Order.objects.filter(status=Order.STATUS_PAID)
112
126
 
113
127
  # --- Filter by event if specified ---
@@ -119,74 +133,93 @@ class Command(BaseCommand):
119
133
  except Event.DoesNotExist:
120
134
  self.stderr.write(self.style.WARNING(
121
135
  f" Event '{event_slug}' not found for this organizer. Skipping event filter."))
122
- if organizer_slug and event_slug: continue
136
+ # If filtering by event and it's not found for this org, skip this org entirely
137
+ if organizer_slug and event_slug:
138
+ self.stdout.write(
139
+ f" Skipping organizer '{organizer.slug}' as specified event was not found.")
140
+ continue # Move to the next organizer
123
141
 
124
142
  # --- Filter orders needing geocoding ---
125
- relation_name = 'geocode_data' # Ensure this matches your model
143
+ relation_name = 'geocode_data' # Ensure this matches your model's related_name
126
144
  if not force_recode:
127
145
  try:
128
146
  Order._meta.get_field(relation_name) # Check existence
147
+ # Filter orders that don't have the related geocode entry
129
148
  orders_to_process_qs = orders_qs.filter(**{f'{relation_name}__isnull': True})
130
149
  self.stdout.write(" Selecting paid orders missing geocode data...")
131
150
  except FieldDoesNotExist:
132
- self.stderr.write(
133
- self.style.ERROR(f" Relation '{relation_name}' not found. Skipping organizer."))
134
- continue
151
+ # This indicates a code/model setup error, stop for this org
152
+ self.stderr.write(self.style.ERROR(
153
+ f" Configuration Error: Reverse relation '{relation_name}' not found on Order model. Check OrderGeocodeData model definition (related_name). Skipping organizer '{organizer.slug}'."))
154
+ continue # Skip this organizer
135
155
  except Exception as e:
136
- self.stderr.write(self.style.ERROR(f" Error checking relation: {e}. Skipping organizer."))
137
- continue
156
+ # Catch other potential errors during query construction
157
+ self.stderr.write(self.style.ERROR(
158
+ f" Error checking relation '{relation_name}': {e}. Skipping organizer '{organizer.slug}'."))
159
+ continue # Skip this organizer
138
160
  else:
161
+ # If forcing, process all orders that matched the initial filters (paid, event)
139
162
  orders_to_process_qs = orders_qs
140
- self.stdout.write(self.style.WARNING(" Processing ALL selected paid orders (--force-recode)..."))
163
+ self.stdout.write(
164
+ self.style.WARNING(" Processing ALL selected paid orders (--force-recode specified)..."))
141
165
 
142
166
  # --- Process orders for this scope ---
143
- current_org_orders_count = orders_to_process_qs.count()
144
- all_checked_for_org = orders_qs.count()
145
- total_processed_orders += all_checked_for_org
167
+ current_org_orders_count = orders_to_process_qs.count() # Count orders to queue
168
+ all_checked_for_org = orders_qs.count() # Count all orders checked for this filter set
169
+ total_processed_orders += all_checked_for_org # Add to overall total
146
170
 
147
171
  if current_org_orders_count == 0:
148
172
  self.stdout.write(f" No orders need geocoding ({all_checked_for_org} checked).")
149
- continue
173
+ continue # Skip to next organizer
150
174
 
151
175
  self.stdout.write(
152
176
  f" Found {current_org_orders_count} order(s) to potentially geocode ({all_checked_for_org} checked).")
153
177
  org_queued = 0
154
178
  org_skipped = 0
155
179
 
156
- for order in orders_to_process_qs.iterator():
180
+ # Iterate and queue tasks
181
+ for order in orders_to_process_qs.iterator(): # Use iterator for memory efficiency
157
182
  if dry_run:
183
+ # Provide slightly more info in dry run
158
184
  self.stdout.write(
159
- f" [DRY RUN] Would queue Order: {order.code} (PK: {order.pk}) Event: {order.event.slug}")
185
+ f" [DRY RUN] Would queue Order: {order.code} (PK: {order.pk}, Org PK: {current_organizer_pk}) Event: {order.event.slug}")
160
186
  org_queued += 1
161
187
  else:
162
188
  try:
163
- # --- Pass user_agent to task ---
189
+ # --- Pass user_agent AND organizer_pk to the task ---
164
190
  geocode_order_task.apply_async(
165
- args=[order.pk],
166
- kwargs={'nominatim_user_agent': user_agent} # Pass as kwarg
191
+ args=[order.pk], # Positional arg is order_pk
192
+ kwargs={
193
+ 'nominatim_user_agent': user_agent,
194
+ 'organizer_pk': current_organizer_pk # Pass organizer PK
195
+ }
167
196
  )
197
+ # Don't log every single queue success unless verbose requested
168
198
  org_queued += 1
169
199
  except Exception as e:
170
- self.stderr.write(self.style.ERROR(f" ERROR queuing Order {order.code}: {e}"))
200
+ # Log error if queueing fails
201
+ self.stderr.write(self.style.ERROR(
202
+ f" ERROR queuing task for Order {order.code} (PK: {order.pk}): {e}"))
171
203
  logger.exception(f"Failed to queue geocoding task via command for order {order.code}: {e}")
172
204
  org_skipped += 1
173
205
 
174
- self.stdout.write(f" Queued: {org_queued}, Skipped: {org_skipped} for this organizer.")
206
+ # Report summary for the current organizer
207
+ self.stdout.write(f" Finished Organizer: Queued: {org_queued}, Skipped: {org_skipped}.")
175
208
  total_queued += org_queued
176
209
  total_skipped += org_skipped
177
- # End scope
210
+ # End scope 'with' block
178
211
 
179
- # --- Final Report ---
212
+ # --- Final Overall Report ---
180
213
  self.stdout.write("=" * 40)
181
214
  self.stdout.write("Overall Summary:")
182
215
  self.stdout.write(f" Organizers processed: {len(organizers_to_process)}")
183
- self.stdout.write(f" Total orders checked (paid): {total_processed_orders}")
216
+ self.stdout.write(f" Total orders checked (paid, matching filters): {total_processed_orders}")
184
217
  if dry_run:
185
218
  self.stdout.write(
186
219
  self.style.SUCCESS(f"[DRY RUN] Complete. Would have queued tasks for {total_queued} order(s)."))
187
220
  else:
188
- self.stdout.write(self.style.SUCCESS(f"Complete. Queued tasks for {total_queued} order(s)."))
221
+ self.stdout.write(self.style.SUCCESS(f"Complete. Successfully queued tasks for {total_queued} order(s)."))
189
222
  if total_skipped > 0:
190
- self.stdout.write(
191
- self.style.WARNING(f"Skipped {total_skipped} order(s) total due to errors during queueing."))
223
+ self.stdout.write(self.style.WARNING(
224
+ f"Skipped {total_skipped} order(s) total due to errors during queueing (check logs)."))
192
225
  self.stdout.write("=" * 40)
@@ -3,7 +3,6 @@ from django.dispatch import receiver
3
3
  from django.urls import reverse, NoReverseMatch
4
4
  from django.utils.translation import gettext_lazy as _
5
5
  from django.http import HttpRequest
6
- # Import Django settings to read config in web process
7
6
  from django.conf import settings
8
7
 
9
8
  # --- Pretix Signals ---
@@ -13,50 +12,56 @@ from pretix.control.signals import nav_event
13
12
  # --- Tasks ---
14
13
  from .tasks import geocode_order_task
15
14
  # --- Geocoding Default ---
16
- from .geocoding import DEFAULT_NOMINATIM_USER_AGENT # Import default
15
+ from .geocoding import DEFAULT_NOMINATIM_USER_AGENT
17
16
 
18
17
  logger = logging.getLogger(__name__)
19
18
 
20
19
  # --- Constants ---
21
20
  MAP_VIEW_URL_NAME = 'plugins:pretix_mapplugin:event.settings.salesmap.show'
22
21
  REQUIRED_MAP_PERMISSION = 'can_view_orders'
23
- PLUGIN_NAME = 'pretix_mapplugin' # Define plugin name for settings access
22
+ PLUGIN_NAME = 'pretix_mapplugin'
24
23
 
25
24
 
26
- # --- Signal Receiver for Geocoding (Reads setting, passes to task) ---
25
+ # --- Signal Receiver for Geocoding (Passes organizer_pk) ---
27
26
  @receiver(order_paid, dispatch_uid="sales_mapper_order_paid_geocode")
28
27
  def trigger_geocoding_on_payment(sender, order, **kwargs):
29
28
  """
30
29
  Listens for the order_paid signal, reads geocoding config,
31
- and queues the geocoding task with the config.
30
+ and queues the geocoding task with order_pk, organizer_pk, and config.
32
31
  """
33
- user_agent = DEFAULT_NOMINATIM_USER_AGENT # Start with default
32
+ user_agent = DEFAULT_NOMINATIM_USER_AGENT
33
+ organizer_pk = None # Initialize
34
34
  try:
35
- # --- Read User-Agent from settings (works in web process) ---
36
- # Check structure defensively before accessing
35
+ # Ensure order has event and organizer before proceeding
36
+ if not order or not order.event or not order.event.organizer:
37
+ logger.error(f"Order {order.code} is missing event or organizer information. Cannot queue task.")
38
+ return
39
+
40
+ organizer_pk = order.event.organizer.pk # Get organizer PK
41
+
42
+ # --- Read User-Agent from settings ---
37
43
  if hasattr(settings, 'plugins') and hasattr(settings.plugins, PLUGIN_NAME):
38
44
  plugin_settings = getattr(settings.plugins, PLUGIN_NAME)
39
- user_agent = plugin_settings.get(
40
- 'nominatim_user_agent', # Setting name in pretix.cfg
41
- DEFAULT_NOMINATIM_USER_AGENT
42
- )
45
+ user_agent = plugin_settings.get('nominatim_user_agent', DEFAULT_NOMINATIM_USER_AGENT)
43
46
  else:
44
- logger.warning(
45
- f"Could not access settings.plugins.{PLUGIN_NAME}, "
46
- "using default Nominatim User-Agent for task."
47
- )
47
+ logger.warning(f"Could not access settings.plugins.{PLUGIN_NAME}, using default User-Agent.")
48
48
 
49
- # --- Queue task with user_agent as keyword argument ---
49
+ # --- Queue task with user_agent and organizer_pk as keyword arguments ---
50
50
  geocode_order_task.apply_async(
51
- args=[order.pk],
52
- kwargs={'nominatim_user_agent': user_agent} # Pass as kwarg
51
+ args=[order.pk], # Keep order_pk as positional argument
52
+ kwargs={
53
+ 'nominatim_user_agent': user_agent,
54
+ 'organizer_pk': organizer_pk # Pass organizer PK
55
+ }
53
56
  )
54
- logger.info(f"Geocoding task queued for paid order {order.code} (PK: {order.pk}).")
57
+ logger.info(f"Geocoding task queued for paid order {order.code} (PK: {order.pk}, Org PK: {organizer_pk}).")
55
58
 
56
- except ImportError: # Error finding geocode_order_task itself if tasks.py fails
59
+ except ImportError:
57
60
  logger.exception("Could not import geocode_order_task. Check tasks.py.")
58
61
  except Exception as e:
59
- logger.exception(f"Failed to queue geocoding task for order {order.code}: {e}")
62
+ # Log the organizer PK as well if available
63
+ org_info = f" (Org PK: {organizer_pk})" if organizer_pk else ""
64
+ logger.exception(f"Failed to queue geocoding task for order {order.code}{org_info}: {e}")
60
65
 
61
66
 
62
67
  # --- Signal Receiver for Adding Navigation Item (No changes needed) ---
@@ -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 +0,0 @@
1
- __version__ = "0.1.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes