geovisio 2.7.1__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 +25 -4
- geovisio/admin_cli/__init__.py +3 -1
- geovisio/admin_cli/user.py +75 -0
- geovisio/config_app.py +86 -4
- geovisio/templates/main.html +2 -2
- geovisio/templates/viewer.html +3 -3
- 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 +859 -0
- geovisio/translations/de/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/de/LC_MESSAGES/messages.po +106 -1
- geovisio/translations/el/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/en/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/en/LC_MESSAGES/messages.po +218 -133
- geovisio/translations/eo/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/eo/LC_MESSAGES/messages.po +856 -0
- geovisio/translations/es/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/es/LC_MESSAGES/messages.po +4 -3
- geovisio/translations/fi/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/fr/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/fr/LC_MESSAGES/messages.po +66 -3
- 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 +884 -0
- geovisio/translations/ja/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/ja/LC_MESSAGES/messages.po +807 -0
- geovisio/translations/ko/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/messages.pot +191 -122
- geovisio/translations/nl/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/pl/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/pl/LC_MESSAGES/messages.po +728 -0
- geovisio/translations/zh_Hant/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/zh_Hant/LC_MESSAGES/messages.po +719 -0
- geovisio/utils/auth.py +80 -8
- geovisio/utils/link.py +3 -2
- geovisio/utils/loggers.py +14 -0
- geovisio/utils/model_query.py +55 -0
- geovisio/utils/params.py +7 -4
- geovisio/utils/pictures.py +12 -43
- geovisio/utils/semantics.py +120 -0
- geovisio/utils/sequences.py +10 -1
- geovisio/utils/tokens.py +5 -3
- geovisio/utils/upload_set.py +71 -22
- geovisio/utils/website.py +53 -0
- geovisio/web/annotations.py +17 -0
- geovisio/web/auth.py +11 -6
- geovisio/web/collections.py +217 -61
- geovisio/web/configuration.py +17 -1
- geovisio/web/docs.py +67 -67
- geovisio/web/items.py +220 -96
- geovisio/web/map.py +48 -18
- geovisio/web/pages.py +240 -0
- geovisio/web/params.py +17 -0
- geovisio/web/prepare.py +165 -0
- geovisio/web/stac.py +17 -4
- geovisio/web/tokens.py +14 -4
- geovisio/web/upload_set.py +108 -14
- geovisio/web/users.py +203 -44
- geovisio/workers/runner_pictures.py +61 -22
- {geovisio-2.7.1.dist-info → geovisio-2.8.1.dist-info}/METADATA +8 -6
- geovisio-2.8.1.dist-info/RECORD +92 -0
- {geovisio-2.7.1.dist-info → geovisio-2.8.1.dist-info}/WHEEL +1 -1
- geovisio-2.7.1.dist-info/RECORD +0 -70
- {geovisio-2.7.1.dist-info → geovisio-2.8.1.dist-info/licenses}/LICENSE +0 -0
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)."""
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
model_config = ConfigDict(use_attribute_docstrings=True, extra="forbid")
|
|
86
|
+
|
|
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
|
|
@@ -185,8 +273,7 @@ def getUploadSet(upload_set_id):
|
|
|
185
273
|
|
|
186
274
|
|
|
187
275
|
@bp.route("/upload_sets/<uuid:upload_set_id>/files", methods=["GET"])
|
|
188
|
-
|
|
189
|
-
def getUploadSetFiles(upload_set_id, account=None):
|
|
276
|
+
def getUploadSetFiles(upload_set_id):
|
|
190
277
|
"""List the files of an UploadSet
|
|
191
278
|
---
|
|
192
279
|
tags:
|
|
@@ -210,13 +297,20 @@ def getUploadSetFiles(upload_set_id, account=None):
|
|
|
210
297
|
schema:
|
|
211
298
|
$ref: '#/components/schemas/GeoVisioUploadSetFiles'
|
|
212
299
|
"""
|
|
300
|
+
account = utils.auth.get_current_account()
|
|
301
|
+
|
|
213
302
|
u = get_simple_upload_set(upload_set_id)
|
|
214
303
|
if u is None:
|
|
215
304
|
raise errors.InvalidAPIUsage(_("UploadSet doesn't exist"), status_code=404)
|
|
216
|
-
if account is not None and account.id != str(u.account_id):
|
|
217
|
-
raise errors.InvalidAPIUsage(_("You're not authorized to list pictures in this upload set"), status_code=403)
|
|
218
305
|
|
|
219
306
|
upload_set_files = get_upload_set_files(upload_set_id)
|
|
307
|
+
|
|
308
|
+
if account is None or account.id != str(u.account_id):
|
|
309
|
+
# if the user is not the owner of the upload set, we remove the picture_id since we might leak too many information
|
|
310
|
+
# not sure about this one, this could evolve in the future
|
|
311
|
+
for f in upload_set_files.files:
|
|
312
|
+
f.picture_id = None
|
|
313
|
+
|
|
220
314
|
return upload_set_files.model_dump_json(exclude_none=True), 200, {"Content-Type": "application/json"}
|
|
221
315
|
|
|
222
316
|
|
geovisio/web/users.py
CHANGED
|
@@ -1,43 +1,91 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from uuid import UUID
|
|
1
3
|
import flask
|
|
2
|
-
from flask import request, current_app, url_for
|
|
4
|
+
from flask import request, current_app, session, url_for
|
|
3
5
|
from flask_babel import gettext as _
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, ValidationError, computed_field
|
|
4
7
|
from geovisio.utils import auth, db
|
|
5
8
|
from geovisio import errors
|
|
6
|
-
from psycopg.rows import dict_row
|
|
9
|
+
from psycopg.rows import dict_row, class_row
|
|
7
10
|
from psycopg.sql import SQL
|
|
8
11
|
|
|
12
|
+
from geovisio.utils.link import Link, make_link
|
|
13
|
+
from geovisio.utils.model_query import get_db_params_and_values
|
|
14
|
+
from geovisio.utils.params import validation_error
|
|
9
15
|
from geovisio.web import stac
|
|
16
|
+
from geovisio.web.auth import NEXT_URL_KEY
|
|
10
17
|
from geovisio.web.utils import get_root_link
|
|
11
18
|
|
|
12
19
|
bp = flask.Blueprint("user", __name__, url_prefix="/api/users")
|
|
13
20
|
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
|
|
37
|
+
class UserInfo(BaseModel):
|
|
38
|
+
name: str
|
|
39
|
+
"""Name of the user"""
|
|
40
|
+
id: UUID
|
|
41
|
+
"""Unique identifier of the user"""
|
|
42
|
+
collaborative_metadata: Optional[bool] = None
|
|
43
|
+
"""If true, the user can edit the metadata of all sequences. If unset, default to the instance's default configuration."""
|
|
44
|
+
|
|
45
|
+
tos_accepted: Optional[bool] = None
|
|
46
|
+
"""True means the user has accepted the terms of service (tos). Can only be seen by the user itself"""
|
|
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
|
+
|
|
53
|
+
@computed_field
|
|
54
|
+
@property
|
|
55
|
+
def links(self) -> List[Link]:
|
|
56
|
+
userMapUrl = (
|
|
57
|
+
flask.url_for("map.getUserTile", userId=self.id, x="11111111", y="22222222", z="33333333", format="mvt", _external=True)
|
|
58
|
+
.replace("11111111", "{x}")
|
|
59
|
+
.replace("22222222", "{y}")
|
|
60
|
+
.replace("33333333", "{z}")
|
|
61
|
+
)
|
|
62
|
+
return [
|
|
63
|
+
make_link(rel="catalog", route="stac.getUserCatalog", userId=self.id),
|
|
64
|
+
make_link(rel="collection", route="stac_collections.getUserCollection", userId=self.id),
|
|
65
|
+
Link(
|
|
66
|
+
rel="user-xyz",
|
|
67
|
+
type="application/vnd.mapbox-vector-tile",
|
|
68
|
+
title="Pictures and sequences vector tiles for a given user",
|
|
69
|
+
href=userMapUrl,
|
|
70
|
+
),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _get_user_info(account: auth.Account):
|
|
75
|
+
user_info = UserInfo(id=account.id, name=account.name, collaborative_metadata=account.collaborative_metadata)
|
|
76
|
+
logged_account = auth.get_current_account()
|
|
77
|
+
if logged_account is not None and account.id == logged_account.id:
|
|
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
|
+
)
|
|
87
|
+
|
|
88
|
+
return user_info.model_dump(exclude_unset=True), 200, {"Content-Type": "application/json"}
|
|
41
89
|
|
|
42
90
|
|
|
43
91
|
@bp.route("/me")
|
|
@@ -55,7 +103,7 @@ def getMyUserInfo(account):
|
|
|
55
103
|
schema:
|
|
56
104
|
$ref: '#/components/schemas/GeoVisioUser'
|
|
57
105
|
"""
|
|
58
|
-
return _get_user_info(account
|
|
106
|
+
return _get_user_info(account)
|
|
59
107
|
|
|
60
108
|
|
|
61
109
|
@bp.route("/<uuid:userId>")
|
|
@@ -79,21 +127,29 @@ def getUserInfo(userId):
|
|
|
79
127
|
schema:
|
|
80
128
|
$ref: '#/components/schemas/GeoVisioUser'
|
|
81
129
|
"""
|
|
82
|
-
account = db.fetchone(
|
|
130
|
+
account = db.fetchone(
|
|
131
|
+
current_app,
|
|
132
|
+
SQL("SELECT name, id::text, collaborative_metadata, role, tos_accepted FROM accounts WHERE id = %s"),
|
|
133
|
+
[userId],
|
|
134
|
+
row_factory=class_row(auth.Account),
|
|
135
|
+
)
|
|
83
136
|
if not account:
|
|
84
137
|
raise errors.InvalidAPIUsage(_("Impossible to find user"), status_code=404)
|
|
85
138
|
|
|
86
|
-
return _get_user_info(account
|
|
139
|
+
return _get_user_info(account)
|
|
87
140
|
|
|
88
141
|
|
|
89
142
|
@bp.route("/me/catalog")
|
|
90
143
|
@auth.login_required_with_redirect()
|
|
91
144
|
def getMyCatalog(account):
|
|
92
|
-
"""Get current logged user catalog
|
|
145
|
+
"""Get current logged user catalog.
|
|
146
|
+
|
|
147
|
+
Note that this route is deprecated in favor of `/api/users/me/collection`. This new route provides more information and offers more filtering and sorting options.
|
|
93
148
|
---
|
|
94
149
|
tags:
|
|
95
150
|
- Users
|
|
96
151
|
- Sequences
|
|
152
|
+
deprecated: true
|
|
97
153
|
responses:
|
|
98
154
|
200:
|
|
99
155
|
description: the Catalog listing all sequences associated to given user. Note that it's similar to the user's colletion, but with less metadata since a STAC collection is an enhanced STAC catalog.
|
|
@@ -102,18 +158,37 @@ def getMyCatalog(account):
|
|
|
102
158
|
schema:
|
|
103
159
|
$ref: '#/components/schemas/GeoVisioCatalog'
|
|
104
160
|
"""
|
|
105
|
-
return flask.redirect(
|
|
161
|
+
return flask.redirect(
|
|
162
|
+
flask.url_for(
|
|
163
|
+
"stac.getUserCatalog",
|
|
164
|
+
userId=account.id,
|
|
165
|
+
limit=request.args.get("limit"),
|
|
166
|
+
page=request.args.get("page"),
|
|
167
|
+
_external=True,
|
|
168
|
+
)
|
|
169
|
+
)
|
|
106
170
|
|
|
107
171
|
|
|
108
172
|
@bp.route("/me/collection")
|
|
109
173
|
@auth.login_required_with_redirect()
|
|
110
174
|
def getMyCollection(account):
|
|
111
175
|
"""Get current logged user collection
|
|
176
|
+
|
|
177
|
+
Note that the result can also be a CSV file, if the "Accept" header is set to "text/csv", or if the "format" query parameter is set to "csv".
|
|
178
|
+
|
|
112
179
|
---
|
|
113
180
|
tags:
|
|
114
181
|
- Users
|
|
115
182
|
- Sequences
|
|
116
183
|
parameters:
|
|
184
|
+
- name: format
|
|
185
|
+
in: query
|
|
186
|
+
description: Expected output format (STAC JSON or a csv file)
|
|
187
|
+
required: false
|
|
188
|
+
schema:
|
|
189
|
+
type: string
|
|
190
|
+
enum: [csv, json]
|
|
191
|
+
default: json
|
|
117
192
|
- $ref: '#/components/parameters/STAC_collections_limit'
|
|
118
193
|
- $ref: '#/components/parameters/STAC_collections_filter'
|
|
119
194
|
- $ref: '#/components/parameters/STAC_bbox'
|
|
@@ -125,19 +200,14 @@ def getMyCollection(account):
|
|
|
125
200
|
application/json:
|
|
126
201
|
schema:
|
|
127
202
|
$ref: '#/components/schemas/GeoVisioCollectionOfCollection'
|
|
203
|
+
|
|
204
|
+
text/csv:
|
|
205
|
+
schema:
|
|
206
|
+
$ref: '#/components/schemas/GeoVisioCSVCollections'
|
|
128
207
|
"""
|
|
208
|
+
from geovisio.web.collections import getUserCollection
|
|
129
209
|
|
|
130
|
-
return
|
|
131
|
-
flask.url_for(
|
|
132
|
-
"stac_collections.getUserCollection",
|
|
133
|
-
userId=account.id,
|
|
134
|
-
filter=request.args.get("filter"),
|
|
135
|
-
limit=request.args.get("limit"),
|
|
136
|
-
sortby=request.args.get("sortby"),
|
|
137
|
-
bbox=request.args.get("bbox"),
|
|
138
|
-
_external=True,
|
|
139
|
-
)
|
|
140
|
-
)
|
|
210
|
+
return getUserCollection(userId=account.id, userIdMatchesAccount=True)
|
|
141
211
|
|
|
142
212
|
|
|
143
213
|
@bp.route("/search")
|
|
@@ -272,3 +342,92 @@ LIMIT {limit};"""
|
|
|
272
342
|
if r["has_seq"]
|
|
273
343
|
],
|
|
274
344
|
}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class UserConfiguration(BaseModel):
|
|
348
|
+
collaborative_metadata: Optional[bool] = None
|
|
349
|
+
"""If true, all sequences's metadata will be, by default, editable by all users.
|
|
350
|
+
|
|
351
|
+
If not set, it will default to the instance default collaborative editing policy."""
|
|
352
|
+
|
|
353
|
+
def has_override(self) -> bool:
|
|
354
|
+
return bool(self.model_fields_set)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@bp.route("/me", methods=["PATCH"])
|
|
358
|
+
@auth.login_required()
|
|
359
|
+
def patchUserConfiguration(account):
|
|
360
|
+
"""Edit the current user configuration
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
tags:
|
|
364
|
+
- Users
|
|
365
|
+
requestBody:
|
|
366
|
+
content:
|
|
367
|
+
application/json:
|
|
368
|
+
schema:
|
|
369
|
+
$ref: '#/components/schemas/GeoVisioUserConfiguration'
|
|
370
|
+
security:
|
|
371
|
+
- bearerToken: []
|
|
372
|
+
- cookieAuth: []
|
|
373
|
+
responses:
|
|
374
|
+
200:
|
|
375
|
+
description: the user configuration
|
|
376
|
+
content:
|
|
377
|
+
application/json:
|
|
378
|
+
schema:
|
|
379
|
+
$ref: '#/components/schemas/GeoVisioUser'
|
|
380
|
+
"""
|
|
381
|
+
metadata = None
|
|
382
|
+
try:
|
|
383
|
+
if request.is_json and request.json:
|
|
384
|
+
metadata = UserConfiguration(**request.json)
|
|
385
|
+
except ValidationError as ve:
|
|
386
|
+
raise errors.InvalidAPIUsage(_("Impossible to parse parameters"), payload=validation_error(ve))
|
|
387
|
+
|
|
388
|
+
if not metadata:
|
|
389
|
+
return _get_user_info(account)
|
|
390
|
+
params = get_db_params_and_values(metadata)
|
|
391
|
+
if metadata.has_override():
|
|
392
|
+
|
|
393
|
+
fields = params.fields_for_set_list()
|
|
394
|
+
|
|
395
|
+
account = db.fetchone(
|
|
396
|
+
current_app,
|
|
397
|
+
SQL("UPDATE accounts SET {fields} WHERE id = %(account_id)s RETURNING *").format(fields=SQL(", ").join(fields)),
|
|
398
|
+
params.params_as_dict | {"account_id": account.id},
|
|
399
|
+
row_factory=class_row(auth.Account),
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
return _get_user_info(account)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
@bp.route("/me/accept_tos", methods=["POST"])
|
|
406
|
+
@auth.login_required()
|
|
407
|
+
def accept_tos(account: auth.Account):
|
|
408
|
+
"""
|
|
409
|
+
Accept the terms of service for the current user
|
|
410
|
+
---
|
|
411
|
+
tags:
|
|
412
|
+
- Auth
|
|
413
|
+
responses:
|
|
414
|
+
200:
|
|
415
|
+
description: the user configuration
|
|
416
|
+
content:
|
|
417
|
+
application/json:
|
|
418
|
+
schema:
|
|
419
|
+
$ref: '#/components/schemas/GeoVisioUser'
|
|
420
|
+
"""
|
|
421
|
+
# Note: accepting twice does not change the accepted_at date
|
|
422
|
+
account = db.fetchone(
|
|
423
|
+
current_app,
|
|
424
|
+
SQL("UPDATE accounts SET tos_accepted_at = COALESCE(tos_accepted_at, NOW()) WHERE id = %(account_id)s RETURNING *"),
|
|
425
|
+
{"account_id": account.id},
|
|
426
|
+
row_factory=class_row(auth.Account),
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# we persist in the cookie the fact that the tos have been accepted
|
|
430
|
+
session[auth.ACCOUNT_KEY] = account.model_dump(exclude_none=True)
|
|
431
|
+
session.permanent = True
|
|
432
|
+
|
|
433
|
+
return _get_user_info(account)
|