pretix-map 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. pretix_map-0.1.6.dist-info/METADATA +88 -0
  2. {pretix_map-0.1.4.dist-info → pretix_map-0.1.6.dist-info}/RECORD +32 -30
  3. {pretix_map-0.1.4.dist-info → pretix_map-0.1.6.dist-info}/WHEEL +1 -1
  4. {pretix_map-0.1.4.dist-info → pretix_map-0.1.6.dist-info}/licenses/LICENSE +15 -15
  5. pretix_mapplugin/__init__.py +1 -1
  6. pretix_mapplugin/apps.py +28 -28
  7. pretix_mapplugin/geocoding.py +162 -102
  8. pretix_mapplugin/locale/de/LC_MESSAGES/django.po +12 -12
  9. pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po +12 -12
  10. pretix_mapplugin/management/commands/geocode_existing_orders.py +271 -271
  11. pretix_mapplugin/migrations/0001_initial.py +27 -27
  12. pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py +32 -32
  13. pretix_mapplugin/migrations/0003_mapmilestone.py +27 -0
  14. pretix_mapplugin/models.py +71 -47
  15. pretix_mapplugin/signals.py +77 -92
  16. pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +100 -51
  17. pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +611 -452
  18. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css +59 -59
  19. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css +14 -14
  20. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js +10 -10
  21. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js +14419 -14419
  22. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js +14512 -14512
  23. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css +661 -661
  24. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js +5 -5
  25. pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js +2 -2
  26. pretix_mapplugin/tasks.py +144 -113
  27. pretix_mapplugin/templates/pretix_mapplugin/map_page.html +189 -88
  28. pretix_mapplugin/templates/pretix_mapplugin/milestones.html +53 -0
  29. pretix_mapplugin/urls.py +38 -21
  30. pretix_mapplugin/views.py +295 -163
  31. pretix_map-0.1.4.dist-info/METADATA +0 -195
  32. {pretix_map-0.1.4.dist-info → pretix_map-0.1.6.dist-info}/entry_points.txt +0 -0
  33. {pretix_map-0.1.4.dist-info → pretix_map-0.1.6.dist-info}/top_level.txt +0 -0
@@ -1,88 +1,189 @@
1
- {% extends "pretixcontrol/event/settings_base.html" %}
2
- {% load i18n %}
3
- {% load static %}
4
- {% load eventurl %}
5
-
6
- {% block title %}{% trans "Ticket Sales Map" %}{% endblock %}
7
-
8
- {% block inside %}
9
-
10
- <div class="plugin-map-content-wrapper">
11
-
12
- <h1>{% trans "Ticket Sales Map" %}</h1>
13
-
14
- <div class="form-inline map-controls-row"
15
- style="margin-bottom: 1em; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 15px;">
16
-
17
- <div class="map-buttons-group" style="display: flex; flex-wrap: wrap; gap: 10px; align-items: center;">
18
- <div class="form-group">
19
- <button id="view-toggle-btn" class="btn btn-default" disabled>Switch to Heatmap View</button>
20
- </div>
21
- <div class="form-group">
22
- <button id="cluster-toggle-btn" class="btn btn-default" disabled style="display: inline-block;">
23
- Disable Clustering
24
- </button>
25
- </div>
26
- </div>
27
- <div id="heatmap-options-panel" class="panel panel-default"
28
- style="display: none; padding: 10px 15px; border-radius: 4px; min-width: 350px;">
29
- <h5 style="margin-top: 0; margin-bottom: 10px;">{% trans "Heatmap Options" %}</h5>
30
- <div class="form-horizontal">
31
- <div class="form-group form-group-sm" style="margin-bottom: 5px;">
32
- <label for="heatmap-radius" class="col-sm-3 control-label"
33
- style="padding-top: 5px;">Radius</label>
34
- <div class="col-sm-7">
35
- <input type="range" id="heatmap-radius" class="form-control" min="1" max="100" value="25"
36
- step="1" disabled>
37
- </div>
38
- <div class="col-sm-2">
39
- <span id="radius-value" class="form-control-static">25</span>
40
- </div>
41
- </div>
42
- <div class="form-group form-group-sm" style="margin-bottom: 5px;">
43
- <label for="heatmap-blur" class="col-sm-3 control-label" style="padding-top: 5px;">Blur</label>
44
- <div class="col-sm-7">
45
- <input type="range" id="heatmap-blur" class="form-control" min="1" max="50" value="15"
46
- step="1" disabled>
47
- </div>
48
- <div class="col-sm-2">
49
- <span id="blur-value" class="form-control-static">15</span>
50
- </div>
51
- </div>
52
- <div class="form-group form-group-sm" style="margin-bottom: 5px;">
53
- <label for="heatmap-maxZoom" class="col-sm-3 control-label" style="padding-top: 5px;">Max
54
- Zoom</label>
55
- <div class="col-sm-7">
56
- <input type="range" id="heatmap-maxZoom" class="form-control" min="1" max="18" value="18"
57
- step="1" disabled>
58
- </div>
59
- <div class="col-sm-2">
60
- <span id="maxzoom-value" class="form-control-static">18</span>
61
- </div>
62
- </div>
63
- </div>
64
- </div>
65
- </div>
66
-
67
- <div class="map-wrapper" style="position: relative; border: 1px solid #ccc; flex-grow: 1; min-height: 0;">
68
- <div id="sales-map-container"
69
- data-data-url="{% url 'plugins:pretix_mapplugin:event.settings.salesmap.data' organizer=request.organizer.slug event=request.event.slug %}">
70
- </div>
71
- <div id="map-status-overlay"
72
- style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); z-index: 1000; display: flex; justify-content: center; align-items: center; text-align: center;">
73
- <p>Loading map data...</p>
74
- </div>
75
- </div>
76
- </div>
77
-
78
- <script src="
79
- {% static 'pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js' %}"></script>
80
- <link rel="stylesheet" href="{% static 'pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css' %}"/>
81
- <link rel="stylesheet"
82
- href="{% static 'pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css' %}"/>
83
- <script src="{% static 'pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js' %}"></script>
84
-
85
- <script src="{% static 'pretix_mapplugin/js/salesmap.js' %}"></script>
86
- <link rel="stylesheet" href="{% static 'pretix_mapplugin/css/salesmap.css' %}"/>
87
-
88
- {% endblock %}
1
+ {% extends "pretixcontrol/event/settings_base.html" %}
2
+ {% load i18n %}
3
+ {% load static %}
4
+ {% load eventurl %}
5
+
6
+ {% block title %}{% trans "Ticket Sales Map" %}{% endblock %}
7
+
8
+ {% block inside %}
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
+ <div class="plugin-map-content-wrapper" style="display: flex; flex-direction: column; min-height: 85vh;">
11
+
12
+ <h1>{% trans "Ticket Sales Map" %}</h1>
13
+
14
+ <div class="form-inline map-controls-row" style="margin-bottom: 1em; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px;">
15
+
16
+ <div class="map-buttons-group" style="display: flex; flex-wrap: wrap; gap: 5px; align-items: center;">
17
+ <div class="form-group">
18
+ <button id="view-toggle-btn" class="btn btn-default" disabled>{% trans "Heatmap View" %}</button>
19
+ </div>
20
+ <div class="form-group">
21
+ <button id="cluster-toggle-btn" class="btn btn-default" disabled>{% trans "Clustering" %}</button>
22
+ </div>
23
+ <div class="form-group">
24
+ <button id="grid-toggle-btn" class="btn btn-default" disabled>{% trans "Density Grid" %}</button>
25
+ </div>
26
+ <div class="form-group">
27
+ <button id="weight-toggle-btn" class="btn btn-default" disabled>{% trans "Weight by Revenue" %}</button>
28
+ </div>
29
+ <div class="form-group">
30
+ <div class="checkbox" style="margin-left: 10px; margin-top: 8px;">
31
+ <label><input type="checkbox" id="show-canceled-check"> {% trans "Show Canceled" %}</label>
32
+ </div>
33
+ </div>
34
+ <div class="form-group">
35
+ <button id="filter-toggle-btn" class="btn btn-default" disabled>{% trans "Filter" %} <span class="caret"></span></button>
36
+ </div>
37
+ <div class="form-group">
38
+ <select id="compare-event-select" class="form-control" style="width: auto;" disabled>
39
+ <option value="">{% trans "Compare..." %}</option>
40
+ {% for e in other_events %}
41
+ <option value="{{ e.slug }}">{{ e.name }}</option>
42
+ {% endfor %}
43
+ </select>
44
+ </div>
45
+ <div class="form-group">
46
+ <button id="list-toggle-btn" class="btn btn-info" disabled>{% trans "Failed Orders" %}</button>
47
+ </div>
48
+ <div class="form-group">
49
+ <button id="stats-toggle-btn" class="btn btn-primary" disabled>{% trans "Analytics View" %}</button>
50
+ </div>
51
+ {% if "can_change_event_settings" in request.eventperm %}
52
+ <div class="form-group">
53
+ <form action="{% url 'plugins:pretix_mapplugin:event.settings.salesmap.trigger' organizer=request.organizer.slug event=request.event.slug %}" method="post" style="display: inline-block;">
54
+ {% csrf_token %}
55
+ <button type="submit" class="btn btn-warning">
56
+ <i class="fa fa-refresh"></i> {% trans "Geocode all" %}
57
+ </button>
58
+ </form>
59
+ </div>
60
+ {% endif %}
61
+ </div>
62
+
63
+ <div id="heatmap-options-panel" class="panel panel-default" style="display: none; padding: 10px; min-width: 300px;">
64
+ <div style="display: flex; justify-content: space-between;">
65
+ <h5 style="margin: 0;">{% trans "View Options" %}</h5>
66
+ <button id="heatmap-reset-btn" class="btn btn-xs btn-link">Reset</button>
67
+ </div>
68
+ <div class="form-horizontal" style="margin-top: 5px;">
69
+ <div id="heatmap-only-controls">
70
+ <div class="form-group form-group-sm"><label class="col-sm-4">Radius</label><div class="col-sm-6"><input type="range" id="heatmap-radius" min="1" max="100" value="25"></div><div class="col-sm-2"><span id="radius-value">25</span></div></div>
71
+ <div class="form-group form-group-sm"><label class="col-sm-4">Blur</label><div class="col-sm-6"><input type="range" id="heatmap-blur" min="1" max="50" value="15"></div><div class="col-sm-2"><span id="blur-value">15</span></div></div>
72
+ <div class="form-group form-group-sm"><label class="col-sm-4">Max Zoom</label><div class="col-sm-6"><input type="range" id="heatmap-maxZoom" min="1" max="18" value="18"></div><div class="col-sm-2"><span id="maxzoom-value">18</span></div></div>
73
+ </div>
74
+ <div id="grid-only-controls" style="display: none;">
75
+ <div class="form-group form-group-sm">
76
+ <label class="col-sm-4">{% trans "Resolution" %}</label>
77
+ <div class="col-sm-6"><input type="range" id="grid-size-slider" min="1" max="100" value="70"></div>
78
+ <div class="col-sm-2"><span id="grid-size-value">0.12</span></div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <div id="filter-options-panel" class="panel panel-default" style="display: none; padding: 10px; min-width: 200px; max-height: 300px; overflow-y: auto;">
85
+ <h5 style="margin: 0 0 5px 0;">Ticket Types</h5>
86
+ <div id="filter-checkboxes"></div>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="map-wrapper">
91
+ <div id="map-split-root" class="map-split-container">
92
+ <div id="sales-map-container" style="min-height: 500px;" data-data-url="{% url 'plugins:pretix_mapplugin:event.settings.salesmap.data' organizer=request.organizer.slug event=request.event.slug %}">
93
+ <div class="map-label-overlay" id="main-map-label" style="display: none;">{{ request.event.name }}</div>
94
+ </div>
95
+ <div id="sales-map-compare-container" style="min-height: 500px;">
96
+ <div class="map-label-overlay" id="compare-map-label"></div>
97
+ </div>
98
+ </div>
99
+
100
+ <div id="failed-orders-container" style="display: none; padding: 20px; background: white; flex-grow: 1; overflow-y: auto;">
101
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
102
+ <h3 style="margin: 0;">{% trans "Orders with missing geodata" %}</h3>
103
+ {% if "can_change_event_settings" in request.eventperm %}
104
+ <form action="{% url 'plugins:pretix_mapplugin:event.settings.salesmap.trigger' organizer=request.organizer.slug event=request.event.slug %}" method="post">
105
+ {% csrf_token %}
106
+ <button type="submit" class="btn btn-warning">
107
+ <i class="fa fa-refresh"></i> {% trans "Retry all geocoding" %}
108
+ </button>
109
+ </form>
110
+ {% endif %}
111
+ </div>
112
+ <table class="table table-hover">
113
+ <thead><tr><th>Code</th><th>Address</th><th>Action</th></tr></thead>
114
+ <tbody id="failed-orders-tbody"></tbody>
115
+ </table>
116
+ </div>
117
+
118
+ <div id="analytics-view-container" style="display: none; padding: 20px; background: #fafafa; flex-grow: 1; overflow-y: auto;">
119
+ <div class="row">
120
+ <div class="col-md-4">
121
+ <div class="panel panel-default">
122
+ <div class="panel-heading">Key Figures</div>
123
+ <div class="panel-body" id="stats-summary-text"></div>
124
+ </div>
125
+ </div>
126
+ <div class="col-md-8">
127
+ <div class="panel panel-default">
128
+ <div class="panel-heading">Top Cities (Sales Count)</div>
129
+ <div class="panel-body"><canvas id="cities-chart" height="150"></canvas></div>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ <div class="row" style="margin-top: 20px;">
134
+ <div class="col-md-6">
135
+ <div class="panel panel-default">
136
+ <div class="panel-heading">Ticket Type Distribution</div>
137
+ <div class="panel-body"><canvas id="items-chart" height="200"></canvas></div>
138
+ </div>
139
+ </div>
140
+ <div class="col-md-6">
141
+ <div class="panel panel-default">
142
+ <div class="panel-heading">Revenue by Ticket Type</div>
143
+ <div class="panel-body"><canvas id="revenue-chart" height="200"></canvas></div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ <div id="map-status-overlay" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); z-index: 1000; display: flex; justify-content: center; align-items: center;">
150
+ <p>Loading...</p>
151
+ </div>
152
+ </div>
153
+
154
+ <div id="timeline-controls" style="margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 4px; border: 1px solid #ddd; position: relative;">
155
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
156
+ <div>
157
+ <label style="margin-bottom: 0;">{% trans "Timeline:" %} <span id="timeline-date-display" style="font-weight: normal;"></span></label>
158
+ <span id="timeline-count-display" class="badge"></span>
159
+ </div>
160
+ <div>
161
+ <select id="timeline-interval-select" class="form-control input-sm" style="width: auto; display: inline-block;">
162
+ <option value="hour">{% trans "Hourly" %}</option>
163
+ <option value="day" selected>{% trans "Daily" %}</option>
164
+ <option value="week">{% trans "Weekly" %}</option>
165
+ <option value="month">{% trans "Monthly" %}</option>
166
+ </select>
167
+ </div>
168
+ </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
+ </div>
181
+ </div>
182
+
183
+ <script src="{% static 'pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js' %}"></script>
184
+ <link rel="stylesheet" href="{% static 'pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css' %}"/>
185
+ <link rel="stylesheet" href="{% static 'pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css' %}"/>
186
+ <script src="{% static 'pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js' %}"></script>
187
+ <script src="{% static 'pretix_mapplugin/js/salesmap.js' %}"></script>
188
+ <link rel="stylesheet" href="{% static 'pretix_mapplugin/css/salesmap.css' %}"/>
189
+ {% endblock %}
@@ -0,0 +1,53 @@
1
+ {% extends "pretixcontrol/event/settings_base.html" %}
2
+ {% load i18n %}
3
+ {% load bootstrap3 %}
4
+
5
+ {% block title %}{% trans "Map Milestones" %}{% endblock %}
6
+
7
+ {% block inside %}
8
+ <h1>{% trans "Map Milestones" %}</h1>
9
+ <p>
10
+ {% trans "Define important dates (e.g. newsletter dispatch, ticket start) to see them in the Sales Map timeline." %}
11
+ </p>
12
+ <form action="" method="post" class="form-horizontal">
13
+ {% csrf_token %}
14
+ <div class="formset" data-formset-prefix="{{ formset.prefix }}">
15
+ {{ formset.management_form }}
16
+ {% bootstrap_formset_errors formset %}
17
+ <table class="table table-condensed">
18
+ <thead>
19
+ <tr>
20
+ <th>{% trans "Date" %}</th>
21
+ <th>{% trans "Label" %}</th>
22
+ <th>{% trans "Delete" %}</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ {% for form in formset %}
27
+ <tr class="formset-row">
28
+ <td>
29
+ {{ form.id }}
30
+ {% bootstrap_field form.date layout='inline' %}
31
+ </td>
32
+ <td>
33
+ {% bootstrap_field form.label layout='inline' %}
34
+ </td>
35
+ <td>
36
+ {% if formset.can_delete %}
37
+ {% bootstrap_field form.DELETE layout='inline' %}
38
+ {% endif %}
39
+ </td>
40
+ </tr>
41
+ {% endfor %}
42
+ </tbody>
43
+ </table>
44
+ </div>
45
+ <div class="form-group">
46
+ <div class="col-sm-offset-2 col-sm-10">
47
+ <button type="submit" class="btn btn-primary">
48
+ {% trans "Save" %}
49
+ </button>
50
+ </div>
51
+ </div>
52
+ </form>
53
+ {% endblock %}
pretix_mapplugin/urls.py CHANGED
@@ -1,21 +1,38 @@
1
- from django.urls import re_path
2
-
3
- from .views import SalesMapDataView, SalesMapView # Import your views
4
-
5
- # Define the URL patterns for the event settings area
6
- # These URLs will be prefixed with /control/event/<organizer>/<event>/
7
- # based on how Pretix includes plugin URLs.
8
- urlpatterns = [
9
- # URL for the API endpoint providing coordinate data
10
- re_path(
11
- r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/sales-map/data/',
12
- SalesMapDataView.as_view(),
13
- name="event.settings.salesmap.data", # Unique name for URL reversing
14
- ),
15
- # URL for the HTML page displaying the map
16
- re_path(
17
- r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/sales-map/',
18
- SalesMapView.as_view(),
19
- name="event.settings.salesmap.show", # Unique name for URL reversing
20
- ),
21
- ]
1
+ from django.urls import re_path
2
+
3
+ from .views import SalesMapDataView, SalesMapView, MilestoneSettingsView, TriggerGeocodingView, SingleGeocodeView
4
+
5
+ # Define the URL patterns for the event settings area
6
+ # These URLs will be prefixed with /control/event/<organizer>/<event>/
7
+ urlpatterns = [
8
+ # URL for the API endpoint providing coordinate data
9
+ re_path(
10
+ r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/sales-map/data/',
11
+ SalesMapDataView.as_view(),
12
+ name="event.settings.salesmap.data",
13
+ ),
14
+ # URL for triggering geocoding for all
15
+ re_path(
16
+ r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/sales-map/trigger/',
17
+ TriggerGeocodingView.as_view(),
18
+ name="event.settings.salesmap.trigger",
19
+ ),
20
+ # URL for single order geocoding
21
+ re_path(
22
+ r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/sales-map/retry/(?P<order>[^/]+)/',
23
+ SingleGeocodeView.as_view(),
24
+ name="event.settings.salesmap.retry",
25
+ ),
26
+ # URL for milestone management
27
+ re_path(
28
+ r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/sales-map/milestones/',
29
+ MilestoneSettingsView.as_view(),
30
+ name="event.settings.salesmap.milestones",
31
+ ),
32
+ # URL for the HTML page displaying the map
33
+ re_path(
34
+ r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/sales-map/',
35
+ SalesMapView.as_view(),
36
+ name="event.settings.salesmap.show",
37
+ ),
38
+ ]