pretix-map 0.0.5__tar.gz → 0.0.6__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.
- {pretix_map-0.0.5/pretix_map.egg-info → pretix_map-0.0.6}/PKG-INFO +1 -1
- {pretix_map-0.0.5 → pretix_map-0.0.6/pretix_map.egg-info}/PKG-INFO +1 -1
- pretix_map-0.0.6/pretix_mapplugin/__init__.py +1 -0
- pretix_map-0.0.6/pretix_mapplugin/geocoding.py +102 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/management/commands/geocode_existing_orders.py +79 -54
- pretix_map-0.0.6/pretix_mapplugin/signals.py +87 -0
- pretix_map-0.0.6/pretix_mapplugin/tasks.py +91 -0
- pretix_map-0.0.5/pretix_mapplugin/__init__.py +0 -1
- pretix_map-0.0.5/pretix_mapplugin/geocoding.py +0 -113
- pretix_map-0.0.5/pretix_mapplugin/signals.py +0 -73
- pretix_map-0.0.5/pretix_mapplugin/tasks.py +0 -74
- {pretix_map-0.0.5 → pretix_map-0.0.6}/LICENSE +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/MANIFEST.in +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/README.rst +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_map.egg-info/SOURCES.txt +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_map.egg-info/dependency_links.txt +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_map.egg-info/entry_points.txt +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_map.egg-info/requires.txt +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_map.egg-info/top_level.txt +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/apps.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/locale/de/LC_MESSAGES/django.mo +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/locale/de/LC_MESSAGES/django.po +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/locale/de_Informal/.gitkeep +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.mo +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/management/__init__.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/management/commands/__init__.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/migrations/0001_initial.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/migrations/__init__.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/models.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/.gitkeep +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers-2x.png +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers.png +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon-2x.png +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon.png +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-shadow.png +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js.map +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js.map +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js.map +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js.map +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/templates/pretix_mapplugin/.gitkeep +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/templates/pretix_mapplugin/map_page.html +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/urls.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/views.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/pyproject.toml +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/setup.cfg +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/setup.py +0 -0
- {pretix_map-0.0.5 → pretix_map-0.0.6}/tests/test_main.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pretix-map
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.6
|
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.0.
|
3
|
+
Version: 0.0.6
|
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.0.6"
|
@@ -0,0 +1,102 @@
|
|
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,16 +1,36 @@
|
|
1
1
|
import logging
|
2
|
-
from django.core.exceptions import FieldDoesNotExist
|
3
2
|
from django.core.management.base import BaseCommand, CommandError
|
3
|
+
from django.core.exceptions import FieldDoesNotExist
|
4
4
|
|
5
|
-
# Import
|
5
|
+
# --- Import Pretix Global Settings accessor ---
|
6
6
|
from django_scopes import scope
|
7
7
|
|
8
|
-
#
|
9
|
-
|
8
|
+
# Check if Pretix version uses AbstractSettingsHolder or GlobalSettingsObject
|
9
|
+
# Adjust import based on Pretix version if needed. Assume AbstractSettingsHolder for newer Pretix.
|
10
|
+
try:
|
11
|
+
from pretix.base.settings import GlobalSettingsObject as SettingsProxy
|
12
|
+
except ImportError:
|
13
|
+
try:
|
14
|
+
# Older pretix might use this pattern
|
15
|
+
from pretix.base.services.config import load_config
|
16
|
+
|
10
17
|
|
11
|
-
|
18
|
+
class SettingsProxy:
|
19
|
+
def __init__(self):
|
20
|
+
self.settings = load_config()
|
21
|
+
except ImportError:
|
22
|
+
# Fallback or raise error if neither is found
|
23
|
+
logger.error("Could not determine Pretix settings accessor for management command.")
|
24
|
+
raise ImportError("Cannot find Pretix settings accessor.")
|
25
|
+
|
26
|
+
# --- Import necessary Pretix models ---
|
27
|
+
from pretix.base.models import Order, Event, Organizer
|
28
|
+
|
29
|
+
# --- Import your Geocode model and the task ---
|
12
30
|
from pretix_mapplugin.models import OrderGeocodeData
|
13
31
|
from pretix_mapplugin.tasks import geocode_order_task
|
32
|
+
# --- Import Default User-Agent ---
|
33
|
+
from pretix_mapplugin.geocoding import DEFAULT_NOMINATIM_USER_AGENT
|
14
34
|
|
15
35
|
logger = logging.getLogger(__name__)
|
16
36
|
|
@@ -20,23 +40,16 @@ class Command(BaseCommand):
|
|
20
40
|
|
21
41
|
def add_arguments(self, parser):
|
22
42
|
parser.add_argument(
|
23
|
-
'--organizer',
|
24
|
-
type=str,
|
25
|
-
help='Slug of a specific organizer to process orders for.',
|
43
|
+
'--organizer', type=str, help='Slug of a specific organizer to process orders for.',
|
26
44
|
)
|
27
45
|
parser.add_argument(
|
28
|
-
'--event',
|
29
|
-
type=str,
|
30
|
-
help='Slug of a specific event to process orders for. Requires --organizer.',
|
46
|
+
'--event', type=str, help='Slug of a specific event to process orders for. Requires --organizer.',
|
31
47
|
)
|
32
48
|
parser.add_argument(
|
33
|
-
'--dry-run',
|
34
|
-
action='store_true',
|
35
|
-
help='Simulate the process without actually queuing tasks.',
|
49
|
+
'--dry-run', action='store_true', help='Simulate the process without actually queuing tasks.',
|
36
50
|
)
|
37
51
|
parser.add_argument(
|
38
|
-
'--force-recode',
|
39
|
-
action='store_true',
|
52
|
+
'--force-recode', action='store_true',
|
40
53
|
help='Queue geocoding even for orders that already have geocode data.',
|
41
54
|
)
|
42
55
|
|
@@ -49,86 +62,97 @@ class Command(BaseCommand):
|
|
49
62
|
if event_slug and not organizer_slug:
|
50
63
|
raise CommandError("You must specify --organizer when using --event.")
|
51
64
|
|
65
|
+
# --- Read User-Agent using Pretix Settings accessor ---
|
66
|
+
user_agent = DEFAULT_NOMINATIM_USER_AGENT
|
67
|
+
try:
|
68
|
+
# Get settings holder instance
|
69
|
+
gs = SettingsProxy()
|
70
|
+
# 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.
|
73
|
+
setting_key = 'plugin:pretix_mapplugin:nominatim_user_agent'
|
74
|
+
user_agent = gs.settings.get(setting_key, DEFAULT_NOMINATIM_USER_AGENT)
|
75
|
+
|
76
|
+
if user_agent == DEFAULT_NOMINATIM_USER_AGENT:
|
77
|
+
self.stdout.write(self.style.WARNING(
|
78
|
+
"Using default Nominatim User-Agent. Please set a specific "
|
79
|
+
f"'{setting_key}' in your pretix.cfg."
|
80
|
+
))
|
81
|
+
except Exception as e:
|
82
|
+
# Catch broad exception during settings access
|
83
|
+
self.stderr.write(self.style.ERROR(f"Failed to read plugin settings: {e}. Using default User-Agent."))
|
84
|
+
# Continue with default user_agent
|
85
|
+
# --- End Read User-Agent ---
|
86
|
+
|
52
87
|
# --- Determine which organizers to process ---
|
53
88
|
organizers_to_process = []
|
54
89
|
if organizer_slug:
|
55
90
|
try:
|
56
|
-
# Fetch specific organizer (outside scope)
|
57
91
|
organizer = Organizer.objects.get(slug=organizer_slug)
|
58
92
|
organizers_to_process.append(organizer)
|
59
93
|
self.stdout.write(f"Processing specified organizer: {organizer.name} ({organizer_slug})")
|
60
94
|
except Organizer.DoesNotExist:
|
61
95
|
raise CommandError(f"Organizer with slug '{organizer_slug}' not found.")
|
62
96
|
else:
|
63
|
-
# Fetch all organizers (outside scope)
|
64
97
|
organizers_to_process = list(Organizer.objects.all())
|
65
98
|
self.stdout.write(f"Processing all {len(organizers_to_process)} organizers...")
|
66
99
|
|
67
100
|
# --- Initialize counters ---
|
68
101
|
total_queued = 0
|
69
102
|
total_skipped = 0
|
70
|
-
total_processed_orders = 0
|
103
|
+
total_processed_orders = 0
|
71
104
|
|
72
105
|
# --- Iterate through organizers and activate scope ---
|
73
106
|
for organizer in organizers_to_process:
|
74
107
|
self.stdout.write(f"\n--- Processing Organizer: {organizer.name} ({organizer.slug}) ---")
|
75
108
|
|
76
|
-
# --- Activate scope for this organizer ---
|
77
109
|
with scope(organizer=organizer):
|
78
|
-
# ---
|
79
|
-
|
80
|
-
# Start with paid orders FOR THIS ORGANIZER
|
110
|
+
# --- Get orders ---
|
81
111
|
orders_qs = Order.objects.filter(status=Order.STATUS_PAID)
|
82
112
|
|
83
|
-
# Filter by
|
84
|
-
if event_slug and organizer.slug == organizer_slug:
|
113
|
+
# --- Filter by event if specified ---
|
114
|
+
if event_slug and organizer.slug == organizer_slug:
|
85
115
|
try:
|
86
|
-
|
87
|
-
event = Event.objects.get(slug=event_slug) # No need for organizer filter here
|
116
|
+
event = Event.objects.get(slug=event_slug)
|
88
117
|
orders_qs = orders_qs.filter(event=event)
|
89
118
|
self.stdout.write(f" Filtering orders for event: {event.name} ({event_slug})")
|
90
119
|
except Event.DoesNotExist:
|
91
|
-
# Don't raise CommandError, just report and skip event for this organizer
|
92
120
|
self.stderr.write(self.style.WARNING(
|
93
121
|
f" Event '{event_slug}' not found for this organizer. Skipping event filter."))
|
94
|
-
|
95
|
-
if organizer_slug and event_slug:
|
96
|
-
continue
|
122
|
+
if organizer_slug and event_slug: continue
|
97
123
|
|
98
|
-
# Filter orders needing geocoding
|
124
|
+
# --- Filter orders needing geocoding ---
|
125
|
+
relation_name = 'geocode_data' # Ensure this matches your model
|
99
126
|
if not force_recode:
|
100
127
|
try:
|
101
|
-
# Check
|
102
|
-
relation_name = 'geocode_data' # Change if necessary
|
103
|
-
Order._meta.get_field(relation_name)
|
128
|
+
Order._meta.get_field(relation_name) # Check existence
|
104
129
|
orders_to_process_qs = orders_qs.filter(**{f'{relation_name}__isnull': True})
|
105
130
|
self.stdout.write(" Selecting paid orders missing geocode data...")
|
106
131
|
except FieldDoesNotExist:
|
107
|
-
self.stderr.write(self.style.ERROR(
|
108
|
-
f" Could not find reverse relation '{relation_name}' on Order model. Check OrderGeocodeData model. Skipping organizer."))
|
109
|
-
continue # Skip this organizer if relation is wrong
|
110
|
-
except Exception as e:
|
111
132
|
self.stderr.write(
|
112
|
-
self.style.ERROR(f"
|
133
|
+
self.style.ERROR(f" Relation '{relation_name}' not found. Skipping organizer."))
|
134
|
+
continue
|
135
|
+
except Exception as e:
|
136
|
+
self.stderr.write(self.style.ERROR(f" Error checking relation: {e}. Skipping organizer."))
|
113
137
|
continue
|
114
138
|
else:
|
115
139
|
orders_to_process_qs = orders_qs
|
116
|
-
self.stdout.write(self.style.WARNING(
|
117
|
-
" Processing ALL selected paid orders for this organizer (--force-recode)..."))
|
140
|
+
self.stdout.write(self.style.WARNING(" Processing ALL selected paid orders (--force-recode)..."))
|
118
141
|
|
119
|
-
#
|
142
|
+
# --- Process orders for this scope ---
|
120
143
|
current_org_orders_count = orders_to_process_qs.count()
|
121
|
-
|
144
|
+
all_checked_for_org = orders_qs.count()
|
145
|
+
total_processed_orders += all_checked_for_org
|
122
146
|
|
123
147
|
if current_org_orders_count == 0:
|
124
|
-
self.stdout.write(" No orders need geocoding
|
125
|
-
continue
|
148
|
+
self.stdout.write(f" No orders need geocoding ({all_checked_for_org} checked).")
|
149
|
+
continue
|
126
150
|
|
127
|
-
self.stdout.write(
|
151
|
+
self.stdout.write(
|
152
|
+
f" Found {current_org_orders_count} order(s) to potentially geocode ({all_checked_for_org} checked).")
|
128
153
|
org_queued = 0
|
129
154
|
org_skipped = 0
|
130
155
|
|
131
|
-
# Iterate and queue (within scope)
|
132
156
|
for order in orders_to_process_qs.iterator():
|
133
157
|
if dry_run:
|
134
158
|
self.stdout.write(
|
@@ -136,9 +160,11 @@ class Command(BaseCommand):
|
|
136
160
|
org_queued += 1
|
137
161
|
else:
|
138
162
|
try:
|
139
|
-
|
140
|
-
|
141
|
-
|
163
|
+
# --- Pass user_agent to task ---
|
164
|
+
geocode_order_task.apply_async(
|
165
|
+
args=[order.pk],
|
166
|
+
kwargs={'nominatim_user_agent': user_agent} # Pass as kwarg
|
167
|
+
)
|
142
168
|
org_queued += 1
|
143
169
|
except Exception as e:
|
144
170
|
self.stderr.write(self.style.ERROR(f" ERROR queuing Order {order.code}: {e}"))
|
@@ -148,14 +174,13 @@ class Command(BaseCommand):
|
|
148
174
|
self.stdout.write(f" Queued: {org_queued}, Skipped: {org_skipped} for this organizer.")
|
149
175
|
total_queued += org_queued
|
150
176
|
total_skipped += org_skipped
|
151
|
-
|
152
|
-
# Scope for 'organizer' is automatically deactivated here by 'with' statement
|
177
|
+
# End scope
|
153
178
|
|
154
179
|
# --- Final Report ---
|
155
180
|
self.stdout.write("=" * 40)
|
156
181
|
self.stdout.write("Overall Summary:")
|
157
182
|
self.stdout.write(f" Organizers processed: {len(organizers_to_process)}")
|
158
|
-
self.stdout.write(f" Total orders checked (paid): {total_processed_orders}")
|
183
|
+
self.stdout.write(f" Total orders checked (paid): {total_processed_orders}")
|
159
184
|
if dry_run:
|
160
185
|
self.stdout.write(
|
161
186
|
self.style.SUCCESS(f"[DRY RUN] Complete. Would have queued tasks for {total_queued} order(s)."))
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import logging
|
2
|
+
from django.dispatch import receiver
|
3
|
+
from django.urls import reverse, NoReverseMatch
|
4
|
+
from django.utils.translation import gettext_lazy as _
|
5
|
+
from django.http import HttpRequest
|
6
|
+
# Import Django settings to read config in web process
|
7
|
+
from django.conf import settings
|
8
|
+
|
9
|
+
# --- Pretix Signals ---
|
10
|
+
from pretix.base.signals import order_paid
|
11
|
+
from pretix.control.signals import nav_event
|
12
|
+
|
13
|
+
# --- Tasks ---
|
14
|
+
from .tasks import geocode_order_task
|
15
|
+
# --- Geocoding Default ---
|
16
|
+
from .geocoding import DEFAULT_NOMINATIM_USER_AGENT # Import default
|
17
|
+
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
# --- Constants ---
|
21
|
+
MAP_VIEW_URL_NAME = 'plugins:pretix_mapplugin:event.settings.salesmap.show'
|
22
|
+
REQUIRED_MAP_PERMISSION = 'can_view_orders'
|
23
|
+
PLUGIN_NAME = 'pretix_mapplugin' # Define plugin name for settings access
|
24
|
+
|
25
|
+
|
26
|
+
# --- Signal Receiver for Geocoding (Reads setting, passes to task) ---
|
27
|
+
@receiver(order_paid, dispatch_uid="sales_mapper_order_paid_geocode")
|
28
|
+
def trigger_geocoding_on_payment(sender, order, **kwargs):
|
29
|
+
"""
|
30
|
+
Listens for the order_paid signal, reads geocoding config,
|
31
|
+
and queues the geocoding task with the config.
|
32
|
+
"""
|
33
|
+
user_agent = DEFAULT_NOMINATIM_USER_AGENT # Start with default
|
34
|
+
try:
|
35
|
+
# --- Read User-Agent from settings (works in web process) ---
|
36
|
+
# Check structure defensively before accessing
|
37
|
+
if hasattr(settings, 'plugins') and hasattr(settings.plugins, PLUGIN_NAME):
|
38
|
+
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
|
+
)
|
43
|
+
else:
|
44
|
+
logger.warning(
|
45
|
+
f"Could not access settings.plugins.{PLUGIN_NAME}, "
|
46
|
+
"using default Nominatim User-Agent for task."
|
47
|
+
)
|
48
|
+
|
49
|
+
# --- Queue task with user_agent as keyword argument ---
|
50
|
+
geocode_order_task.apply_async(
|
51
|
+
args=[order.pk],
|
52
|
+
kwargs={'nominatim_user_agent': user_agent} # Pass as kwarg
|
53
|
+
)
|
54
|
+
logger.info(f"Geocoding task queued for paid order {order.code} (PK: {order.pk}).")
|
55
|
+
|
56
|
+
except ImportError: # Error finding geocode_order_task itself if tasks.py fails
|
57
|
+
logger.exception("Could not import geocode_order_task. Check tasks.py.")
|
58
|
+
except Exception as e:
|
59
|
+
logger.exception(f"Failed to queue geocoding task for order {order.code}: {e}")
|
60
|
+
|
61
|
+
|
62
|
+
# --- Signal Receiver for Adding Navigation Item (No changes needed) ---
|
63
|
+
@receiver(nav_event, dispatch_uid="sales_mapper_nav_event_add_map")
|
64
|
+
def add_map_nav_item(sender, request: HttpRequest, **kwargs):
|
65
|
+
"""
|
66
|
+
Adds a navigation item for the Sales Map to the event control panel sidebar.
|
67
|
+
"""
|
68
|
+
has_permission = request.user.has_event_permission(request.organizer, request.event, REQUIRED_MAP_PERMISSION,
|
69
|
+
request=request)
|
70
|
+
if not has_permission: return []
|
71
|
+
try:
|
72
|
+
map_url = reverse(MAP_VIEW_URL_NAME, kwargs={
|
73
|
+
'organizer': request.organizer.slug,
|
74
|
+
'event': request.event.slug,
|
75
|
+
})
|
76
|
+
except NoReverseMatch:
|
77
|
+
logger.error(f"Could not reverse URL for map view '{MAP_VIEW_URL_NAME}'. Check urls.py.")
|
78
|
+
return []
|
79
|
+
is_active = False
|
80
|
+
if hasattr(request, 'resolver_match') and request.resolver_match:
|
81
|
+
is_active = request.resolver_match.view_name == MAP_VIEW_URL_NAME
|
82
|
+
return [{
|
83
|
+
'label': _('Sales Map'),
|
84
|
+
'url': map_url,
|
85
|
+
'active': is_active,
|
86
|
+
'icon': 'map-o',
|
87
|
+
}]
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import logging
|
2
|
+
from django.db import transaction
|
3
|
+
from django.core.exceptions import ObjectDoesNotExist
|
4
|
+
|
5
|
+
# --- Use Pretix Celery app instance ---
|
6
|
+
from pretix.celery_app import app
|
7
|
+
# --- Import necessary Pretix models ---
|
8
|
+
from pretix.base.models import Order
|
9
|
+
|
10
|
+
# --- Import your Geocode model and geocoding functions ---
|
11
|
+
from .models import OrderGeocodeData
|
12
|
+
from .geocoding import (
|
13
|
+
get_formatted_address_from_order,
|
14
|
+
geocode_address,
|
15
|
+
DEFAULT_NOMINATIM_USER_AGENT # Import default for safety/logging
|
16
|
+
)
|
17
|
+
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
|
21
|
+
# Define the Celery task
|
22
|
+
# bind=True gives access to self (the task instance) for retrying
|
23
|
+
# ignore_result=True as we don't need the return value stored in Celery backend
|
24
|
+
@app.task(bind=True, max_retries=3, default_retry_delay=60, ignore_result=True)
|
25
|
+
def geocode_order_task(self, order_pk: int,
|
26
|
+
nominatim_user_agent: str | None = None): # Added nominatim_user_agent kwarg
|
27
|
+
"""
|
28
|
+
Celery task to geocode the address for a given order PK.
|
29
|
+
Accepts the Nominatim User-Agent as an argument.
|
30
|
+
"""
|
31
|
+
try:
|
32
|
+
# Fetch order with related address and country data efficiently
|
33
|
+
order = Order.objects.select_related(
|
34
|
+
'invoice_address',
|
35
|
+
'invoice_address__country'
|
36
|
+
).get(pk=order_pk)
|
37
|
+
logger.info(f"Starting geocoding task for Order {order.code} (PK: {order_pk})")
|
38
|
+
|
39
|
+
# Check if already geocoded to prevent redundant work
|
40
|
+
# Replace 'geocode_data' if your related_name is different
|
41
|
+
relation_name = 'geocode_data' # Ensure this matches your OrderGeocodeData.order related_name
|
42
|
+
if hasattr(order, relation_name) and getattr(order, relation_name) is not None:
|
43
|
+
logger.info(f"Geocode data already exists for Order {order.code}. Skipping.")
|
44
|
+
return # Exit successfully
|
45
|
+
|
46
|
+
# 1. Get formatted address string
|
47
|
+
address_str = get_formatted_address_from_order(order)
|
48
|
+
if not address_str:
|
49
|
+
logger.info(f"Order {order.code} has no address suitable for geocoding. Storing null coordinates.")
|
50
|
+
# Store null to prevent reprocessing
|
51
|
+
with transaction.atomic():
|
52
|
+
OrderGeocodeData.objects.update_or_create(
|
53
|
+
order=order,
|
54
|
+
defaults={'latitude': None, 'longitude': None}
|
55
|
+
)
|
56
|
+
return # Exit successfully, nothing to geocode
|
57
|
+
|
58
|
+
# 2. Perform geocoding, passing the user agent received by the task
|
59
|
+
logger.debug(f"Attempting to geocode address for Order {order.code}: '{address_str}'")
|
60
|
+
coordinates = geocode_address(address_str, nominatim_user_agent=nominatim_user_agent)
|
61
|
+
|
62
|
+
# 3. Store result (or null if failed) using atomic transaction
|
63
|
+
with transaction.atomic():
|
64
|
+
if coordinates:
|
65
|
+
latitude, longitude = coordinates
|
66
|
+
obj, created = OrderGeocodeData.objects.update_or_create(
|
67
|
+
order=order,
|
68
|
+
defaults={'latitude': latitude, 'longitude': longitude}
|
69
|
+
)
|
70
|
+
log_level = logging.INFO if created else logging.DEBUG # Be less noisy on updates
|
71
|
+
logger.log(log_level,
|
72
|
+
f"Saved{' new' if created else ' updated'} geocode data for Order {order.code}: ({latitude}, {longitude})")
|
73
|
+
else:
|
74
|
+
logger.warning(f"Geocoding failed for Order {order.code}. Storing null coordinates.")
|
75
|
+
# Store nulls to indicate an attempt was made and failed
|
76
|
+
obj, created = OrderGeocodeData.objects.update_or_create(
|
77
|
+
order=order,
|
78
|
+
defaults={'latitude': None, 'longitude': None}
|
79
|
+
)
|
80
|
+
log_level = logging.INFO if created else logging.DEBUG
|
81
|
+
logger.log(log_level,
|
82
|
+
f"Saved{' new' if created else ' updated'} null geocode data for Order {order.code} after failed attempt.")
|
83
|
+
|
84
|
+
except ObjectDoesNotExist: # More specific exception
|
85
|
+
logger.error(f"Order with PK {order_pk} not found in geocode_order_task.")
|
86
|
+
# Don't retry if the order doesn't exist
|
87
|
+
except Exception as e:
|
88
|
+
# Catch any other unexpected errors
|
89
|
+
logger.exception(f"Unexpected error in geocode_order_task for Order PK {order_pk}: {e}")
|
90
|
+
# Retry on potentially temporary errors (database, network issues etc.)
|
91
|
+
raise self.retry(exc=e) # Let Celery handle retry logic
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "0.0.5"
|
@@ -1,113 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
# --- Import Django settings ---
|
4
|
-
from django.conf import settings
|
5
|
-
from geopy.exc import GeocoderServiceError, GeocoderTimedOut
|
6
|
-
from geopy.geocoders import Nominatim
|
7
|
-
from time import sleep
|
8
|
-
|
9
|
-
# Configure logging for your plugin
|
10
|
-
logger = logging.getLogger(__name__)
|
11
|
-
|
12
|
-
# --- Configuration Default ---
|
13
|
-
# Define a default/fallback User-Agent. Users *should* override this in pretix.cfg.
|
14
|
-
DEFAULT_NOMINATIM_USER_AGENT = "pretix-map-plugin/unknown (Please configure nominatim_user_agent in pretix.cfg)"
|
15
|
-
|
16
|
-
|
17
|
-
# --- Geocoding Function ---
|
18
|
-
|
19
|
-
def geocode_address(address_string: str) -> tuple[float, float] | None:
|
20
|
-
"""
|
21
|
-
Tries to geocode a given address string using Nominatim, reading the
|
22
|
-
User-Agent from Pretix configuration.
|
23
|
-
|
24
|
-
Args:
|
25
|
-
address_string: A single string representing the address.
|
26
|
-
|
27
|
-
Returns:
|
28
|
-
A tuple (latitude, longitude) if successful, otherwise None.
|
29
|
-
"""
|
30
|
-
# --- Get User-Agent from Pretix Settings ---
|
31
|
-
# Access plugin settings via settings.plugins.<your_plugin_name>
|
32
|
-
# The .get() method allows providing a default value if the setting is missing.
|
33
|
-
user_agent = settings.plugins.pretix_mapplugin.get(
|
34
|
-
'nominatim_user_agent', # The setting name defined in pretix.cfg
|
35
|
-
DEFAULT_NOMINATIM_USER_AGENT
|
36
|
-
)
|
37
|
-
|
38
|
-
# Log a warning if the default User-Agent is being used, as it's required
|
39
|
-
# by Nominatim policy to be specific and include contact info.
|
40
|
-
if user_agent == DEFAULT_NOMINATIM_USER_AGENT:
|
41
|
-
logger.warning(
|
42
|
-
"Using default Nominatim User-Agent. Please set a specific "
|
43
|
-
"'nominatim_user_agent' under [pretix_mapplugin] in your "
|
44
|
-
"pretix.cfg according to Nominatim's usage policy."
|
45
|
-
)
|
46
|
-
# --- End Settings Retrieval ---
|
47
|
-
|
48
|
-
# Initialize the geolocator with the configured or default user_agent
|
49
|
-
geolocator = Nominatim(user_agent=user_agent)
|
50
|
-
|
51
|
-
try:
|
52
|
-
# Add a 1-second delay to respect Nominatim's usage policy (1 req/sec)
|
53
|
-
sleep(1)
|
54
|
-
|
55
|
-
location = geolocator.geocode(address_string, timeout=10)
|
56
|
-
|
57
|
-
if location:
|
58
|
-
logger.debug(
|
59
|
-
f"Geocoded '{address_string}' to ({location.latitude}, {location.longitude}) using User-Agent: {user_agent}")
|
60
|
-
return (location.latitude, location.longitude)
|
61
|
-
else:
|
62
|
-
logger.warning(f"Could not geocode address: {address_string} (Address not found by Nominatim)")
|
63
|
-
return None
|
64
|
-
|
65
|
-
except GeocoderTimedOut:
|
66
|
-
logger.error(f"Geocoding timed out for address: {address_string}")
|
67
|
-
return None
|
68
|
-
except GeocoderServiceError as e:
|
69
|
-
logger.error(f"Geocoding service error for address '{address_string}': {e}")
|
70
|
-
return None
|
71
|
-
except Exception as e:
|
72
|
-
logger.exception(f"An unexpected error occurred during geocoding for address '{address_string}': {e}")
|
73
|
-
return None
|
74
|
-
|
75
|
-
|
76
|
-
# --- Helper to Format Address from Pretix Order (No changes needed here) ---
|
77
|
-
|
78
|
-
def get_formatted_address_from_order(order) -> str | None:
|
79
|
-
"""
|
80
|
-
Creates a formatted address string from a Pretix order's invoice address.
|
81
|
-
"""
|
82
|
-
if not order.invoice_address:
|
83
|
-
return None
|
84
|
-
parts = []
|
85
|
-
if order.invoice_address.street: parts.append(order.invoice_address.street)
|
86
|
-
if order.invoice_address.city: parts.append(order.invoice_address.city)
|
87
|
-
if order.invoice_address.zipcode: parts.append(order.invoice_address.zipcode)
|
88
|
-
if order.invoice_address.state: parts.append(order.invoice_address.state)
|
89
|
-
if order.invoice_address.country: parts.append(str(order.invoice_address.country.name))
|
90
|
-
if not parts: return None
|
91
|
-
full_address = ", ".join(filter(None, parts))
|
92
|
-
return full_address
|
93
|
-
|
94
|
-
|
95
|
-
# --- Example Usage (Conceptual - No changes needed here) ---
|
96
|
-
# This function itself isn't called directly, the logic is in tasks.py
|
97
|
-
def process_order_for_geocoding(order):
|
98
|
-
"""Conceptual function showing how to use the geocoding."""
|
99
|
-
address_str = get_formatted_address_from_order(order)
|
100
|
-
if not address_str:
|
101
|
-
logger.info(f"Order {order.code} has no invoice address to geocode.")
|
102
|
-
return None
|
103
|
-
|
104
|
-
coordinates = geocode_address(address_str) # This now uses the configured User-Agent
|
105
|
-
|
106
|
-
if coordinates:
|
107
|
-
latitude, longitude = coordinates
|
108
|
-
logger.info(f"Successfully geocoded Order {order.code}: ({latitude}, {longitude})")
|
109
|
-
# Store coordinates...
|
110
|
-
return coordinates
|
111
|
-
else:
|
112
|
-
logger.warning(f"Failed to geocode Order {order.code} with address: {address_str}")
|
113
|
-
return None
|
@@ -1,73 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
from django.dispatch import receiver
|
3
|
-
from django.http import HttpRequest # For type hinting
|
4
|
-
from django.urls import NoReverseMatch, reverse # Import reverse and NoReverseMatch
|
5
|
-
from django.utils.translation import gettext_lazy as _ # For translatable labels
|
6
|
-
|
7
|
-
# --- Pretix Signals ---
|
8
|
-
from pretix.base.signals import order_paid
|
9
|
-
from pretix.control.signals import nav_event # Import the navigation signal
|
10
|
-
|
11
|
-
# --- Tasks ---
|
12
|
-
from .tasks import geocode_order_task
|
13
|
-
|
14
|
-
logger = logging.getLogger(__name__)
|
15
|
-
|
16
|
-
# --- Constants ---
|
17
|
-
MAP_VIEW_URL_NAME = 'plugins:pretix_mapplugin:event.settings.salesmap.show'
|
18
|
-
# Define the permission required to see the map link
|
19
|
-
REQUIRED_MAP_PERMISSION = 'can_view_orders'
|
20
|
-
|
21
|
-
|
22
|
-
# --- Signal Receiver for Geocoding (Keep As Is) ---
|
23
|
-
@receiver(order_paid, dispatch_uid="sales_mapper_order_paid_geocode")
|
24
|
-
def trigger_geocoding_on_payment(sender, order, **kwargs):
|
25
|
-
# ... (keep your existing geocoding logic) ...
|
26
|
-
try:
|
27
|
-
geocode_order_task.apply_async(args=[order.pk])
|
28
|
-
logger.info(f"Geocoding task queued for paid order {order.code} (PK: {order.pk}).")
|
29
|
-
except NameError:
|
30
|
-
logger.error("geocode_order_task not found. Make sure it's imported correctly.")
|
31
|
-
except Exception as e:
|
32
|
-
logger.exception(f"Failed to queue geocoding task for order {order.code}: {e}")
|
33
|
-
|
34
|
-
|
35
|
-
# --- Signal Receiver for Adding Navigation Item ---
|
36
|
-
@receiver(nav_event, dispatch_uid="sales_mapper_nav_event_add_map")
|
37
|
-
def add_map_nav_item(sender, request: HttpRequest, **kwargs):
|
38
|
-
"""
|
39
|
-
Adds a navigation item for the Sales Map to the event control panel sidebar.
|
40
|
-
"""
|
41
|
-
# Check if the user has the required permission for the current event
|
42
|
-
has_permission = request.user.has_event_permission(
|
43
|
-
request.organizer, request.event, REQUIRED_MAP_PERMISSION, request=request
|
44
|
-
)
|
45
|
-
if not has_permission:
|
46
|
-
return [] # Return empty list if user lacks permission
|
47
|
-
|
48
|
-
# Try to generate the URL for the map view
|
49
|
-
try:
|
50
|
-
map_url = reverse(MAP_VIEW_URL_NAME, kwargs={
|
51
|
-
'organizer': request.organizer.slug,
|
52
|
-
'event': request.event.slug,
|
53
|
-
})
|
54
|
-
except NoReverseMatch:
|
55
|
-
logger.error(f"Could not reverse URL for map view '{MAP_VIEW_URL_NAME}'. Check urls.py.")
|
56
|
-
return [] # Return empty list if URL cannot be generated
|
57
|
-
|
58
|
-
# Check if the current page *is* the map page to set the 'active' state
|
59
|
-
is_active = False
|
60
|
-
if hasattr(request, 'resolver_match') and request.resolver_match:
|
61
|
-
is_active = request.resolver_match.view_name == MAP_VIEW_URL_NAME
|
62
|
-
|
63
|
-
# Define the navigation item dictionary
|
64
|
-
nav_item = {
|
65
|
-
'label': _('Sales Map'), # Translatable label
|
66
|
-
'url': map_url,
|
67
|
-
'active': is_active,
|
68
|
-
'icon': 'map-o', # Font Awesome icon name (fa-map-o) - adjust if needed
|
69
|
-
# 'category': _('Orders'), # Optional: Suggests category, placement might vary
|
70
|
-
}
|
71
|
-
|
72
|
-
# Return the item in a list
|
73
|
-
return [nav_item]
|
@@ -1,74 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
from pretix.base.models import Order
|
3
|
-
from pretix.celery_app import app # Import the Pretix Celery app instance
|
4
|
-
|
5
|
-
from .geocoding import ( # Import from Step 3
|
6
|
-
geocode_address,
|
7
|
-
get_formatted_address_from_order,
|
8
|
-
)
|
9
|
-
from .models import OrderGeocodeData
|
10
|
-
|
11
|
-
logger = logging.getLogger(__name__)
|
12
|
-
|
13
|
-
@app.task(bind=True, max_retries=3, default_retry_delay=60) # Configure retry behavior
|
14
|
-
def geocode_order_task(self, order_pk: int):
|
15
|
-
"""
|
16
|
-
Celery task to geocode an order's address and store the result.
|
17
|
-
"""
|
18
|
-
try:
|
19
|
-
order = Order.objects.select_related('invoice_address').get(pk=order_pk)
|
20
|
-
logger.info(f"Starting geocoding task for Order {order.code} (PK: {order_pk})")
|
21
|
-
|
22
|
-
# Check if already geocoded to prevent redundant work (e.g., if task retries)
|
23
|
-
if OrderGeocodeData.objects.filter(order=order).exists():
|
24
|
-
logger.info(f"Geocode data already exists for Order {order.code}. Skipping.")
|
25
|
-
return # Exit successfully
|
26
|
-
|
27
|
-
# 1. Get formatted address
|
28
|
-
address_str = get_formatted_address_from_order(order)
|
29
|
-
if not address_str:
|
30
|
-
logger.warning(f"Order {order.code} has no invoice address to geocode.")
|
31
|
-
return # Exit successfully, nothing to do
|
32
|
-
|
33
|
-
# 2. Perform geocoding (using function from Step 3)
|
34
|
-
logger.debug(f"Attempting to geocode address for Order {order.code}: '{address_str}'")
|
35
|
-
coordinates = geocode_address(address_str) # This handles its own errors/logging
|
36
|
-
|
37
|
-
# 3. Store result if successful
|
38
|
-
if coordinates:
|
39
|
-
latitude, longitude = coordinates
|
40
|
-
try:
|
41
|
-
# Use update_or_create to handle potential race conditions gracefully,
|
42
|
-
# although the initial check makes it less likely.
|
43
|
-
obj, created = OrderGeocodeData.objects.update_or_create(
|
44
|
-
order=order,
|
45
|
-
defaults={
|
46
|
-
'latitude': latitude,
|
47
|
-
'longitude': longitude
|
48
|
-
}
|
49
|
-
)
|
50
|
-
if created:
|
51
|
-
logger.info(f"Successfully geocoded and stored coordinates for Order {order.code}: ({latitude}, {longitude})")
|
52
|
-
else:
|
53
|
-
logger.info(f"Successfully geocoded and updated coordinates for Order {order.code}: ({latitude}, {longitude})")
|
54
|
-
|
55
|
-
except Exception as e:
|
56
|
-
logger.exception(f"Failed to save geocode data for Order {order.code} to database: {e}")
|
57
|
-
# Optionally retry the task if saving failed
|
58
|
-
self.retry(exc=e)
|
59
|
-
else:
|
60
|
-
# Geocoding function failed (already logged within geocode_address)
|
61
|
-
logger.warning(f"Geocoding failed for Order {order.code}. No coordinates stored.")
|
62
|
-
# Decide if you want to retry here based on the type of geocoding failure
|
63
|
-
# For example, don't retry if address was not found, but maybe retry on timeout.
|
64
|
-
# The geocode_address function would need to return more info for that.
|
65
|
-
# For now, we just log and don't store anything.
|
66
|
-
|
67
|
-
except Order.DoesNotExist:
|
68
|
-
logger.error(f"Order with PK {order_pk} not found for geocoding task.")
|
69
|
-
# Don't retry if the order doesn't exist
|
70
|
-
except Exception as e:
|
71
|
-
# Catch any other unexpected errors in the task
|
72
|
-
logger.exception(f"Unexpected error in geocode_order_task for Order PK {order_pk}: {e}")
|
73
|
-
# Retry on unexpected errors
|
74
|
-
self.retry(exc=e)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.mo
RENAMED
File without changes
|
{pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css
RENAMED
File without changes
|
{pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pretix_map-0.0.5 → pretix_map-0.0.6}/pretix_mapplugin/templates/pretix_mapplugin/map_page.html
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|