geovisio 2.8.0__py3-none-any.whl → 2.8.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.
- geovisio/__init__.py +16 -3
- geovisio/config_app.py +0 -1
- geovisio/translations/br/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/br/LC_MESSAGES/messages.po +762 -0
- geovisio/translations/da/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/da/LC_MESSAGES/messages.po +10 -1
- geovisio/translations/de/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/de/LC_MESSAGES/messages.po +10 -1
- geovisio/translations/en/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/en/LC_MESSAGES/messages.po +9 -7
- geovisio/translations/eo/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/eo/LC_MESSAGES/messages.po +67 -1
- geovisio/translations/es/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/es/LC_MESSAGES/messages.po +4 -3
- geovisio/translations/fr/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/fr/LC_MESSAGES/messages.po +30 -4
- geovisio/translations/hu/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/hu/LC_MESSAGES/messages.po +4 -3
- geovisio/translations/it/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/it/LC_MESSAGES/messages.po +10 -1
- geovisio/translations/ja/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/ja/LC_MESSAGES/messages.po +242 -154
- geovisio/translations/pl/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/pl/LC_MESSAGES/messages.po +4 -3
- geovisio/utils/loggers.py +14 -0
- geovisio/utils/params.py +7 -4
- geovisio/utils/upload_set.py +24 -10
- geovisio/utils/website.py +3 -0
- geovisio/web/auth.py +3 -2
- geovisio/web/docs.py +3 -14
- geovisio/web/upload_set.py +98 -10
- geovisio/web/users.py +31 -4
- {geovisio-2.8.0.dist-info → geovisio-2.8.1.dist-info}/METADATA +4 -3
- {geovisio-2.8.0.dist-info → geovisio-2.8.1.dist-info}/RECORD +36 -33
- {geovisio-2.8.0.dist-info → geovisio-2.8.1.dist-info}/WHEEL +1 -1
- {geovisio-2.8.0.dist-info → geovisio-2.8.1.dist-info/licenses}/LICENSE +0 -0
|
Binary file
|
|
@@ -8,8 +8,8 @@ msgstr ""
|
|
|
8
8
|
"Project-Id-Version: PROJECT VERSION\n"
|
|
9
9
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
|
10
10
|
"POT-Creation-Date: 2024-10-28 10:15+0100\n"
|
|
11
|
-
"PO-Revision-Date:
|
|
12
|
-
"Last-Translator:
|
|
11
|
+
"PO-Revision-Date: 2025-02-27 12:32+0000\n"
|
|
12
|
+
"Last-Translator: PanierAvide <adrien@pavie.info>\n"
|
|
13
13
|
"Language-Team: Polish <http://weblate.panoramax.xyz/projects/panoramax/api/"
|
|
14
14
|
"pl/>\n"
|
|
15
15
|
"Language: pl\n"
|
|
@@ -250,6 +250,7 @@ msgid "Sequence %(c)s is in %(s)s state, its visibility can't be changed for now
|
|
|
250
250
|
msgstr ""
|
|
251
251
|
|
|
252
252
|
#: geovisio/web/collections.py:766
|
|
253
|
+
#, fuzzy
|
|
253
254
|
msgid "Sequence doesn't exists"
|
|
254
255
|
msgstr "Sekwencja nie istnieje"
|
|
255
256
|
|
|
@@ -332,7 +333,7 @@ msgid "No more items in this collection (last available rank is %(r)s)"
|
|
|
332
333
|
msgstr ""
|
|
333
334
|
|
|
334
335
|
#: geovisio/web/items.py:413
|
|
335
|
-
#, python-format
|
|
336
|
+
#, fuzzy, python-format
|
|
336
337
|
msgid "Picture with id %(p)s does not exists"
|
|
337
338
|
msgstr "Zdjęcie z id %(p)s nie istnieje"
|
|
338
339
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LoggingWithExtra(logging.LoggerAdapter):
|
|
5
|
+
"""Add some metadata to the log message"""
|
|
6
|
+
|
|
7
|
+
def process(self, msg, kwargs):
|
|
8
|
+
sep = " " if self.extra else ""
|
|
9
|
+
return f"{self.extra if self.extra else ''}{sep}{msg}", kwargs
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def getLoggerWithExtra(logger_name, extra):
|
|
13
|
+
"""Create a logger with extra information. Those information will be displayed in the log message"""
|
|
14
|
+
return LoggingWithExtra(logging.getLogger(logger_name), extra)
|
geovisio/utils/params.py
CHANGED
|
@@ -12,9 +12,12 @@ def validation_error(e: ValidationError):
|
|
|
12
12
|
}
|
|
13
13
|
if d["input"]:
|
|
14
14
|
detail["input"] = d["input"]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
try:
|
|
16
|
+
if "user_agent" in detail["input"]:
|
|
17
|
+
del detail["input"]["user_agent"]
|
|
18
|
+
if len(detail["input"]) == 0:
|
|
19
|
+
del detail["input"]
|
|
20
|
+
except TypeError:
|
|
21
|
+
pass
|
|
19
22
|
details.append(detail)
|
|
20
23
|
return {"details": details}
|
geovisio/utils/upload_set.py
CHANGED
|
@@ -17,6 +17,8 @@ from flask import current_app
|
|
|
17
17
|
from flask_babel import gettext as _
|
|
18
18
|
from geopic_tag_reader import sequence as geopic_sequence, reader
|
|
19
19
|
|
|
20
|
+
from geovisio.utils.loggers import getLoggerWithExtra
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
class AggregatedStatus(BaseModel):
|
|
22
24
|
"""Aggregated status"""
|
|
@@ -435,6 +437,7 @@ def dispatch(upload_set_id: UUID):
|
|
|
435
437
|
if not db_upload_set:
|
|
436
438
|
raise Exception(f"Upload set {upload_set_id} not found")
|
|
437
439
|
|
|
440
|
+
logger = getLoggerWithExtra("geovisio.upload_set", {"upload_set_id": str(upload_set_id)})
|
|
438
441
|
with db.conn(current_app) as conn:
|
|
439
442
|
with conn.transaction(), conn.cursor(row_factory=dict_row) as cursor:
|
|
440
443
|
|
|
@@ -493,19 +496,19 @@ WHERE p.upload_set_id = %(upload_set_id)s"""
|
|
|
493
496
|
maxDistance=db_upload_set.duplicate_distance, maxRotationAngle=db_upload_set.duplicate_rotation
|
|
494
497
|
),
|
|
495
498
|
sortMethod=db_upload_set.sort_method,
|
|
496
|
-
splitParams=geopic_sequence.SplitParams(
|
|
499
|
+
splitParams=geopic_sequence.SplitParams(
|
|
500
|
+
maxDistance=db_upload_set.split_distance, maxTime=db_upload_set.split_time.total_seconds()
|
|
501
|
+
),
|
|
497
502
|
)
|
|
498
503
|
reused_sequence = set()
|
|
499
504
|
|
|
500
505
|
pics_to_delete_duplicates = [pics_by_filename[p.filename]["id"] for p in report.duplicate_pictures or []]
|
|
501
506
|
pics_to_delete = pics_to_delete_duplicates + pics_to_delete_bug
|
|
502
507
|
if pics_to_delete:
|
|
503
|
-
|
|
504
|
-
f"
|
|
505
|
-
)
|
|
506
|
-
logging.debug(
|
|
507
|
-
f"For uploadset '{upload_set_id}', duplicate pictures {[p.filename for p in report.duplicate_pictures or []]}"
|
|
508
|
+
logger.debug(
|
|
509
|
+
f"nb duplicate pictures {len(pics_to_delete_duplicates)} {f' and {len(pics_to_delete_bug)} pictures without files' if pics_to_delete_bug else ''}"
|
|
508
510
|
)
|
|
511
|
+
logger.debug(f"duplicate pictures {[p.filename for p in report.duplicate_pictures or []]}")
|
|
509
512
|
|
|
510
513
|
cursor.execute(SQL("CREATE TEMPORARY TABLE tmp_duplicates(picture_id UUID) ON COMMIT DROP"))
|
|
511
514
|
with cursor.copy("COPY tmp_duplicates(picture_id) FROM stdin;") as copy:
|
|
@@ -521,6 +524,8 @@ WHERE p.upload_set_id = %(upload_set_id)s"""
|
|
|
521
524
|
cursor.execute(SQL("DELETE FROM pictures WHERE id IN (select picture_id FROM tmp_duplicates)"))
|
|
522
525
|
|
|
523
526
|
number_title = len(report.sequences) > 1
|
|
527
|
+
existing_sequences = set(p["sequence_id"] for p in db_pics if p["sequence_id"])
|
|
528
|
+
new_sequence_ids = set()
|
|
524
529
|
for i, s in enumerate(report.sequences, start=1):
|
|
525
530
|
existing_sequence = next(
|
|
526
531
|
(seq for p in s.pictures if (seq := pics_by_filename[p.filename]["sequence_id"]) not in reused_sequence),
|
|
@@ -528,9 +533,7 @@ WHERE p.upload_set_id = %(upload_set_id)s"""
|
|
|
528
533
|
)
|
|
529
534
|
# if some of the pictures were already in a sequence, we should not create a new one
|
|
530
535
|
if existing_sequence:
|
|
531
|
-
|
|
532
|
-
f"For uploadset '{upload_set_id}', sequence {existing_sequence} already contains pictures, we will not create a new one"
|
|
533
|
-
)
|
|
536
|
+
logger.info(f"sequence {existing_sequence} already contains pictures, we will not create a new one")
|
|
534
537
|
# we should wipe the sequences_pictures though
|
|
535
538
|
seq_id = existing_sequence
|
|
536
539
|
cursor.execute(
|
|
@@ -554,6 +557,8 @@ RETURNING id"""
|
|
|
554
557
|
).fetchone()
|
|
555
558
|
seq_id = seq_id["id"]
|
|
556
559
|
|
|
560
|
+
new_sequence_ids.add(seq_id)
|
|
561
|
+
|
|
557
562
|
with cursor.copy("COPY sequences_pictures(seq_id, pic_id, rank) FROM stdin;") as copy:
|
|
558
563
|
for i, p in enumerate(s.pictures, 1):
|
|
559
564
|
copy.write_row(
|
|
@@ -562,8 +567,17 @@ RETURNING id"""
|
|
|
562
567
|
|
|
563
568
|
sequences.add_finalization_job(cursor=cursor, seqId=seq_id)
|
|
564
569
|
|
|
570
|
+
# we can delete all the old sequences
|
|
571
|
+
sequences_to_delete = existing_sequences - new_sequence_ids
|
|
572
|
+
if sequences_to_delete:
|
|
573
|
+
logger.debug(f"sequences to delete = {sequences_to_delete} (existing = {existing_sequences}, new = {new_sequence_ids})")
|
|
574
|
+
conn.execute(SQL("DELETE FROM sequences_pictures WHERE seq_id = ANY(%(seq_ids)s)"), {"seq_ids": list(sequences_to_delete)})
|
|
575
|
+
conn.execute(
|
|
576
|
+
SQL("UPDATE sequences SET status = 'deleted' WHERE id = ANY(%(seq_ids)s)"), {"seq_ids": list(sequences_to_delete)}
|
|
577
|
+
)
|
|
578
|
+
|
|
565
579
|
for s in report.sequences_splits or []:
|
|
566
|
-
|
|
580
|
+
logger.debug(f"split = {s.prevPic.filename} -> {s.nextPic.filename} : {s.reason}")
|
|
567
581
|
conn.execute(SQL("UPDATE upload_sets SET dispatched = true WHERE id = %(upload_set_id)s"), {"upload_set_id": db_upload_set.id})
|
|
568
582
|
|
|
569
583
|
|
geovisio/utils/website.py
CHANGED
|
@@ -37,6 +37,9 @@ class Website:
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
def _to_url(self, route: str, params: Optional[Dict[str, str]] = None):
|
|
40
|
+
if not self.url:
|
|
41
|
+
return None
|
|
42
|
+
|
|
40
43
|
base_url = self.url if self.url != WEBSITE_UNDER_SAME_HOST else url_for("index", _external=True)
|
|
41
44
|
|
|
42
45
|
from urllib.parse import urlencode
|
geovisio/web/auth.py
CHANGED
|
@@ -94,8 +94,9 @@ def auth():
|
|
|
94
94
|
if not tos_accepted and current_app.config["API_ENFORCE_TOS_ACCEPTANCE"]:
|
|
95
95
|
args = {"next_url": next_url} if next_url else None
|
|
96
96
|
next_url = current_app.config["API_WEBSITE_URL"].tos_validation_page(args)
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
|
|
98
|
+
if next_url is None:
|
|
99
|
+
next_url = "/"
|
|
99
100
|
|
|
100
101
|
response = flask.make_response(redirect(next_url))
|
|
101
102
|
|
geovisio/web/docs.py
CHANGED
|
@@ -647,20 +647,9 @@ Available properties are:
|
|
|
647
647
|
"GeoVisioUserConfiguration": users.UserConfiguration.model_json_schema(
|
|
648
648
|
ref_template="#/components/schemas/GeoVisioUserConfiguration/$defs/{model}", mode="serialization"
|
|
649
649
|
),
|
|
650
|
-
"GeoVisioUser":
|
|
651
|
-
"
|
|
652
|
-
|
|
653
|
-
"id": {"type": "string", "format": "uuid"},
|
|
654
|
-
"name": {"type": "string"},
|
|
655
|
-
"links": {
|
|
656
|
-
"type": "array",
|
|
657
|
-
"items": {
|
|
658
|
-
"type": "object",
|
|
659
|
-
"properties": {"href": {"type": "string"}, "ref": {"type": "string"}, "type": {"type": "string"}},
|
|
660
|
-
},
|
|
661
|
-
},
|
|
662
|
-
},
|
|
663
|
-
},
|
|
650
|
+
"GeoVisioUser": users.UserInfo.model_json_schema(
|
|
651
|
+
ref_template="#/components/schemas/GeoVisioUser/$defs/{model}", mode="serialization"
|
|
652
|
+
),
|
|
664
653
|
"GeoVisioUserAuth": {
|
|
665
654
|
"type": "object",
|
|
666
655
|
"properties": {
|
geovisio/web/upload_set.py
CHANGED
|
@@ -2,7 +2,7 @@ from copy import deepcopy
|
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
|
|
4
4
|
import PIL
|
|
5
|
-
from geovisio.utils import auth
|
|
5
|
+
from geovisio.utils import auth, model_query
|
|
6
6
|
from psycopg.rows import class_row, dict_row
|
|
7
7
|
from psycopg.sql import SQL
|
|
8
8
|
from flask import current_app, request, Blueprint, url_for
|
|
@@ -68,21 +68,32 @@ class UploadSetCreationParameter(BaseModel):
|
|
|
68
68
|
model_config = ConfigDict(use_attribute_docstrings=True)
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
class UploadSetUpdateParameter(BaseModel):
|
|
72
|
+
"""Parameters used to update an UploadSet"""
|
|
73
|
+
|
|
74
|
+
sort_method: Optional[geopic_sequence.SortMethod] = None
|
|
75
|
+
"""Strategy used for sorting your pictures. Either by filename or EXIF time, in ascending or descending order."""
|
|
76
|
+
split_distance: Optional[int] = None
|
|
77
|
+
"""Maximum distance between two pictures to be considered in the same sequence (in meters)."""
|
|
78
|
+
split_time: Optional[timedelta] = None
|
|
79
|
+
"""Maximum time interval between two pictures to be considered in the same sequence."""
|
|
80
|
+
duplicate_distance: Optional[float] = None
|
|
81
|
+
"""Maximum distance between two pictures to be considered as duplicates (in meters)."""
|
|
82
|
+
duplicate_rotation: Optional[int] = None
|
|
83
|
+
"""Maximum angle of rotation for two too-close-pictures to be considered as duplicates (in degrees)."""
|
|
84
|
+
|
|
85
|
+
model_config = ConfigDict(use_attribute_docstrings=True, extra="forbid")
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if isinstance(v, Dict):
|
|
78
|
-
params_as_dict[k] = Jsonb(v) # convert dict to jsonb in database
|
|
87
|
+
|
|
88
|
+
def create_upload_set(params: UploadSetCreationParameter, accountId: UUID) -> UploadSet:
|
|
89
|
+
db_params = model_query.get_db_params_and_values(params, account_id=accountId)
|
|
79
90
|
|
|
80
91
|
db_upload_set = db.fetchone(
|
|
81
92
|
current_app,
|
|
82
93
|
SQL("INSERT INTO upload_sets({fields}) VALUES({values}) RETURNING *").format(
|
|
83
|
-
fields=
|
|
94
|
+
fields=db_params.fields(), values=db_params.placeholders()
|
|
84
95
|
),
|
|
85
|
-
params_as_dict,
|
|
96
|
+
db_params.params_as_dict,
|
|
86
97
|
row_factory=class_row(UploadSet),
|
|
87
98
|
)
|
|
88
99
|
|
|
@@ -92,6 +103,25 @@ def create_upload_set(params: UploadSetCreationParameter, accountId: UUID) -> Up
|
|
|
92
103
|
return db_upload_set
|
|
93
104
|
|
|
94
105
|
|
|
106
|
+
def update_upload_set(upload_set_id: UUID, params: UploadSetUpdateParameter) -> UploadSet:
|
|
107
|
+
db_params = model_query.get_db_params_and_values(params)
|
|
108
|
+
|
|
109
|
+
with db.conn(current_app) as conn, conn.transaction():
|
|
110
|
+
import psycopg
|
|
111
|
+
|
|
112
|
+
cur = psycopg.ClientCursor(conn)
|
|
113
|
+
q = SQL("UPDATE upload_sets SET {fields} WHERE id = %(upload_set_id)s").format(fields=db_params.fields_for_set())
|
|
114
|
+
print(cur.mogrify(q, db_params.params_as_dict | {"upload_set_id": upload_set_id}))
|
|
115
|
+
|
|
116
|
+
with db.execute(
|
|
117
|
+
current_app,
|
|
118
|
+
SQL("UPDATE upload_sets SET {fields} WHERE id = %(upload_set_id)s").format(fields=db_params.fields_for_set()),
|
|
119
|
+
db_params.params_as_dict | {"upload_set_id": upload_set_id},
|
|
120
|
+
):
|
|
121
|
+
# we get a full uploadset response
|
|
122
|
+
return get_upload_set(upload_set_id)
|
|
123
|
+
|
|
124
|
+
|
|
95
125
|
@bp.route("/upload_sets", methods=["POST"])
|
|
96
126
|
@auth.login_required_by_setting("API_FORCE_AUTH_ON_UPLOAD")
|
|
97
127
|
def postUploadSet(account=None):
|
|
@@ -150,6 +180,64 @@ def postUploadSet(account=None):
|
|
|
150
180
|
)
|
|
151
181
|
|
|
152
182
|
|
|
183
|
+
@bp.route("/upload_sets/<uuid:upload_set_id>", methods=["PATCH"])
|
|
184
|
+
@auth.login_required_by_setting("API_FORCE_AUTH_ON_UPLOAD")
|
|
185
|
+
def patchUploadSet(upload_set_id, account=None):
|
|
186
|
+
"""Update an existing UploadSet.
|
|
187
|
+
|
|
188
|
+
Note that the upload set will not be dispatched again, so if you changed the dispatch parameters (like split_distance, split_time, duplicate_distance, duplicate_rotation, ...), you need to call the `POST /api/upload_sets/:id/complete` endpoint to dispatch the upload set afterward.
|
|
189
|
+
---
|
|
190
|
+
tags:
|
|
191
|
+
- Upload
|
|
192
|
+
- UploadSet
|
|
193
|
+
parameters:
|
|
194
|
+
- name: upload_set_id
|
|
195
|
+
in: path
|
|
196
|
+
description: ID of the UploadSet
|
|
197
|
+
required: true
|
|
198
|
+
schema:
|
|
199
|
+
type: string
|
|
200
|
+
requestBody:
|
|
201
|
+
content:
|
|
202
|
+
application/json:
|
|
203
|
+
schema:
|
|
204
|
+
$ref: '#/components/schemas/GeoVisioUploadSet'
|
|
205
|
+
security:
|
|
206
|
+
- bearerToken: []
|
|
207
|
+
- cookieAuth: []
|
|
208
|
+
responses:
|
|
209
|
+
200:
|
|
210
|
+
description: the UploadSet metadata
|
|
211
|
+
content:
|
|
212
|
+
application/json:
|
|
213
|
+
schema:
|
|
214
|
+
$ref: '#/components/schemas/GeoVisioUploadSet'
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
if request.is_json and request.json is not None:
|
|
218
|
+
try:
|
|
219
|
+
params = UploadSetUpdateParameter(**request.json)
|
|
220
|
+
except ValidationError as ve:
|
|
221
|
+
raise errors.InvalidAPIUsage(_("Impossible to update the UploadSet"), payload=validation_error(ve))
|
|
222
|
+
else:
|
|
223
|
+
raise errors.InvalidAPIUsage(_("Parameter for updating an UploadSet should be a valid JSON"), status_code=415)
|
|
224
|
+
|
|
225
|
+
upload_set = get_simple_upload_set(upload_set_id)
|
|
226
|
+
if upload_set is None:
|
|
227
|
+
raise errors.InvalidAPIUsage(_("UploadSet doesn't exist"), status_code=404)
|
|
228
|
+
|
|
229
|
+
if account and str(upload_set.account_id) != account.id:
|
|
230
|
+
raise errors.InvalidAPIUsage(_("You are not allowed to update this upload set"), status_code=403)
|
|
231
|
+
|
|
232
|
+
if not params.model_fields_set:
|
|
233
|
+
# nothing to update, return the upload set
|
|
234
|
+
upload_set = get_upload_set(upload_set_id)
|
|
235
|
+
else:
|
|
236
|
+
upload_set = update_upload_set(upload_set_id, params)
|
|
237
|
+
|
|
238
|
+
return upload_set.model_dump_json(exclude_none=True), 200, {"Content-Type": "application/json"}
|
|
239
|
+
|
|
240
|
+
|
|
153
241
|
@bp.route("/upload_sets/<uuid:upload_set_id>", methods=["GET"])
|
|
154
242
|
def getUploadSet(upload_set_id):
|
|
155
243
|
"""Get an existing UploadSet
|
geovisio/web/users.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
from uuid import UUID
|
|
3
3
|
import flask
|
|
4
|
-
from flask import
|
|
4
|
+
from flask import request, current_app, session, url_for
|
|
5
5
|
from flask_babel import gettext as _
|
|
6
|
-
from pydantic import BaseModel,
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, ValidationError, computed_field
|
|
7
7
|
from geovisio.utils import auth, db
|
|
8
8
|
from geovisio import errors
|
|
9
9
|
from psycopg.rows import dict_row, class_row
|
|
@@ -19,6 +19,21 @@ from geovisio.web.utils import get_root_link
|
|
|
19
19
|
bp = flask.Blueprint("user", __name__, url_prefix="/api/users")
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
class Permissions(BaseModel):
|
|
23
|
+
"""Role and permissions of a user"""
|
|
24
|
+
|
|
25
|
+
role: auth.AccountRole
|
|
26
|
+
"""Role of the user"""
|
|
27
|
+
can_check_reports: bool
|
|
28
|
+
"""Is account legitimate to read any report ?"""
|
|
29
|
+
can_edit_excluded_areas: bool
|
|
30
|
+
"""Is account legitimate to read and edit excluded areas ?"""
|
|
31
|
+
can_edit_pages: bool
|
|
32
|
+
"""Is account legitimate to edit web pages ?"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(use_attribute_docstrings=True, use_enum_values=True)
|
|
35
|
+
|
|
36
|
+
|
|
22
37
|
class UserInfo(BaseModel):
|
|
23
38
|
name: str
|
|
24
39
|
"""Name of the user"""
|
|
@@ -30,6 +45,11 @@ class UserInfo(BaseModel):
|
|
|
30
45
|
tos_accepted: Optional[bool] = None
|
|
31
46
|
"""True means the user has accepted the terms of service (tos). Can only be seen by the user itself"""
|
|
32
47
|
|
|
48
|
+
permissions: Optional[Permissions] = None
|
|
49
|
+
"""The user role and permissions. Can only be seen by the user itself"""
|
|
50
|
+
|
|
51
|
+
model_config = ConfigDict(use_attribute_docstrings=True)
|
|
52
|
+
|
|
33
53
|
@computed_field
|
|
34
54
|
@property
|
|
35
55
|
def links(self) -> List[Link]:
|
|
@@ -55,8 +75,15 @@ def _get_user_info(account: auth.Account):
|
|
|
55
75
|
user_info = UserInfo(id=account.id, name=account.name, collaborative_metadata=account.collaborative_metadata)
|
|
56
76
|
logged_account = auth.get_current_account()
|
|
57
77
|
if logged_account is not None and account.id == logged_account.id:
|
|
58
|
-
# we show the term of service acceptance only if the user is the logged user
|
|
59
|
-
|
|
78
|
+
# we show the term of service acceptance only if the user is the logged user and if ToS are mandatory
|
|
79
|
+
if flask.current_app.config["API_ENFORCE_TOS_ACCEPTANCE"]:
|
|
80
|
+
user_info.tos_accepted = account.tos_accepted
|
|
81
|
+
user_info.permissions = Permissions(
|
|
82
|
+
role=account.role,
|
|
83
|
+
can_check_reports=account.can_check_reports(),
|
|
84
|
+
can_edit_excluded_areas=account.can_edit_excluded_areas(),
|
|
85
|
+
can_edit_pages=account.can_edit_pages(),
|
|
86
|
+
)
|
|
60
87
|
|
|
61
88
|
return user_info.model_dump(exclude_unset=True), 200, {"Content-Type": "application/json"}
|
|
62
89
|
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: geovisio
|
|
3
|
-
Version: 2.8.
|
|
3
|
+
Version: 2.8.1
|
|
4
4
|
Summary: GeoVisio API - Main
|
|
5
5
|
Author-email: Adrien PAVIE <panieravide@riseup.net>, Antoine Desbordes <antoine.desbordes@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
License-File: LICENSE
|
|
9
10
|
Requires-Dist: Flask ~= 2.3
|
|
10
11
|
Requires-Dist: psycopg[pool] ~= 3.1
|
|
11
12
|
Requires-Dist: flasgger ~= 0.9.7
|
|
12
13
|
Requires-Dist: Pillow ~= 9.4
|
|
13
14
|
Requires-Dist: Flask-Cors ~= 4.0
|
|
14
15
|
Requires-Dist: fs ~= 2.4
|
|
15
|
-
Requires-Dist: fs-s3fs-forked ~= 1.1.
|
|
16
|
+
Requires-Dist: fs-s3fs-forked ~= 1.1.4
|
|
16
17
|
Requires-Dist: flask-compress ~= 1.14
|
|
17
18
|
Requires-Dist: requests ~= 2.31
|
|
18
19
|
Requires-Dist: yoyo-migrations ~= 8.2
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
geovisio/__init__.py,sha256=
|
|
2
|
-
geovisio/config_app.py,sha256=
|
|
1
|
+
geovisio/__init__.py,sha256=h7zC88f4S9UjmjGNmSux7v1JqN2Vo05f_8Pv97Zq8Hg,7754
|
|
2
|
+
geovisio/config_app.py,sha256=u44GidJcV4HzCESONi0PsnbfGZqzQ3sxdRXKhpji22o,14198
|
|
3
3
|
geovisio/db_migrations.py,sha256=9lHkyG_RiCWzrFkfwhkslScUsbCZScN-KVhkXrtnPDo,4560
|
|
4
4
|
geovisio/errors.py,sha256=uTn-kI7SUl5OPB8Mv3Qqu7Ucp5JvcqWPQFfgLCqsEpI,1376
|
|
5
5
|
geovisio/admin_cli/__init__.py,sha256=1e0hX771-3iG8eBcNmVvUYyg8qXnpng-9YWvi3MI3Kg,3248
|
|
@@ -12,34 +12,36 @@ geovisio/admin_cli/user.py,sha256=4ml2E_aphz3I3NcuUPB2dwe_jXhcE7AGa0R5VTm3_ik,27
|
|
|
12
12
|
geovisio/templates/main.html,sha256=VDVQwCZ1mNjH7sH4VOIdn8gM09R9LJZX49SPtA2VEzM,2963
|
|
13
13
|
geovisio/templates/viewer.html,sha256=JErXdU2ujj4LdMHgQbYNCTfKuYGEXbJTQwBE-K_MNXQ,892
|
|
14
14
|
geovisio/translations/messages.pot,sha256=SUPAgov3RzwVw0LNOMn2NkTthXfirbBDIQK_2BaZ2FI,19445
|
|
15
|
-
geovisio/translations/
|
|
16
|
-
geovisio/translations/
|
|
17
|
-
geovisio/translations/
|
|
18
|
-
geovisio/translations/
|
|
15
|
+
geovisio/translations/br/LC_MESSAGES/messages.mo,sha256=FjvUouzaLCBxvVH51A4PVwGGFk6mAepYasnI4v8LcnQ,719
|
|
16
|
+
geovisio/translations/br/LC_MESSAGES/messages.po,sha256=rHkbb3QTngCJxz0WmoBqMl4H-x2bXKYKxdZCEZv2P9w,19434
|
|
17
|
+
geovisio/translations/da/LC_MESSAGES/messages.mo,sha256=46hqJP4gsM3ZK2DG3L47GeC9Q-xFWrY36_OkAJCkuIg,21262
|
|
18
|
+
geovisio/translations/da/LC_MESSAGES/messages.po,sha256=MRmMNc6oQL9R8rvDewHCUhZMSH2TbsfskspCFQzKk2s,29393
|
|
19
|
+
geovisio/translations/de/LC_MESSAGES/messages.mo,sha256=hWb_yaHUHan3tFTyTbnDKJgfnD4wuaeO2nhc24V2ARM,22831
|
|
20
|
+
geovisio/translations/de/LC_MESSAGES/messages.po,sha256=nmlWG_BmcvksfDW9H8d3bTTgIgd-8ucF9lcLg3WB6us,31078
|
|
19
21
|
geovisio/translations/el/LC_MESSAGES/messages.mo,sha256=vy1jtEG6mLS5sYWPfQIr5U4XsZ21ZzSbsHAJHGQXZSY,433
|
|
20
22
|
geovisio/translations/el/LC_MESSAGES/messages.po,sha256=gDr-pDCsQGrCXBMBcDwlfsxcGWF1NIEqGrqPcZy65-4,17405
|
|
21
|
-
geovisio/translations/en/LC_MESSAGES/messages.mo,sha256=
|
|
22
|
-
geovisio/translations/en/LC_MESSAGES/messages.po,sha256=
|
|
23
|
-
geovisio/translations/eo/LC_MESSAGES/messages.mo,sha256=
|
|
24
|
-
geovisio/translations/eo/LC_MESSAGES/messages.po,sha256=
|
|
25
|
-
geovisio/translations/es/LC_MESSAGES/messages.mo,sha256=
|
|
26
|
-
geovisio/translations/es/LC_MESSAGES/messages.po,sha256=
|
|
23
|
+
geovisio/translations/en/LC_MESSAGES/messages.mo,sha256=KzPxb2olWNvZrUZE9hY0ComHM6t1B2kV5bWvQQ9GUMo,20018
|
|
24
|
+
geovisio/translations/en/LC_MESSAGES/messages.po,sha256=RRfEZ2nZFNWhrbZOPzigy63TVK8cbp8vRikXIj0ibT4,28041
|
|
25
|
+
geovisio/translations/eo/LC_MESSAGES/messages.mo,sha256=67UES-hJwqciW0AiJ2fwcN0K34dBkYNRG2SqVQn3va8,21029
|
|
26
|
+
geovisio/translations/eo/LC_MESSAGES/messages.po,sha256=rzlHUGUoaRkYZY431Y3d7GaAbEcvo9NUL1wSn9Gzapw,29177
|
|
27
|
+
geovisio/translations/es/LC_MESSAGES/messages.mo,sha256=R5JmcfauTrQxIynQNT7asjdLEJC9-VEMXYrcugfBbsY,18950
|
|
28
|
+
geovisio/translations/es/LC_MESSAGES/messages.po,sha256=XzVBY4dISzRcZWw7jMmbGsnDrJvyhzX2L_o9k5B14Hw,26780
|
|
27
29
|
geovisio/translations/fi/LC_MESSAGES/messages.mo,sha256=6-WCesFiV00MkNM_Wpi7-D51DOZRNg_QOM2sL7-UPhA,626
|
|
28
30
|
geovisio/translations/fi/LC_MESSAGES/messages.po,sha256=UFT4YCfEazxLij8Ovk2vZqx55e2Yctbf_3xM5KDrXhw,14685
|
|
29
|
-
geovisio/translations/fr/LC_MESSAGES/messages.mo,sha256=
|
|
30
|
-
geovisio/translations/fr/LC_MESSAGES/messages.po,sha256=
|
|
31
|
-
geovisio/translations/hu/LC_MESSAGES/messages.mo,sha256=
|
|
32
|
-
geovisio/translations/hu/LC_MESSAGES/messages.po,sha256=
|
|
33
|
-
geovisio/translations/it/LC_MESSAGES/messages.mo,sha256=
|
|
34
|
-
geovisio/translations/it/LC_MESSAGES/messages.po,sha256=
|
|
35
|
-
geovisio/translations/ja/LC_MESSAGES/messages.mo,sha256=
|
|
36
|
-
geovisio/translations/ja/LC_MESSAGES/messages.po,sha256=
|
|
31
|
+
geovisio/translations/fr/LC_MESSAGES/messages.mo,sha256=tvecIWSq7ocm_Q_dd3F1dqZz0GvAWeR685cC7pnQi4g,22297
|
|
32
|
+
geovisio/translations/fr/LC_MESSAGES/messages.po,sha256=unb6P5I9cAMQt5oUr0HhEPmVR3jb7LMnr_e0gAjWwBU,30292
|
|
33
|
+
geovisio/translations/hu/LC_MESSAGES/messages.mo,sha256=0Hb7mv7p1BVM8QqZIYUtF3LRym8Sl9HFWfZAa00TobU,20013
|
|
34
|
+
geovisio/translations/hu/LC_MESSAGES/messages.po,sha256=0nLbjStLDrj7U9mqdIf5gnQ_XzSnhnGQeJ1wLK8l8GM,27494
|
|
35
|
+
geovisio/translations/it/LC_MESSAGES/messages.mo,sha256=a9vR4JuHQY0kwET-OUAD51FgUcCGf68_Cg-P-UOpxRc,22533
|
|
36
|
+
geovisio/translations/it/LC_MESSAGES/messages.po,sha256=kKMX_OabFjzMfYwKA4Gu_n1kND1OKokYwAGs9SSXcj0,30669
|
|
37
|
+
geovisio/translations/ja/LC_MESSAGES/messages.mo,sha256=ZPHJrNdf4bgiNFjxP8W41fkZ2OTJ7Swrqt-Hkh5LfO8,24194
|
|
38
|
+
geovisio/translations/ja/LC_MESSAGES/messages.po,sha256=hE6WOQPaLPjury-bFO3xUJin7bWlcwv0ewSsCpScDdY,32627
|
|
37
39
|
geovisio/translations/ko/LC_MESSAGES/messages.mo,sha256=eKuQS9zLcJ9s-DzbfR-QK2INBJL10jTIQ1kuSTdJ9Rg,426
|
|
38
40
|
geovisio/translations/ko/LC_MESSAGES/messages.po,sha256=uq19EZaeRB-obmE1hYnckA8T12JuuU3nXYyKaMR4tiU,17405
|
|
39
41
|
geovisio/translations/nl/LC_MESSAGES/messages.mo,sha256=aKM90Hp4Eh9vCQba_tlfjEWlhygLXWGq_SVYqBw9IA4,1592
|
|
40
42
|
geovisio/translations/nl/LC_MESSAGES/messages.po,sha256=m69xfphxpgfPOuUrBK51XrR8UFwqCEBZpnb_5B1mGOU,15302
|
|
41
|
-
geovisio/translations/pl/LC_MESSAGES/messages.mo,sha256=
|
|
42
|
-
geovisio/translations/pl/LC_MESSAGES/messages.po,sha256=
|
|
43
|
+
geovisio/translations/pl/LC_MESSAGES/messages.mo,sha256=0iFTAhma7jjyl13DCLr2Xr0hgDSN-_fOqcKoYcdDwGE,9912
|
|
44
|
+
geovisio/translations/pl/LC_MESSAGES/messages.po,sha256=e8HW1RKsdkR-aL7peBMuqRUQoqKvzr6Eq0sC1in5XY0,22187
|
|
43
45
|
geovisio/translations/zh_Hant/LC_MESSAGES/messages.mo,sha256=TmRUyfTGoBpU-2BE-nKjhwdr9r0ikDioVQU-JQ_ih90,431
|
|
44
46
|
geovisio/translations/zh_Hant/LC_MESSAGES/messages.po,sha256=LnnKlHy8t_54nNsLDBqC1eEPwPx49h1Um9mQj6l9hv0,18357
|
|
45
47
|
geovisio/utils/__init__.py,sha256=g4SWVoV73cSXjf5-5D9-HmyB5xKmHSuxxOGWnx7W3V0,71
|
|
@@ -50,8 +52,9 @@ geovisio/utils/extent.py,sha256=vzOHvbG6lpSNt7KrsaonBOx7Tz46S1J603gLbZvs36g,557
|
|
|
50
52
|
geovisio/utils/fields.py,sha256=sNAmrSJ4e-nqm0-LoyO3l4Zynb-Jy8swhwmL3UcDN_o,2129
|
|
51
53
|
geovisio/utils/filesystems.py,sha256=W_wH7TlvdEux_q4FP0XInxruxlbepFSEpJbPLO9Cnr4,4133
|
|
52
54
|
geovisio/utils/link.py,sha256=u9x4xJa57L1448neD7uPJuAA76_sFXVE0-9_zPW-bJM,490
|
|
55
|
+
geovisio/utils/loggers.py,sha256=_OrGXME4o5qQz8VBaLxYopHVK0DY0QgzXu6O2W0WBjo,477
|
|
53
56
|
geovisio/utils/model_query.py,sha256=PtvYCjKVygmicvqqYpCpEKWUIEvwdEG6QMh-JL5E8AQ,2031
|
|
54
|
-
geovisio/utils/params.py,sha256=
|
|
57
|
+
geovisio/utils/params.py,sha256=Yj9-PwC8jxb9LjQZ5K8TERimSsWKwJBHPhUxlzDVMhg,714
|
|
55
58
|
geovisio/utils/pictures.py,sha256=cDDOABzZaTn98Bg8lYgoMlkTNklS9-y-qB-HXTcJ0YM,23092
|
|
56
59
|
geovisio/utils/reports.py,sha256=PgU0Td48WJg6XCq043he8Jif3WCA9nOTaGE0Yovo3h0,5626
|
|
57
60
|
geovisio/utils/semantics.py,sha256=bsPo4n0R0_pU5NdL7-dkx-bMYPhtyVq85z4nHNLptto,4572
|
|
@@ -59,14 +62,14 @@ geovisio/utils/sentry.py,sha256=Dw0rW2sNFbcpGojzpa-vjtJ5S7BH_XhsqfuGYBjHCG0,3759
|
|
|
59
62
|
geovisio/utils/sequences.py,sha256=S3OaMozzk9viYJMgHyzGYP1hZFsYjF8Ez2hXFTIBimI,25307
|
|
60
63
|
geovisio/utils/time.py,sha256=-hOs7FSx-8eiGmMQtTOrCoF8d_yMlj2306vfxJftEr8,642
|
|
61
64
|
geovisio/utils/tokens.py,sha256=tkihnnXqgQeIME_d12tC8PVrPN90A0i9k6UPEbgZ9TQ,3047
|
|
62
|
-
geovisio/utils/upload_set.py,sha256=
|
|
63
|
-
geovisio/utils/website.py,sha256=
|
|
65
|
+
geovisio/utils/upload_set.py,sha256=OVChyYFVd6maXcoMoiT-tA1n6tPIezTEJiRdipw_hIw,27247
|
|
66
|
+
geovisio/utils/website.py,sha256=812_leydUaI_gPZAnkVizGH1ZJqJkoAE1usFCrRNHCI,1959
|
|
64
67
|
geovisio/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
68
|
geovisio/web/annotations.py,sha256=TdivDOcVh83HRkBXBTxFD7J-VbZ1gnVesPfTNY-3uW4,653
|
|
66
|
-
geovisio/web/auth.py,sha256
|
|
69
|
+
geovisio/web/auth.py,sha256=d0g3EweC74_OOvD9fxb71M-VuQLD_BdlOcKtpwH4t5o,7143
|
|
67
70
|
geovisio/web/collections.py,sha256=pt181nK3bTa1UYn9qpi3RBGH5h5I8VKMOb_Kfa-c-EU,46470
|
|
68
71
|
geovisio/web/configuration.py,sha256=tWZYxOoqI2MQwmuHk1I9J2DKzDqpLBVmWRDSsx18U7E,2177
|
|
69
|
-
geovisio/web/docs.py,sha256=
|
|
72
|
+
geovisio/web/docs.py,sha256=UgiX_uXAdzjk08Q5Slp40-ayfhhfaHMZn88SU8IBQAg,55660
|
|
70
73
|
geovisio/web/excluded_areas.py,sha256=5BNSZ0UqgFMtgvgrJ73eYGJXPJRnV-mGEs36WDRRxTk,13024
|
|
71
74
|
geovisio/web/items.py,sha256=1a_O5tes2NL4DHqDWsgJn_PQQYx4Ft6v4t7ibzkgtdw,61357
|
|
72
75
|
geovisio/web/map.py,sha256=DaigXevz4lL7WGjPFCsKbXvjdDevFj9gpH0-22JsJOI,25328
|
|
@@ -78,12 +81,12 @@ geovisio/web/reports.py,sha256=8v9a4PMM9RsvSGadZEN2o5PTKG_TohjyMMEBfFeY13E,14123
|
|
|
78
81
|
geovisio/web/rss.py,sha256=NLUd2Or92tcKRaGUHAze6QMLWczHyzag9ybOzrA8djE,2962
|
|
79
82
|
geovisio/web/stac.py,sha256=1uoSUOgCxOdH4UQuUvt-0xJaPLtPcAD54WvQg0lvxwM,14850
|
|
80
83
|
geovisio/web/tokens.py,sha256=l7CAM0FQ6qAcoUhtIRysKc9Gndlji_wOMpkXLsPP1pI,9599
|
|
81
|
-
geovisio/web/upload_set.py,sha256=
|
|
82
|
-
geovisio/web/users.py,sha256=
|
|
84
|
+
geovisio/web/upload_set.py,sha256=lFryohoh40UaGoHPL-RV7ypWrYlOwYlW6Y5m65dkEyA,34251
|
|
85
|
+
geovisio/web/users.py,sha256=PeB2hcyyLvNDi98iNl8P4wr3X4r3Yz6FVP8Yx_U6Nac,14054
|
|
83
86
|
geovisio/web/utils.py,sha256=kudTbV4Tgtkbd4oUWFTFpyWNINpxAa-VQNbxYEiR6pM,3640
|
|
84
87
|
geovisio/workers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
88
|
geovisio/workers/runner_pictures.py,sha256=Y4x345tp0Y3RAFnoYpcDhyg6dJS1OYx79XGGDIttcps,22898
|
|
86
|
-
geovisio-2.8.
|
|
87
|
-
geovisio-2.8.
|
|
88
|
-
geovisio-2.8.
|
|
89
|
-
geovisio-2.8.
|
|
89
|
+
geovisio-2.8.1.dist-info/licenses/LICENSE,sha256=iRFSz7MJ7_j4hh3hvIgzNbS2buy5NMva8lulaixd3IE,1069
|
|
90
|
+
geovisio-2.8.1.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82
|
|
91
|
+
geovisio-2.8.1.dist-info/METADATA,sha256=r3Tlh_xpB_BVY27DjaV8W6UVjz__db7Z55zWHj536zQ,4321
|
|
92
|
+
geovisio-2.8.1.dist-info/RECORD,,
|
|
File without changes
|