umap-project 2.0.4__py3-none-any.whl → 2.1.1__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.
- umap/__init__.py +1 -1
- umap/fields.py +3 -1
- umap/locale/br/LC_MESSAGES/django.po +76 -71
- umap/locale/en/LC_MESSAGES/django.po +41 -41
- umap/locale/hu/LC_MESSAGES/django.po +42 -42
- umap/locale/it/LC_MESSAGES/django.po +64 -58
- umap/locale/ms/LC_MESSAGES/django.po +62 -57
- umap/migrations/0018_datalayer_uuid.py +62 -0
- umap/migrations/0019_migrate_internal_remote_datalayers.py +52 -0
- umap/models.py +20 -3
- umap/settings/base.py +1 -0
- umap/settings/dev.py +1 -0
- umap/static/umap/js/modules/browser.js +2 -2
- umap/static/umap/js/modules/global.js +14 -4
- umap/static/umap/js/modules/i18n.js +35 -0
- umap/static/umap/js/modules/leaflet-configure.js +7 -0
- umap/static/umap/js/modules/schema.js +388 -0
- umap/static/umap/js/modules/urls.js +17 -2
- umap/static/umap/js/modules/utils.js +24 -0
- umap/static/umap/js/umap.controls.js +9 -10
- umap/static/umap/js/umap.core.js +5 -5
- umap/static/umap/js/umap.features.js +23 -9
- umap/static/umap/js/umap.forms.js +49 -299
- umap/static/umap/js/umap.icon.js +2 -2
- umap/static/umap/js/umap.js +26 -129
- umap/static/umap/js/umap.layer.js +9 -9
- umap/static/umap/js/umap.popup.js +3 -0
- umap/static/umap/js/umap.share.js +1 -1
- umap/static/umap/locale/am_ET.json +229 -225
- umap/static/umap/locale/ar.json +229 -225
- umap/static/umap/locale/ast.json +229 -225
- umap/static/umap/locale/bg.json +229 -225
- umap/static/umap/locale/br.json +237 -233
- umap/static/umap/locale/ca.json +229 -225
- umap/static/umap/locale/cs_CZ.json +229 -225
- umap/static/umap/locale/da.json +229 -225
- umap/static/umap/locale/de.json +229 -225
- umap/static/umap/locale/el.json +229 -225
- umap/static/umap/locale/en.json +230 -233
- umap/static/umap/locale/en_US.json +229 -225
- umap/static/umap/locale/es.json +229 -225
- umap/static/umap/locale/et.json +229 -225
- umap/static/umap/locale/eu.json +226 -198
- umap/static/umap/locale/fa_IR.json +229 -225
- umap/static/umap/locale/fi.json +229 -225
- umap/static/umap/locale/fr.json +229 -232
- umap/static/umap/locale/gl.json +229 -225
- umap/static/umap/locale/he.json +229 -225
- umap/static/umap/locale/hr.json +229 -225
- umap/static/umap/locale/hu.json +229 -232
- umap/static/umap/locale/id.json +229 -225
- umap/static/umap/locale/is.json +229 -225
- umap/static/umap/locale/it.json +229 -232
- umap/static/umap/locale/ja.json +229 -225
- umap/static/umap/locale/ko.json +229 -225
- umap/static/umap/locale/lt.json +229 -225
- umap/static/umap/locale/ms.json +229 -232
- umap/static/umap/locale/nl.json +232 -228
- umap/static/umap/locale/no.json +229 -225
- umap/static/umap/locale/pl.json +229 -225
- umap/static/umap/locale/pl_PL.json +229 -225
- umap/static/umap/locale/pt.json +229 -225
- umap/static/umap/locale/pt_BR.json +229 -225
- umap/static/umap/locale/pt_PT.json +229 -225
- umap/static/umap/locale/ro.json +229 -225
- umap/static/umap/locale/ru.json +229 -225
- umap/static/umap/locale/sk_SK.json +229 -225
- umap/static/umap/locale/sl.json +229 -225
- umap/static/umap/locale/sr.json +229 -225
- umap/static/umap/locale/sv.json +229 -225
- umap/static/umap/locale/th_TH.json +229 -225
- umap/static/umap/locale/tr.json +229 -225
- umap/static/umap/locale/uk_UA.json +229 -225
- umap/static/umap/locale/vi.json +229 -225
- umap/static/umap/locale/vi_VN.json +229 -225
- umap/static/umap/locale/zh.json +229 -225
- umap/static/umap/locale/zh_CN.json +229 -225
- umap/static/umap/locale/zh_TW.Big5.json +229 -225
- umap/static/umap/locale/zh_TW.json +229 -232
- umap/static/umap/test/index.html +0 -2
- umap/static/umap/{test → unittests}/URLs.js +5 -0
- umap/static/umap/vendors/leaflet/leaflet-src.esm.js +7064 -7064
- umap/static/umap/vendors/photon/leaflet.photon.js +3 -0
- umap/templates/umap/js.html +8 -6
- umap/templatetags/umap_tags.py +3 -2
- umap/tests/integration/test_browser.py +40 -0
- umap/tests/integration/test_collaborative_editing.py +72 -3
- umap/tests/integration/test_export_map.py +226 -9
- umap/tests/integration/test_features_id_generation.py +51 -0
- umap/tests/integration/test_owned_map.py +14 -1
- umap/tests/integration/test_statics.py +3 -3
- umap/tests/integration/test_tilelayer.py +3 -3
- umap/tests/settings.py +3 -3
- umap/tests/test_datalayer_views.py +77 -20
- umap/tests/test_map_views.py +20 -0
- umap/tests/test_merge_features.py +25 -5
- umap/urls.py +12 -12
- umap/utils.py +7 -0
- umap/views.py +58 -49
- umap/wsgi.py +1 -0
- {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/METADATA +9 -9
- {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/RECORD +105 -99
- umap/static/umap/test/Map.Export.js +0 -106
- {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/WHEEL +0 -0
- {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/entry_points.txt +0 -0
- {umap_project-2.0.4.dist-info → umap_project-2.1.1.dist-info}/licenses/LICENSE +0 -0
umap/views.py
CHANGED
|
@@ -38,11 +38,11 @@ from django.http import (
|
|
|
38
38
|
from django.middleware.gzip import re_accepts_gzip
|
|
39
39
|
from django.shortcuts import get_object_or_404
|
|
40
40
|
from django.urls import resolve, reverse, reverse_lazy
|
|
41
|
+
from django.utils import translation
|
|
41
42
|
from django.utils.encoding import smart_bytes
|
|
42
43
|
from django.utils.http import http_date
|
|
43
44
|
from django.utils.timezone import make_aware
|
|
44
45
|
from django.utils.translation import gettext as _
|
|
45
|
-
from django.utils.translation import to_locale
|
|
46
46
|
from django.views.decorators.cache import cache_control
|
|
47
47
|
from django.views.decorators.http import require_GET
|
|
48
48
|
from django.views.generic import DetailView, TemplateView, View
|
|
@@ -67,7 +67,14 @@ from .forms import (
|
|
|
67
67
|
UserProfileForm,
|
|
68
68
|
)
|
|
69
69
|
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
|
|
70
|
-
from .utils import
|
|
70
|
+
from .utils import (
|
|
71
|
+
ConflictError,
|
|
72
|
+
_urls_for_js,
|
|
73
|
+
gzip_file,
|
|
74
|
+
is_ajax,
|
|
75
|
+
json_dumps,
|
|
76
|
+
merge_features,
|
|
77
|
+
)
|
|
71
78
|
|
|
72
79
|
User = get_user_model()
|
|
73
80
|
|
|
@@ -315,14 +322,14 @@ class UserDownload(DetailView, SearchMixin):
|
|
|
315
322
|
with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
|
|
316
323
|
for map_ in self.get_maps():
|
|
317
324
|
umapjson = map_.generate_umapjson(self.request)
|
|
318
|
-
geojson_file = io.StringIO(
|
|
325
|
+
geojson_file = io.StringIO(json_dumps(umapjson))
|
|
319
326
|
file_name = f"umap_backup_{map_.slug}_{map_.pk}.umap"
|
|
320
327
|
zip_file.writestr(file_name, geojson_file.getvalue())
|
|
321
328
|
|
|
322
329
|
response = HttpResponse(zip_buffer.getvalue(), content_type="application/zip")
|
|
323
|
-
response[
|
|
324
|
-
"
|
|
325
|
-
|
|
330
|
+
response["Content-Disposition"] = (
|
|
331
|
+
'attachment; filename="umap_backup_complete.zip"'
|
|
332
|
+
)
|
|
326
333
|
return response
|
|
327
334
|
|
|
328
335
|
|
|
@@ -354,7 +361,7 @@ class MapsShowCase(View):
|
|
|
354
361
|
}
|
|
355
362
|
|
|
356
363
|
geojson = {"type": "FeatureCollection", "features": [make(m) for m in maps]}
|
|
357
|
-
return HttpResponse(smart_bytes(
|
|
364
|
+
return HttpResponse(smart_bytes(json_dumps(geojson)))
|
|
358
365
|
|
|
359
366
|
|
|
360
367
|
showcase = MapsShowCase.as_view()
|
|
@@ -441,7 +448,7 @@ ajax_proxy = AjaxProxy.as_view()
|
|
|
441
448
|
|
|
442
449
|
|
|
443
450
|
def simple_json_response(**kwargs):
|
|
444
|
-
return HttpResponse(
|
|
451
|
+
return HttpResponse(json_dumps(kwargs), content_type="application/json")
|
|
445
452
|
|
|
446
453
|
|
|
447
454
|
# ############## #
|
|
@@ -488,7 +495,7 @@ class MapDetailMixin:
|
|
|
488
495
|
"urls": _urls_for_js(),
|
|
489
496
|
"tilelayers": TileLayer.get_list(),
|
|
490
497
|
"editMode": self.edit_mode,
|
|
491
|
-
"
|
|
498
|
+
"schema": Map.extra_schema,
|
|
492
499
|
"umap_id": self.get_umap_id(),
|
|
493
500
|
"starred": self.is_starred(),
|
|
494
501
|
"licences": dict((l.name, l.json) for l in Licence.objects.all()),
|
|
@@ -529,7 +536,7 @@ class MapDetailMixin:
|
|
|
529
536
|
if hasattr(self.request, "LANGUAGE_CODE"):
|
|
530
537
|
lang = self.request.LANGUAGE_CODE
|
|
531
538
|
properties["lang"] = lang
|
|
532
|
-
locale = to_locale(lang)
|
|
539
|
+
locale = translation.to_locale(lang)
|
|
533
540
|
properties["locale"] = locale
|
|
534
541
|
context["locale"] = locale
|
|
535
542
|
geojson = self.get_geojson()
|
|
@@ -537,7 +544,7 @@ class MapDetailMixin:
|
|
|
537
544
|
geojson["properties"] = {}
|
|
538
545
|
geojson["properties"].update(properties)
|
|
539
546
|
geojson["properties"]["datalayers"] = self.get_datalayers()
|
|
540
|
-
context["map_settings"] =
|
|
547
|
+
context["map_settings"] = json_dumps(geojson, indent=settings.DEBUG)
|
|
541
548
|
self.set_preconnect(geojson["properties"], context)
|
|
542
549
|
return context
|
|
543
550
|
|
|
@@ -608,7 +615,9 @@ class MapView(MapDetailMixin, PermissionsMixin, DetailView):
|
|
|
608
615
|
if request.META.get("QUERY_STRING"):
|
|
609
616
|
canonical = "?".join([canonical, request.META["QUERY_STRING"]])
|
|
610
617
|
return HttpResponsePermanentRedirect(canonical)
|
|
611
|
-
|
|
618
|
+
response = super(MapView, self).get(request, *args, **kwargs)
|
|
619
|
+
response["Access-Control-Allow-Origin"] = "*"
|
|
620
|
+
return response
|
|
612
621
|
|
|
613
622
|
def get_canonical_url(self):
|
|
614
623
|
return self.object.get_absolute_url()
|
|
@@ -666,9 +675,9 @@ class MapDownload(DetailView):
|
|
|
666
675
|
def render_to_response(self, context, *args, **kwargs):
|
|
667
676
|
umapjson = self.object.generate_umapjson(self.request)
|
|
668
677
|
response = simple_json_response(**umapjson)
|
|
669
|
-
response[
|
|
670
|
-
"
|
|
671
|
-
|
|
678
|
+
response["Content-Disposition"] = (
|
|
679
|
+
f'attachment; filename="umap_backup_{self.object.slug}.umap"'
|
|
680
|
+
)
|
|
672
681
|
return response
|
|
673
682
|
|
|
674
683
|
|
|
@@ -692,6 +701,8 @@ class MapOEmbed(View):
|
|
|
692
701
|
raise Http404("Host not allowed.")
|
|
693
702
|
|
|
694
703
|
url_path = parsed_url.path
|
|
704
|
+
lang = translation.get_language_from_path(url_path)
|
|
705
|
+
translation.activate(lang)
|
|
695
706
|
view, args, kwargs = resolve(url_path)
|
|
696
707
|
if "slug" not in kwargs or "map_id" not in kwargs:
|
|
697
708
|
raise Http404("Invalid URL path.")
|
|
@@ -715,7 +726,9 @@ class MapOEmbed(View):
|
|
|
715
726
|
f'<p><a href="//{netloc}{map_url}">{label}</a></p>'
|
|
716
727
|
)
|
|
717
728
|
data["html"] = html
|
|
718
|
-
|
|
729
|
+
response = simple_json_response(**data)
|
|
730
|
+
response["Access-Control-Allow-Origin"] = "*"
|
|
731
|
+
return response
|
|
719
732
|
|
|
720
733
|
|
|
721
734
|
class MapViewGeoJSON(MapView):
|
|
@@ -962,20 +975,20 @@ class GZipMixin(object):
|
|
|
962
975
|
|
|
963
976
|
@property
|
|
964
977
|
def path(self):
|
|
965
|
-
return self.object.geojson.path
|
|
978
|
+
return Path(self.object.geojson.path)
|
|
966
979
|
|
|
967
980
|
@property
|
|
968
981
|
def gzip_path(self):
|
|
969
982
|
return Path(f"{self.path}{self.EXT}")
|
|
970
983
|
|
|
971
|
-
def
|
|
972
|
-
|
|
973
|
-
return
|
|
984
|
+
def read_version(self, path):
|
|
985
|
+
# Remove optional .gz, then .geojson, then return the trailing version from path.
|
|
986
|
+
return str(path.with_suffix("").with_suffix("")).split("_")[-1]
|
|
974
987
|
|
|
975
988
|
@property
|
|
976
|
-
def
|
|
989
|
+
def version(self):
|
|
977
990
|
# Prior to 1.3.0 we did not set gzip mtime as geojson mtime,
|
|
978
|
-
# but we switched from If-Match header to
|
|
991
|
+
# but we switched from If-Match header to If-Unmodified-Since
|
|
979
992
|
# and when users accepts gzip their last modified value is the gzip
|
|
980
993
|
# (when umap is served by nginx and X-Accel-Redirect)
|
|
981
994
|
# one, so we need to compare with that value in that case.
|
|
@@ -985,7 +998,7 @@ class GZipMixin(object):
|
|
|
985
998
|
if self.accepts_gzip and self.gzip_path.exists()
|
|
986
999
|
else self.path
|
|
987
1000
|
)
|
|
988
|
-
return self.
|
|
1001
|
+
return self.read_version(path)
|
|
989
1002
|
|
|
990
1003
|
@property
|
|
991
1004
|
def accepts_gzip(self):
|
|
@@ -1007,8 +1020,8 @@ class DataLayerView(GZipMixin, BaseDetailView):
|
|
|
1007
1020
|
|
|
1008
1021
|
if getattr(settings, "UMAP_XSENDFILE_HEADER", None):
|
|
1009
1022
|
response = HttpResponse()
|
|
1010
|
-
|
|
1011
|
-
response[settings.UMAP_XSENDFILE_HEADER] =
|
|
1023
|
+
internal_path = str(path).replace(settings.MEDIA_ROOT, "/internal")
|
|
1024
|
+
response[settings.UMAP_XSENDFILE_HEADER] = internal_path
|
|
1012
1025
|
else:
|
|
1013
1026
|
# Do not use in production
|
|
1014
1027
|
# (no gzip/cache-control/If-Modified-Since/If-None-Match)
|
|
@@ -1016,7 +1029,7 @@ class DataLayerView(GZipMixin, BaseDetailView):
|
|
|
1016
1029
|
with open(path, "rb") as f:
|
|
1017
1030
|
# Should not be used in production!
|
|
1018
1031
|
response = HttpResponse(f.read(), content_type="application/geo+json")
|
|
1019
|
-
response["
|
|
1032
|
+
response["X-Datalayer-Version"] = self.version
|
|
1020
1033
|
response["Content-Length"] = statobj.st_size
|
|
1021
1034
|
return response
|
|
1022
1035
|
|
|
@@ -1024,9 +1037,8 @@ class DataLayerView(GZipMixin, BaseDetailView):
|
|
|
1024
1037
|
class DataLayerVersion(DataLayerView):
|
|
1025
1038
|
@property
|
|
1026
1039
|
def path(self):
|
|
1027
|
-
return
|
|
1028
|
-
|
|
1029
|
-
path=self.object.get_version_path(self.kwargs["name"]),
|
|
1040
|
+
return Path(settings.MEDIA_ROOT) / self.object.get_version_path(
|
|
1041
|
+
self.kwargs["name"]
|
|
1030
1042
|
)
|
|
1031
1043
|
|
|
1032
1044
|
|
|
@@ -1037,11 +1049,11 @@ class DataLayerCreate(FormLessEditMixin, GZipMixin, CreateView):
|
|
|
1037
1049
|
def form_valid(self, form):
|
|
1038
1050
|
form.instance.map = self.kwargs["map_inst"]
|
|
1039
1051
|
self.object = form.save()
|
|
1040
|
-
# Simple response with only
|
|
1052
|
+
# Simple response with only metadata (including new id)
|
|
1041
1053
|
response = simple_json_response(
|
|
1042
1054
|
**self.object.metadata(self.request.user, self.request)
|
|
1043
1055
|
)
|
|
1044
|
-
response["
|
|
1056
|
+
response["X-Datalayer-Version"] = self.version
|
|
1045
1057
|
return response
|
|
1046
1058
|
|
|
1047
1059
|
|
|
@@ -1049,30 +1061,29 @@ class DataLayerUpdate(FormLessEditMixin, GZipMixin, UpdateView):
|
|
|
1049
1061
|
model = DataLayer
|
|
1050
1062
|
form_class = DataLayerForm
|
|
1051
1063
|
|
|
1052
|
-
def
|
|
1053
|
-
return
|
|
1064
|
+
def has_changes_since(self, incoming_version):
|
|
1065
|
+
return incoming_version and self.version != incoming_version
|
|
1054
1066
|
|
|
1055
|
-
def merge(self,
|
|
1067
|
+
def merge(self, reference_version):
|
|
1056
1068
|
"""
|
|
1057
|
-
Attempt to apply the incoming changes to the
|
|
1058
|
-
|
|
1069
|
+
Attempt to apply the incoming changes to the reference, and then merge it
|
|
1070
|
+
with the last document we have on storage.
|
|
1059
1071
|
|
|
1060
1072
|
Returns either None (if the merge failed) or the merged python GeoJSON object.
|
|
1061
1073
|
"""
|
|
1062
1074
|
|
|
1063
|
-
# Use
|
|
1075
|
+
# Use the provided info to find the correct version in our storage.
|
|
1064
1076
|
for name in self.object.get_versions():
|
|
1065
|
-
path =
|
|
1066
|
-
if
|
|
1077
|
+
path = Path(settings.MEDIA_ROOT) / self.object.get_version_path(name)
|
|
1078
|
+
if reference_version == self.read_version(path):
|
|
1067
1079
|
with open(path) as f:
|
|
1068
1080
|
reference = json.loads(f.read())
|
|
1069
1081
|
break
|
|
1070
1082
|
else:
|
|
1071
1083
|
# If the document is not found, we can't merge.
|
|
1072
1084
|
return None
|
|
1073
|
-
|
|
1074
1085
|
# New data received in the request.
|
|
1075
|
-
|
|
1086
|
+
incoming = json.loads(self.request.FILES["geojson"].read())
|
|
1076
1087
|
|
|
1077
1088
|
# Latest known version of the data.
|
|
1078
1089
|
with open(self.path) as f:
|
|
@@ -1082,7 +1093,7 @@ class DataLayerUpdate(FormLessEditMixin, GZipMixin, UpdateView):
|
|
|
1082
1093
|
merged_features = merge_features(
|
|
1083
1094
|
reference.get("features", []),
|
|
1084
1095
|
latest.get("features", []),
|
|
1085
|
-
|
|
1096
|
+
incoming.get("features", []),
|
|
1086
1097
|
)
|
|
1087
1098
|
latest["features"] = merged_features
|
|
1088
1099
|
return latest
|
|
@@ -1097,16 +1108,15 @@ class DataLayerUpdate(FormLessEditMixin, GZipMixin, UpdateView):
|
|
|
1097
1108
|
if not self.object.can_edit(user=self.request.user, request=self.request):
|
|
1098
1109
|
return HttpResponseForbidden()
|
|
1099
1110
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
merged = self.merge(ius_header)
|
|
1111
|
+
reference_version = self.request.headers.get("X-Datalayer-Reference")
|
|
1112
|
+
if self.has_changes_since(reference_version):
|
|
1113
|
+
merged = self.merge(reference_version)
|
|
1104
1114
|
if not merged:
|
|
1105
1115
|
return HttpResponse(status=412)
|
|
1106
1116
|
|
|
1107
1117
|
# Replace the uploaded file by the merged version.
|
|
1108
1118
|
self.request.FILES["geojson"].file = BytesIO(
|
|
1109
|
-
|
|
1119
|
+
json_dumps(merged).encode("utf-8")
|
|
1110
1120
|
)
|
|
1111
1121
|
|
|
1112
1122
|
# Mark the data to be reloaded by form_valid
|
|
@@ -1120,8 +1130,7 @@ class DataLayerUpdate(FormLessEditMixin, GZipMixin, UpdateView):
|
|
|
1120
1130
|
data["geojson"] = json.loads(self.object.geojson.read().decode())
|
|
1121
1131
|
self.request.session["needs_reload"] = False
|
|
1122
1132
|
response = simple_json_response(**data)
|
|
1123
|
-
|
|
1124
|
-
response["Last-Modified"] = self.last_modified
|
|
1133
|
+
response["X-Datalayer-Version"] = self.version
|
|
1125
1134
|
return response
|
|
1126
1135
|
|
|
1127
1136
|
|
umap/wsgi.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: umap-project
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Create maps with OpenStreetMap layers in a minute and embed them in your site.
|
|
5
5
|
Author-email: Yohan Boniface <yb@enix.org>
|
|
6
6
|
Maintainer-email: David Larlet <david@larlet.fr>
|
|
@@ -19,7 +19,7 @@ Requires-Python: >=3.10
|
|
|
19
19
|
Requires-Dist: django-agnocomplete==2.2.0
|
|
20
20
|
Requires-Dist: django-environ==0.11.2
|
|
21
21
|
Requires-Dist: django-probes==1.7.0
|
|
22
|
-
Requires-Dist: django==5.0.
|
|
22
|
+
Requires-Dist: django==5.0.3
|
|
23
23
|
Requires-Dist: pillow==10.2.0
|
|
24
24
|
Requires-Dist: psycopg==3.1.18
|
|
25
25
|
Requires-Dist: rcssmin==1.1.2
|
|
@@ -29,22 +29,22 @@ Requires-Dist: social-auth-app-django==5.4.0
|
|
|
29
29
|
Requires-Dist: social-auth-core==4.5.3
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: djlint==1.34.1; extra == 'dev'
|
|
32
|
-
Requires-Dist: hatch==1.9.
|
|
32
|
+
Requires-Dist: hatch==1.9.4; extra == 'dev'
|
|
33
33
|
Requires-Dist: isort==5.13.2; extra == 'dev'
|
|
34
|
-
Requires-Dist: mkdocs-material==9.5.
|
|
34
|
+
Requires-Dist: mkdocs-material==9.5.14; extra == 'dev'
|
|
35
35
|
Requires-Dist: mkdocs==1.5.3; extra == 'dev'
|
|
36
|
-
Requires-Dist: pymdown-extensions==10.7; extra == 'dev'
|
|
37
|
-
Requires-Dist: ruff==0.
|
|
36
|
+
Requires-Dist: pymdown-extensions==10.7.1; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff==0.3.3; extra == 'dev'
|
|
38
38
|
Requires-Dist: vermin==1.6.0; extra == 'dev'
|
|
39
39
|
Provides-Extra: docker
|
|
40
40
|
Requires-Dist: uwsgi==2.0.24; extra == 'docker'
|
|
41
41
|
Provides-Extra: test
|
|
42
42
|
Requires-Dist: factory-boy==3.2.1; extra == 'test'
|
|
43
43
|
Requires-Dist: playwright>=1.39; extra == 'test'
|
|
44
|
-
Requires-Dist: pytest-django==4.
|
|
45
|
-
Requires-Dist: pytest-playwright==0.4.
|
|
44
|
+
Requires-Dist: pytest-django==4.8.0; extra == 'test'
|
|
45
|
+
Requires-Dist: pytest-playwright==0.4.4; extra == 'test'
|
|
46
46
|
Requires-Dist: pytest-xdist<4,>=3.5.0; extra == 'test'
|
|
47
|
-
Requires-Dist: pytest==
|
|
47
|
+
Requires-Dist: pytest==8.0.2; extra == 'test'
|
|
48
48
|
Description-Content-Type: text/markdown
|
|
49
49
|
|
|
50
50
|
# uMap project
|