umap-project 2.8.0a0__py3-none-any.whl → 2.8.0a2__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.
Potentially problematic release.
This version of umap-project might be problematic. Click here for more details.
- umap/__init__.py +1 -1
- umap/locale/en/LC_MESSAGES/django.po +13 -13
- umap/management/commands/empty_trash.py +5 -2
- umap/management/commands/migrate_to_S3.py +3 -3
- umap/settings/base.py +2 -2
- umap/static/umap/js/modules/data/layer.js +39 -18
- umap/static/umap/js/modules/importer.js +58 -18
- umap/static/umap/js/modules/umap.js +5 -3
- umap/static/umap/locale/en.js +3 -1
- umap/static/umap/locale/en.json +3 -1
- umap/static/umap/locale/fr.js +3 -1
- umap/static/umap/locale/fr.json +3 -1
- umap/storage/__init__.py +3 -0
- umap/storage/fs.py +101 -0
- umap/storage/s3.py +61 -0
- umap/storage/staticfiles.py +64 -0
- umap/tests/test_datalayer_s3.py +1 -1
- umap/tests/test_statics.py +1 -1
- umap/views.py +15 -14
- {umap_project-2.8.0a0.dist-info → umap_project-2.8.0a2.dist-info}/METADATA +1 -1
- {umap_project-2.8.0a0.dist-info → umap_project-2.8.0a2.dist-info}/RECORD +24 -21
- umap/storage.py +0 -216
- {umap_project-2.8.0a0.dist-info → umap_project-2.8.0a2.dist-info}/WHEEL +0 -0
- {umap_project-2.8.0a0.dist-info → umap_project-2.8.0a2.dist-info}/entry_points.txt +0 -0
- {umap_project-2.8.0a0.dist-info → umap_project-2.8.0a2.dist-info}/licenses/LICENSE +0 -0
umap/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "2.8.
|
|
1
|
+
VERSION = "2.8.0a2"
|
|
@@ -8,7 +8,7 @@ msgid ""
|
|
|
8
8
|
msgstr ""
|
|
9
9
|
"Project-Id-Version: PACKAGE VERSION\n"
|
|
10
10
|
"Report-Msgid-Bugs-To: \n"
|
|
11
|
-
"POT-Creation-Date: 2024-12-
|
|
11
|
+
"POT-Creation-Date: 2024-12-13 08:26+0000\n"
|
|
12
12
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
13
13
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
14
14
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
@@ -617,57 +617,57 @@ msgstr ""
|
|
|
617
617
|
msgid "View the map"
|
|
618
618
|
msgstr ""
|
|
619
619
|
|
|
620
|
-
#: views.py:
|
|
620
|
+
#: views.py:821
|
|
621
621
|
msgid "See full screen"
|
|
622
622
|
msgstr ""
|
|
623
623
|
|
|
624
|
-
#: views.py:
|
|
624
|
+
#: views.py:964
|
|
625
625
|
msgid "Map editors updated with success!"
|
|
626
626
|
msgstr ""
|
|
627
627
|
|
|
628
|
-
#: views.py:
|
|
628
|
+
#: views.py:1000
|
|
629
629
|
#, python-format
|
|
630
630
|
msgid "The uMap edit link for your map: %(map_name)s"
|
|
631
631
|
msgstr ""
|
|
632
632
|
|
|
633
|
-
#: views.py:
|
|
633
|
+
#: views.py:1003
|
|
634
634
|
#, python-format
|
|
635
635
|
msgid "Here is your secret edit link: %(link)s"
|
|
636
636
|
msgstr ""
|
|
637
637
|
|
|
638
|
-
#: views.py:
|
|
638
|
+
#: views.py:1010
|
|
639
639
|
#, python-format
|
|
640
640
|
msgid "Can't send email to %(email)s"
|
|
641
641
|
msgstr ""
|
|
642
642
|
|
|
643
|
-
#: views.py:
|
|
643
|
+
#: views.py:1013
|
|
644
644
|
#, python-format
|
|
645
645
|
msgid "Email sent to %(email)s"
|
|
646
646
|
msgstr ""
|
|
647
647
|
|
|
648
|
-
#: views.py:
|
|
648
|
+
#: views.py:1024
|
|
649
649
|
msgid "Only its owner can delete the map."
|
|
650
650
|
msgstr ""
|
|
651
651
|
|
|
652
|
-
#: views.py:
|
|
652
|
+
#: views.py:1027
|
|
653
653
|
msgid "Map successfully deleted."
|
|
654
654
|
msgstr ""
|
|
655
655
|
|
|
656
|
-
#: views.py:
|
|
656
|
+
#: views.py:1053
|
|
657
657
|
#, python-format
|
|
658
658
|
msgid ""
|
|
659
659
|
"Your map has been cloned! If you want to edit this map from another "
|
|
660
660
|
"computer, please use this link: %(anonymous_url)s"
|
|
661
661
|
msgstr ""
|
|
662
662
|
|
|
663
|
-
#: views.py:
|
|
663
|
+
#: views.py:1058
|
|
664
664
|
msgid "Congratulations, your map has been cloned!"
|
|
665
665
|
msgstr ""
|
|
666
666
|
|
|
667
|
-
#: views.py:
|
|
667
|
+
#: views.py:1309
|
|
668
668
|
msgid "Layer successfully deleted."
|
|
669
669
|
msgstr ""
|
|
670
670
|
|
|
671
|
-
#: views.py:
|
|
671
|
+
#: views.py:1331
|
|
672
672
|
msgid "Permissions updated with success!"
|
|
673
673
|
msgstr ""
|
|
@@ -23,10 +23,13 @@ class Command(BaseCommand):
|
|
|
23
23
|
|
|
24
24
|
def handle(self, *args, **options):
|
|
25
25
|
days = options["days"]
|
|
26
|
-
since = datetime.utcnow() - timedelta(days=days)
|
|
26
|
+
since = (datetime.utcnow() - timedelta(days=days)).date()
|
|
27
27
|
print(f"Deleting map in trash since {since}")
|
|
28
28
|
maps = Map.objects.filter(share_status=Map.DELETED, modified_at__lt=since)
|
|
29
29
|
for map in maps:
|
|
30
|
+
map_id = map.id
|
|
31
|
+
map_name = map.name
|
|
32
|
+
trashed_at = map.modified_at.date()
|
|
30
33
|
if not options["dry_run"]:
|
|
31
34
|
map.delete()
|
|
32
|
-
print(f"Deleted map {
|
|
35
|
+
print(f"Deleted map {map_name} ({map_id}), trashed at {trashed_at}")
|
|
@@ -2,7 +2,7 @@ from django.conf import settings
|
|
|
2
2
|
from django.core.management.base import BaseCommand
|
|
3
3
|
|
|
4
4
|
from umap.models import DataLayer
|
|
5
|
-
from umap.storage import
|
|
5
|
+
from umap.storage.fs import FSDataStorage
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Command(BaseCommand):
|
|
@@ -11,9 +11,9 @@ class Command(BaseCommand):
|
|
|
11
11
|
def handle(self, *args, **options):
|
|
12
12
|
assert settings.UMAP_READONLY, "You must run that script with a read-only uMap."
|
|
13
13
|
assert (
|
|
14
|
-
settings.STORAGES["data"]["BACKEND"] == "umap.storage.
|
|
14
|
+
settings.STORAGES["data"]["BACKEND"] == "umap.storage.s3.S3DataStorage"
|
|
15
15
|
), "You must configure your storages to point to S3"
|
|
16
|
-
fs_storage =
|
|
16
|
+
fs_storage = FSDataStorage()
|
|
17
17
|
for datalayer in DataLayer.objects.all():
|
|
18
18
|
geojson_fs_path = str(datalayer.geojson)
|
|
19
19
|
try:
|
umap/settings/base.py
CHANGED
|
@@ -176,10 +176,10 @@ STORAGES = {
|
|
|
176
176
|
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
|
177
177
|
},
|
|
178
178
|
"data": {
|
|
179
|
-
"BACKEND": "umap.storage.
|
|
179
|
+
"BACKEND": "umap.storage.fs.FSDataStorage",
|
|
180
180
|
},
|
|
181
181
|
"staticfiles": {
|
|
182
|
-
"BACKEND": "umap.storage.UmapManifestStaticFilesStorage",
|
|
182
|
+
"BACKEND": "umap.storage.staticfiles.UmapManifestStaticFilesStorage",
|
|
183
183
|
},
|
|
184
184
|
}
|
|
185
185
|
# Add application/json and application/geo+json to default django-storages setting
|
|
@@ -252,10 +252,11 @@ export class DataLayer extends ServerStored {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
fromGeoJSON(geojson, sync = true) {
|
|
255
|
-
this.addData(geojson, sync)
|
|
255
|
+
const features = this.addData(geojson, sync)
|
|
256
256
|
this._geojson = geojson
|
|
257
257
|
this.onDataLoaded()
|
|
258
258
|
this.dataChanged()
|
|
259
|
+
return features
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
onDataLoaded() {
|
|
@@ -315,7 +316,7 @@ export class DataLayer extends ServerStored {
|
|
|
315
316
|
const response = await this._umap.request.get(url)
|
|
316
317
|
if (response?.ok) {
|
|
317
318
|
this.clear()
|
|
318
|
-
this._umap.formatter
|
|
319
|
+
return this._umap.formatter
|
|
319
320
|
.parse(await response.text(), this.options.remoteData.format)
|
|
320
321
|
.then((geojson) => this.fromGeoJSON(geojson))
|
|
321
322
|
}
|
|
@@ -443,10 +444,11 @@ export class DataLayer extends ServerStored {
|
|
|
443
444
|
try {
|
|
444
445
|
// Do not fail if remote data is somehow invalid,
|
|
445
446
|
// otherwise the layer becomes uneditable.
|
|
446
|
-
this.makeFeatures(geojson, sync)
|
|
447
|
+
return this.makeFeatures(geojson, sync)
|
|
447
448
|
} catch (err) {
|
|
448
449
|
console.log('Error with DataLayer', this.id)
|
|
449
450
|
console.error(err)
|
|
451
|
+
return []
|
|
450
452
|
}
|
|
451
453
|
}
|
|
452
454
|
|
|
@@ -463,10 +465,13 @@ export class DataLayer extends ServerStored {
|
|
|
463
465
|
? geojson
|
|
464
466
|
: geojson.features || geojson.geometries
|
|
465
467
|
if (!collection) return
|
|
468
|
+
const features = []
|
|
466
469
|
this.sortFeatures(collection)
|
|
467
|
-
for (const
|
|
468
|
-
this.makeFeature(
|
|
470
|
+
for (const featureJson of collection) {
|
|
471
|
+
const feature = this.makeFeature(featureJson, sync)
|
|
472
|
+
if (feature) features.push(feature)
|
|
469
473
|
}
|
|
474
|
+
return features
|
|
470
475
|
}
|
|
471
476
|
|
|
472
477
|
makeFeature(geojson = {}, sync = true, id = null) {
|
|
@@ -503,31 +508,47 @@ export class DataLayer extends ServerStored {
|
|
|
503
508
|
}
|
|
504
509
|
|
|
505
510
|
async importRaw(raw, format) {
|
|
506
|
-
this._umap.formatter
|
|
511
|
+
return this._umap.formatter
|
|
507
512
|
.parse(raw, format)
|
|
508
513
|
.then((geojson) => this.addData(geojson))
|
|
509
|
-
.then(() =>
|
|
510
|
-
|
|
514
|
+
.then((data) => {
|
|
515
|
+
if (data?.length) this.isDirty = true
|
|
516
|
+
return data
|
|
517
|
+
})
|
|
511
518
|
}
|
|
512
519
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
520
|
+
readFile(f) {
|
|
521
|
+
return new Promise((resolve) => {
|
|
522
|
+
const reader = new FileReader()
|
|
523
|
+
reader.onloadend = () => resolve(reader.result)
|
|
524
|
+
reader.readAsText(f)
|
|
525
|
+
})
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
async importFromFiles(files, type) {
|
|
529
|
+
let all = []
|
|
530
|
+
for (const file of files) {
|
|
531
|
+
const features = await this.importFromFile(file, type)
|
|
532
|
+
if (features) {
|
|
533
|
+
all = all.concat(features)
|
|
534
|
+
}
|
|
516
535
|
}
|
|
536
|
+
return new Promise((resolve) => {
|
|
537
|
+
resolve(all)
|
|
538
|
+
})
|
|
517
539
|
}
|
|
518
540
|
|
|
519
|
-
importFromFile(
|
|
520
|
-
const reader = new FileReader()
|
|
541
|
+
async importFromFile(file, type) {
|
|
521
542
|
type = type || Utils.detectFileType(f)
|
|
522
|
-
|
|
523
|
-
|
|
543
|
+
const raw = await this.readFile(file)
|
|
544
|
+
return this.importRaw(raw, type)
|
|
524
545
|
}
|
|
525
546
|
|
|
526
547
|
async importFromUrl(uri, type) {
|
|
527
548
|
uri = this._umap.renderUrl(uri)
|
|
528
549
|
const response = await this._umap.request.get(uri)
|
|
529
550
|
if (response?.ok) {
|
|
530
|
-
this.importRaw(await response.text(), type)
|
|
551
|
+
return this.importRaw(await response.text(), type)
|
|
531
552
|
}
|
|
532
553
|
}
|
|
533
554
|
|
|
@@ -930,9 +951,9 @@ export class DataLayer extends ServerStored {
|
|
|
930
951
|
else this.hide()
|
|
931
952
|
}
|
|
932
953
|
|
|
933
|
-
zoomTo() {
|
|
954
|
+
zoomTo(bounds) {
|
|
934
955
|
if (!this.isVisible()) return
|
|
935
|
-
|
|
956
|
+
bounds = bounds || this.layer.getBounds()
|
|
936
957
|
if (bounds.isValid()) {
|
|
937
958
|
const options = { maxZoom: this.getOption('zoomTo') }
|
|
938
959
|
this._leafletMap.fitBounds(bounds, options)
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DomEvent,
|
|
3
|
+
DomUtil,
|
|
4
|
+
LatLngBounds,
|
|
5
|
+
} from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
2
6
|
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
|
3
7
|
import { translate } from './i18n.js'
|
|
4
8
|
import { SCHEMA } from './schema.js'
|
|
@@ -270,16 +274,12 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
270
274
|
}
|
|
271
275
|
|
|
272
276
|
submit() {
|
|
273
|
-
let hasErrors
|
|
274
277
|
if (this.format === 'umap') {
|
|
275
|
-
|
|
278
|
+
this.full()
|
|
276
279
|
} else if (!this.url) {
|
|
277
|
-
|
|
280
|
+
this.copy()
|
|
278
281
|
} else if (this.action) {
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
if (hasErrors === false) {
|
|
282
|
-
Alert.info(translate('Data successfully imported!'))
|
|
282
|
+
this[this.action]()
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
|
|
@@ -294,8 +294,9 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
294
294
|
} else if (this.url) {
|
|
295
295
|
this._umap.importFromUrl(this.url, this.format)
|
|
296
296
|
}
|
|
297
|
+
this.onSuccess()
|
|
297
298
|
} catch (e) {
|
|
298
|
-
|
|
299
|
+
this.onError(translate('Invalid umap data'))
|
|
299
300
|
console.error(e)
|
|
300
301
|
return false
|
|
301
302
|
}
|
|
@@ -306,7 +307,7 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
306
307
|
return false
|
|
307
308
|
}
|
|
308
309
|
if (!this.format) {
|
|
309
|
-
|
|
310
|
+
this.onError(translate('Please choose a format'))
|
|
310
311
|
return false
|
|
311
312
|
}
|
|
312
313
|
const layer = this.layer
|
|
@@ -318,26 +319,65 @@ export default class Importer extends Utils.WithTemplate {
|
|
|
318
319
|
layer.options.remoteData.proxy = true
|
|
319
320
|
layer.options.remoteData.ttl = SCHEMA.ttl.default
|
|
320
321
|
}
|
|
321
|
-
layer.fetchRemoteData(true)
|
|
322
|
+
layer.fetchRemoteData(true).then((features) => {
|
|
323
|
+
if (features?.length) {
|
|
324
|
+
layer.zoomTo()
|
|
325
|
+
this.onSuccess()
|
|
326
|
+
} else {
|
|
327
|
+
this.onError()
|
|
328
|
+
}
|
|
329
|
+
})
|
|
322
330
|
}
|
|
323
331
|
|
|
324
|
-
copy() {
|
|
332
|
+
async copy() {
|
|
325
333
|
// Format may be guessed from file later.
|
|
326
334
|
// Usefull in case of multiple files with different formats.
|
|
327
335
|
if (!this.format && !this.files.length) {
|
|
328
|
-
|
|
336
|
+
this.onError(translate('Please choose a format'))
|
|
329
337
|
return false
|
|
330
338
|
}
|
|
339
|
+
let promise
|
|
331
340
|
const layer = this.layer
|
|
332
341
|
if (this.clear) layer.empty()
|
|
333
342
|
if (this.files.length) {
|
|
334
|
-
|
|
335
|
-
this._umap.processFileToImport(file, layer, this.format)
|
|
336
|
-
}
|
|
343
|
+
promise = layer.importFromFiles(this.files, this.format)
|
|
337
344
|
} else if (this.raw) {
|
|
338
|
-
layer.importRaw(this.raw, this.format)
|
|
345
|
+
promise = layer.importRaw(this.raw, this.format)
|
|
339
346
|
} else if (this.url) {
|
|
340
|
-
layer.importFromUrl(this.url, this.format)
|
|
347
|
+
promise = layer.importFromUrl(this.url, this.format)
|
|
348
|
+
}
|
|
349
|
+
if (promise) promise.then((data) => this.onCopyFinished(layer, data))
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
onError(message = translate('No data has been found for import')) {
|
|
353
|
+
Alert.error(message)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
onSuccess(count) {
|
|
357
|
+
if (count) {
|
|
358
|
+
Alert.success(translate('Successfully imported {count} feature(s)'), {
|
|
359
|
+
count: count,
|
|
360
|
+
})
|
|
361
|
+
} else {
|
|
362
|
+
Alert.success(translate('Data successfully imported!'))
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
onCopyFinished(layer, features) {
|
|
367
|
+
// undefined features means error, let original error message pop
|
|
368
|
+
if (!features) return
|
|
369
|
+
if (!features.length) {
|
|
370
|
+
this.onError()
|
|
371
|
+
} else {
|
|
372
|
+
const bounds = new LatLngBounds()
|
|
373
|
+
for (const feature of features) {
|
|
374
|
+
const featureBounds = feature.ui.getBounds
|
|
375
|
+
? feature.ui.getBounds()
|
|
376
|
+
: feature.ui.getCenter()
|
|
377
|
+
bounds.extend(featureBounds)
|
|
378
|
+
}
|
|
379
|
+
this.onSuccess(features.length)
|
|
380
|
+
layer.zoomTo(bounds)
|
|
341
381
|
}
|
|
342
382
|
}
|
|
343
383
|
}
|
|
@@ -316,12 +316,14 @@ export default class Umap extends ServerStored {
|
|
|
316
316
|
dataUrl = this.renderUrl(dataUrl)
|
|
317
317
|
dataUrl = this.proxyUrl(dataUrl)
|
|
318
318
|
const datalayer = this.createDataLayer()
|
|
319
|
-
await datalayer
|
|
319
|
+
await datalayer
|
|
320
|
+
.importFromUrl(dataUrl, dataFormat)
|
|
321
|
+
.then(() => datalayer.zoomTo())
|
|
320
322
|
}
|
|
321
323
|
} else if (data) {
|
|
322
324
|
data = decodeURIComponent(data)
|
|
323
325
|
const datalayer = this.createDataLayer()
|
|
324
|
-
await datalayer.importRaw(data, dataFormat)
|
|
326
|
+
await datalayer.importRaw(data, dataFormat).then(() => datalayer.zoomTo())
|
|
325
327
|
}
|
|
326
328
|
}
|
|
327
329
|
|
|
@@ -1514,7 +1516,7 @@ export default class Umap extends ServerStored {
|
|
|
1514
1516
|
processFileToImport(file, layer, type) {
|
|
1515
1517
|
type = type || Utils.detectFileType(file)
|
|
1516
1518
|
if (!type) {
|
|
1517
|
-
|
|
1519
|
+
Alert.error(
|
|
1518
1520
|
translate('Unable to detect format of file {filename}', {
|
|
1519
1521
|
filename: file.name,
|
|
1520
1522
|
})
|
umap/static/umap/locale/en.js
CHANGED
|
@@ -520,7 +520,9 @@ const locale = {
|
|
|
520
520
|
"Import helpers": "Import helpers",
|
|
521
521
|
"Import helpers will fill the URL field for you.": "Import helpers will fill the URL field for you.",
|
|
522
522
|
"Wikipedia": "Wikipedia",
|
|
523
|
-
"Save draft": "Save draft"
|
|
523
|
+
"Save draft": "Save draft",
|
|
524
|
+
"No data has been found for import": "No data has been found for import",
|
|
525
|
+
"Successfully imported {count} feature(s)": "Successfully imported {count} feature(s)"
|
|
524
526
|
}
|
|
525
527
|
L.registerLocale("en", locale)
|
|
526
528
|
L.setLocale("en")
|
umap/static/umap/locale/en.json
CHANGED
|
@@ -520,5 +520,7 @@
|
|
|
520
520
|
"Import helpers": "Import helpers",
|
|
521
521
|
"Import helpers will fill the URL field for you.": "Import helpers will fill the URL field for you.",
|
|
522
522
|
"Wikipedia": "Wikipedia",
|
|
523
|
-
"Save draft": "Save draft"
|
|
523
|
+
"Save draft": "Save draft",
|
|
524
|
+
"No data has been found for import": "No data has been found for import",
|
|
525
|
+
"Successfully imported {count} feature(s)": "Successfully imported {count} feature(s)"
|
|
524
526
|
}
|
umap/static/umap/locale/fr.js
CHANGED
|
@@ -520,7 +520,9 @@ const locale = {
|
|
|
520
520
|
"Import helpers": "Assistants d'import",
|
|
521
521
|
"Import helpers will fill the URL field for you.": "Les assistants d'import vont renseigner le champ URL pour vous.",
|
|
522
522
|
"Wikipedia": "Wikipedia",
|
|
523
|
-
"Save draft": "Enregistrer le brouillon"
|
|
523
|
+
"Save draft": "Enregistrer le brouillon",
|
|
524
|
+
"No data has been found for import": "Aucunes données à importer",
|
|
525
|
+
"Successfully imported {count} feature(s)": "{count} élément(s) ajouté(s) à la carte"
|
|
524
526
|
}
|
|
525
527
|
L.registerLocale("fr", locale)
|
|
526
528
|
L.setLocale("fr")
|
umap/static/umap/locale/fr.json
CHANGED
|
@@ -520,5 +520,7 @@
|
|
|
520
520
|
"Import helpers": "Assistants d'import",
|
|
521
521
|
"Import helpers will fill the URL field for you.": "Les assistants d'import vont renseigner le champ URL pour vous.",
|
|
522
522
|
"Wikipedia": "Wikipedia",
|
|
523
|
-
"Save draft": "Enregistrer le brouillon"
|
|
523
|
+
"Save draft": "Enregistrer le brouillon",
|
|
524
|
+
"No data has been found for import": "Aucunes données à importer",
|
|
525
|
+
"Successfully imported {count} feature(s)": "{count} élément(s) ajouté(s) à la carte"
|
|
524
526
|
}
|
umap/storage/__init__.py
ADDED
umap/storage/fs.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from django.conf import settings
|
|
7
|
+
from django.core.files.storage import FileSystemStorage
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FSDataStorage(FileSystemStorage):
|
|
11
|
+
def get_reference_version(self, instance):
|
|
12
|
+
return self._extract_version_ref(instance.geojson.name)
|
|
13
|
+
|
|
14
|
+
def make_filename(self, instance):
|
|
15
|
+
root = self._base_path(instance)
|
|
16
|
+
name = "%s_%s.geojson" % (instance.pk, int(time.time() * 1000))
|
|
17
|
+
return root / name
|
|
18
|
+
|
|
19
|
+
def list_versions(self, instance):
|
|
20
|
+
root = self._base_path(instance)
|
|
21
|
+
names = self.listdir(root)[1]
|
|
22
|
+
names = [name for name in names if self._is_valid_version(name, instance)]
|
|
23
|
+
versions = [self._version_metadata(name, instance) for name in names]
|
|
24
|
+
versions.sort(reverse=True, key=operator.itemgetter("at"))
|
|
25
|
+
return versions
|
|
26
|
+
|
|
27
|
+
def get_version(self, ref, instance):
|
|
28
|
+
with self.open(self.get_version_path(ref, instance), "r") as f:
|
|
29
|
+
return f.read()
|
|
30
|
+
|
|
31
|
+
def get_version_path(self, ref, instance):
|
|
32
|
+
base_path = Path(settings.MEDIA_ROOT) / self._base_path(instance)
|
|
33
|
+
fullpath = base_path / f"{instance.pk}_{ref}.geojson"
|
|
34
|
+
if instance.old_id and not fullpath.exists():
|
|
35
|
+
fullpath = base_path / f"{instance.old_id}_{ref}.geojson"
|
|
36
|
+
if not fullpath.exists():
|
|
37
|
+
raise ValueError(f"Invalid version reference: {ref}")
|
|
38
|
+
return fullpath
|
|
39
|
+
|
|
40
|
+
def onDatalayerSave(self, instance):
|
|
41
|
+
self._purge_gzip(instance)
|
|
42
|
+
self._purge_old_versions(instance, keep=settings.UMAP_KEEP_VERSIONS)
|
|
43
|
+
|
|
44
|
+
def onDatalayerDelete(self, instance):
|
|
45
|
+
self._purge_gzip(instance)
|
|
46
|
+
self._purge_old_versions(instance, keep=None)
|
|
47
|
+
|
|
48
|
+
def _extract_version_ref(self, path):
|
|
49
|
+
version = path.split(".")[0]
|
|
50
|
+
if "_" in version:
|
|
51
|
+
return version.split("_")[-1]
|
|
52
|
+
return version
|
|
53
|
+
|
|
54
|
+
def _base_path(self, instance):
|
|
55
|
+
path = ["datalayer", str(instance.map.pk)[-1]]
|
|
56
|
+
if len(str(instance.map.pk)) > 1:
|
|
57
|
+
path.append(str(instance.map.pk)[-2])
|
|
58
|
+
path.append(str(instance.map.pk))
|
|
59
|
+
return Path(os.path.join(*path))
|
|
60
|
+
|
|
61
|
+
def _is_valid_version(self, name, instance):
|
|
62
|
+
valid_prefixes = [name.startswith("%s_" % instance.pk)]
|
|
63
|
+
if instance.old_id:
|
|
64
|
+
valid_prefixes.append(name.startswith("%s_" % instance.old_id))
|
|
65
|
+
return any(valid_prefixes) and name.endswith(".geojson")
|
|
66
|
+
|
|
67
|
+
def _version_metadata(self, name, instance):
|
|
68
|
+
ref = self._extract_version_ref(name)
|
|
69
|
+
return {
|
|
70
|
+
"name": name,
|
|
71
|
+
"ref": ref,
|
|
72
|
+
"at": ref,
|
|
73
|
+
"size": self.size(self._base_path(instance) / name),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def _purge_old_versions(self, instance, keep=None):
|
|
77
|
+
root = self._base_path(instance)
|
|
78
|
+
versions = self.list_versions(instance)
|
|
79
|
+
if keep is not None:
|
|
80
|
+
versions = versions[keep:]
|
|
81
|
+
for version in versions:
|
|
82
|
+
name = version["name"]
|
|
83
|
+
# Should not be in the list, but ensure to not delete the file
|
|
84
|
+
# currently used in database
|
|
85
|
+
if keep is not None and instance.geojson.name.endswith(name):
|
|
86
|
+
continue
|
|
87
|
+
try:
|
|
88
|
+
self.delete(root / name)
|
|
89
|
+
except FileNotFoundError:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
def _purge_gzip(self, instance):
|
|
93
|
+
root = self._base_path(instance)
|
|
94
|
+
names = self.listdir(root)[1]
|
|
95
|
+
prefixes = [f"{instance.pk}_"]
|
|
96
|
+
if instance.old_id:
|
|
97
|
+
prefixes.append(f"{instance.old_id}_")
|
|
98
|
+
prefixes = tuple(prefixes)
|
|
99
|
+
for name in names:
|
|
100
|
+
if name.startswith(prefixes) and name.endswith(".gz"):
|
|
101
|
+
self.delete(root / name)
|
umap/storage/s3.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from gzip import GzipFile
|
|
2
|
+
|
|
3
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from botocore.exceptions import ClientError
|
|
7
|
+
from storages.backends.s3 import S3Storage
|
|
8
|
+
except ImportError:
|
|
9
|
+
raise ImproperlyConfigured(
|
|
10
|
+
"You need to install s3 dependencies: pip install umap-project[s3]"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class S3DataStorage(S3Storage):
|
|
15
|
+
gzip = True
|
|
16
|
+
|
|
17
|
+
def get_reference_version(self, instance):
|
|
18
|
+
metadata = self.connection.meta.client.head_object(
|
|
19
|
+
Bucket=self.bucket_name, Key=instance.geojson.name
|
|
20
|
+
)
|
|
21
|
+
# Do not fail if bucket does not handle versioning
|
|
22
|
+
return metadata.get("VersionId", metadata["ETag"])
|
|
23
|
+
|
|
24
|
+
def make_filename(self, instance):
|
|
25
|
+
return f"{str(instance.pk)}.geojson"
|
|
26
|
+
|
|
27
|
+
def list_versions(self, instance):
|
|
28
|
+
response = self.connection.meta.client.list_object_versions(
|
|
29
|
+
Bucket=self.bucket_name, Prefix=instance.geojson.name
|
|
30
|
+
)
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
"ref": version["VersionId"],
|
|
34
|
+
"at": version["LastModified"].timestamp() * 1000,
|
|
35
|
+
"size": version["Size"],
|
|
36
|
+
}
|
|
37
|
+
for version in response["Versions"]
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
def get_version(self, ref, instance):
|
|
41
|
+
try:
|
|
42
|
+
data = self.connection.meta.client.get_object(
|
|
43
|
+
Bucket=self.bucket_name,
|
|
44
|
+
Key=instance.geojson.name,
|
|
45
|
+
VersionId=ref,
|
|
46
|
+
)
|
|
47
|
+
except ClientError:
|
|
48
|
+
raise ValueError(f"Invalid version reference: {ref}")
|
|
49
|
+
return GzipFile(mode="r", fileobj=data["Body"]).read()
|
|
50
|
+
|
|
51
|
+
def get_version_path(self, ref, instance):
|
|
52
|
+
return self.url(instance.geojson.name, parameters={"VersionId": ref})
|
|
53
|
+
|
|
54
|
+
def onDatalayerSave(self, instance):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
def onDatalayerDelete(self, instance):
|
|
58
|
+
return self.connection.meta.client.delete_object(
|
|
59
|
+
Bucket=self.bucket_name,
|
|
60
|
+
Key=instance.geojson.name,
|
|
61
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
|
|
5
|
+
from rcssmin import cssmin
|
|
6
|
+
from rjsmin import jsmin
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UmapManifestStaticFilesStorage(ManifestStaticFilesStorage):
|
|
10
|
+
support_js_module_import_aggregation = True
|
|
11
|
+
max_post_process_passes = 15
|
|
12
|
+
|
|
13
|
+
# We remove `;` at the end of all regexps to match our biome config.
|
|
14
|
+
_js_module_import_aggregation_patterns = (
|
|
15
|
+
"*.js",
|
|
16
|
+
(
|
|
17
|
+
(
|
|
18
|
+
(
|
|
19
|
+
r"""(?P<matched>import(?s:(?P<import>[\s\{].*?))"""
|
|
20
|
+
r"""\s*from\s*['"](?P<url>[\.\/].*?)["']\s*)"""
|
|
21
|
+
),
|
|
22
|
+
'import%(import)s from "%(url)s"\n',
|
|
23
|
+
),
|
|
24
|
+
(
|
|
25
|
+
(
|
|
26
|
+
r"""(?P<matched>export(?s:(?P<exports>[\s\{].*?))"""
|
|
27
|
+
r"""\s*from\s*["'](?P<url>[\.\/].*?)["']\s*)"""
|
|
28
|
+
),
|
|
29
|
+
'export%(exports)s from "%(url)s"\n',
|
|
30
|
+
),
|
|
31
|
+
(
|
|
32
|
+
r"""(?P<matched>import\s*['"](?P<url>[\.\/].*?)["']\s*)""",
|
|
33
|
+
'import"%(url)s"\n',
|
|
34
|
+
),
|
|
35
|
+
(
|
|
36
|
+
r"""(?P<matched>import\(["'](?P<url>.*?)["']\)\.then)""",
|
|
37
|
+
"""import("%(url)s").then""",
|
|
38
|
+
),
|
|
39
|
+
(
|
|
40
|
+
r"""(?P<matched>await import\(["'](?P<url>.*?)["']\))""",
|
|
41
|
+
"""await import("%(url)s")""",
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def post_process(self, paths, **options):
|
|
47
|
+
collected = super().post_process(paths, **options)
|
|
48
|
+
for original_path, processed_path, processed in collected:
|
|
49
|
+
if isinstance(processed, Exception):
|
|
50
|
+
print("Error with file", original_path)
|
|
51
|
+
raise processed
|
|
52
|
+
if processed_path.endswith(".js"):
|
|
53
|
+
path = Path(settings.STATIC_ROOT) / processed_path
|
|
54
|
+
initial = path.read_text()
|
|
55
|
+
if "sourceMappingURL" not in initial: # Already minified.
|
|
56
|
+
minified = jsmin(initial)
|
|
57
|
+
path.write_text(minified)
|
|
58
|
+
if processed_path.endswith(".css"):
|
|
59
|
+
path = Path(settings.STATIC_ROOT) / processed_path
|
|
60
|
+
initial = path.read_text()
|
|
61
|
+
if "sourceMappingURL" not in initial: # Already minified.
|
|
62
|
+
minified = cssmin(initial)
|
|
63
|
+
path.write_text(minified)
|
|
64
|
+
yield original_path, processed_path, True
|
umap/tests/test_datalayer_s3.py
CHANGED
umap/tests/test_statics.py
CHANGED
|
@@ -15,7 +15,7 @@ def staticfiles(settings):
|
|
|
15
15
|
# Make sure settings are properly reset after the test
|
|
16
16
|
settings.STORAGES = deepcopy(settings.STORAGES)
|
|
17
17
|
settings.STORAGES["staticfiles"]["BACKEND"] = (
|
|
18
|
-
"umap.storage.UmapManifestStaticFilesStorage"
|
|
18
|
+
"umap.storage.staticfiles.UmapManifestStaticFilesStorage"
|
|
19
19
|
)
|
|
20
20
|
try:
|
|
21
21
|
call_command("collectstatic", "--noinput")
|
umap/views.py
CHANGED
|
@@ -452,27 +452,27 @@ showcase = MapsShowCase.as_view()
|
|
|
452
452
|
|
|
453
453
|
|
|
454
454
|
def validate_url(request):
|
|
455
|
-
assert request.method == "GET"
|
|
455
|
+
assert request.method == "GET", "Wrong HTTP method"
|
|
456
456
|
url = request.GET.get("url")
|
|
457
|
-
assert url
|
|
457
|
+
assert url, "Missing URL"
|
|
458
458
|
try:
|
|
459
459
|
URLValidator(url)
|
|
460
|
-
except ValidationError:
|
|
461
|
-
raise AssertionError()
|
|
462
|
-
assert "HTTP_REFERER" in request.META
|
|
460
|
+
except ValidationError as err:
|
|
461
|
+
raise AssertionError(err)
|
|
462
|
+
assert "HTTP_REFERER" in request.META, "Missing HTTP_REFERER"
|
|
463
463
|
referer = urlparse(request.META.get("HTTP_REFERER"))
|
|
464
464
|
toproxy = urlparse(url)
|
|
465
465
|
local = urlparse(settings.SITE_URL)
|
|
466
|
-
assert toproxy.hostname
|
|
467
|
-
assert referer.hostname == local.hostname
|
|
468
|
-
assert toproxy.hostname != "localhost"
|
|
469
|
-
assert toproxy.netloc != local.netloc
|
|
466
|
+
assert toproxy.hostname, "No hostname"
|
|
467
|
+
assert referer.hostname == local.hostname, f"{referer.hostname} != {local.hostname}"
|
|
468
|
+
assert toproxy.hostname != "localhost", "Invalid localhost target"
|
|
469
|
+
assert toproxy.netloc != local.netloc, "Invalid netloc"
|
|
470
470
|
try:
|
|
471
471
|
# clean this when in python 3.4
|
|
472
472
|
ipaddress = socket.gethostbyname(toproxy.hostname)
|
|
473
|
-
except:
|
|
474
|
-
raise AssertionError()
|
|
475
|
-
assert not PRIVATE_IP.match(ipaddress)
|
|
473
|
+
except Exception as err:
|
|
474
|
+
raise AssertionError(err)
|
|
475
|
+
assert not PRIVATE_IP.match(ipaddress), "Private IP"
|
|
476
476
|
return url
|
|
477
477
|
|
|
478
478
|
|
|
@@ -480,7 +480,8 @@ class AjaxProxy(View):
|
|
|
480
480
|
def get(self, *args, **kwargs):
|
|
481
481
|
try:
|
|
482
482
|
url = validate_url(self.request)
|
|
483
|
-
except AssertionError:
|
|
483
|
+
except AssertionError as err:
|
|
484
|
+
print(f"AjaxProxy: {err}")
|
|
484
485
|
return HttpResponseBadRequest()
|
|
485
486
|
try:
|
|
486
487
|
ttl = int(self.request.GET.get("ttl"))
|
|
@@ -1168,7 +1169,7 @@ class DataLayerView(BaseDetailView):
|
|
|
1168
1169
|
# (no gzip/cache-control/If-Modified-Since/If-None-Match)
|
|
1169
1170
|
data = self.filedata
|
|
1170
1171
|
response = HttpResponse(data, content_type="application/geo+json")
|
|
1171
|
-
|
|
1172
|
+
response["X-Datalayer-Version"] = self.fileversion
|
|
1172
1173
|
return response
|
|
1173
1174
|
|
|
1174
1175
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
umap/__init__.py,sha256=
|
|
1
|
+
umap/__init__.py,sha256=0_15wRSL9SkA85FevSlxLo6H6SAySXR0t2XW5TtIIQA,20
|
|
2
2
|
umap/admin.py,sha256=LoQytPGK6pLBqZ5QgQ9DIPAxhTG31cTtHOCqO9BY5S4,2645
|
|
3
3
|
umap/apps.py,sha256=5ssKqPUuNJlapaBmr4LY_HDb7J1NFCT3wzythxQOOfs,109
|
|
4
4
|
umap/asgi.py,sha256=CuVSNBwNb4AvuaD_Ha3ehtvf-c46ijZoVOSoP6WhXp8,432
|
|
@@ -10,10 +10,9 @@ umap/forms.py,sha256=fonoSwA02LawR7kXbjEZCH0ZYi53fAbRHYgW2RaqeYw,3803
|
|
|
10
10
|
umap/managers.py,sha256=-lBK0xYFRDfX76qDRdLnZOA8jEPYseEwIj8QOiHVM4w,243
|
|
11
11
|
umap/middleware.py,sha256=p8EPW_gYW8Wh2lk0DNIAkZQbYlBZugW7Yq4iiA7L4aE,514
|
|
12
12
|
umap/models.py,sha256=4SzhKdyWXfJMdzEpCyVPnzbTT-TGbDusAy7SP_8UuwI,17929
|
|
13
|
-
umap/storage.py,sha256=kEzS0BP9jrfVwlUtmEcf4W0t-7uEBqFfgKBlKvQTRVg,7900
|
|
14
13
|
umap/urls.py,sha256=LA3zxyu-GDo8kVqdyU7_bdbDGhDJV8_yFW4oEPTXw4s,7559
|
|
15
14
|
umap/utils.py,sha256=19i8ibi-1IXxafT4k_yOHMhD-DsPH74Ll9qw-UrUkM4,5856
|
|
16
|
-
umap/views.py,sha256=
|
|
15
|
+
umap/views.py,sha256=hMBxefWDUZQUVUb5C477cVyL7hp7OfppZlu4_sw9L4o,46266
|
|
17
16
|
umap/websocket_server.py,sha256=D9sTHhKg0DG37b8bw7KWTKMDc6TPyTkNLCVkh2mlFOo,6604
|
|
18
17
|
umap/wsgi.py,sha256=IopIgnDZbCus3XpSetTHnra9VyzWi0Y2tJo-CmfTWCY,1132
|
|
19
18
|
umap/bin/__init__.py,sha256=iA3ON4A6NCpenrn3q2OgefUKF5QRFIQS-FtS0pxruI8,234
|
|
@@ -38,7 +37,7 @@ umap/locale/de/LC_MESSAGES/django.po,sha256=kVaio9t9AKF3vBcZJ-Q2P6Ua90MIwbIRCABW
|
|
|
38
37
|
umap/locale/el/LC_MESSAGES/django.mo,sha256=bJOH3_UQZoEfYi9mrsWHZfV6fVhMgnuiNQ_Dc_xOcuA,15058
|
|
39
38
|
umap/locale/el/LC_MESSAGES/django.po,sha256=ruqAlyQryr7Ip5aTbjEwH6eumb_b8JS-u6qcl9yjN04,22180
|
|
40
39
|
umap/locale/en/LC_MESSAGES/django.mo,sha256=UXCQbz2AxBvh-IQ7bGgjoBnijo8h9DfE9107A-2Mgkk,337
|
|
41
|
-
umap/locale/en/LC_MESSAGES/django.po,sha256=
|
|
40
|
+
umap/locale/en/LC_MESSAGES/django.po,sha256=Ht498JmD3Zp08W7qpOjJBTngf6oSMW2pR5dT3OneL5Q,13628
|
|
42
41
|
umap/locale/es/LC_MESSAGES/django.mo,sha256=rblKBhki-DnIAVxbo2UCYQCIte1dqRZDyAjDQCJgKug,11985
|
|
43
42
|
umap/locale/es/LC_MESSAGES/django.po,sha256=E0xSceHtg4CkgSHT2mFPRdiBP9n8c-PnKDXHQgAgStA,18690
|
|
44
43
|
umap/locale/et/LC_MESSAGES/django.mo,sha256=Y5ulivMuS2IOR9ITeWRx6PBynPn6LBFUizrFWRraR1s,5051
|
|
@@ -115,10 +114,10 @@ umap/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
115
114
|
umap/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
115
|
umap/management/commands/anonymous_edit_url.py,sha256=hsWgPzZJmLCoDKTWziFUuwq-DdnSiXkSal2t2TIED-s,1070
|
|
117
116
|
umap/management/commands/clean_tilelayer.py,sha256=Rcc2PibUUreU0jUZMtUlyqVvgbQMLMuuCZ2tkrzRqHU,5712
|
|
118
|
-
umap/management/commands/empty_trash.py,sha256=
|
|
117
|
+
umap/management/commands/empty_trash.py,sha256=POmBXloLoPZ_6MFbgsDz4YOKGmEwIWrvMt5v5QMi7ZM,1136
|
|
119
118
|
umap/management/commands/generate_js_locale.py,sha256=wkf-PFIHS7m4ZhyL1ZRMBLqyUeY2SlOrTXS42tE0-bs,1281
|
|
120
119
|
umap/management/commands/import_pictograms.py,sha256=RuQDCoiKamba4l3fZUGAXRyd-3zwWWT5c5AhgDvs7AQ,2369
|
|
121
|
-
umap/management/commands/migrate_to_S3.py,sha256
|
|
120
|
+
umap/management/commands/migrate_to_S3.py,sha256=GBGnydc107v75NYsQfMLLO7Jx0i2g7EKEfE00YZVb1M,1130
|
|
122
121
|
umap/management/commands/run_websocket_server.py,sha256=TyECJWnmZ95KpVEWSaqfXywz5VwIEzPdypU2d6V541c,648
|
|
123
122
|
umap/migrations/0001_initial.py,sha256=dMcXtTKPiA0IqXCrDVctH91Fe0hhc04NxmvcLAULyzE,8787
|
|
124
123
|
umap/migrations/0002_tilelayer_tms.py,sha256=E99JAu1K0NzwsCEJs1z5uGlBkBJmoVb9a3WBKjpLYlo,372
|
|
@@ -147,7 +146,7 @@ umap/migrations/0024_alter_map_share_status.py,sha256=QRjERy2XN0jsc8MM5cCba5freq
|
|
|
147
146
|
umap/migrations/0025_alter_datalayer_geojson.py,sha256=958v9AkpkAR5Q78ZcHC0fgZzN05BdfQwtNvUuPmWEvI,563
|
|
148
147
|
umap/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
149
148
|
umap/settings/__init__.py,sha256=aPJkOTk0uFusIBA-uOjdUi10R5Cxt4jl5yv2_uCTUvo,1702
|
|
150
|
-
umap/settings/base.py,sha256=
|
|
149
|
+
umap/settings/base.py,sha256=rhaIDby2wSb4v8IBx_6xqHVnIigD4G0xzdHX2-9UfNM,11096
|
|
151
150
|
umap/settings/dev.py,sha256=pj1mpmZXiI2syW8pB01wcVeqCFABF3V-nlOxArir4cw,386
|
|
152
151
|
umap/settings/local.py.sample,sha256=wpnoe7qtXer_xBuhWbcbqcSCotTJRu6h8hG7N-sD0b4,3157
|
|
153
152
|
umap/static/.gitignore,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -238,7 +237,7 @@ umap/static/umap/js/modules/formatter.js,sha256=drbIxbDGrcHOUtzJtC4B5iKpm8-YNg_b
|
|
|
238
237
|
umap/static/umap/js/modules/global.js,sha256=7jm6NLZ5PM2yrkbWHdWkoDFcevgIAMqE-vZQRXcIgEo,934
|
|
239
238
|
umap/static/umap/js/modules/help.js,sha256=0vsDTFGcPz2coG_LBeGPSUQupZTFUes6kCwQCPBUjuU,9694
|
|
240
239
|
umap/static/umap/js/modules/i18n.js,sha256=dEpjsWoEZa-Tr5_MDO0tuWkt7kLL3crxXqhttyP-khU,1387
|
|
241
|
-
umap/static/umap/js/modules/importer.js,sha256=
|
|
240
|
+
umap/static/umap/js/modules/importer.js,sha256=6wqs2z-jrfcwvSFvmPuJWDnP9s1dUEe2Oi1ili3FZ0M,10961
|
|
242
241
|
umap/static/umap/js/modules/leaflet-configure.js,sha256=P3aD8iNGxuVNv-xW4Di4txAjNmnlpKtCCzDvPaKEdQ8,243
|
|
243
242
|
umap/static/umap/js/modules/orderable.js,sha256=zDtcElZ_MVPoGba8Iv9bxOzk4vuN7C-5XVl4UomDYHE,2521
|
|
244
243
|
umap/static/umap/js/modules/permissions.js,sha256=RxKrfjLo6hdGuAvwQrzUUJJznD5RkmSkAHVMidA9iKQ,8497
|
|
@@ -249,11 +248,11 @@ umap/static/umap/js/modules/schema.js,sha256=RuBO5obpccTPH_iPXRU-lwyj1SoX9bp619t
|
|
|
249
248
|
umap/static/umap/js/modules/share.js,sha256=s1X59VpMqut_Vhg7l7LV2IZDm9obRVbDH9wZbG5kX3c,7182
|
|
250
249
|
umap/static/umap/js/modules/slideshow.js,sha256=zcRzOMkJvhps1npGRjHkdK4Ce3UkqOsv8OsDgQWO-bg,3567
|
|
251
250
|
umap/static/umap/js/modules/tableeditor.js,sha256=6p2YE2-NF4NkwLDQkqrl90P_PwOYdFao0-ac_AkOwWs,9854
|
|
252
|
-
umap/static/umap/js/modules/umap.js,sha256=
|
|
251
|
+
umap/static/umap/js/modules/umap.js,sha256=VSU8p94tLKZBGeL1NWWmrsT81RTZNW1oROOxsUqeAxI,50071
|
|
253
252
|
umap/static/umap/js/modules/urls.js,sha256=76cFqycj2O8huuoYYBvxnVt2Fc2UDbgrRsiv6lQmcSY,890
|
|
254
253
|
umap/static/umap/js/modules/utils.js,sha256=fYzo-WjRzDZsdjv3CI9U4Es3AqOfuBCJuRq997m2EfQ,12309
|
|
255
254
|
umap/static/umap/js/modules/data/features.js,sha256=rd5WpZoai3u4baWxi_8m2vtwGLuI-9hdH4PGnrjqg_4,31667
|
|
256
|
-
umap/static/umap/js/modules/data/layer.js,sha256=
|
|
255
|
+
umap/static/umap/js/modules/data/layer.js,sha256=uLEVGp3gFq7obA9Ij-RVz-VWqo9SwLQd1uBzmaKYo9o,35101
|
|
257
256
|
umap/static/umap/js/modules/importers/cadastrefr.js,sha256=KHqxHleFRFzNi98gegvUM1R6eJorAGGcMft_ktUg-ug,2262
|
|
258
257
|
umap/static/umap/js/modules/importers/communesfr.js,sha256=6q6ilmYhhuSmgdrvfTyEDNyMLbc9J9Bt8VMZVXB8ZOA,1723
|
|
259
258
|
umap/static/umap/js/modules/importers/datasets.js,sha256=StZbRiq_1vqe0OO1w66k5Lwzju8RntmHpWe9HWIDfRE,1372
|
|
@@ -298,8 +297,8 @@ umap/static/umap/locale/de.js,sha256=wUAKh3L1vhrHH819XeKeLalw8GOhjh5G-yErBUDY4R0
|
|
|
298
297
|
umap/static/umap/locale/de.json,sha256=cM1Q4WS2qCtQebyUHnHmblS4aj9j4TooDwk_nSQ8pqs,32723
|
|
299
298
|
umap/static/umap/locale/el.js,sha256=aK9fBOMEaQf-a3IU3lEhJ5qeDNRtHIR5k_WD0n09d88,41377
|
|
300
299
|
umap/static/umap/locale/el.json,sha256=G3O0k62tIsf654xUZO0x988aKIAo0zZCHzRibDxpjo8,41312
|
|
301
|
-
umap/static/umap/locale/en.js,sha256=
|
|
302
|
-
umap/static/umap/locale/en.json,sha256=
|
|
300
|
+
umap/static/umap/locale/en.js,sha256=89UHZNqyNhy86A0lPxmFzsw7UYV-f6hHEwnYg4yzQAw,30259
|
|
301
|
+
umap/static/umap/locale/en.json,sha256=U5vQn1-kXUsmAm5tTN2-NbC6UulPa0ERVfs4JQywAwo,30194
|
|
303
302
|
umap/static/umap/locale/en_US.json,sha256=E8ScCCs3lXiHOJPqTNxd7maDyhkkF3GNqN-r2p27lLQ,29813
|
|
304
303
|
umap/static/umap/locale/es.js,sha256=xYnvTBRzycvrYhEA5yr8uFV8aeCoVLPsJegdccDdjQg,32933
|
|
305
304
|
umap/static/umap/locale/es.json,sha256=-JF4DZ5B4yk2Qvhj4FsEhZs0UybS0m0t2roH0cvnPXw,32868
|
|
@@ -311,8 +310,8 @@ umap/static/umap/locale/fa_IR.js,sha256=qn-Oc23zaIz_ua3DAvvVljn--jn2fR7eaMF-bsyp
|
|
|
311
310
|
umap/static/umap/locale/fa_IR.json,sha256=ql1IvgBcDHiAD6ho500PwRE2y8x9gSfsKq96IQgAZTw,38633
|
|
312
311
|
umap/static/umap/locale/fi.js,sha256=wyW-hHzNfKHoPKata6sw_TBx3Grz227eXscuabAGbwQ,30915
|
|
313
312
|
umap/static/umap/locale/fi.json,sha256=lDQ6_RxXtcNI_kahg8XBWlVYUE-MvgUqJA_Ue6DJj3c,30850
|
|
314
|
-
umap/static/umap/locale/fr.js,sha256=
|
|
315
|
-
umap/static/umap/locale/fr.json,sha256=
|
|
313
|
+
umap/static/umap/locale/fr.js,sha256=VLtQz8W6InWu90Ccxx3gJZwuVQ2AutTKImvjdbrNv3U,33146
|
|
314
|
+
umap/static/umap/locale/fr.json,sha256=DzTqe44_JAMHYiNFqGbF3VLHmoXSoavJXcvRegigw-8,33081
|
|
316
315
|
umap/static/umap/locale/gl.js,sha256=ninZHPi3xo8aatt9G6zBNXCX6br0Na7-t20JJT0XDX4,31478
|
|
317
316
|
umap/static/umap/locale/gl.json,sha256=b99nZnWgtOZf1Uj-WUCPnWLrcvg5IGDYCV0wc7ntgb8,31413
|
|
318
317
|
umap/static/umap/locale/he.js,sha256=jljbzL6uctwBN5fzwQM4lFZ6ZLXKzldUeDTyzQTSyqU,33381
|
|
@@ -449,6 +448,10 @@ umap/static/umap/vendors/tokml/tokml.es.js,sha256=BqL0WqFH5UZAh_S_265E6PWZjPMYxe
|
|
|
449
448
|
umap/static/umap/vendors/tokml/tokml.es.mjs.map,sha256=vw5JxZFh_2_xM1cHI51r1Bf48JaBKzNcR7ddgaaF8KY,45844
|
|
450
449
|
umap/static/umap/vendors/toolbar/leaflet.toolbar.css,sha256=5KVBOQ0ivsFuafKYvVm32wJ_fi7w8Li1-2Rwwcv85jA,2244
|
|
451
450
|
umap/static/umap/vendors/toolbar/leaflet.toolbar.js,sha256=HXh_bR49ZFpJ-GNXDNo2eHy-fJmrWehAzUeHgGhu__c,5326
|
|
451
|
+
umap/storage/__init__.py,sha256=Aj421eIsZhsu0B7zd5lTJufVYr0EtUkH0lTqpbBh8AU,85
|
|
452
|
+
umap/storage/fs.py,sha256=CdjnDe4UVMt_9Yp2uZqvtarfHTAYRed6Tid2UU-sBgY,3721
|
|
453
|
+
umap/storage/s3.py,sha256=KAYu3vAqXbd5UhaoPxG6zcGtBfKZOzzi-6uY6YEuIcY,1962
|
|
454
|
+
umap/storage/staticfiles.py,sha256=mxFcenC1JECmpNy4H0e7vX8GObDZVXzs1RPjQFWNd5k,2473
|
|
452
455
|
umap/templates/404.html,sha256=1yLlD8rSF_9cfjm5FYy--P46HLVbHeFkJiW9nRzM--E,399
|
|
453
456
|
umap/templates/500.html,sha256=Z8x47OVfYXquAYAlmRB0EJVTCiCaBppFFiFEmoYsMYY,5202
|
|
454
457
|
umap/templates/base.html,sha256=_Q0Ikwese3vlUl0pKQdzHDy_oRT9OV4uWh0RGFaYAQA,1499
|
|
@@ -495,14 +498,14 @@ umap/tests/conftest.py,sha256=KQCZanCTl1ABLIKOuyxS_cpBoXGiwjDc29jsLBiSWxY,1633
|
|
|
495
498
|
umap/tests/settings.py,sha256=tY70LMFXyo_WijswqGyeWai7vBzM62k7IA8pkkbc9y4,816
|
|
496
499
|
umap/tests/test_clean_tilelayer.py,sha256=wGTd_AHOTmQ4QMswAyc-1_lJmQOSyhY3OahLAusEIdA,2515
|
|
497
500
|
umap/tests/test_datalayer.py,sha256=NWX7o-sLOrq3nHT0GDywz5vtJU4HJhZZh11r_rWxhVA,9539
|
|
498
|
-
umap/tests/test_datalayer_s3.py,sha256=
|
|
501
|
+
umap/tests/test_datalayer_s3.py,sha256=6V3AK22AXkFNjx5__SyBk0Uj0rTDAjJQv67r7D_MDVc,4155
|
|
499
502
|
umap/tests/test_datalayer_views.py,sha256=Fx_oQF3hBC2FVHTTjTScXbFS2d7FRKdBL7QFFexvKkg,22880
|
|
500
503
|
umap/tests/test_empty_trash.py,sha256=9dYdnQqzlfgkExQxiQDqQ4flKsPTZ97uB3te5wmZ0Nw,1112
|
|
501
504
|
umap/tests/test_licence.py,sha256=BxNY3gdKhIoc2u5OPmAkmjCp0jJN-Jm-uPOfAZlpOHA,339
|
|
502
505
|
umap/tests/test_map.py,sha256=vrtheSMQNk45kBIcJ0QY9K7HKYee5yg4Vnp78DyaIwQ,5170
|
|
503
506
|
umap/tests/test_map_views.py,sha256=EKLJnQ-xk_bkaJ6P7OJ2bnya5orbaSnWFV8GIYcjDNk,33823
|
|
504
507
|
umap/tests/test_merge_features.py,sha256=uLZSW00WAI8_nZS0KPP8gg8U4nnky-XGb-VhhKUxv1M,2275
|
|
505
|
-
umap/tests/test_statics.py,sha256=
|
|
508
|
+
umap/tests/test_statics.py,sha256=xKuxT8Xj5Ii7gKISuiSfDj7dpjmJ2Ierby3Lg-haZCg,1264
|
|
506
509
|
umap/tests/test_team_views.py,sha256=vExhJ3c1cJ7vgxe0G20UzTKkzR5D2UgAapk09muUg5w,4481
|
|
507
510
|
umap/tests/test_tilelayer.py,sha256=toVpVutEvMLWKx5uH7ZbGNPGzqICZx1_S2OOpIfYPfQ,603
|
|
508
511
|
umap/tests/test_utils.py,sha256=noh-AFL3qV-dNZYr8L1acsYC02SI710Bq2ZXV-jBEzk,407
|
|
@@ -569,8 +572,8 @@ umap/tests/integration/test_view_marker.py,sha256=ZLS6-GOWYpjeoYGHiHa7HesXJTLu9w
|
|
|
569
572
|
umap/tests/integration/test_view_polygon.py,sha256=NMJC6Nt9VpQ8FIU9Pqq2OspHv49xsWlsoXCr8iBa0VA,2060
|
|
570
573
|
umap/tests/integration/test_view_polyline.py,sha256=aJoXKmLhJaN0yhPdDCVskZNGx3q3mLDkjVPhZ30cadA,13959
|
|
571
574
|
umap/tests/integration/test_websocket_sync.py,sha256=Xjn8z7Gj2PAmPmLkMTsHztFmhzsfyE3vg-wfewpA2I4,15511
|
|
572
|
-
umap_project-2.8.
|
|
573
|
-
umap_project-2.8.
|
|
574
|
-
umap_project-2.8.
|
|
575
|
-
umap_project-2.8.
|
|
576
|
-
umap_project-2.8.
|
|
575
|
+
umap_project-2.8.0a2.dist-info/METADATA,sha256=Z8IXnRgLp4AOUzTAkfRUB5hatnauca2C0V7NrLZieHk,2993
|
|
576
|
+
umap_project-2.8.0a2.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
577
|
+
umap_project-2.8.0a2.dist-info/entry_points.txt,sha256=gz-KDQfEsMLBae8ABOD3foJsCYGPW1tA4Y394R_1RW8,39
|
|
578
|
+
umap_project-2.8.0a2.dist-info/licenses/LICENSE,sha256=kQtrtRKgiPhcl7aO0-lmvbrNAXu7WHyiXvPrUk-TD2Q,820
|
|
579
|
+
umap_project-2.8.0a2.dist-info/RECORD,,
|
umap/storage.py
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import operator
|
|
2
|
-
import os
|
|
3
|
-
import shutil
|
|
4
|
-
import time
|
|
5
|
-
from gzip import GzipFile
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
from botocore.exceptions import ClientError
|
|
9
|
-
from django.conf import settings
|
|
10
|
-
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
|
|
11
|
-
from django.core.files.storage import FileSystemStorage
|
|
12
|
-
from rcssmin import cssmin
|
|
13
|
-
from rjsmin import jsmin
|
|
14
|
-
from storages.backends.s3 import S3Storage
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class UmapManifestStaticFilesStorage(ManifestStaticFilesStorage):
|
|
18
|
-
support_js_module_import_aggregation = True
|
|
19
|
-
max_post_process_passes = 15
|
|
20
|
-
|
|
21
|
-
# We remove `;` at the end of all regexps to match our biome config.
|
|
22
|
-
_js_module_import_aggregation_patterns = (
|
|
23
|
-
"*.js",
|
|
24
|
-
(
|
|
25
|
-
(
|
|
26
|
-
(
|
|
27
|
-
r"""(?P<matched>import(?s:(?P<import>[\s\{].*?))"""
|
|
28
|
-
r"""\s*from\s*['"](?P<url>[\.\/].*?)["']\s*)"""
|
|
29
|
-
),
|
|
30
|
-
'import%(import)s from "%(url)s"\n',
|
|
31
|
-
),
|
|
32
|
-
(
|
|
33
|
-
(
|
|
34
|
-
r"""(?P<matched>export(?s:(?P<exports>[\s\{].*?))"""
|
|
35
|
-
r"""\s*from\s*["'](?P<url>[\.\/].*?)["']\s*)"""
|
|
36
|
-
),
|
|
37
|
-
'export%(exports)s from "%(url)s"\n',
|
|
38
|
-
),
|
|
39
|
-
(
|
|
40
|
-
r"""(?P<matched>import\s*['"](?P<url>[\.\/].*?)["']\s*)""",
|
|
41
|
-
'import"%(url)s"\n',
|
|
42
|
-
),
|
|
43
|
-
(
|
|
44
|
-
r"""(?P<matched>import\(["'](?P<url>.*?)["']\)\.then)""",
|
|
45
|
-
"""import("%(url)s").then""",
|
|
46
|
-
),
|
|
47
|
-
(
|
|
48
|
-
r"""(?P<matched>await import\(["'](?P<url>.*?)["']\))""",
|
|
49
|
-
"""await import("%(url)s")""",
|
|
50
|
-
),
|
|
51
|
-
),
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
def post_process(self, paths, **options):
|
|
55
|
-
collected = super().post_process(paths, **options)
|
|
56
|
-
for original_path, processed_path, processed in collected:
|
|
57
|
-
if isinstance(processed, Exception):
|
|
58
|
-
print("Error with file", original_path)
|
|
59
|
-
raise processed
|
|
60
|
-
if processed_path.endswith(".js"):
|
|
61
|
-
path = Path(settings.STATIC_ROOT) / processed_path
|
|
62
|
-
initial = path.read_text()
|
|
63
|
-
if "sourceMappingURL" not in initial: # Already minified.
|
|
64
|
-
minified = jsmin(initial)
|
|
65
|
-
path.write_text(minified)
|
|
66
|
-
if processed_path.endswith(".css"):
|
|
67
|
-
path = Path(settings.STATIC_ROOT) / processed_path
|
|
68
|
-
initial = path.read_text()
|
|
69
|
-
if "sourceMappingURL" not in initial: # Already minified.
|
|
70
|
-
minified = cssmin(initial)
|
|
71
|
-
path.write_text(minified)
|
|
72
|
-
yield original_path, processed_path, True
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class UmapS3(S3Storage):
|
|
76
|
-
gzip = True
|
|
77
|
-
|
|
78
|
-
def get_reference_version(self, instance):
|
|
79
|
-
metadata = self.connection.meta.client.head_object(
|
|
80
|
-
Bucket=self.bucket_name, Key=instance.geojson.name
|
|
81
|
-
)
|
|
82
|
-
# Do not fail if bucket does not handle versioning
|
|
83
|
-
return metadata.get("VersionId", metadata["ETag"])
|
|
84
|
-
|
|
85
|
-
def make_filename(self, instance):
|
|
86
|
-
return f"{str(instance.pk)}.geojson"
|
|
87
|
-
|
|
88
|
-
def list_versions(self, instance):
|
|
89
|
-
response = self.connection.meta.client.list_object_versions(
|
|
90
|
-
Bucket=self.bucket_name, Prefix=instance.geojson.name
|
|
91
|
-
)
|
|
92
|
-
return [
|
|
93
|
-
{
|
|
94
|
-
"ref": version["VersionId"],
|
|
95
|
-
"at": version["LastModified"].timestamp() * 1000,
|
|
96
|
-
"size": version["Size"],
|
|
97
|
-
}
|
|
98
|
-
for version in response["Versions"]
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
def get_version(self, ref, instance):
|
|
102
|
-
try:
|
|
103
|
-
data = self.connection.meta.client.get_object(
|
|
104
|
-
Bucket=self.bucket_name,
|
|
105
|
-
Key=instance.geojson.name,
|
|
106
|
-
VersionId=ref,
|
|
107
|
-
)
|
|
108
|
-
except ClientError:
|
|
109
|
-
raise ValueError(f"Invalid version reference: {ref}")
|
|
110
|
-
return GzipFile(mode="r", fileobj=data["Body"]).read()
|
|
111
|
-
|
|
112
|
-
def get_version_path(self, ref, instance):
|
|
113
|
-
return self.url(instance.geojson.name, parameters={"VersionId": ref})
|
|
114
|
-
|
|
115
|
-
def onDatalayerSave(self, instance):
|
|
116
|
-
pass
|
|
117
|
-
|
|
118
|
-
def onDatalayerDelete(self, instance):
|
|
119
|
-
return self.connection.meta.client.delete_object(
|
|
120
|
-
Bucket=self.bucket_name,
|
|
121
|
-
Key=instance.geojson.name,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class UmapFileSystem(FileSystemStorage):
|
|
126
|
-
def get_reference_version(self, instance):
|
|
127
|
-
return self._extract_version_ref(instance.geojson.name)
|
|
128
|
-
|
|
129
|
-
def make_filename(self, instance):
|
|
130
|
-
root = self._base_path(instance)
|
|
131
|
-
name = "%s_%s.geojson" % (instance.pk, int(time.time() * 1000))
|
|
132
|
-
return root / name
|
|
133
|
-
|
|
134
|
-
def list_versions(self, instance):
|
|
135
|
-
root = self._base_path(instance)
|
|
136
|
-
names = self.listdir(root)[1]
|
|
137
|
-
names = [name for name in names if self._is_valid_version(name, instance)]
|
|
138
|
-
versions = [self._version_metadata(name, instance) for name in names]
|
|
139
|
-
versions.sort(reverse=True, key=operator.itemgetter("at"))
|
|
140
|
-
return versions
|
|
141
|
-
|
|
142
|
-
def get_version(self, ref, instance):
|
|
143
|
-
with self.open(self.get_version_path(ref, instance), "r") as f:
|
|
144
|
-
return f.read()
|
|
145
|
-
|
|
146
|
-
def get_version_path(self, ref, instance):
|
|
147
|
-
base_path = Path(settings.MEDIA_ROOT) / self._base_path(instance)
|
|
148
|
-
fullpath = base_path / f"{instance.pk}_{ref}.geojson"
|
|
149
|
-
if instance.old_id and not fullpath.exists():
|
|
150
|
-
fullpath = base_path / f"{instance.old_id}_{ref}.geojson"
|
|
151
|
-
if not fullpath.exists():
|
|
152
|
-
raise ValueError(f"Invalid version reference: {ref}")
|
|
153
|
-
return fullpath
|
|
154
|
-
|
|
155
|
-
def onDatalayerSave(self, instance):
|
|
156
|
-
self._purge_gzip(instance)
|
|
157
|
-
self._purge_old_versions(instance, keep=settings.UMAP_KEEP_VERSIONS)
|
|
158
|
-
|
|
159
|
-
def onDatalayerDelete(self, instance):
|
|
160
|
-
self._purge_gzip(instance)
|
|
161
|
-
self._purge_old_versions(instance, keep=None)
|
|
162
|
-
|
|
163
|
-
def _extract_version_ref(self, path):
|
|
164
|
-
version = path.split(".")[0]
|
|
165
|
-
if "_" in version:
|
|
166
|
-
return version.split("_")[-1]
|
|
167
|
-
return version
|
|
168
|
-
|
|
169
|
-
def _base_path(self, instance):
|
|
170
|
-
path = ["datalayer", str(instance.map.pk)[-1]]
|
|
171
|
-
if len(str(instance.map.pk)) > 1:
|
|
172
|
-
path.append(str(instance.map.pk)[-2])
|
|
173
|
-
path.append(str(instance.map.pk))
|
|
174
|
-
return Path(os.path.join(*path))
|
|
175
|
-
|
|
176
|
-
def _is_valid_version(self, name, instance):
|
|
177
|
-
valid_prefixes = [name.startswith("%s_" % instance.pk)]
|
|
178
|
-
if instance.old_id:
|
|
179
|
-
valid_prefixes.append(name.startswith("%s_" % instance.old_id))
|
|
180
|
-
return any(valid_prefixes) and name.endswith(".geojson")
|
|
181
|
-
|
|
182
|
-
def _version_metadata(self, name, instance):
|
|
183
|
-
ref = self._extract_version_ref(name)
|
|
184
|
-
return {
|
|
185
|
-
"name": name,
|
|
186
|
-
"ref": ref,
|
|
187
|
-
"at": ref,
|
|
188
|
-
"size": self.size(self._base_path(instance) / name),
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
def _purge_old_versions(self, instance, keep=None):
|
|
192
|
-
root = self._base_path(instance)
|
|
193
|
-
versions = self.list_versions(instance)
|
|
194
|
-
if keep is not None:
|
|
195
|
-
versions = versions[keep:]
|
|
196
|
-
for version in versions:
|
|
197
|
-
name = version["name"]
|
|
198
|
-
# Should not be in the list, but ensure to not delete the file
|
|
199
|
-
# currently used in database
|
|
200
|
-
if keep is not None and instance.geojson.name.endswith(name):
|
|
201
|
-
continue
|
|
202
|
-
try:
|
|
203
|
-
self.delete(root / name)
|
|
204
|
-
except FileNotFoundError:
|
|
205
|
-
pass
|
|
206
|
-
|
|
207
|
-
def _purge_gzip(self, instance):
|
|
208
|
-
root = self._base_path(instance)
|
|
209
|
-
names = self.listdir(root)[1]
|
|
210
|
-
prefixes = [f"{instance.pk}_"]
|
|
211
|
-
if instance.old_id:
|
|
212
|
-
prefixes.append(f"{instance.old_id}_")
|
|
213
|
-
prefixes = tuple(prefixes)
|
|
214
|
-
for name in names:
|
|
215
|
-
if name.startswith(prefixes) and name.endswith(".gz"):
|
|
216
|
-
self.delete(root / name)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|