geovisio 2.4.0__py3-none-any.whl → 2.6.0__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 +3 -2
- geovisio/admin_cli/__init__.py +2 -2
- geovisio/admin_cli/db.py +11 -0
- geovisio/admin_cli/sequence_heading.py +2 -2
- geovisio/config_app.py +25 -0
- geovisio/templates/main.html +2 -2
- geovisio/utils/pictures.py +75 -30
- geovisio/utils/sequences.py +232 -34
- geovisio/web/auth.py +15 -2
- geovisio/web/collections.py +161 -111
- geovisio/web/docs.py +178 -4
- geovisio/web/items.py +169 -114
- geovisio/web/map.py +309 -93
- geovisio/web/params.py +82 -4
- geovisio/web/stac.py +14 -4
- geovisio/web/tokens.py +7 -3
- geovisio/web/users.py +4 -4
- geovisio/web/utils.py +10 -0
- geovisio/workers/runner_pictures.py +73 -70
- geovisio-2.6.0.dist-info/METADATA +92 -0
- geovisio-2.6.0.dist-info/RECORD +41 -0
- geovisio-2.4.0.dist-info/METADATA +0 -115
- geovisio-2.4.0.dist-info/RECORD +0 -41
- {geovisio-2.4.0.dist-info → geovisio-2.6.0.dist-info}/LICENSE +0 -0
- {geovisio-2.4.0.dist-info → geovisio-2.6.0.dist-info}/WHEEL +0 -0
geovisio/web/collections.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from geovisio import errors, utils
|
|
3
|
-
from geovisio.utils import auth
|
|
4
|
-
from geovisio.utils.sequences import get_pagination_links
|
|
3
|
+
from geovisio.utils import auth, sequences
|
|
5
4
|
from geovisio.web.params import (
|
|
6
5
|
parse_datetime,
|
|
7
6
|
parse_datetime_interval,
|
|
@@ -12,9 +11,7 @@ from geovisio.web.params import (
|
|
|
12
11
|
)
|
|
13
12
|
from geovisio.utils.sequences import (
|
|
14
13
|
STAC_FIELD_MAPPINGS,
|
|
15
|
-
STAC_FIELD_TO_SQL_FILTER,
|
|
16
14
|
CollectionsRequest,
|
|
17
|
-
Collections,
|
|
18
15
|
get_collections,
|
|
19
16
|
)
|
|
20
17
|
from geovisio.utils.fields import SortBy, SortByField, SQLDirection, Bounds, BBox
|
|
@@ -22,6 +19,7 @@ from geovisio.web.rss import dbSequencesToGeoRSS
|
|
|
22
19
|
import psycopg
|
|
23
20
|
from psycopg.rows import dict_row
|
|
24
21
|
from psycopg.sql import SQL
|
|
22
|
+
import json
|
|
25
23
|
from flask import current_app, request, url_for, Blueprint
|
|
26
24
|
from geovisio.web.utils import (
|
|
27
25
|
STAC_VERSION,
|
|
@@ -34,8 +32,6 @@ from geovisio.web.utils import (
|
|
|
34
32
|
removeNoneInDict,
|
|
35
33
|
)
|
|
36
34
|
from geovisio.workers import runner_pictures
|
|
37
|
-
import geovisio.utils.sequences
|
|
38
|
-
from typing import Dict, Any
|
|
39
35
|
|
|
40
36
|
|
|
41
37
|
bp = Blueprint("stac_collections", __name__, url_prefix="/api")
|
|
@@ -70,6 +66,7 @@ def dbSequenceToStacCollection(dbSeq, description="A sequence of geolocated pict
|
|
|
70
66
|
"created": dbTsToStac(dbSeq["created"]),
|
|
71
67
|
"updated": dbTsToStac(dbSeq.get("updated")),
|
|
72
68
|
"geovisio:status": dbSeq.get("status"),
|
|
69
|
+
"geovisio:sorted-by": dbSeq.get("current_sort"),
|
|
73
70
|
"providers": [
|
|
74
71
|
{"name": dbSeq["account_name"], "roles": ["producer"]},
|
|
75
72
|
],
|
|
@@ -274,7 +271,7 @@ def getAllCollections():
|
|
|
274
271
|
|
|
275
272
|
additional_filters = request.args.get("filter")
|
|
276
273
|
|
|
277
|
-
pagination_links = get_pagination_links(
|
|
274
|
+
pagination_links = sequences.get_pagination_links(
|
|
278
275
|
route="stac_collections.getAllCollections",
|
|
279
276
|
routeArgs={"limit": collection_request.limit},
|
|
280
277
|
field=sortBy.fields[0].field.stac,
|
|
@@ -331,37 +328,38 @@ def getCollection(collectionId):
|
|
|
331
328
|
with conn.cursor() as cursor:
|
|
332
329
|
record = cursor.execute(
|
|
333
330
|
"""
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
331
|
+
SELECT
|
|
332
|
+
s.id,
|
|
333
|
+
s.metadata->>'title' AS name,
|
|
334
|
+
ST_XMin(s.bbox) AS minx,
|
|
335
|
+
ST_YMin(s.bbox) AS miny,
|
|
336
|
+
ST_XMax(s.bbox) AS maxx,
|
|
337
|
+
ST_YMax(s.bbox) AS maxy,
|
|
338
|
+
s.status AS status,
|
|
339
|
+
accounts.name AS account_name,
|
|
340
|
+
s.inserted_at AS created,
|
|
341
|
+
s.updated_at AS updated,
|
|
342
|
+
s.current_sort AS current_sort,
|
|
343
|
+
a.*,
|
|
344
|
+
min_picture_ts AS mints,
|
|
345
|
+
max_picture_ts AS maxts,
|
|
346
|
+
nb_pictures AS nbpic
|
|
347
|
+
FROM sequences s
|
|
348
|
+
JOIN accounts ON s.account_id = accounts.id, (
|
|
349
|
+
SELECT
|
|
350
|
+
array_agg(DISTINCT jsonb_build_object(
|
|
351
|
+
'make', metadata->>'make',
|
|
352
|
+
'model', metadata->>'model',
|
|
353
|
+
'focal_length', metadata->>'focal_length',
|
|
354
|
+
'field_of_view', metadata->>'field_of_view'
|
|
355
|
+
)) AS metas
|
|
356
|
+
FROM pictures p
|
|
357
|
+
JOIN sequences_pictures sp ON sp.seq_id = %(id)s AND sp.pic_id = p.id
|
|
358
|
+
) a
|
|
359
|
+
WHERE s.id = %(id)s
|
|
360
|
+
AND (s.status != 'hidden' OR s.account_id = %(account)s)
|
|
361
|
+
AND s.status != 'deleted'
|
|
362
|
+
""",
|
|
365
363
|
params,
|
|
366
364
|
).fetchone()
|
|
367
365
|
|
|
@@ -411,17 +409,17 @@ def getCollectionThumbnail(collectionId):
|
|
|
411
409
|
with conn.cursor() as cursor:
|
|
412
410
|
records = cursor.execute(
|
|
413
411
|
"""
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
412
|
+
SELECT
|
|
413
|
+
sp.pic_id
|
|
414
|
+
FROM sequences_pictures sp
|
|
415
|
+
JOIN pictures p ON sp.pic_id = p.id
|
|
416
|
+
JOIN sequences s ON sp.seq_id = s.id
|
|
417
|
+
WHERE
|
|
418
|
+
sp.seq_id = %(seq)s
|
|
419
|
+
AND (p.status = 'ready' OR p.account_id = %(account)s)
|
|
420
|
+
AND is_sequence_visible_by_user(s, %(account)s)
|
|
421
|
+
LIMIT 1
|
|
422
|
+
""",
|
|
425
423
|
params,
|
|
426
424
|
).fetchone()
|
|
427
425
|
|
|
@@ -464,7 +462,7 @@ def postCollection(account=None):
|
|
|
464
462
|
# Parse received parameters
|
|
465
463
|
metadata = {}
|
|
466
464
|
content_type = request.headers.get("Content-Type")
|
|
467
|
-
if request.is_json:
|
|
465
|
+
if request.is_json and request.json:
|
|
468
466
|
metadata["title"] = request.json.get("title")
|
|
469
467
|
elif content_type in ["multipart/form-data", "application/x-www-form-urlencoded"]:
|
|
470
468
|
metadata["title"] = request.form.get("title")
|
|
@@ -473,7 +471,7 @@ def postCollection(account=None):
|
|
|
473
471
|
|
|
474
472
|
# Create sequence folder
|
|
475
473
|
accountId = accountIdOrDefault(account)
|
|
476
|
-
seqId =
|
|
474
|
+
seqId = sequences.createSequence(metadata, accountId)
|
|
477
475
|
|
|
478
476
|
# Return created sequence
|
|
479
477
|
return (
|
|
@@ -527,8 +525,8 @@ def patchCollection(collectionId, account):
|
|
|
527
525
|
# Parse received parameters
|
|
528
526
|
metadata = {}
|
|
529
527
|
content_type = (request.headers.get("Content-Type") or "").split(";")[0]
|
|
530
|
-
for param in ["visible", "title"]:
|
|
531
|
-
if request.is_json:
|
|
528
|
+
for param in ["visible", "title", "relative_heading", "sortby"]:
|
|
529
|
+
if request.is_json and request.json:
|
|
532
530
|
metadata[param] = request.json.get(param)
|
|
533
531
|
elif content_type in ["multipart/form-data", "application/x-www-form-urlencoded"]:
|
|
534
532
|
metadata[param] = request.form.get(param)
|
|
@@ -547,13 +545,29 @@ def patchCollection(collectionId, account):
|
|
|
547
545
|
if not (isinstance(newTitle, str) and len(newTitle) <= 250):
|
|
548
546
|
raise errors.InvalidAPIUsage("Sequence title is not valid, should be a string with a max of 250 characters", status_code=400)
|
|
549
547
|
|
|
548
|
+
# Check if sortby is valid
|
|
549
|
+
sortby = metadata.get("sortby")
|
|
550
|
+
if sortby is not None:
|
|
551
|
+
if sortby not in ["+gpsdate", "-gpsdate", "+filedate", "-filedate", "+filename", "-filename"]:
|
|
552
|
+
raise errors.InvalidAPIUsage("Sort order parameter is invalid", status_code=400)
|
|
553
|
+
|
|
554
|
+
# Check if relative_heading is valid
|
|
555
|
+
relHeading = metadata.get("relative_heading")
|
|
556
|
+
if relHeading is not None:
|
|
557
|
+
try:
|
|
558
|
+
relHeading = int(relHeading)
|
|
559
|
+
if relHeading < -180 or relHeading > 180:
|
|
560
|
+
raise ValueError()
|
|
561
|
+
except ValueError:
|
|
562
|
+
raise errors.InvalidAPIUsage("Relative heading is not valid, should be an integer in degrees from -180 to 180", status_code=400)
|
|
563
|
+
|
|
550
564
|
# If no parameter is changed, no need to contact DB, just return sequence as is
|
|
551
|
-
if {visible, newTitle} == {None}:
|
|
565
|
+
if {visible, newTitle, relHeading, sortby} == {None}:
|
|
552
566
|
return getCollection(collectionId)
|
|
553
567
|
|
|
554
568
|
# Check if sequence exists and if given account is authorized to edit
|
|
555
569
|
with psycopg.connect(current_app.config["DB_URL"], row_factory=dict_row) as conn, conn.cursor() as cursor:
|
|
556
|
-
seq = cursor.execute("SELECT status, metadata, account_id FROM sequences WHERE id = %s", [collectionId]).fetchone()
|
|
570
|
+
seq = cursor.execute("SELECT status, metadata, account_id, current_sort FROM sequences WHERE id = %s", [collectionId]).fetchone()
|
|
557
571
|
|
|
558
572
|
# Sequence not found
|
|
559
573
|
if not seq:
|
|
@@ -573,9 +587,8 @@ def patchCollection(collectionId, account):
|
|
|
573
587
|
f"Sequence {collectionId} is in {oldStatus} state, its visibility can't be changed for now", status_code=400
|
|
574
588
|
)
|
|
575
589
|
|
|
576
|
-
# Let's edit this picture
|
|
577
590
|
sqlUpdates = []
|
|
578
|
-
sqlParams = {"id": collectionId}
|
|
591
|
+
sqlParams = {"id": collectionId, "account": account.id}
|
|
579
592
|
|
|
580
593
|
if visible is not None:
|
|
581
594
|
newStatus = "ready" if visible is True else "hidden"
|
|
@@ -583,22 +596,57 @@ def patchCollection(collectionId, account):
|
|
|
583
596
|
sqlUpdates.append(SQL("status = %(status)s"))
|
|
584
597
|
sqlParams["status"] = newStatus
|
|
585
598
|
|
|
599
|
+
new_metadata = {}
|
|
586
600
|
if newTitle is not None and oldTitle != newTitle:
|
|
587
|
-
|
|
588
|
-
|
|
601
|
+
new_metadata["title"] = newTitle
|
|
602
|
+
if relHeading:
|
|
603
|
+
new_metadata["relative_heading"] = relHeading
|
|
604
|
+
|
|
605
|
+
if new_metadata:
|
|
606
|
+
sqlUpdates.append(SQL("metadata = metadata || %(new_metadata)s"))
|
|
607
|
+
from psycopg.types.json import Jsonb
|
|
608
|
+
|
|
609
|
+
sqlParams["new_metadata"] = Jsonb(new_metadata)
|
|
610
|
+
|
|
611
|
+
if sortby is not None:
|
|
612
|
+
sqlUpdates.append(SQL("current_sort = %(sort)s"))
|
|
613
|
+
sqlParams["sort"] = sortby
|
|
589
614
|
|
|
590
615
|
if len(sqlUpdates) > 0:
|
|
616
|
+
# Note: we set the field `last_account_to_edit` to track who changed the collection last (later we'll make it possible for everybody to edit some collection fields)
|
|
617
|
+
# setting this field will trigger the history tracking of the collection (using postgres trigger)
|
|
618
|
+
sqlUpdates.append(SQL("last_account_to_edit = %(account)s"))
|
|
619
|
+
|
|
591
620
|
cursor.execute(
|
|
592
621
|
SQL(
|
|
593
622
|
"""
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
623
|
+
UPDATE sequences
|
|
624
|
+
SET {updates}
|
|
625
|
+
WHERE id = %(id)s
|
|
626
|
+
"""
|
|
598
627
|
).format(updates=SQL(", ").join(sqlUpdates)),
|
|
599
628
|
sqlParams,
|
|
600
629
|
)
|
|
601
|
-
|
|
630
|
+
|
|
631
|
+
# Edits picture sort order
|
|
632
|
+
if sortby is not None:
|
|
633
|
+
direction = sequences.Direction(sortby[0])
|
|
634
|
+
order = sequences.CollectionSortOrder(sortby[1:])
|
|
635
|
+
sequences.sort_collection(cursor, collectionId, sequences.CollectionSort(order=order, direction=direction))
|
|
636
|
+
if not relHeading:
|
|
637
|
+
# if we do not plan to override headings specifically, we recompute headings that have not bee provided by the users
|
|
638
|
+
# with the new movement track
|
|
639
|
+
sequences.update_headings(cursor, collectionId, editingAccount=account.id)
|
|
640
|
+
|
|
641
|
+
# Edits relative heading of pictures in sequence
|
|
642
|
+
if relHeading is not None:
|
|
643
|
+
# New heading is computed based on sequence movement track
|
|
644
|
+
# We take each picture and its following, compute azimuth,
|
|
645
|
+
# then add given relative heading to offset picture heading.
|
|
646
|
+
# Last picture is computed based on previous one in sequence.
|
|
647
|
+
sequences.update_headings(cursor, collectionId, relativeHeading=relHeading, updateOnlyMissing=False, editingAccount=account.id)
|
|
648
|
+
|
|
649
|
+
conn.commit()
|
|
602
650
|
|
|
603
651
|
# Redirect response to a classic GET
|
|
604
652
|
return getCollection(collectionId)
|
|
@@ -665,7 +713,7 @@ def deleteCollection(collectionId, account):
|
|
|
665
713
|
INSERT INTO pictures_to_process(picture_id, task)
|
|
666
714
|
SELECT pic_id, 'delete' FROM picWithoutOtherSeq
|
|
667
715
|
ON CONFLICT (picture_id) DO UPDATE SET task = 'delete'
|
|
668
|
-
|
|
716
|
+
""",
|
|
669
717
|
{"seq": collectionId},
|
|
670
718
|
).rowcount
|
|
671
719
|
|
|
@@ -681,7 +729,7 @@ def deleteCollection(collectionId, account):
|
|
|
681
729
|
SELECT pic_id FROM sequences_pictures WHERE pic_id IN (SELECT pic_id FROM pic2rm) AND seq_id != %(seq)s
|
|
682
730
|
)
|
|
683
731
|
UPDATE pictures SET status = 'waiting-for-delete' WHERE id IN (SELECT pic_id FROM picWithoutOtherSeq)
|
|
684
|
-
|
|
732
|
+
""",
|
|
685
733
|
{"seq": collectionId},
|
|
686
734
|
).rowcount
|
|
687
735
|
|
|
@@ -722,6 +770,18 @@ def getCollectionImportStatus(collectionId):
|
|
|
722
770
|
with psycopg.connect(current_app.config["DB_URL"], row_factory=dict_row) as conn:
|
|
723
771
|
with conn.cursor() as cursor:
|
|
724
772
|
sequence_status = cursor.execute(
|
|
773
|
+
SQL(
|
|
774
|
+
"""SELECT status
|
|
775
|
+
FROM sequences
|
|
776
|
+
WHERE id = %(seq_id)s
|
|
777
|
+
AND (status != 'hidden' OR account_id = %(account)s)-- show deleted sequence here"""
|
|
778
|
+
),
|
|
779
|
+
params,
|
|
780
|
+
).fetchone()
|
|
781
|
+
if sequence_status is None:
|
|
782
|
+
raise errors.InvalidAPIUsage("Sequence doesn't exists", status_code=404)
|
|
783
|
+
|
|
784
|
+
pics_status = cursor.execute(
|
|
725
785
|
"""WITH
|
|
726
786
|
pic_jobs_stats AS (
|
|
727
787
|
SELECT
|
|
@@ -748,47 +808,37 @@ pic_jobs_stats AS (
|
|
|
748
808
|
pic_jobs_stats.nb_errors,
|
|
749
809
|
pic_jobs_stats.last_job_finished_at
|
|
750
810
|
FROM sequences s
|
|
751
|
-
|
|
752
|
-
|
|
811
|
+
JOIN sequences_pictures sp ON sp.seq_id = s.id
|
|
812
|
+
JOIN pictures p ON sp.pic_id = p.id
|
|
753
813
|
LEFT JOIN pic_jobs_stats ON pic_jobs_stats.picture_id = p.id
|
|
754
814
|
WHERE
|
|
755
815
|
s.id = %(seq_id)s
|
|
756
816
|
AND (p IS NULL OR p.status != 'hidden' OR p.account_id = %(account)s)
|
|
757
|
-
AND (s.status != 'hidden' OR s.account_id = %(account)s) -- show deleted sequence here
|
|
758
817
|
ORDER BY s.id, sp.rank
|
|
759
818
|
)
|
|
760
|
-
SELECT
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
'nb_errors', i.nb_errors,
|
|
776
|
-
'processed_at', i.last_job_finished_at,
|
|
777
|
-
'rank', i.rank
|
|
778
|
-
)
|
|
779
|
-
)
|
|
819
|
+
SELECT json_strip_nulls(
|
|
820
|
+
json_build_object(
|
|
821
|
+
'id', i.id,
|
|
822
|
+
-- status is a bit deprecated, we'll split this field in more fields (like `processing_in_progress`, `hidden`, ...)
|
|
823
|
+
-- but we maintain it for retrocompatibility
|
|
824
|
+
'status', CASE
|
|
825
|
+
WHEN i.is_job_running IS TRUE THEN 'preparing'
|
|
826
|
+
WHEN i.last_job_error IS NOT NULL THEN 'broken'
|
|
827
|
+
ELSE i.status
|
|
828
|
+
END,
|
|
829
|
+
'processing_in_progress', i.is_job_running,
|
|
830
|
+
'process_error', i.last_job_error,
|
|
831
|
+
'nb_errors', i.nb_errors,
|
|
832
|
+
'processed_at', i.last_job_finished_at,
|
|
833
|
+
'rank', i.rank
|
|
780
834
|
)
|
|
781
|
-
)
|
|
782
|
-
FROM items i
|
|
783
|
-
JOIN sequences s on i.seq_id = s.id
|
|
784
|
-
GROUP by s.id;""",
|
|
835
|
+
) as pic_status
|
|
836
|
+
FROM items i;""",
|
|
785
837
|
params,
|
|
786
838
|
).fetchall()
|
|
839
|
+
pics = [p["pic_status"] for p in pics_status if len(p["pic_status"]) > 0]
|
|
787
840
|
|
|
788
|
-
|
|
789
|
-
raise errors.InvalidAPIUsage("Sequence doesn't exists", status_code=404)
|
|
790
|
-
|
|
791
|
-
return sequence_status[0]["sequence"]
|
|
841
|
+
return {"status": sequence_status["status"], "items": pics}
|
|
792
842
|
|
|
793
843
|
|
|
794
844
|
@bp.route("/users/<uuid:userId>/collection")
|
|
@@ -881,23 +931,23 @@ def getUserCollection(userId, userIdMatchesAccount=False):
|
|
|
881
931
|
meta_collection = cursor.execute(
|
|
882
932
|
SQL(
|
|
883
933
|
"""SELECT
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
934
|
+
COUNT(sp.pic_id) AS nbpic,
|
|
935
|
+
COUNT(s.id) AS nbseq,
|
|
936
|
+
MIN(p.ts) AS mints,
|
|
937
|
+
MAX(p.ts) AS maxts,
|
|
938
|
+
MIN(GREATEST(-180, ST_X(p.geom))) AS minx,
|
|
939
|
+
MIN(GREATEST(-90, ST_Y(p.geom))) AS miny,
|
|
940
|
+
MAX(LEAST(180, ST_X(p.geom))) AS maxx,
|
|
941
|
+
MAX(LEAST(90, ST_Y(p.geom))) AS maxy,
|
|
892
942
|
MIN(s.inserted_at) AS created,
|
|
893
943
|
MAX(s.updated_at) AS updated,
|
|
894
944
|
MIN({order_column}) AS min_order,
|
|
895
945
|
MAX({order_column}) AS max_order
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
946
|
+
FROM sequences s
|
|
947
|
+
LEFT JOIN sequences_pictures sp ON s.id = sp.seq_id
|
|
948
|
+
LEFT JOIN pictures p on sp.pic_id = p.id
|
|
949
|
+
WHERE {filter}
|
|
950
|
+
"""
|
|
901
951
|
).format(
|
|
902
952
|
filter=SQL(" AND ").join(meta_filter),
|
|
903
953
|
order_column=collection_request.sort_by.fields[0].field.sql_filter,
|
|
@@ -955,7 +1005,7 @@ def getUserCollection(userId, userIdMatchesAccount=False):
|
|
|
955
1005
|
# if some filters were given, we continue to pass them to the pagination
|
|
956
1006
|
additional_filters = request.args.get("filter")
|
|
957
1007
|
|
|
958
|
-
pagination_links = get_pagination_links(
|
|
1008
|
+
pagination_links = sequences.get_pagination_links(
|
|
959
1009
|
route="stac_collections.getUserCollection",
|
|
960
1010
|
routeArgs={"userId": str(userId), "limit": collection_request.limit},
|
|
961
1011
|
field=sortBy.fields[0].field.stac,
|