pretix-map 0.1.7__tar.gz → 0.1.8__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.1.7/pretix_map.egg-info → pretix_map-0.1.8}/PKG-INFO +1 -1
- {pretix_map-0.1.7 → pretix_map-0.1.8/pretix_map.egg-info}/PKG-INFO +1 -1
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_map.egg-info/SOURCES.txt +1 -0
- pretix_map-0.1.8/pretix_mapplugin/__init__.py +1 -0
- pretix_map-0.1.8/pretix_mapplugin/migrations/0004_ordergeocodedata_success_level.py +15 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/models.py +6 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +146 -92
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/tasks.py +17 -4
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/templates/pretix_mapplugin/map_page.html +20 -15
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/templates/pretix_mapplugin/milestones.html +25 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/views.py +11 -2
- pretix_map-0.1.7/pretix_mapplugin/__init__.py +0 -1
- {pretix_map-0.1.7 → pretix_map-0.1.8}/LICENSE +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/MANIFEST.in +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/README.md +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_map.egg-info/dependency_links.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_map.egg-info/entry_points.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_map.egg-info/requires.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_map.egg-info/top_level.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/apps.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/geocoding.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/locale/de/LC_MESSAGES/django.po +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/locale/de_Informal/.gitkeep +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/management/__init__.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/management/commands/__init__.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/management/commands/geocode_existing_orders.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/migrations/0001_initial.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/migrations/0003_mapmilestone.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/migrations/__init__.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/signals.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/.gitkeep +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers-2x.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon-2x.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-shadow.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js.map +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js.map +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js.map +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js.map +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/templates/pretix_mapplugin/.gitkeep +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/urls.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/pyproject.toml +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/setup.cfg +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/setup.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.8}/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.
|
|
3
|
+
Version: 0.1.8
|
|
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.
|
|
3
|
+
Version: 0.1.8
|
|
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>
|
|
@@ -27,6 +27,7 @@ pretix_mapplugin/management/commands/geocode_existing_orders.py
|
|
|
27
27
|
pretix_mapplugin/migrations/0001_initial.py
|
|
28
28
|
pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py
|
|
29
29
|
pretix_mapplugin/migrations/0003_mapmilestone.py
|
|
30
|
+
pretix_mapplugin/migrations/0004_ordergeocodedata_success_level.py
|
|
30
31
|
pretix_mapplugin/migrations/__init__.py
|
|
31
32
|
pretix_mapplugin/static/pretix_mapplugin/.gitkeep
|
|
32
33
|
pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.8"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from django.db import migrations, models
|
|
2
|
+
|
|
3
|
+
class Migration(migrations.Migration):
|
|
4
|
+
|
|
5
|
+
dependencies = [
|
|
6
|
+
('pretix_mapplugin', '0003_mapmilestone'),
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
operations = [
|
|
10
|
+
migrations.AddField(
|
|
11
|
+
model_name='ordergeocodedata',
|
|
12
|
+
name='success_level',
|
|
13
|
+
field=models.CharField(blank=True, help_text='Precision of geocoding (e.g., street, city, zip, failed)', max_length=50, null=True),
|
|
14
|
+
),
|
|
15
|
+
]
|
|
@@ -21,6 +21,12 @@ class OrderGeocodeData(LoggedModel): # Keep LoggedModel if you want audit logs
|
|
|
21
21
|
null=True, # Allow NULL in the database if geocoding fails
|
|
22
22
|
blank=True # Allow blank in forms/admin
|
|
23
23
|
)
|
|
24
|
+
success_level = models.CharField(
|
|
25
|
+
max_length=50,
|
|
26
|
+
null=True,
|
|
27
|
+
blank=True,
|
|
28
|
+
help_text="Precision of geocoding (e.g., street, city, zip, failed)"
|
|
29
|
+
)
|
|
24
30
|
|
|
25
31
|
# Change to auto_now to update timestamp on every save (successful or null)
|
|
26
32
|
last_geocoded_at = models.DateTimeField(
|
{pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js
RENAMED
|
@@ -66,7 +66,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
66
66
|
|
|
67
67
|
// --- State ---
|
|
68
68
|
let map = null, mapCompare = null, allData = [], filteredData = [], displayData = [], milestones = [], currency = "EUR";
|
|
69
|
-
let itemAvailData = {};
|
|
70
69
|
let compareData = [], compareFilteredData = [], compareDisplayData = [];
|
|
71
70
|
let pinLayer = null, heatmapLayer = null, gridLayer = null, comparisonLayer = null, eventMarkerLayer = null;
|
|
72
71
|
let pinLayerComp = null, heatmapLayerComp = null, gridLayerComp = null, eventMarkerLayerComp = null;
|
|
@@ -75,6 +74,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
75
74
|
let availableItems = new Set(), selectedItems = new Set();
|
|
76
75
|
let currentDisplayMode = 'map'; // map | list | stats
|
|
77
76
|
let charts = {};
|
|
77
|
+
let itemAvailData = {};
|
|
78
78
|
|
|
79
79
|
function updateStatus(msg, isErr = false) {
|
|
80
80
|
if (sel.overlay) {
|
|
@@ -160,6 +160,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
160
160
|
minOpacity: 0.4
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
|
+
if (heatmapLayerComp) {
|
|
164
|
+
heatmapLayerComp.setOptions({
|
|
165
|
+
radius: parseInt(sel.radiusIn.value),
|
|
166
|
+
blur: parseInt(sel.blurIn.value),
|
|
167
|
+
maxZoom: parseInt(sel.maxZoomIn.value),
|
|
168
|
+
minOpacity: 0.4
|
|
169
|
+
});
|
|
170
|
+
}
|
|
163
171
|
};
|
|
164
172
|
[sel.radiusIn, sel.blurIn, sel.maxZoomIn].forEach(i => i.oninput = updateHeat);
|
|
165
173
|
sel.heatmapReset.onclick = () => {
|
|
@@ -173,6 +181,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
173
181
|
function switchDisplayMode(mode) {
|
|
174
182
|
currentDisplayMode = mode;
|
|
175
183
|
sel.map.style.display = (mode === 'map' ? 'block' : 'none');
|
|
184
|
+
sel.mapSplitRoot.style.display = (mode === 'map' ? 'flex' : 'none'); // FIX
|
|
176
185
|
sel.failedContainer.style.display = (mode === 'list' ? 'block' : 'none');
|
|
177
186
|
sel.analyticsView.style.display = (mode === 'stats' ? 'block' : 'none');
|
|
178
187
|
|
|
@@ -181,7 +190,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
181
190
|
sel.listToggle.textContent = (mode === 'list' ? 'Show Map' : 'Failed Orders');
|
|
182
191
|
sel.statsToggle.textContent = (mode === 'stats' ? 'Show Map' : 'Analytics View');
|
|
183
192
|
|
|
184
|
-
if (mode === 'map')
|
|
193
|
+
if (mode === 'map') {
|
|
194
|
+
map.invalidateSize();
|
|
195
|
+
if (mapCompare) mapCompare.invalidateSize();
|
|
196
|
+
}
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
function renderStats(s) {
|
|
@@ -249,92 +261,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
249
261
|
updateTimelineDisplay();
|
|
250
262
|
}
|
|
251
263
|
|
|
252
|
-
function renderItemAvailability(steps) {
|
|
253
|
-
if (!sel.itemAvailList || !steps || steps.length === 0) return;
|
|
254
|
-
sel.itemAvailList.innerHTML = '';
|
|
255
|
-
sel.itemAvailContainer.style.display = 'block';
|
|
256
|
-
|
|
257
|
-
const minTs = steps[0];
|
|
258
|
-
const maxTs = steps[steps.length - 1];
|
|
259
|
-
const range = maxTs - minTs;
|
|
260
|
-
|
|
261
|
-
Object.keys(itemAvailData).forEach(itemName => {
|
|
262
|
-
const d = itemAvailData[itemName];
|
|
263
|
-
const firstTs = d.first_sale ? new Date(d.first_sale).getTime() : null;
|
|
264
|
-
const lastTs = d.last_sale ? new Date(d.last_sale).getTime() : null;
|
|
265
|
-
const availFrom = d.available_from ? new Date(d.available_from).getTime() : null;
|
|
266
|
-
const availUntil = d.available_until ? new Date(d.available_until).getTime() : null;
|
|
267
|
-
|
|
268
|
-
if (!firstTs && !availFrom) return;
|
|
269
|
-
|
|
270
|
-
const row = document.createElement('div');
|
|
271
|
-
row.style.height = '14px';
|
|
272
|
-
row.style.marginBottom = '2px';
|
|
273
|
-
row.style.position = 'relative';
|
|
274
|
-
row.style.width = '100%';
|
|
275
|
-
row.style.display = 'flex';
|
|
276
|
-
row.style.alignItems = 'center';
|
|
277
|
-
|
|
278
|
-
const label = document.createElement('div');
|
|
279
|
-
label.textContent = itemName;
|
|
280
|
-
label.style.width = '120px';
|
|
281
|
-
label.style.overflow = 'hidden';
|
|
282
|
-
label.style.textOverflow = 'ellipsis';
|
|
283
|
-
label.style.whiteSpace = 'nowrap';
|
|
284
|
-
label.style.marginRight = '5px';
|
|
285
|
-
row.appendChild(label);
|
|
286
|
-
|
|
287
|
-
const barContainer = document.createElement('div');
|
|
288
|
-
barContainer.style.flexGrow = '1';
|
|
289
|
-
barContainer.style.height = '8px';
|
|
290
|
-
barContainer.style.background = '#f9f9f9';
|
|
291
|
-
barContainer.style.position = 'relative';
|
|
292
|
-
barContainer.style.borderRadius = '4px';
|
|
293
|
-
row.appendChild(barContainer);
|
|
294
|
-
|
|
295
|
-
// Planned availability (light grey bar)
|
|
296
|
-
if (availFrom || availUntil) {
|
|
297
|
-
const start = availFrom ? Math.max(availFrom, minTs) : minTs;
|
|
298
|
-
const end = availUntil ? Math.min(availUntil, maxTs) : maxTs;
|
|
299
|
-
if (start < end) {
|
|
300
|
-
const left = ((start - minTs) / range) * 100;
|
|
301
|
-
const width = ((end - start) / range) * 100;
|
|
302
|
-
const bar = document.createElement('div');
|
|
303
|
-
bar.style.position = 'absolute';
|
|
304
|
-
bar.style.left = `${left}%`;
|
|
305
|
-
bar.style.width = `${width}%`;
|
|
306
|
-
bar.style.height = '100%';
|
|
307
|
-
bar.style.background = '#eee';
|
|
308
|
-
bar.style.borderRadius = '4px';
|
|
309
|
-
bar.title = `${itemName} (Configured Availability)`;
|
|
310
|
-
barContainer.appendChild(bar);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Actual sales period (blue bar)
|
|
315
|
-
if (firstTs && lastTs) {
|
|
316
|
-
const start = Math.max(firstTs, minTs);
|
|
317
|
-
const end = Math.min(lastTs, maxTs);
|
|
318
|
-
if (start <= end) {
|
|
319
|
-
const left = ((start - minTs) / range) * 100;
|
|
320
|
-
const width = Math.max(((end - start) / range) * 100, 0.5); // Min width for visibility
|
|
321
|
-
const bar = document.createElement('div');
|
|
322
|
-
bar.style.position = 'absolute';
|
|
323
|
-
bar.style.left = `${left}%`;
|
|
324
|
-
bar.style.width = `${width}%`;
|
|
325
|
-
bar.style.height = '100%';
|
|
326
|
-
bar.style.background = 'rgba(54, 162, 235, 0.6)';
|
|
327
|
-
bar.style.borderRadius = '4px';
|
|
328
|
-
bar.style.zIndex = '1';
|
|
329
|
-
bar.title = `${itemName} (Actual Sales)`;
|
|
330
|
-
barContainer.appendChild(bar);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
sel.itemAvailList.appendChild(row);
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
|
|
338
264
|
function renderTimelineVolumeChart(steps) {
|
|
339
265
|
if (!sel.timelineChart) return;
|
|
340
266
|
const ctx = sel.timelineChart.getContext('2d');
|
|
@@ -415,6 +341,87 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
415
341
|
});
|
|
416
342
|
}
|
|
417
343
|
|
|
344
|
+
function renderItemAvailability(steps) {
|
|
345
|
+
if (!sel.itemAvailList || !steps || steps.length === 0) return;
|
|
346
|
+
sel.itemAvailList.innerHTML = '';
|
|
347
|
+
|
|
348
|
+
const minTs = steps[0];
|
|
349
|
+
const maxTs = steps[steps.length - 1];
|
|
350
|
+
const range = maxTs - minTs;
|
|
351
|
+
|
|
352
|
+
Object.keys(itemAvailData).forEach(itemName => {
|
|
353
|
+
const d = itemAvailData[itemName];
|
|
354
|
+
const firstTs = d.first_sale ? new Date(d.first_sale).getTime() : null;
|
|
355
|
+
const lastTs = d.last_sale ? new Date(d.last_sale).getTime() : null;
|
|
356
|
+
const availFrom = d.available_from ? new Date(d.available_from).getTime() : null;
|
|
357
|
+
const availUntil = d.available_until ? new Date(d.available_until).getTime() : null;
|
|
358
|
+
|
|
359
|
+
if (!firstTs && !availFrom) return;
|
|
360
|
+
|
|
361
|
+
const row = document.createElement('div');
|
|
362
|
+
row.style.marginBottom = '12px';
|
|
363
|
+
row.style.position = 'relative';
|
|
364
|
+
row.style.width = '100%';
|
|
365
|
+
|
|
366
|
+
const label = document.createElement('div');
|
|
367
|
+
label.textContent = itemName;
|
|
368
|
+
label.style.fontSize = '9px';
|
|
369
|
+
label.style.color = '#333';
|
|
370
|
+
label.style.marginBottom = '2px';
|
|
371
|
+
label.style.fontWeight = 'bold';
|
|
372
|
+
row.appendChild(label);
|
|
373
|
+
|
|
374
|
+
const barContainer = document.createElement('div');
|
|
375
|
+
barContainer.style.width = '100%';
|
|
376
|
+
barContainer.style.height = '6px';
|
|
377
|
+
barContainer.style.background = '#f0f0f0';
|
|
378
|
+
barContainer.style.position = 'relative';
|
|
379
|
+
barContainer.style.borderRadius = '3px';
|
|
380
|
+
row.appendChild(barContainer);
|
|
381
|
+
|
|
382
|
+
// Planned availability (light grey bar)
|
|
383
|
+
if (availFrom || availUntil) {
|
|
384
|
+
const start = availFrom ? Math.max(availFrom, minTs) : minTs;
|
|
385
|
+
const end = availUntil ? Math.min(availUntil, maxTs) : maxTs;
|
|
386
|
+
if (start < end) {
|
|
387
|
+
const left = ((start - minTs) / range) * 100;
|
|
388
|
+
const width = ((end - start) / range) * 100;
|
|
389
|
+
const bar = document.createElement('div');
|
|
390
|
+
bar.style.position = 'absolute';
|
|
391
|
+
bar.style.left = `${left}%`;
|
|
392
|
+
bar.style.width = `${width}%`;
|
|
393
|
+
bar.style.height = '100%';
|
|
394
|
+
bar.style.background = '#ddd';
|
|
395
|
+
bar.style.borderRadius = '3px';
|
|
396
|
+
bar.title = `${itemName} (Configured Availability)`;
|
|
397
|
+
barContainer.appendChild(bar);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Actual sales period (blue bar)
|
|
402
|
+
if (firstTs && lastTs) {
|
|
403
|
+
const start = Math.max(firstTs, minTs);
|
|
404
|
+
const end = Math.min(lastTs, maxTs);
|
|
405
|
+
if (start <= end) {
|
|
406
|
+
const left = ((start - minTs) / range) * 100;
|
|
407
|
+
const width = Math.max(((end - start) / range) * 100, 0.5);
|
|
408
|
+
const bar = document.createElement('div');
|
|
409
|
+
bar.style.position = 'absolute';
|
|
410
|
+
bar.style.left = `${left}%`;
|
|
411
|
+
bar.style.width = `${width}%`;
|
|
412
|
+
bar.style.height = '100%';
|
|
413
|
+
bar.style.background = '#36A2EB';
|
|
414
|
+
bar.style.borderRadius = '3px';
|
|
415
|
+
bar.style.zIndex = '1';
|
|
416
|
+
bar.title = `${itemName} (Actual Sales)`;
|
|
417
|
+
barContainer.appendChild(bar);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
sel.itemAvailList.appendChild(row);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
418
425
|
function updateTimelineDisplay() {
|
|
419
426
|
const steps = sel.timeSlider._steps;
|
|
420
427
|
if (!steps) return;
|
|
@@ -447,13 +454,17 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
447
454
|
|
|
448
455
|
function refreshLayers() {
|
|
449
456
|
if (!map) return;
|
|
457
|
+
|
|
458
|
+
// Remove old layers from both maps
|
|
450
459
|
[pinLayer, heatmapLayer, gridLayer].forEach(l => { if (l && map.hasLayer(l)) map.removeLayer(l); });
|
|
451
460
|
if (mapCompare) {
|
|
452
461
|
[pinLayerComp, heatmapLayerComp, gridLayerComp, eventMarkerLayerComp].forEach(l => { if (l && mapCompare.hasLayer(l)) mapCompare.removeLayer(l); });
|
|
453
462
|
}
|
|
454
463
|
if (comparisonLayer && map.hasLayer(comparisonLayer)) map.removeLayer(comparisonLayer);
|
|
455
464
|
|
|
465
|
+
// Check if split mode is needed (Comparison selected AND heatmap/grid view)
|
|
456
466
|
const isSplitNeeded = currentCompareSlug && (currentView === 'heatmap' || currentView === 'grid');
|
|
467
|
+
|
|
457
468
|
if (isSplitNeeded) {
|
|
458
469
|
sel.mapSplitRoot.classList.add('map-split-active');
|
|
459
470
|
sel.mainLabel.style.display = 'block';
|
|
@@ -466,19 +477,25 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
466
477
|
setTimeout(() => { map.invalidateSize(); }, 50);
|
|
467
478
|
}
|
|
468
479
|
|
|
480
|
+
// Create layers for main map
|
|
469
481
|
createPinLayer(); createHeatmapLayer(); createGridLayer();
|
|
482
|
+
|
|
483
|
+
// Create layers for comparison map if in split mode
|
|
470
484
|
if (isSplitNeeded) {
|
|
471
485
|
createPinLayer(true); createHeatmapLayer(true); createGridLayer(true);
|
|
472
486
|
} else if (currentCompareSlug && currentView === 'pins') {
|
|
473
|
-
|
|
487
|
+
// Pins comparison: Dots on the main map
|
|
488
|
+
const dots = compareData.map(l => l.lat ? L.circleMarker([l.lat, l.lon], { radius: 4, color: '#888', fillOpacity: 0.5 }).bindTooltip("Comparison Order") : null).filter(l => l);
|
|
474
489
|
comparisonLayer = L.layerGroup(dots);
|
|
475
490
|
}
|
|
491
|
+
|
|
476
492
|
showCurrentView();
|
|
477
493
|
}
|
|
478
494
|
|
|
479
495
|
function createPinLayer(isCompare = false) {
|
|
480
496
|
const data = isCompare ? compareDisplayData : displayData;
|
|
481
497
|
if (data.length === 0) { if(isCompare) pinLayerComp = null; else pinLayer = null; return; }
|
|
498
|
+
|
|
482
499
|
const markers = data.map(loc => {
|
|
483
500
|
if (loc.lat == null) return null;
|
|
484
501
|
let icon = icons.blue;
|
|
@@ -489,6 +506,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
489
506
|
if (loc.order_url) m.on('click', () => window.open(loc.order_url, '_blank'));
|
|
490
507
|
return m;
|
|
491
508
|
}).filter(m => m !== null);
|
|
509
|
+
|
|
492
510
|
const layer = isClustering ? L.markerClusterGroup().addLayers(markers) : L.layerGroup(markers);
|
|
493
511
|
if (isCompare) pinLayerComp = layer; else pinLayer = layer;
|
|
494
512
|
}
|
|
@@ -496,6 +514,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
496
514
|
function createHeatmapLayer(isCompare = false) {
|
|
497
515
|
const data = isCompare ? compareDisplayData : displayData;
|
|
498
516
|
if (data.length === 0) { if(isCompare) heatmapLayerComp = null; else heatmapLayer = null; return; }
|
|
517
|
+
|
|
499
518
|
let maxRev = Math.max(...data.map(d => d.revenue || 0), 1);
|
|
500
519
|
const points = data.map(l => [l.lat, l.lon, isWeighted ? (l.revenue || 0) / maxRev : 1.0]);
|
|
501
520
|
const layer = L.heatLayer(points, {
|
|
@@ -510,9 +529,11 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
510
529
|
function createGridLayer(isCompare = false) {
|
|
511
530
|
const data = isCompare ? compareDisplayData : displayData;
|
|
512
531
|
if (data.length === 0) { if(isCompare) gridLayerComp = null; else gridLayer = null; return; }
|
|
532
|
+
|
|
513
533
|
const rawVal = parseInt(sel.gridSizeSlider.value);
|
|
514
534
|
const gridSize = 0.4 * Math.pow(rawVal / 100, 2);
|
|
515
535
|
sel.gridSizeVal.textContent = gridSize.toFixed(3);
|
|
536
|
+
|
|
516
537
|
const bins = {}; let maxVal = 0;
|
|
517
538
|
data.forEach(l => {
|
|
518
539
|
if (l.lat == null) return;
|
|
@@ -532,18 +553,24 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
532
553
|
|
|
533
554
|
function showCurrentView() {
|
|
534
555
|
if (!map) return;
|
|
556
|
+
|
|
557
|
+
// Main map view
|
|
535
558
|
if (currentView === 'pins' && pinLayer) map.addLayer(pinLayer);
|
|
536
559
|
else if (currentView === 'heatmap' && heatmapLayer) map.addLayer(heatmapLayer);
|
|
537
560
|
else if (currentView === 'grid' && gridLayer) map.addLayer(gridLayer);
|
|
538
561
|
|
|
562
|
+
// Control panel updates
|
|
539
563
|
sel.heatmapPanel.style.display = (currentView === 'heatmap' || currentView === 'grid' ? 'block' : 'none');
|
|
540
564
|
sel.heatmapControls.style.display = (currentView === 'heatmap' ? 'block' : 'none');
|
|
541
565
|
sel.gridControls.style.display = (currentView === 'grid' ? 'block' : 'none');
|
|
542
566
|
|
|
543
567
|
if (sel.clusterToggle) sel.clusterToggle.style.display = (currentView === 'pins' ? 'inline-block' : 'none');
|
|
544
568
|
if (eventMarkerLayer) map.addLayer(eventMarkerLayer);
|
|
569
|
+
|
|
570
|
+
// Dot comparison on main map
|
|
545
571
|
if (comparisonLayer && currentView === 'pins') map.addLayer(comparisonLayer);
|
|
546
572
|
|
|
573
|
+
// Comparison map view (Split mode)
|
|
547
574
|
if (mapCompare && sel.mapSplitRoot.classList.contains('map-split-active')) {
|
|
548
575
|
if (currentView === 'pins' && pinLayerComp) mapCompare.addLayer(pinLayerComp);
|
|
549
576
|
else if (currentView === 'heatmap' && heatmapLayerComp) mapCompare.addLayer(heatmapLayerComp);
|
|
@@ -570,12 +597,18 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
570
597
|
sel.compareLabel.textContent = "";
|
|
571
598
|
refreshLayers(); return;
|
|
572
599
|
}
|
|
600
|
+
|
|
573
601
|
const option = sel.compareSelect.options[sel.compareSelect.selectedIndex];
|
|
574
602
|
sel.compareLabel.textContent = option.text;
|
|
603
|
+
|
|
575
604
|
const newUrl = sel.map.dataset.dataUrl.replace(/\/event\/([^\/]+)\/([^\/]+)\//, `/event/$1/${slug}/`);
|
|
576
605
|
fetch(newUrl).then(r => r.json()).then(data => {
|
|
577
606
|
compareData = (data.locations || []).map(loc => { loc.ts = loc.date ? new Date(loc.date).getTime() : 0; return loc; }).sort((a, b) => a.ts - b.ts);
|
|
607
|
+
|
|
608
|
+
// Handle event marker for comparison map
|
|
578
609
|
if (data.event_marker) renderEventMarker(data.event_marker, true);
|
|
610
|
+
|
|
611
|
+
// Sync filters for comparison data
|
|
579
612
|
applyFilters();
|
|
580
613
|
});
|
|
581
614
|
}
|
|
@@ -593,14 +626,35 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
593
626
|
function applyFilters() {
|
|
594
627
|
showCanceled = sel.showCanceledCheck.checked;
|
|
595
628
|
const filterFn = loc => (loc.items.length === 0 || loc.items.some(i => selectedItems.has(i))) && (showCanceled || loc.status !== 'canceled');
|
|
629
|
+
|
|
596
630
|
filteredData = allData.filter(filterFn);
|
|
597
|
-
if (compareData.length > 0)
|
|
631
|
+
if (compareData.length > 0) {
|
|
632
|
+
compareFilteredData = compareData.filter(filterFn);
|
|
633
|
+
}
|
|
634
|
+
|
|
598
635
|
updateTimelineSlider();
|
|
599
636
|
}
|
|
600
637
|
|
|
601
638
|
function renderFailedOrders(orders) {
|
|
602
639
|
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
|
603
|
-
sel.failedTbody.innerHTML = orders.map(o =>
|
|
640
|
+
sel.failedTbody.innerHTML = orders.map(o => {
|
|
641
|
+
let statusBadge = `<span class="label label-danger">Failed</span>`;
|
|
642
|
+
if (o.success_level === 'city') statusBadge = `<span class="label label-warning">Only City</span>`;
|
|
643
|
+
else if (o.success_level === 'zip') statusBadge = `<span class="label label-info">Only Zip</span>`;
|
|
644
|
+
|
|
645
|
+
return `<tr>
|
|
646
|
+
<td><code>${o.code}</code></td>
|
|
647
|
+
<td>${o.address}</td>
|
|
648
|
+
<td>${statusBadge}</td>
|
|
649
|
+
<td>
|
|
650
|
+
<div class="btn-group">
|
|
651
|
+
<a href="${o.url}" target="_blank" class="btn btn-default btn-xs">View</a>
|
|
652
|
+
<button class="btn btn-warning btn-xs retry-btn" data-url="${o.retry_url}">Retry</button>
|
|
653
|
+
</div>
|
|
654
|
+
</td>
|
|
655
|
+
</tr>`;
|
|
656
|
+
}).join('') || '<tr><td colspan="4">None</td></tr>';
|
|
657
|
+
|
|
604
658
|
sel.failedTbody.querySelectorAll('.retry-btn').forEach(btn => btn.onclick = () => {
|
|
605
659
|
btn.disabled = true; btn.innerHTML = '...';
|
|
606
660
|
fetch(btn.dataset.url, { method: 'POST', headers: { 'X-CSRFToken': csrfToken } }).then(r => r.json()).then(d => { if(d.success) { btn.className = 'btn btn-success btn-xs'; btn.innerHTML = '✔'; setTimeout(() => fetchData(sel.map.dataset.dataUrl), 1000); } });
|
|
@@ -608,4 +662,4 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
608
662
|
}
|
|
609
663
|
|
|
610
664
|
init();
|
|
611
|
-
});
|
|
665
|
+
});
|
|
@@ -106,27 +106,40 @@ def geocode_order_task(self, order_pk: int, organizer_pk: int | None = None, nom
|
|
|
106
106
|
# Try candidates in order
|
|
107
107
|
coordinates = None
|
|
108
108
|
successful_candidate = None
|
|
109
|
+
success_level = "failed"
|
|
109
110
|
|
|
110
|
-
for candidate in candidates:
|
|
111
|
+
for i, candidate in enumerate(candidates):
|
|
111
112
|
logger.debug(f"Attempting to geocode candidate for Order {order.code}: '{candidate}'")
|
|
112
113
|
coordinates = geocode_address(candidate, nominatim_user_agent=nominatim_user_agent)
|
|
113
114
|
if coordinates:
|
|
114
115
|
successful_candidate = candidate
|
|
116
|
+
# Map candidate index to level (0: full, 1: zip+city, 2: zip)
|
|
117
|
+
if i == 0: success_level = "street"
|
|
118
|
+
elif i == 1: success_level = "city"
|
|
119
|
+
else: success_level = "zip"
|
|
115
120
|
break # Success!
|
|
116
121
|
|
|
117
122
|
with transaction.atomic():
|
|
118
123
|
if coordinates:
|
|
119
124
|
latitude, longitude = coordinates
|
|
120
125
|
obj, created = OrderGeocodeData.objects.update_or_create(
|
|
121
|
-
order=order, defaults={
|
|
126
|
+
order=order, defaults={
|
|
127
|
+
'latitude': latitude,
|
|
128
|
+
'longitude': longitude,
|
|
129
|
+
'success_level': success_level
|
|
130
|
+
}
|
|
122
131
|
)
|
|
123
132
|
log_level = logging.INFO if created else logging.DEBUG
|
|
124
133
|
logger.log(log_level,
|
|
125
|
-
f"Saved{' new' if created else ' updated'} geocode data for Order {order.code} using '{successful_candidate}': ({latitude}, {longitude})")
|
|
134
|
+
f"Saved{' new' if created else ' updated'} geocode data for Order {order.code} using '{successful_candidate}' (Level: {success_level}): ({latitude}, {longitude})")
|
|
126
135
|
else:
|
|
127
136
|
logger.warning(f"Geocoding failed for Order {order.code} (tried {len(candidates)} candidates). Storing null coordinates.")
|
|
128
137
|
obj, created = OrderGeocodeData.objects.update_or_create(
|
|
129
|
-
order=order, defaults={
|
|
138
|
+
order=order, defaults={
|
|
139
|
+
'latitude': None,
|
|
140
|
+
'longitude': None,
|
|
141
|
+
'success_level': 'failed'
|
|
142
|
+
}
|
|
130
143
|
)
|
|
131
144
|
# --- Scope deactivated automatically ---
|
|
132
145
|
|
{pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/templates/pretix_mapplugin/map_page.html
RENAMED
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
{% endif %}
|
|
111
111
|
</div>
|
|
112
112
|
<table class="table table-hover">
|
|
113
|
-
<thead><tr><th>Code</th><th>Address</th><th>Action</th></tr></thead>
|
|
113
|
+
<thead><tr><th>Code</th><th>Address</th><th>Status</th><th>Action</th></tr></thead>
|
|
114
114
|
<tbody id="failed-orders-tbody"></tbody>
|
|
115
115
|
</table>
|
|
116
116
|
</div>
|
|
@@ -151,10 +151,26 @@
|
|
|
151
151
|
</div>
|
|
152
152
|
</div>
|
|
153
153
|
|
|
154
|
-
<div id="timeline-controls" style="margin-top: 10px; padding:
|
|
155
|
-
<div style="
|
|
154
|
+
<div id="timeline-controls" style="margin-top: 10px; padding: 15px; background: #f5f5f5; border-radius: 4px; border: 1px solid #ddd; position: relative;">
|
|
155
|
+
<div id="timeline-chart-container" style="position: relative; height: 50px; margin-bottom: 10px; background: white; border: 1px solid #eee; overflow: hidden;">
|
|
156
|
+
<canvas id="timeline-volume-chart" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
|
|
157
|
+
<div id="timeline-milestone-markers" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"></div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div style="display: flex; gap: 15px; align-items: center; margin-bottom: 15px;">
|
|
161
|
+
<button id="timeline-play-btn" class="btn btn-sm btn-default" style="flex-shrink: 0;"><i class="fa fa-play"></i> Play</button>
|
|
162
|
+
<div style="flex-grow: 1; position: relative; padding: 0 5px;">
|
|
163
|
+
<input type="range" id="timeline-slider" min="0" max="100" value="100" style="width: 100%; cursor: pointer; position: relative; z-index: 5; margin: 0;">
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div id="timeline-item-availability" style="margin-top: 10px; background: white; border: 1px solid #eee; padding: 10px 0;">
|
|
168
|
+
<div id="item-availability-list" style="position: relative; padding: 0 5px;"></div>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px; font-size: 12px; color: #666;">
|
|
156
172
|
<div>
|
|
157
|
-
<label style="margin-bottom: 0;">{% trans "Timeline:" %} <span id="timeline-date-display" style="font-weight:
|
|
173
|
+
<label style="margin-bottom: 0;">{% trans "Timeline:" %} <span id="timeline-date-display" style="font-weight: bold; color: #333;"></span></label>
|
|
158
174
|
<span id="timeline-count-display" class="badge"></span>
|
|
159
175
|
</div>
|
|
160
176
|
<div>
|
|
@@ -166,17 +182,6 @@
|
|
|
166
182
|
</select>
|
|
167
183
|
</div>
|
|
168
184
|
</div>
|
|
169
|
-
<div id="timeline-chart-container" style="position: relative; height: 40px; margin-bottom: 5px; background: white; border: 1px solid #eee; overflow: hidden;">
|
|
170
|
-
<canvas id="timeline-volume-chart" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
|
|
171
|
-
<div id="timeline-milestone-markers" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"></div>
|
|
172
|
-
</div>
|
|
173
|
-
<div id="timeline-item-availability" style="margin-bottom: 10px; font-size: 10px; max-height: 100px; overflow-y: auto; background: white; border: 1px solid #eee; display: none;">
|
|
174
|
-
<div id="item-availability-list" style="padding: 5px; position: relative;"></div>
|
|
175
|
-
</div>
|
|
176
|
-
<div style="display: flex; gap: 10px; align-items: center;">
|
|
177
|
-
<button id="timeline-play-btn" class="btn btn-sm btn-default"><i class="fa fa-play"></i> Play</button>
|
|
178
|
-
<input type="range" id="timeline-slider" min="0" max="100" value="100" style="flex-grow: 1; cursor: pointer; position: relative; z-index: 5;">
|
|
179
|
-
</div>
|
|
180
185
|
</div>
|
|
181
186
|
</div>
|
|
182
187
|
|
{pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/templates/pretix_mapplugin/milestones.html
RENAMED
|
@@ -50,4 +50,29 @@
|
|
|
50
50
|
</div>
|
|
51
51
|
</div>
|
|
52
52
|
</form>
|
|
53
|
+
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/main.js" %}"></script>
|
|
54
|
+
<script type="text/javascript">
|
|
55
|
+
$(function () {
|
|
56
|
+
$(".datepickerfield").each(function () {
|
|
57
|
+
var format = $("body").attr("data-dateformat");
|
|
58
|
+
$(this).datetimepicker({
|
|
59
|
+
format: format,
|
|
60
|
+
locale: $("body").attr("data-datelocale"),
|
|
61
|
+
useCurrent: false,
|
|
62
|
+
showClear: true,
|
|
63
|
+
icons: {
|
|
64
|
+
time: 'fa fa-clock-o',
|
|
65
|
+
date: 'fa fa-calendar',
|
|
66
|
+
up: 'fa fa-chevron-up',
|
|
67
|
+
down: 'fa fa-chevron-down',
|
|
68
|
+
previous: 'fa fa-chevron-left',
|
|
69
|
+
next: 'fa fa-chevron-right',
|
|
70
|
+
today: 'fa fa-dot-circle-o',
|
|
71
|
+
clear: 'fa fa-trash',
|
|
72
|
+
close: 'fa fa-times'
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
</script>
|
|
53
78
|
{% endblock %}
|
|
@@ -220,11 +220,15 @@ class SalesMapDataView(EventSettingsViewMixin, View):
|
|
|
220
220
|
tooltip_parts.append(f"<strong>Items:</strong> {order.positions.count()}")
|
|
221
221
|
tooltip_parts.append(f"<strong>Total:</strong> {order.total} {event.currency}")
|
|
222
222
|
if dist_km: tooltip_parts.append(f"<strong>Dist:</strong> {dist_km:.1f} km")
|
|
223
|
+
if entry.success_level:
|
|
224
|
+
level_map = {'street': _('Street'), 'city': _('City'), 'zip': _('Zip code')}
|
|
225
|
+
tooltip_parts.append(f"<strong>Precision:</strong> {level_map.get(entry.success_level, entry.success_level)}")
|
|
223
226
|
|
|
224
227
|
locations_data.append({
|
|
225
228
|
"lat": entry.latitude, "lon": entry.longitude, "tooltip": "<br>".join(tooltip_parts),
|
|
226
229
|
"order_url": reverse('control:event.order', kwargs={'organizer': organizer.slug, 'event': event.slug, 'code': order.code}),
|
|
227
|
-
"items": item_names, "date": iso_date, "dist": dist_km, "revenue": revenue, "status": status
|
|
230
|
+
"items": item_names, "date": iso_date, "dist": dist_km, "revenue": revenue, "status": status,
|
|
231
|
+
"precision": entry.success_level
|
|
228
232
|
})
|
|
229
233
|
|
|
230
234
|
failed_entries = OrderGeocodeData.objects.filter(order__event=event, latitude__isnull=True).select_related('order', 'order__invoice_address')
|
|
@@ -233,7 +237,8 @@ class SalesMapDataView(EventSettingsViewMixin, View):
|
|
|
233
237
|
'pk': entry.order.pk, 'code': entry.order.code,
|
|
234
238
|
'address': get_best_address_string(entry.order) or _("No address"),
|
|
235
239
|
'url': reverse('control:event.order', kwargs={'organizer': organizer.slug, 'event': event.slug, 'code': entry.order.code}),
|
|
236
|
-
'retry_url': reverse('plugins:pretix_mapplugin:event.settings.salesmap.retry', kwargs={'organizer': organizer.slug, 'event': event.slug, 'order': entry.order.pk})
|
|
240
|
+
'retry_url': reverse('plugins:pretix_mapplugin:event.settings.salesmap.retry', kwargs={'organizer': organizer.slug, 'event': event.slug, 'order': entry.order.pk}),
|
|
241
|
+
'success_level': entry.success_level or 'failed'
|
|
237
242
|
})
|
|
238
243
|
|
|
239
244
|
avg_dist_val = round(sum(distances) / len(distances), 1) if distances else 0
|
|
@@ -291,6 +296,10 @@ class SalesMapView(EventSettingsViewMixin, TemplateView):
|
|
|
291
296
|
'https://cdn.jsdelivr.net',
|
|
292
297
|
"'unsafe-eval'" # Needed for some charting libs
|
|
293
298
|
],
|
|
299
|
+
'connect-src': [
|
|
300
|
+
'https://cdn.jsdelivr.net',
|
|
301
|
+
'https://*.tile.openstreetmap.org'
|
|
302
|
+
],
|
|
294
303
|
'style-src': ["'unsafe-inline'"]
|
|
295
304
|
}
|
|
296
305
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.7"
|
|
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.1.7 → pretix_map-0.1.8}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pretix_map-0.1.7 → pretix_map-0.1.8}/pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|