pretix-map 0.1.7__tar.gz → 0.1.9__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.9}/PKG-INFO +1 -1
- {pretix_map-0.1.7 → pretix_map-0.1.9/pretix_map.egg-info}/PKG-INFO +1 -1
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_map.egg-info/SOURCES.txt +1 -0
- pretix_map-0.1.9/pretix_mapplugin/__init__.py +1 -0
- pretix_map-0.1.9/pretix_mapplugin/migrations/0004_ordergeocodedata_success_level.py +15 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/models.py +6 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +110 -120
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/tasks.py +17 -4
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/templates/pretix_mapplugin/map_page.html +20 -15
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/templates/pretix_mapplugin/milestones.html +25 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/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.9}/LICENSE +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/MANIFEST.in +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/README.md +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_map.egg-info/dependency_links.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_map.egg-info/entry_points.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_map.egg-info/requires.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_map.egg-info/top_level.txt +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/apps.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/geocoding.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/locale/de/LC_MESSAGES/django.po +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/locale/de_Informal/.gitkeep +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/management/__init__.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/management/commands/__init__.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/management/commands/geocode_existing_orders.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/migrations/0001_initial.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/migrations/0003_mapmilestone.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/migrations/__init__.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/signals.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/.gitkeep +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers-2x.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/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.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-shadow.png +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/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.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js.map +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js.map +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js.map +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/templates/pretix_mapplugin/.gitkeep +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pretix_mapplugin/urls.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/pyproject.toml +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/setup.cfg +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/setup.py +0 -0
- {pretix_map-0.1.7 → pretix_map-0.1.9}/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.9
|
|
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.9
|
|
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.9"
|
|
@@ -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.9}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js
RENAMED
|
@@ -53,6 +53,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
53
53
|
compareLabel: document.getElementById('compare-map-label'),
|
|
54
54
|
mainLabel: document.getElementById('main-map-label'),
|
|
55
55
|
heatmapReset: document.getElementById('heatmap-reset-btn'),
|
|
56
|
+
timelineContainer: document.getElementById('timeline-controls'),
|
|
56
57
|
timeSlider: document.getElementById('timeline-slider'),
|
|
57
58
|
timeDisplay: document.getElementById('timeline-date-display'),
|
|
58
59
|
timePlayBtn: document.getElementById('timeline-play-btn'),
|
|
@@ -66,7 +67,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
66
67
|
|
|
67
68
|
// --- State ---
|
|
68
69
|
let map = null, mapCompare = null, allData = [], filteredData = [], displayData = [], milestones = [], currency = "EUR";
|
|
69
|
-
let itemAvailData = {};
|
|
70
70
|
let compareData = [], compareFilteredData = [], compareDisplayData = [];
|
|
71
71
|
let pinLayer = null, heatmapLayer = null, gridLayer = null, comparisonLayer = null, eventMarkerLayer = null;
|
|
72
72
|
let pinLayerComp = null, heatmapLayerComp = null, gridLayerComp = null, eventMarkerLayerComp = null;
|
|
@@ -75,6 +75,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
75
75
|
let availableItems = new Set(), selectedItems = new Set();
|
|
76
76
|
let currentDisplayMode = 'map'; // map | list | stats
|
|
77
77
|
let charts = {};
|
|
78
|
+
let itemAvailData = {};
|
|
78
79
|
|
|
79
80
|
function updateStatus(msg, isErr = false) {
|
|
80
81
|
if (sel.overlay) {
|
|
@@ -152,14 +153,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
152
153
|
sel.blurVal.textContent = sel.blurIn.value;
|
|
153
154
|
sel.maxZoomVal.textContent = sel.maxZoomIn.value;
|
|
154
155
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
156
|
+
const opt = {
|
|
157
|
+
radius: parseInt(sel.radiusIn.value),
|
|
158
|
+
blur: parseInt(sel.blurIn.value),
|
|
159
|
+
maxZoom: parseInt(sel.maxZoomIn.value),
|
|
160
|
+
minOpacity: 0.4
|
|
161
|
+
};
|
|
162
|
+
if (heatmapLayer) heatmapLayer.setOptions(opt);
|
|
163
|
+
if (heatmapLayerComp) heatmapLayerComp.setOptions(opt);
|
|
163
164
|
};
|
|
164
165
|
[sel.radiusIn, sel.blurIn, sel.maxZoomIn].forEach(i => i.oninput = updateHeat);
|
|
165
166
|
sel.heatmapReset.onclick = () => {
|
|
@@ -172,7 +173,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
172
173
|
|
|
173
174
|
function switchDisplayMode(mode) {
|
|
174
175
|
currentDisplayMode = mode;
|
|
175
|
-
|
|
176
|
+
const isMap = (mode === 'map');
|
|
177
|
+
sel.map.style.display = isMap ? 'block' : 'none';
|
|
178
|
+
sel.mapSplitRoot.style.display = isMap ? 'flex' : 'none';
|
|
179
|
+
sel.timelineContainer.style.display = isMap ? 'block' : 'none';
|
|
176
180
|
sel.failedContainer.style.display = (mode === 'list' ? 'block' : 'none');
|
|
177
181
|
sel.analyticsView.style.display = (mode === 'stats' ? 'block' : 'none');
|
|
178
182
|
|
|
@@ -181,7 +185,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
181
185
|
sel.listToggle.textContent = (mode === 'list' ? 'Show Map' : 'Failed Orders');
|
|
182
186
|
sel.statsToggle.textContent = (mode === 'stats' ? 'Show Map' : 'Analytics View');
|
|
183
187
|
|
|
184
|
-
if (
|
|
188
|
+
if (isMap) {
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
map.invalidateSize();
|
|
191
|
+
if (mapCompare) mapCompare.invalidateSize();
|
|
192
|
+
}, 50);
|
|
193
|
+
}
|
|
185
194
|
}
|
|
186
195
|
|
|
187
196
|
function renderStats(s) {
|
|
@@ -249,98 +258,11 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
249
258
|
updateTimelineDisplay();
|
|
250
259
|
}
|
|
251
260
|
|
|
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
261
|
function renderTimelineVolumeChart(steps) {
|
|
339
262
|
if (!sel.timelineChart) return;
|
|
340
263
|
const ctx = sel.timelineChart.getContext('2d');
|
|
341
264
|
if (charts.timeline) charts.timeline.destroy();
|
|
342
265
|
|
|
343
|
-
// Calculate counts per step from filteredData
|
|
344
266
|
const stepCounts = steps.map(ts => {
|
|
345
267
|
const nextTsIndex = steps.indexOf(ts) + 1;
|
|
346
268
|
const nextTs = nextTsIndex < steps.length ? steps[nextTsIndex] : Infinity;
|
|
@@ -363,10 +285,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
363
285
|
responsive: true,
|
|
364
286
|
maintainAspectRatio: false,
|
|
365
287
|
plugins: { legend: { display: false }, tooltip: { enabled: false } },
|
|
366
|
-
scales: {
|
|
367
|
-
x: { display: false },
|
|
368
|
-
y: { display: false, beginAtZero: true }
|
|
369
|
-
}
|
|
288
|
+
scales: { x: { display: false }, y: { display: false, beginAtZero: true } }
|
|
370
289
|
}
|
|
371
290
|
});
|
|
372
291
|
}
|
|
@@ -374,20 +293,13 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
374
293
|
function renderMilestoneMarkers(steps) {
|
|
375
294
|
if (!sel.timelineMarkers || !steps || steps.length === 0) return;
|
|
376
295
|
sel.timelineMarkers.innerHTML = '';
|
|
377
|
-
|
|
378
296
|
milestones.forEach(m => {
|
|
379
|
-
|
|
380
|
-
let closestIdx = -1;
|
|
381
|
-
let minDiff = Infinity;
|
|
297
|
+
let closestIdx = -1, minDiff = Infinity;
|
|
382
298
|
steps.forEach((ts, idx) => {
|
|
383
299
|
const diff = Math.abs(ts - m.ts);
|
|
384
|
-
if (diff < minDiff) {
|
|
385
|
-
minDiff = diff;
|
|
386
|
-
closestIdx = idx;
|
|
387
|
-
}
|
|
300
|
+
if (diff < minDiff) { minDiff = diff; closestIdx = idx; }
|
|
388
301
|
});
|
|
389
|
-
|
|
390
|
-
if (closestIdx !== -1 && minDiff < 86400000 * 2) { // Only show if close enough (2 days)
|
|
302
|
+
if (closestIdx !== -1 && minDiff < 86400000 * 2) {
|
|
391
303
|
const percent = (closestIdx / (steps.length - 1)) * 100;
|
|
392
304
|
const marker = document.createElement('div');
|
|
393
305
|
marker.className = 'timeline-milestone-marker';
|
|
@@ -398,7 +310,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
398
310
|
marker.style.height = '100%';
|
|
399
311
|
marker.style.background = '#d9534f';
|
|
400
312
|
marker.style.zIndex = '2';
|
|
401
|
-
|
|
402
313
|
const label = document.createElement('span');
|
|
403
314
|
label.textContent = m.label;
|
|
404
315
|
label.style.position = 'absolute';
|
|
@@ -408,13 +319,85 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
408
319
|
label.style.whiteSpace = 'nowrap';
|
|
409
320
|
label.style.background = 'rgba(255,255,255,0.7)';
|
|
410
321
|
label.style.padding = '0 2px';
|
|
411
|
-
|
|
412
322
|
marker.appendChild(label);
|
|
413
323
|
sel.timelineMarkers.appendChild(marker);
|
|
414
324
|
}
|
|
415
325
|
});
|
|
416
326
|
}
|
|
417
327
|
|
|
328
|
+
function renderItemAvailability(steps) {
|
|
329
|
+
if (!sel.itemAvailList || !steps || steps.length === 0) return;
|
|
330
|
+
sel.itemAvailList.innerHTML = '';
|
|
331
|
+
const minTs = steps[0], maxTs = steps[steps.length - 1], range = maxTs - minTs;
|
|
332
|
+
|
|
333
|
+
Object.keys(itemAvailData).forEach(itemName => {
|
|
334
|
+
const d = itemAvailData[itemName];
|
|
335
|
+
const firstTs = d.first_sale ? new Date(d.first_sale).getTime() : null;
|
|
336
|
+
const lastTs = d.last_sale ? new Date(d.last_sale).getTime() : null;
|
|
337
|
+
const availFrom = d.available_from ? new Date(d.available_from).getTime() : null;
|
|
338
|
+
const availUntil = d.available_until ? new Date(d.available_until).getTime() : null;
|
|
339
|
+
|
|
340
|
+
if (!firstTs && !availFrom) return;
|
|
341
|
+
|
|
342
|
+
const row = document.createElement('div');
|
|
343
|
+
row.style.marginBottom = '12px';
|
|
344
|
+
row.style.position = 'relative';
|
|
345
|
+
row.style.width = '100%';
|
|
346
|
+
|
|
347
|
+
const label = document.createElement('div');
|
|
348
|
+
label.textContent = itemName;
|
|
349
|
+
label.style.fontSize = '9px';
|
|
350
|
+
label.style.color = '#333';
|
|
351
|
+
label.style.marginBottom = '2px';
|
|
352
|
+
label.style.fontWeight = 'bold';
|
|
353
|
+
row.appendChild(label);
|
|
354
|
+
|
|
355
|
+
const barContainer = document.createElement('div');
|
|
356
|
+
barContainer.style.width = '100%';
|
|
357
|
+
barContainer.style.height = '6px';
|
|
358
|
+
barContainer.style.background = '#f0f0f0';
|
|
359
|
+
barContainer.style.position = 'relative';
|
|
360
|
+
barContainer.style.borderRadius = '3px';
|
|
361
|
+
row.appendChild(barContainer);
|
|
362
|
+
|
|
363
|
+
if (availFrom || availUntil) {
|
|
364
|
+
const start = availFrom ? Math.max(availFrom, minTs) : minTs;
|
|
365
|
+
const end = availUntil ? Math.min(availUntil, maxTs) : maxTs;
|
|
366
|
+
if (start < end) {
|
|
367
|
+
const left = ((start - minTs) / range) * 100;
|
|
368
|
+
const width = ((end - start) / range) * 100;
|
|
369
|
+
const bar = document.createElement('div');
|
|
370
|
+
bar.style.position = 'absolute';
|
|
371
|
+
bar.style.left = `${left}%`;
|
|
372
|
+
bar.style.width = `${width}%`;
|
|
373
|
+
bar.style.height = '100%';
|
|
374
|
+
bar.style.background = '#ddd';
|
|
375
|
+
bar.style.borderRadius = '3px';
|
|
376
|
+
barContainer.appendChild(bar);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (firstTs && lastTs) {
|
|
381
|
+
const start = Math.max(firstTs, minTs);
|
|
382
|
+
const end = Math.min(lastTs, maxTs);
|
|
383
|
+
if (start <= end) {
|
|
384
|
+
const left = ((start - minTs) / range) * 100;
|
|
385
|
+
const width = Math.max(((end - start) / range) * 100, 0.5);
|
|
386
|
+
const bar = document.createElement('div');
|
|
387
|
+
bar.style.position = 'absolute';
|
|
388
|
+
bar.style.left = `${left}%`;
|
|
389
|
+
bar.style.width = `${width}%`;
|
|
390
|
+
bar.style.height = '100%';
|
|
391
|
+
bar.style.background = '#36A2EB';
|
|
392
|
+
bar.style.borderRadius = '3px';
|
|
393
|
+
bar.style.zIndex = '1';
|
|
394
|
+
barContainer.appendChild(bar);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
sel.itemAvailList.appendChild(row);
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
418
401
|
function updateTimelineDisplay() {
|
|
419
402
|
const steps = sel.timeSlider._steps;
|
|
420
403
|
if (!steps) return;
|
|
@@ -447,6 +430,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
447
430
|
|
|
448
431
|
function refreshLayers() {
|
|
449
432
|
if (!map) return;
|
|
433
|
+
|
|
450
434
|
[pinLayer, heatmapLayer, gridLayer].forEach(l => { if (l && map.hasLayer(l)) map.removeLayer(l); });
|
|
451
435
|
if (mapCompare) {
|
|
452
436
|
[pinLayerComp, heatmapLayerComp, gridLayerComp, eventMarkerLayerComp].forEach(l => { if (l && mapCompare.hasLayer(l)) mapCompare.removeLayer(l); });
|
|
@@ -454,6 +438,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
454
438
|
if (comparisonLayer && map.hasLayer(comparisonLayer)) map.removeLayer(comparisonLayer);
|
|
455
439
|
|
|
456
440
|
const isSplitNeeded = currentCompareSlug && (currentView === 'heatmap' || currentView === 'grid');
|
|
441
|
+
|
|
457
442
|
if (isSplitNeeded) {
|
|
458
443
|
sel.mapSplitRoot.classList.add('map-split-active');
|
|
459
444
|
sel.mainLabel.style.display = 'block';
|
|
@@ -467,12 +452,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
467
452
|
}
|
|
468
453
|
|
|
469
454
|
createPinLayer(); createHeatmapLayer(); createGridLayer();
|
|
455
|
+
|
|
470
456
|
if (isSplitNeeded) {
|
|
471
457
|
createPinLayer(true); createHeatmapLayer(true); createGridLayer(true);
|
|
472
458
|
} else if (currentCompareSlug && currentView === 'pins') {
|
|
473
|
-
const dots = compareData.map(l => l.lat ? L.circleMarker([l.lat, l.lon], { radius: 4, color: '#888', fillOpacity: 0.5 }).bindTooltip("Comparison") : null).filter(l => l);
|
|
459
|
+
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
460
|
comparisonLayer = L.layerGroup(dots);
|
|
475
461
|
}
|
|
462
|
+
|
|
476
463
|
showCurrentView();
|
|
477
464
|
}
|
|
478
465
|
|
|
@@ -535,11 +522,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
535
522
|
if (currentView === 'pins' && pinLayer) map.addLayer(pinLayer);
|
|
536
523
|
else if (currentView === 'heatmap' && heatmapLayer) map.addLayer(heatmapLayer);
|
|
537
524
|
else if (currentView === 'grid' && gridLayer) map.addLayer(gridLayer);
|
|
538
|
-
|
|
539
525
|
sel.heatmapPanel.style.display = (currentView === 'heatmap' || currentView === 'grid' ? 'block' : 'none');
|
|
540
526
|
sel.heatmapControls.style.display = (currentView === 'heatmap' ? 'block' : 'none');
|
|
541
527
|
sel.gridControls.style.display = (currentView === 'grid' ? 'block' : 'none');
|
|
542
|
-
|
|
543
528
|
if (sel.clusterToggle) sel.clusterToggle.style.display = (currentView === 'pins' ? 'inline-block' : 'none');
|
|
544
529
|
if (eventMarkerLayer) map.addLayer(eventMarkerLayer);
|
|
545
530
|
if (comparisonLayer && currentView === 'pins') map.addLayer(comparisonLayer);
|
|
@@ -555,10 +540,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
555
540
|
function renderEventMarker(em, isCompare = false) {
|
|
556
541
|
const layer = L.marker([em.lat, em.lon], { icon: icons.red, zIndexOffset: 2000 }).bindTooltip(`<strong>EVENT: ${em.name}</strong><br>${em.location}`);
|
|
557
542
|
if (isCompare) {
|
|
558
|
-
if (eventMarkerLayerComp
|
|
543
|
+
if (eventMarkerLayerComp) mapCompare.removeLayer(eventMarkerLayerComp);
|
|
559
544
|
eventMarkerLayerComp = layer.addTo(mapCompare);
|
|
560
545
|
} else {
|
|
561
|
-
if (eventMarkerLayer
|
|
546
|
+
if (eventMarkerLayer) map.removeLayer(eventMarkerLayer);
|
|
562
547
|
eventMarkerLayer = layer.addTo(map);
|
|
563
548
|
}
|
|
564
549
|
}
|
|
@@ -600,7 +585,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
600
585
|
|
|
601
586
|
function renderFailedOrders(orders) {
|
|
602
587
|
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
|
603
|
-
sel.failedTbody.innerHTML = orders.map(o =>
|
|
588
|
+
sel.failedTbody.innerHTML = orders.map(o => {
|
|
589
|
+
let statusBadge = `<span class="label label-danger">Failed</span>`;
|
|
590
|
+
if (o.success_level === 'city') statusBadge = `<span class="label label-warning">Only City</span>`;
|
|
591
|
+
else if (o.success_level === 'zip') statusBadge = `<span class="label label-info">Only Zip</span>`;
|
|
592
|
+
return `<tr><td><code>${o.code}</code></td><td>${o.address}</td><td>${statusBadge}</td><td><div class="btn-group"><a href="${o.url}" target="_blank" class="btn btn-default btn-xs">View</a><button class="btn btn-warning btn-xs retry-btn" data-url="${o.retry_url}">Retry</button></div></td></tr>`;
|
|
593
|
+
}).join('') || '<tr><td colspan="4">None</td></tr>';
|
|
604
594
|
sel.failedTbody.querySelectorAll('.retry-btn').forEach(btn => btn.onclick = () => {
|
|
605
595
|
btn.disabled = true; btn.innerHTML = '...';
|
|
606
596
|
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); } });
|
|
@@ -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.9}/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; flex-shrink: 0;">
|
|
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; max-height: 150px; overflow-y: auto;">
|
|
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.9}/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.9}/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.9}/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
|