geovisio 2.7.0__py3-none-any.whl → 2.8.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.
Files changed (64) hide show
  1. geovisio/__init__.py +11 -3
  2. geovisio/admin_cli/__init__.py +3 -1
  3. geovisio/admin_cli/cleanup.py +2 -2
  4. geovisio/admin_cli/user.py +75 -0
  5. geovisio/config_app.py +87 -4
  6. geovisio/templates/main.html +2 -2
  7. geovisio/templates/viewer.html +3 -3
  8. geovisio/translations/da/LC_MESSAGES/messages.mo +0 -0
  9. geovisio/translations/da/LC_MESSAGES/messages.po +850 -0
  10. geovisio/translations/de/LC_MESSAGES/messages.mo +0 -0
  11. geovisio/translations/de/LC_MESSAGES/messages.po +235 -2
  12. geovisio/translations/el/LC_MESSAGES/messages.mo +0 -0
  13. geovisio/translations/el/LC_MESSAGES/messages.po +685 -0
  14. geovisio/translations/en/LC_MESSAGES/messages.mo +0 -0
  15. geovisio/translations/en/LC_MESSAGES/messages.po +244 -153
  16. geovisio/translations/eo/LC_MESSAGES/messages.mo +0 -0
  17. geovisio/translations/eo/LC_MESSAGES/messages.po +790 -0
  18. geovisio/translations/es/LC_MESSAGES/messages.mo +0 -0
  19. geovisio/translations/fi/LC_MESSAGES/messages.mo +0 -0
  20. geovisio/translations/fr/LC_MESSAGES/messages.mo +0 -0
  21. geovisio/translations/fr/LC_MESSAGES/messages.po +40 -3
  22. geovisio/translations/hu/LC_MESSAGES/messages.mo +0 -0
  23. geovisio/translations/hu/LC_MESSAGES/messages.po +773 -0
  24. geovisio/translations/it/LC_MESSAGES/messages.mo +0 -0
  25. geovisio/translations/it/LC_MESSAGES/messages.po +875 -0
  26. geovisio/translations/ja/LC_MESSAGES/messages.mo +0 -0
  27. geovisio/translations/ja/LC_MESSAGES/messages.po +719 -0
  28. geovisio/translations/ko/LC_MESSAGES/messages.mo +0 -0
  29. geovisio/translations/messages.pot +225 -148
  30. geovisio/translations/nl/LC_MESSAGES/messages.mo +0 -0
  31. geovisio/translations/nl/LC_MESSAGES/messages.po +24 -16
  32. geovisio/translations/pl/LC_MESSAGES/messages.mo +0 -0
  33. geovisio/translations/pl/LC_MESSAGES/messages.po +727 -0
  34. geovisio/translations/zh_Hant/LC_MESSAGES/messages.mo +0 -0
  35. geovisio/translations/zh_Hant/LC_MESSAGES/messages.po +719 -0
  36. geovisio/utils/auth.py +80 -8
  37. geovisio/utils/link.py +3 -2
  38. geovisio/utils/model_query.py +55 -0
  39. geovisio/utils/pictures.py +29 -62
  40. geovisio/utils/semantics.py +120 -0
  41. geovisio/utils/sequences.py +30 -23
  42. geovisio/utils/tokens.py +5 -3
  43. geovisio/utils/upload_set.py +87 -64
  44. geovisio/utils/website.py +50 -0
  45. geovisio/web/annotations.py +17 -0
  46. geovisio/web/auth.py +9 -5
  47. geovisio/web/collections.py +235 -63
  48. geovisio/web/configuration.py +17 -1
  49. geovisio/web/docs.py +99 -54
  50. geovisio/web/items.py +233 -100
  51. geovisio/web/map.py +129 -31
  52. geovisio/web/pages.py +240 -0
  53. geovisio/web/params.py +17 -0
  54. geovisio/web/prepare.py +165 -0
  55. geovisio/web/stac.py +17 -4
  56. geovisio/web/tokens.py +14 -4
  57. geovisio/web/upload_set.py +19 -10
  58. geovisio/web/users.py +176 -44
  59. geovisio/workers/runner_pictures.py +75 -50
  60. {geovisio-2.7.0.dist-info → geovisio-2.8.0.dist-info}/METADATA +6 -5
  61. geovisio-2.8.0.dist-info/RECORD +89 -0
  62. {geovisio-2.7.0.dist-info → geovisio-2.8.0.dist-info}/WHEEL +1 -1
  63. geovisio-2.7.0.dist-info/RECORD +0 -66
  64. {geovisio-2.7.0.dist-info → geovisio-2.8.0.dist-info}/LICENSE +0 -0
@@ -6,13 +6,14 @@ from geovisio.utils import db, sequences, upload_set
6
6
  import psycopg
7
7
  from psycopg.rows import dict_row
8
8
  from psycopg.sql import SQL
9
+ from psycopg.types.json import Jsonb
9
10
  import sentry_sdk
10
11
  from geovisio import errors
11
12
  from dataclasses import dataclass
12
13
  import logging
13
14
  from contextlib import contextmanager
14
15
  from enum import Enum
15
- from typing import Any, Optional
16
+ from typing import Any, Dict, Optional
16
17
  import threading
17
18
  from uuid import UUID
18
19
  from croniter import croniter
@@ -21,7 +22,7 @@ import geovisio.utils.filesystems
21
22
 
22
23
  log = logging.getLogger("geovisio.runner_pictures")
23
24
 
24
- PICTURE_PROCESS_MAX_RETRY = 10 # Number of times a job will be retryed if there is a `RecoverableProcessException` during process (like if the blurring api is not reachable).
25
+ PROCESS_MAX_RETRY = 5 # Number of times a job will be retryed if there is a `RecoverableProcessException` during process (like if the blurring api is not reachable).
25
26
 
26
27
 
27
28
  class PictureBackgroundProcessor(object):
@@ -50,9 +51,6 @@ class PictureBackgroundProcessor(object):
50
51
  return self.executor.submit(worker.process_jobs)
51
52
 
52
53
 
53
- # background_processor = PictureBackgroundProcessor()
54
-
55
-
56
54
  class ProcessTask(str, Enum):
57
55
  prepare = "prepare"
58
56
  delete = "delete"
@@ -64,6 +62,7 @@ class ProcessTask(str, Enum):
64
62
  class DbPicture:
65
63
  id: UUID
66
64
  metadata: dict
65
+ skip_blurring: bool
67
66
 
68
67
  def blurred_by_author(self):
69
68
  return self.metadata.get("blurredByAuthor", False)
@@ -118,7 +117,7 @@ def processPictureFiles(pic: DbPicture, config):
118
117
  config : dict
119
118
  Flask app.config (passed as param to allow using ThreadPoolExecutor)
120
119
  """
121
- skipBlur = pic.blurred_by_author() or config.get("API_BLUR_URL") is None
120
+ skipBlur = pic.skip_blurring or config.get("API_BLUR_URL") is None
122
121
  fses = config["FILESYSTEMS"]
123
122
  fs = fses.permanent if skipBlur else fses.tmp
124
123
  picHdPath = utils.pictures.getHDPicturePath(pic.id)
@@ -132,8 +131,6 @@ def processPictureFiles(pic: DbPicture, config):
132
131
  raise Exception(f"Impossible to find picture file: {picHdPath}")
133
132
 
134
133
  with fs.openbin(picHdPath) as pictureBytes:
135
- picture = Image.open(pictureBytes)
136
-
137
134
  # Create picture folders for this specific picture
138
135
  picDerivatesFolder = utils.pictures.getPictureFolderPath(pic.id)
139
136
  fses.derivates.makedirs(picDerivatesFolder, recreate=True)
@@ -143,9 +140,14 @@ def processPictureFiles(pic: DbPicture, config):
143
140
  if not skipBlur:
144
141
  with sentry_sdk.start_span(description="Blurring picture"):
145
142
  try:
146
- picture = utils.pictures.createBlurredHDPicture(fses.permanent, config.get("API_BLUR_URL"), pictureBytes, picHdPath)
143
+ picture = utils.pictures.createBlurredHDPicture(
144
+ fses.permanent,
145
+ config.get("API_BLUR_URL"),
146
+ pictureBytes,
147
+ picHdPath,
148
+ )
147
149
  except Exception as e:
148
- logging.exception(e)
150
+ log.exception(f"impossible to blur picture {pic.id}")
149
151
  raise RecoverableProcessException("Blur API failure: " + errors.getMessageFromException(e)) from e
150
152
 
151
153
  # Delete original unblurred file
@@ -166,15 +168,26 @@ def processPictureFiles(pic: DbPicture, config):
166
168
  else:
167
169
  # Make sure image rotation is always applied
168
170
  # -> Not necessary on pictures from blur API, as SGBlur ensures rotation is always applied
171
+ picture = Image.open(pictureBytes)
169
172
  picture = ImageOps.exif_transpose(picture)
170
173
 
171
174
  # Always pre-generate thumbnail
172
- utils.pictures.createThumbPicture(fses.derivates, picture, picDerivatesFolder + "/thumb.jpg", pic.metadata["type"])
175
+ utils.pictures.createThumbPicture(
176
+ fses.derivates,
177
+ picture,
178
+ picDerivatesFolder + "/thumb.jpg",
179
+ pic.metadata["type"],
180
+ )
173
181
 
174
182
  # Create SD and tiles
175
183
  if config.get("PICTURE_PROCESS_DERIVATES_STRATEGY") == "PREPROCESS":
176
184
  utils.pictures.generatePictureDerivates(
177
- fses.derivates, picture, pic.metadata, picDerivatesFolder, pic.metadata["type"], skipThumbnail=True
185
+ fses.derivates,
186
+ picture,
187
+ pic.metadata,
188
+ picDerivatesFolder,
189
+ pic.metadata["type"],
190
+ skipThumbnail=True,
178
191
  )
179
192
 
180
193
 
@@ -298,14 +311,13 @@ def process_next_job(app):
298
311
  span.set_data("pic_id", job.pic.id)
299
312
  with utils.time.log_elapsed(f"Deleting picture {job.pic.id}"):
300
313
  _delete_picture(job.pic)
301
- elif job.task == ProcessTask.delete and job.upload_set:
302
- with sentry_sdk.start_span(description="Deleting upload set") as span:
303
- span.set_data("us_id", job.upload_set.id)
304
- with utils.time.log_elapsed(f"Deleting upload set {job.upload_set.id}"):
305
- _delete_upload_set(job.upload_set)
306
314
  elif job.task == ProcessTask.dispatch and job.upload_set:
307
315
  with utils.time.log_elapsed(f"Dispatching upload set {job.upload_set.id}"):
308
- upload_set.dispatch(job.upload_set.id)
316
+ try:
317
+ upload_set.dispatch(job.upload_set.id)
318
+ except Exception as e:
319
+ log.exception(f"impossible to dispatch upload set {job.upload_set.id}")
320
+ raise RecoverableProcessException("Upload set dispatch error: " + errors.getMessageFromException(e)) from e
309
321
  elif job.task == ProcessTask.finalize and job.seq:
310
322
  with utils.time.log_elapsed(f"Finalizing sequence {job.seq.id}"):
311
323
  with job.reporting_conn.cursor(row_factory=dict_row) as cursor:
@@ -328,7 +340,7 @@ def _get_next_job(app):
328
340
  with app.pool.connection() as locking_transaction:
329
341
  with locking_transaction.transaction(), locking_transaction.cursor(row_factory=dict_row) as cursor:
330
342
  r = cursor.execute(
331
- """SELECT j.id, j.picture_id, j.upload_set_id, j.sequence_id, j.task, p.metadata
343
+ """SELECT j.id, j.picture_id, j.upload_set_id, j.sequence_id, j.task, j.picture_to_delete_id, p.metadata, j.args
332
344
  FROM job_queue j
333
345
  LEFT JOIN pictures p ON p.id = j.picture_id
334
346
  ORDER by
@@ -343,7 +355,14 @@ def _get_next_job(app):
343
355
  else:
344
356
  log.debug(f"Processing {r['id']}")
345
357
 
346
- db_pic = DbPicture(id=r["picture_id"], metadata=r["metadata"]) if r["picture_id"] is not None else None
358
+ # picture id can either be in `picture_id` (and it will be a foreign key to picture) or in `picture_to_delete_id`
359
+ # (and it will not a foreign key since the picture's row will already have been deleted from the db)
360
+ pic_id = r["picture_id"] or r["picture_to_delete_id"]
361
+ db_pic = (
362
+ DbPicture(id=pic_id, metadata=r["metadata"], skip_blurring=(r["args"] or {}).get("skip_blurring", False))
363
+ if pic_id is not None
364
+ else None
365
+ )
347
366
  db_seq = DbSequence(id=r["sequence_id"]) if r["sequence_id"] is not None else None
348
367
  db_upload_set = DbUploadSet(id=r["upload_set_id"]) if r["upload_set_id"] is not None else None
349
368
 
@@ -355,6 +374,7 @@ def _get_next_job(app):
355
374
  db_seq=db_seq,
356
375
  db_upload_set=db_upload_set,
357
376
  task=ProcessTask(r["task"]),
377
+ args=r["args"],
358
378
  )
359
379
  try:
360
380
  yield job
@@ -365,7 +385,13 @@ def _get_next_job(app):
365
385
  except RecoverableProcessException as e:
366
386
  _mark_process_as_error(locking_transaction, job, e, recoverable=True)
367
387
  except RetryLaterProcessException as e:
368
- _mark_process_as_error(locking_transaction, job, e, recoverable=True, mark_as_error=False)
388
+ _mark_process_as_error(
389
+ locking_transaction,
390
+ job,
391
+ e,
392
+ recoverable=True,
393
+ mark_as_error=False,
394
+ )
369
395
  except InterruptedError as interruption:
370
396
  log.error(f"Interruption received, stoping job {job.label()}")
371
397
  # starts a new connection, since the current one can be corrupted by the exception
@@ -409,6 +435,18 @@ def _finalize_sequence(job: DbJob):
409
435
 
410
436
 
411
437
  def _finalize_job(conn, job: DbJob):
438
+ try:
439
+ # we try to see if our job_history row is still here.
440
+ # It can have been removed if the object this job was preparing has been deleted during the process (since the job_history table store foreign keys)
441
+ job.reporting_conn.execute(
442
+ "SELECT id FROM job_history WHERE id = %(id)s FOR UPDATE NOWAIT",
443
+ {"id": job.job_history_id},
444
+ )
445
+ except psycopg.errors.LockNotAvailable:
446
+ logging.info(
447
+ f"The job {job.job_history_id} ({job.label()}) has likely been deleted during the process (it can happen if the picture/upload_set/sequence has been deleted by another process), we don't need to finalize it"
448
+ )
449
+ return
412
450
  job.reporting_conn.execute(
413
451
  "UPDATE job_history SET finished_at = CURRENT_TIMESTAMP WHERE id = %(id)s",
414
452
  {"id": job.job_history_id},
@@ -416,19 +454,12 @@ def _finalize_job(conn, job: DbJob):
416
454
  if job.task == ProcessTask.prepare and job.pic:
417
455
  # Note: the status is slowly been deprecated by replacing it with more precise status, and in the end it will be removed
418
456
  job.reporting_conn.execute(
419
- "UPDATE pictures SET status = 'ready', preparing_status = 'prepared' WHERE id = %(pic_id)s",
457
+ "UPDATE pictures SET status = (CASE WHEN status = 'hidden' THEN 'hidden' ELSE 'ready' END)::picture_status, preparing_status = 'prepared' WHERE id = %(pic_id)s",
420
458
  {"pic_id": job.pic.id},
421
459
  )
422
460
 
423
461
  # Add a task to finalize the sequence
424
462
  _finalize_sequence(job)
425
- elif job.task == ProcessTask.delete and job.pic:
426
- conn.execute(
427
- "DELETE FROM pictures WHERE id = %(pic_id)s",
428
- {"pic_id": job.pic.id},
429
- )
430
- elif job.task == ProcessTask.delete and job.upload_set:
431
- conn.execute(SQL("DELETE FROM upload_sets WHERE id = %s"), [job.upload_set.id])
432
463
 
433
464
  conn.execute("DELETE FROM job_queue WHERE id = %(job_id)s", {"job_id": job.job_queue_id})
434
465
 
@@ -440,17 +471,20 @@ def _initialize_job(
440
471
  db_seq: Optional[DbSequence],
441
472
  db_upload_set: Optional[DbUploadSet],
442
473
  task: ProcessTask,
474
+ args: Optional[Dict[Any, Any]],
443
475
  ) -> DbJob:
444
476
  r = reporting_conn.execute(
445
- """INSERT INTO job_history(job_id, picture_id, sequence_id, upload_set_id, job_task)
446
- VALUES (%(job_id)s, %(pic_id)s, %(seq_id)s, %(us_id)s, %(task)s)
477
+ """INSERT INTO job_history(job_id, picture_id, sequence_id, upload_set_id, picture_to_delete_id, job_task, args)
478
+ VALUES (%(job_id)s, %(pic_id)s, %(seq_id)s, %(us_id)s, %(pic_to_delete)s, %(task)s, %(args)s)
447
479
  RETURNING id""",
448
480
  {
449
481
  "job_id": job_queue_id,
450
- "pic_id": db_pic.id if db_pic else None,
482
+ "pic_id": db_pic.id if db_pic and task != ProcessTask.delete else None,
451
483
  "seq_id": db_seq.id if db_seq else None,
484
+ "pic_to_delete": db_pic.id if db_pic and task == ProcessTask.delete else None,
452
485
  "us_id": db_upload_set.id if db_upload_set else None,
453
486
  "task": task.value,
487
+ "args": Jsonb(args),
454
488
  },
455
489
  ).fetchone()
456
490
 
@@ -468,7 +502,13 @@ def _initialize_job(
468
502
  )
469
503
 
470
504
 
471
- def _mark_process_as_error(conn, job: DbJob, e: Exception, recoverable: bool = False, mark_as_error: bool = True):
505
+ def _mark_process_as_error(
506
+ conn,
507
+ job: DbJob,
508
+ e: Exception,
509
+ recoverable: bool = False,
510
+ mark_as_error: bool = True,
511
+ ):
472
512
  job.reporting_conn.execute(
473
513
  """UPDATE job_history SET
474
514
  error = %(err)s, finished_at = CURRENT_TIMESTAMP
@@ -478,14 +518,13 @@ def _mark_process_as_error(conn, job: DbJob, e: Exception, recoverable: bool = F
478
518
  if recoverable:
479
519
  if mark_as_error:
480
520
  nb_error = conn.execute(
481
- """
482
- UPDATE job_queue SET
521
+ """UPDATE job_queue SET
483
522
  nb_errors = nb_errors + 1
484
523
  WHERE id = %(id)s
485
524
  RETURNING nb_errors""",
486
525
  {"err": str(e), "id": job.job_queue_id},
487
526
  ).fetchone()
488
- if nb_error and nb_error[0] > PICTURE_PROCESS_MAX_RETRY:
527
+ if nb_error and nb_error[0] > PROCESS_MAX_RETRY:
489
528
  logging.info(f"Job {job.label()} has failed {nb_error} times, we stop trying to process it.")
490
529
  recoverable = False
491
530
  else:
@@ -512,17 +551,3 @@ def _delete_picture(pic: DbPicture):
512
551
  """Delete a picture from the filesystem"""
513
552
  log.debug(f"Deleting picture files {pic.id}")
514
553
  utils.pictures.removeAllFiles(pic.id)
515
-
516
-
517
- def _delete_upload_set(upload_set: DbUploadSet):
518
- """Delete an upload set
519
- We do this in the job queue since we want to wait for all its pictures to be deleted
520
- """
521
- with db.conn(current_app) as conn:
522
- with conn.transaction(), conn.cursor() as cursor:
523
- # we want to wait for all pictures to be deleted
524
- has_more_pictures = cursor.execute("SELECT 1 FROM pictures WHERE upload_set_id = %s LIMIT 1", [upload_set.id]).fetchone()
525
- if has_more_pictures and has_more_pictures[0]:
526
- logging.info(f"More pictures to be deleted, upload_set {upload_set.id} will be deleted later")
527
- raise RetryLaterProcessException("More pictures to be deleted, upload_set will be deleted later")
528
- # Note: the real deletion will be done on job completion so the lock is released
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: geovisio
3
- Version: 2.7.0
3
+ Version: 2.8.0
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
@@ -20,17 +20,18 @@ Requires-Dist: psycopg-binary ~= 3.1
20
20
  Requires-Dist: python-dotenv ~= 0.21
21
21
  Requires-Dist: authlib ~= 1.2
22
22
  Requires-Dist: Flask-Executor ~= 1.0
23
- Requires-Dist: geopic-tag-reader[write-exif] == 1.3.1
23
+ Requires-Dist: geopic-tag-reader[write-exif] == 1.4.2
24
24
  Requires-Dist: rfeed ~= 1.1.1
25
25
  Requires-Dist: sentry-sdk[flask] ~= 1.31
26
26
  Requires-Dist: pygeofilter[backend-native] ~= 0.2.4
27
- Requires-Dist: python-dateutil ~= 2.8.2
27
+ Requires-Dist: python-dateutil ~= 2.9.0
28
28
  Requires-Dist: tzdata ~= 2024.1
29
29
  Requires-Dist: croniter ~= 2.0.5
30
30
  Requires-Dist: pydantic ~= 2.7
31
31
  Requires-Dist: pydantic-extra-types ~= 2.7
32
32
  Requires-Dist: flask-babel ~= 4.0.0
33
33
  Requires-Dist: geojson-pydantic ~= 1.1.0
34
+ Requires-Dist: email-validator ~= 2.2.0
34
35
  Requires-Dist: flit ~= 3.9.0 ; extra == "build"
35
36
  Requires-Dist: coverage ~= 6.5 ; extra == "dev"
36
37
  Requires-Dist: protobuf ~= 4.21 ; extra == "dev"
@@ -45,7 +46,7 @@ Requires-Dist: black ~= 24.1 ; extra == "dev"
45
46
  Requires-Dist: pre-commit ~= 3.3 ; extra == "dev"
46
47
  Requires-Dist: pyyaml ~= 6.0 ; extra == "dev"
47
48
  Requires-Dist: openapi-spec-validator ~= 0.7 ; extra == "dev"
48
- Requires-Dist: stac-api-validator ~= 0.6.3 ; extra == "dev"
49
+ Requires-Dist: stac-api-validator ~= 0.6.4 ; extra == "dev"
49
50
  Requires-Dist: mkdocs-material ~= 9.5.21 ; extra == "docs"
50
51
  Requires-Dist: mkdocs-swagger-ui-tag ~= 0.6.10 ; extra == "docs"
51
52
  Project-URL: Home, https://gitlab.com/panoramax/server/api
@@ -0,0 +1,89 @@
1
+ geovisio/__init__.py,sha256=SPh7-7e0TuxuQNMcF9hDwkg7hW0bJDpbcRnxwu_5DQk,7145
2
+ geovisio/config_app.py,sha256=8RlBxn2LcA71o0jcfR5UrVz12ig985MNUgCj54ZydpA,14356
3
+ geovisio/db_migrations.py,sha256=9lHkyG_RiCWzrFkfwhkslScUsbCZScN-KVhkXrtnPDo,4560
4
+ geovisio/errors.py,sha256=uTn-kI7SUl5OPB8Mv3Qqu7Ucp5JvcqWPQFfgLCqsEpI,1376
5
+ geovisio/admin_cli/__init__.py,sha256=1e0hX771-3iG8eBcNmVvUYyg8qXnpng-9YWvi3MI3Kg,3248
6
+ geovisio/admin_cli/cleanup.py,sha256=G85I7rrfPJwaArL6MQAnC04Ye9wWciA-Yqu5iv23uJ0,4862
7
+ geovisio/admin_cli/db.py,sha256=mJ-cGuOAAsg-ovbP9L1kyL4xE0C4bYRuozzqQkaFyw8,897
8
+ geovisio/admin_cli/default_account_tokens.py,sha256=W-v5uPjCBvAujjAUx1HrfgjPj-tEyncb-EUMLpsWc9w,469
9
+ geovisio/admin_cli/reorder_sequences.py,sha256=LKKzdO2w4N-cQmi6rqKHKYG5YGzPxYRTbnfcPKakuYM,2826
10
+ geovisio/admin_cli/sequence_heading.py,sha256=BEPuRfCDXXpqSSzK2ysrxHf0OD4THzrMI_YK2uXQlGk,633
11
+ geovisio/admin_cli/user.py,sha256=4ml2E_aphz3I3NcuUPB2dwe_jXhcE7AGa0R5VTm3_ik,2753
12
+ geovisio/templates/main.html,sha256=VDVQwCZ1mNjH7sH4VOIdn8gM09R9LJZX49SPtA2VEzM,2963
13
+ geovisio/templates/viewer.html,sha256=JErXdU2ujj4LdMHgQbYNCTfKuYGEXbJTQwBE-K_MNXQ,892
14
+ geovisio/translations/messages.pot,sha256=SUPAgov3RzwVw0LNOMn2NkTthXfirbBDIQK_2BaZ2FI,19445
15
+ geovisio/translations/da/LC_MESSAGES/messages.mo,sha256=zFlDCgA4l-1MamoKMXfFR3diQk67gXM2pEFiZvZwsow,21014
16
+ geovisio/translations/da/LC_MESSAGES/messages.po,sha256=Yt1JSEG9e_ljgRvc0u3cknQ5ViU-eJq9XNQ__1Nz-Vs,29084
17
+ geovisio/translations/de/LC_MESSAGES/messages.mo,sha256=rO-g7WJC74c9MeuqSAuc3bNZX4G5Vz4O-sHILirjYYk,22590
18
+ geovisio/translations/de/LC_MESSAGES/messages.po,sha256=0LcBQjH0LpsHSypXo7kKQKZC0K_yPNyHBuEh5aF7lIk,30776
19
+ geovisio/translations/el/LC_MESSAGES/messages.mo,sha256=vy1jtEG6mLS5sYWPfQIr5U4XsZ21ZzSbsHAJHGQXZSY,433
20
+ geovisio/translations/el/LC_MESSAGES/messages.po,sha256=gDr-pDCsQGrCXBMBcDwlfsxcGWF1NIEqGrqPcZy65-4,17405
21
+ geovisio/translations/en/LC_MESSAGES/messages.mo,sha256=TdE2eywWyvkF0x0qsBPHju3TugQLLqtTk4ZqnAxuJHs,19953
22
+ geovisio/translations/en/LC_MESSAGES/messages.po,sha256=Q7j87mLUtyG-wrVvLQXlbTecW8p04a71FnN919UjZN8,27942
23
+ geovisio/translations/eo/LC_MESSAGES/messages.mo,sha256=3xtkpuSWl-Wb61cwlB7yK40_JgLMJNB5k1FCFuCfqM8,19330
24
+ geovisio/translations/eo/LC_MESSAGES/messages.po,sha256=Gd9n9ZalupqFAlI48y-VQ-XZumTVWnpEOy8-VXnR3Us,26977
25
+ geovisio/translations/es/LC_MESSAGES/messages.mo,sha256=ULQDhq4enQyjfAWxDq13BwHBPibg3Yt4ys6XrfQF5tM,19111
26
+ geovisio/translations/es/LC_MESSAGES/messages.po,sha256=NoyuXR_2iugWHLTcoUZSNLUSCpt8jMyl0FUD4p7N99w,26775
27
+ geovisio/translations/fi/LC_MESSAGES/messages.mo,sha256=6-WCesFiV00MkNM_Wpi7-D51DOZRNg_QOM2sL7-UPhA,626
28
+ geovisio/translations/fi/LC_MESSAGES/messages.po,sha256=UFT4YCfEazxLij8Ovk2vZqx55e2Yctbf_3xM5KDrXhw,14685
29
+ geovisio/translations/fr/LC_MESSAGES/messages.mo,sha256=2kjpCTUbrbAUfFJZ17kR5FAdY_LD5Bh9POzkQvo06-Q,21712
30
+ geovisio/translations/fr/LC_MESSAGES/messages.po,sha256=e-PEtl8S4Hjymh9-rv4R45RM3dq-C1M2hdF4CE5Nm6E,29384
31
+ geovisio/translations/hu/LC_MESSAGES/messages.mo,sha256=R-0QJl78CNJepsXi8uunlCA-QHhB0_t1Xj6e_EI_NI4,20156
32
+ geovisio/translations/hu/LC_MESSAGES/messages.po,sha256=Cs1EaEfVISyIsKxnK-f0gy0ocJdey5o-620mkvW1SAs,27472
33
+ geovisio/translations/it/LC_MESSAGES/messages.mo,sha256=jpTyt3Kv19Qu7EHGTzunZyXGMGsg3cg3_EBVxvNmuYA,22260
34
+ geovisio/translations/it/LC_MESSAGES/messages.po,sha256=Jw3y5igDkAVvTmJz0sYoopXvOBops5lRqdC6F-j2AXQ,30335
35
+ geovisio/translations/ja/LC_MESSAGES/messages.mo,sha256=5t8PzVwGf7ePX3mCQI65pGFOLzF2sQbMPm8svxkxNAE,426
36
+ geovisio/translations/ja/LC_MESSAGES/messages.po,sha256=a5S3Lceg47RFSuq7Lcytmu1ni0JGj8nPjWXdF1ZWRVs,18352
37
+ geovisio/translations/ko/LC_MESSAGES/messages.mo,sha256=eKuQS9zLcJ9s-DzbfR-QK2INBJL10jTIQ1kuSTdJ9Rg,426
38
+ geovisio/translations/ko/LC_MESSAGES/messages.po,sha256=uq19EZaeRB-obmE1hYnckA8T12JuuU3nXYyKaMR4tiU,17405
39
+ geovisio/translations/nl/LC_MESSAGES/messages.mo,sha256=aKM90Hp4Eh9vCQba_tlfjEWlhygLXWGq_SVYqBw9IA4,1592
40
+ geovisio/translations/nl/LC_MESSAGES/messages.po,sha256=m69xfphxpgfPOuUrBK51XrR8UFwqCEBZpnb_5B1mGOU,15302
41
+ geovisio/translations/pl/LC_MESSAGES/messages.mo,sha256=0RiGTq49esjtIrBomzeFwG6dWdAlDhJxIOv4AgNjyOQ,10083
42
+ geovisio/translations/pl/LC_MESSAGES/messages.po,sha256=JpN6yFqnpyqkMjU3YZLAMr65npQ7AybOLB3ARJoL94g,22192
43
+ geovisio/translations/zh_Hant/LC_MESSAGES/messages.mo,sha256=TmRUyfTGoBpU-2BE-nKjhwdr9r0ikDioVQU-JQ_ih90,431
44
+ geovisio/translations/zh_Hant/LC_MESSAGES/messages.po,sha256=LnnKlHy8t_54nNsLDBqC1eEPwPx49h1Um9mQj6l9hv0,18357
45
+ geovisio/utils/__init__.py,sha256=g4SWVoV73cSXjf5-5D9-HmyB5xKmHSuxxOGWnx7W3V0,71
46
+ geovisio/utils/auth.py,sha256=_vvkBTvjRXYlnyaHziNWJjiGKulomMqex-CDbv1dbKQ,13845
47
+ geovisio/utils/db.py,sha256=DFyCEB5-xTUo6sn79SYJCzuvlgFNDVyNJ48Mana5vPI,2625
48
+ geovisio/utils/excluded_areas.py,sha256=6f3wwsgNpJKxAXnHH8RKlktgHpsG-0QVNTWDDTFqPZ8,2585
49
+ geovisio/utils/extent.py,sha256=vzOHvbG6lpSNt7KrsaonBOx7Tz46S1J603gLbZvs36g,557
50
+ geovisio/utils/fields.py,sha256=sNAmrSJ4e-nqm0-LoyO3l4Zynb-Jy8swhwmL3UcDN_o,2129
51
+ geovisio/utils/filesystems.py,sha256=W_wH7TlvdEux_q4FP0XInxruxlbepFSEpJbPLO9Cnr4,4133
52
+ geovisio/utils/link.py,sha256=u9x4xJa57L1448neD7uPJuAA76_sFXVE0-9_zPW-bJM,490
53
+ geovisio/utils/model_query.py,sha256=PtvYCjKVygmicvqqYpCpEKWUIEvwdEG6QMh-JL5E8AQ,2031
54
+ geovisio/utils/params.py,sha256=s9kBPHm4gRhMx10SD7mOPdG0tR_n_O-g_rgL8Fife6s,630
55
+ geovisio/utils/pictures.py,sha256=cDDOABzZaTn98Bg8lYgoMlkTNklS9-y-qB-HXTcJ0YM,23092
56
+ geovisio/utils/reports.py,sha256=PgU0Td48WJg6XCq043he8Jif3WCA9nOTaGE0Yovo3h0,5626
57
+ geovisio/utils/semantics.py,sha256=bsPo4n0R0_pU5NdL7-dkx-bMYPhtyVq85z4nHNLptto,4572
58
+ geovisio/utils/sentry.py,sha256=Dw0rW2sNFbcpGojzpa-vjtJ5S7BH_XhsqfuGYBjHCG0,3759
59
+ geovisio/utils/sequences.py,sha256=S3OaMozzk9viYJMgHyzGYP1hZFsYjF8Ez2hXFTIBimI,25307
60
+ geovisio/utils/time.py,sha256=-hOs7FSx-8eiGmMQtTOrCoF8d_yMlj2306vfxJftEr8,642
61
+ geovisio/utils/tokens.py,sha256=tkihnnXqgQeIME_d12tC8PVrPN90A0i9k6UPEbgZ9TQ,3047
62
+ geovisio/utils/upload_set.py,sha256=P27ABINmMgNaFGMqd6rLLbeaOSddTz7Tchs2ByKDHeM,26478
63
+ geovisio/utils/website.py,sha256=wQosLHD-7_ONReJijKCjGUawKz1eCyBLWwkSWxd6ei8,1909
64
+ geovisio/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
+ geovisio/web/annotations.py,sha256=TdivDOcVh83HRkBXBTxFD7J-VbZ1gnVesPfTNY-3uW4,653
66
+ geovisio/web/auth.py,sha256=-msYF5q2OUsa3rYH7H0vjclt6JEaPy9CHzjWmCCwZUU,7139
67
+ geovisio/web/collections.py,sha256=pt181nK3bTa1UYn9qpi3RBGH5h5I8VKMOb_Kfa-c-EU,46470
68
+ geovisio/web/configuration.py,sha256=tWZYxOoqI2MQwmuHk1I9J2DKzDqpLBVmWRDSsx18U7E,2177
69
+ geovisio/web/docs.py,sha256=RxMtyH_Urcr0YWPTZz1epMKr-iHRwRXBdRzgdhZzaWU,56054
70
+ geovisio/web/excluded_areas.py,sha256=5BNSZ0UqgFMtgvgrJ73eYGJXPJRnV-mGEs36WDRRxTk,13024
71
+ geovisio/web/items.py,sha256=1a_O5tes2NL4DHqDWsgJn_PQQYx4Ft6v4t7ibzkgtdw,61357
72
+ geovisio/web/map.py,sha256=DaigXevz4lL7WGjPFCsKbXvjdDevFj9gpH0-22JsJOI,25328
73
+ geovisio/web/pages.py,sha256=Hkc3KJFE6D38vGnkCK5WUBJ8KQemI1f1wGXpxeiOiNo,6632
74
+ geovisio/web/params.py,sha256=Ip2qFR2Z28OTP7Fe1EuineHKUMmlZ_u5dFW77MqdLD0,21071
75
+ geovisio/web/pictures.py,sha256=qbhgLsI6YtpFxXn1a3dzO66nnVrWglRZSXWmlfJr1tU,6394
76
+ geovisio/web/prepare.py,sha256=R10_xf6O9dmAAwOMC-vsaxgNTdc9BkDJLATqH6MKtCw,5620
77
+ geovisio/web/reports.py,sha256=8v9a4PMM9RsvSGadZEN2o5PTKG_TohjyMMEBfFeY13E,14123
78
+ geovisio/web/rss.py,sha256=NLUd2Or92tcKRaGUHAze6QMLWczHyzag9ybOzrA8djE,2962
79
+ geovisio/web/stac.py,sha256=1uoSUOgCxOdH4UQuUvt-0xJaPLtPcAD54WvQg0lvxwM,14850
80
+ geovisio/web/tokens.py,sha256=l7CAM0FQ6qAcoUhtIRysKc9Gndlji_wOMpkXLsPP1pI,9599
81
+ geovisio/web/upload_set.py,sha256=IkZrSosJ6LenqEsNFx4BOULZCUrcl8yIz_sO4B5xyeM,30671
82
+ geovisio/web/users.py,sha256=CB5fJxZyIUDtlQ-BKfV4zZtnlSGqgZehsMk2rlVTvHk,13038
83
+ geovisio/web/utils.py,sha256=kudTbV4Tgtkbd4oUWFTFpyWNINpxAa-VQNbxYEiR6pM,3640
84
+ geovisio/workers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ geovisio/workers/runner_pictures.py,sha256=Y4x345tp0Y3RAFnoYpcDhyg6dJS1OYx79XGGDIttcps,22898
86
+ geovisio-2.8.0.dist-info/LICENSE,sha256=iRFSz7MJ7_j4hh3hvIgzNbS2buy5NMva8lulaixd3IE,1069
87
+ geovisio-2.8.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
88
+ geovisio-2.8.0.dist-info/METADATA,sha256=xWj0uWtGYpQQ_Fmcp6XPd6MslTkeQHlVybq_7zSEjgs,4299
89
+ geovisio-2.8.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.9.0
2
+ Generator: flit 3.10.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,66 +0,0 @@
1
- geovisio/__init__.py,sha256=uFnmcVLE7WveiJ0LsK7qNN7WiVXldYlKLIG1J6RnD9M,6937
2
- geovisio/config_app.py,sha256=TiG90GE4JXr0Le4a5bRMqTP02TBivb4deWmPLLd94Fc,10349
3
- geovisio/db_migrations.py,sha256=9lHkyG_RiCWzrFkfwhkslScUsbCZScN-KVhkXrtnPDo,4560
4
- geovisio/errors.py,sha256=uTn-kI7SUl5OPB8Mv3Qqu7Ucp5JvcqWPQFfgLCqsEpI,1376
5
- geovisio/admin_cli/__init__.py,sha256=8xlb3WN1DtEVurW9B0VuOFtI33oRV5TvxV6hOkUUpM0,3173
6
- geovisio/admin_cli/cleanup.py,sha256=SevtztDBN9844wT_6G_DubTNoK2b3N4n53LFgAPRxcI,4882
7
- geovisio/admin_cli/db.py,sha256=mJ-cGuOAAsg-ovbP9L1kyL4xE0C4bYRuozzqQkaFyw8,897
8
- geovisio/admin_cli/default_account_tokens.py,sha256=W-v5uPjCBvAujjAUx1HrfgjPj-tEyncb-EUMLpsWc9w,469
9
- geovisio/admin_cli/reorder_sequences.py,sha256=LKKzdO2w4N-cQmi6rqKHKYG5YGzPxYRTbnfcPKakuYM,2826
10
- geovisio/admin_cli/sequence_heading.py,sha256=BEPuRfCDXXpqSSzK2ysrxHf0OD4THzrMI_YK2uXQlGk,633
11
- geovisio/templates/main.html,sha256=7s4eKMfWKCkpspRqeCx-G_euEq932ac-PLP5uBYGa48,2949
12
- geovisio/templates/viewer.html,sha256=XIHlaKV_a_iOxmjcTdUdToVjt5nB2NkZlMhQLoDfTAg,865
13
- geovisio/translations/messages.pot,sha256=-m51uG-Jr3h8yyhh--edb-nbCaB0rwlnbZC3Pf5nLV0,17416
14
- geovisio/translations/de/LC_MESSAGES/messages.mo,sha256=4Zx9ZOcyP1g8fpm1GyVaWEX7DfWMdc0fzV3NK-Y7Lsw,16370
15
- geovisio/translations/de/LC_MESSAGES/messages.po,sha256=1ixFHS45NsTbbQrn49oxWOlSQpETZ3bygKK25DKVdto,22448
16
- geovisio/translations/en/LC_MESSAGES/messages.mo,sha256=Kbh0Y4BPspwssVhvLBZ7i6R1CEoipYbEH2fokM3P3qY,17721
17
- geovisio/translations/en/LC_MESSAGES/messages.po,sha256=K2oQvqgISp6pXzyJc-xihOzfHSbfjwsYowEYdfFpgZc,24881
18
- geovisio/translations/es/LC_MESSAGES/messages.mo,sha256=R1uvDSt0uKMV0gVr0foc_0iiYBLc1ls2ryBlWHVZ_DQ,19111
19
- geovisio/translations/es/LC_MESSAGES/messages.po,sha256=NoyuXR_2iugWHLTcoUZSNLUSCpt8jMyl0FUD4p7N99w,26775
20
- geovisio/translations/fi/LC_MESSAGES/messages.mo,sha256=Z52s0OukqMwi58FpfLnBd8ED429LPSGG4shba4HPFgY,626
21
- geovisio/translations/fi/LC_MESSAGES/messages.po,sha256=UFT4YCfEazxLij8Ovk2vZqx55e2Yctbf_3xM5KDrXhw,14685
22
- geovisio/translations/fr/LC_MESSAGES/messages.mo,sha256=wnsuokhgoduq-Vvmhc8HvxIe5_wjJPVsrms3hHnSfPE,20613
23
- geovisio/translations/fr/LC_MESSAGES/messages.po,sha256=FqwR-VEHrN7_YzSYqIbvoEiV9YEjfrwQI53zgPanSNw,28020
24
- geovisio/translations/ko/LC_MESSAGES/messages.mo,sha256=cqo1DInz1VObD4Cj_2Tqmm5dGoNQfpssRiyv8BaxxF4,426
25
- geovisio/translations/ko/LC_MESSAGES/messages.po,sha256=uq19EZaeRB-obmE1hYnckA8T12JuuU3nXYyKaMR4tiU,17405
26
- geovisio/translations/nl/LC_MESSAGES/messages.mo,sha256=RsL6Km4aFAS7lGLqP7Tqx2T-0Mlwqybl4ROCkpi7h68,433
27
- geovisio/translations/nl/LC_MESSAGES/messages.po,sha256=R5c0GKQ2RgezM7wkYwymaSXZ68dhE30_c-WpjlV_Fxk,14613
28
- geovisio/utils/__init__.py,sha256=g4SWVoV73cSXjf5-5D9-HmyB5xKmHSuxxOGWnx7W3V0,71
29
- geovisio/utils/auth.py,sha256=iIssHEDqAWp5D514pmYjoAzr-Jy6VDSDM3STtM8M56o,10810
30
- geovisio/utils/db.py,sha256=DFyCEB5-xTUo6sn79SYJCzuvlgFNDVyNJ48Mana5vPI,2625
31
- geovisio/utils/excluded_areas.py,sha256=6f3wwsgNpJKxAXnHH8RKlktgHpsG-0QVNTWDDTFqPZ8,2585
32
- geovisio/utils/extent.py,sha256=vzOHvbG6lpSNt7KrsaonBOx7Tz46S1J603gLbZvs36g,557
33
- geovisio/utils/fields.py,sha256=sNAmrSJ4e-nqm0-LoyO3l4Zynb-Jy8swhwmL3UcDN_o,2129
34
- geovisio/utils/filesystems.py,sha256=W_wH7TlvdEux_q4FP0XInxruxlbepFSEpJbPLO9Cnr4,4133
35
- geovisio/utils/link.py,sha256=lMWSUcAjQzq-r-U6VEIMR2ViVs5K8PVX7coF6XMFrmk,378
36
- geovisio/utils/params.py,sha256=s9kBPHm4gRhMx10SD7mOPdG0tR_n_O-g_rgL8Fife6s,630
37
- geovisio/utils/pictures.py,sha256=B5tFa_v_WjGpyYJs8x5G11jTpD4GEqM8UzVrCdrU7Sk,23764
38
- geovisio/utils/reports.py,sha256=PgU0Td48WJg6XCq043he8Jif3WCA9nOTaGE0Yovo3h0,5626
39
- geovisio/utils/sentry.py,sha256=Dw0rW2sNFbcpGojzpa-vjtJ5S7BH_XhsqfuGYBjHCG0,3759
40
- geovisio/utils/sequences.py,sha256=H2Ef-XhQf0OfbLuvuUHC1bX2o7n3UQ1E0fmszlNz18A,24874
41
- geovisio/utils/time.py,sha256=-hOs7FSx-8eiGmMQtTOrCoF8d_yMlj2306vfxJftEr8,642
42
- geovisio/utils/tokens.py,sha256=evcboxseJ1jt0LAOkQz-ZEZ--v2P3Lh9JsaldQeHzLg,2890
43
- geovisio/utils/upload_set.py,sha256=yzL6MQLGUnipbdk4D3Q0InHV90tbbLSojLBe9phIRj8,24541
44
- geovisio/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- geovisio/web/auth.py,sha256=bpMb6ch6LJ8FIIfzYcrAjLS2yvwO4ohrLXfxdSYGLsQ,6882
46
- geovisio/web/collections.py,sha256=xgejdbUSWNGDGnVXIBh9Zq8LD0TapbhQBLhHv7dH_Ck,38037
47
- geovisio/web/configuration.py,sha256=8pHGG6LEID1NqaiAq0qg4kdRG5_uuM_F25afLt62_G0,1706
48
- geovisio/web/docs.py,sha256=LdMxqZQs3moYHWWxJDY2J_pCS3i2zLNi2IUJ9Lwtrwo,52519
49
- geovisio/web/excluded_areas.py,sha256=5BNSZ0UqgFMtgvgrJ73eYGJXPJRnV-mGEs36WDRRxTk,13024
50
- geovisio/web/items.py,sha256=Ms_ojboK22S1pAFZAuJPRkcqS9sd3grP7FOu4phQFbg,54291
51
- geovisio/web/map.py,sha256=CoscEcuzGsEU27y2we-bWl7kfGkbe4Q9J4PUOgOtLpQ,20175
52
- geovisio/web/params.py,sha256=1hO3MQuEXJBk5ZQAk-Mau9gRxwWUITxQkgtSnDEN06E,20542
53
- geovisio/web/pictures.py,sha256=qbhgLsI6YtpFxXn1a3dzO66nnVrWglRZSXWmlfJr1tU,6394
54
- geovisio/web/reports.py,sha256=8v9a4PMM9RsvSGadZEN2o5PTKG_TohjyMMEBfFeY13E,14123
55
- geovisio/web/rss.py,sha256=NLUd2Or92tcKRaGUHAze6QMLWczHyzag9ybOzrA8djE,2962
56
- geovisio/web/stac.py,sha256=V00aTv74faiCQRslsv1_dWZpZTNXAB5YryOA4O_sJh4,14382
57
- geovisio/web/tokens.py,sha256=AeQE_Xkqb988wRNnT5fkPOB7xsrHDqyI2GXM7drqzIo,8904
58
- geovisio/web/upload_set.py,sha256=uU0jtFjSYq0f0BwRKZ7sJxr6EEpnmp2D098YxoajcdY,30433
59
- geovisio/web/users.py,sha256=HN6vcoTwi4VPoCofw2_o6HO7tLWh6mA6KfOe8w8Sv9c,8468
60
- geovisio/web/utils.py,sha256=kudTbV4Tgtkbd4oUWFTFpyWNINpxAa-VQNbxYEiR6pM,3640
61
- geovisio/workers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
- geovisio/workers/runner_pictures.py,sha256=mqtjzbCZw0aRXhRki4CPEL5-9tq5q2kRqloKDFwpw9U,22151
63
- geovisio-2.7.0.dist-info/LICENSE,sha256=iRFSz7MJ7_j4hh3hvIgzNbS2buy5NMva8lulaixd3IE,1069
64
- geovisio-2.7.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
65
- geovisio-2.7.0.dist-info/METADATA,sha256=alZz_qktBwhysrEBytugFi79xJUgIJAq_aiHwFSWZCY,4259
66
- geovisio-2.7.0.dist-info/RECORD,,