geovisio 2.11.0__py3-none-any.whl → 2.12.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 +30 -2
- geovisio/admin_cli/db.py +1 -1
- geovisio/db_migrations.py +2 -2
- geovisio/migrations/20221201_01_wpCGc-initial-schema.rollback.sql +9 -0
- geovisio/migrations/20221201_01_wpCGc-initial-schema.sql +71 -0
- geovisio/migrations/20221201_02_ZG8AR-camera-information.rollback.sql +5 -0
- geovisio/migrations/20221201_02_ZG8AR-camera-information.sql +10 -0
- geovisio/migrations/20221222_01_fsB6f-add-account.rollback.sql +7 -0
- geovisio/migrations/20221222_01_fsB6f-add-account.sql +33 -0
- geovisio/migrations/20230113_01_0co97-rm-metadata-duplicates.rollback.sql +5 -0
- geovisio/migrations/20230113_01_0co97-rm-metadata-duplicates.sql +5 -0
- geovisio/migrations/20230116_01_9PkjZ-add-oauth-provider.rollback.sql +12 -0
- geovisio/migrations/20230116_01_9PkjZ-add-oauth-provider.sql +10 -0
- geovisio/migrations/20230117_01_K71Pd-pictures-ts-index.rollback.sql +4 -0
- geovisio/migrations/20230117_01_K71Pd-pictures-ts-index.sql +4 -0
- geovisio/migrations/20230130_01_VRIv2-sequences-account.rollback.sql +5 -0
- geovisio/migrations/20230130_01_VRIv2-sequences-account.sql +25 -0
- geovisio/migrations/20230324_01_ba9WA-status.rollback.sql +35 -0
- geovisio/migrations/20230324_01_ba9WA-status.sql +11 -0
- geovisio/migrations/20230324_02_efgI6-picture-process.rollback.sql +20 -0
- geovisio/migrations/20230324_02_efgI6-picture-process.sql +44 -0
- geovisio/migrations/20230407_01_wofh1-computed-headings.rollback.sql +6 -0
- geovisio/migrations/20230407_01_wofh1-computed-headings.sql +6 -0
- geovisio/migrations/20230417_01_ZgLMY-add-exif-metadata-column-for-pictures.rollback.sql +4 -0
- geovisio/migrations/20230417_01_ZgLMY-add-exif-metadata-column-for-pictures.sql +4 -0
- geovisio/migrations/20230420_01_elaN3-remove-picture-and-sequence-file-paths.rollback.sql +5 -0
- geovisio/migrations/20230420_01_elaN3-remove-picture-and-sequence-file-paths.sql +5 -0
- geovisio/migrations/20230425_01_gYP77-pictures-edits-triggers.rollback.sql +5 -0
- geovisio/migrations/20230425_01_gYP77-pictures-edits-triggers.sql +64 -0
- geovisio/migrations/20230427_01_k5e5w-timestamps.rollback.sql +10 -0
- geovisio/migrations/20230427_01_k5e5w-timestamps.sql +19 -0
- geovisio/migrations/20230511_01_TdpKo-tokens.rollback.sql +7 -0
- geovisio/migrations/20230511_01_TdpKo-tokens.sql +34 -0
- geovisio/migrations/20230615_01_u7aRf-pic-delete-cascade.rollback.sql +10 -0
- geovisio/migrations/20230615_01_u7aRf-pic-delete-cascade.sql +12 -0
- geovisio/migrations/20230623_01_y1SiQ-pic-deletion-task.rollback.sql +39 -0
- geovisio/migrations/20230623_01_y1SiQ-pic-deletion-task.sql +36 -0
- geovisio/migrations/20230629_01_ZdB3i-compute-heading-0.sql +27 -0
- geovisio/migrations/20230711_01_JGSPB-inserted-at-index.rollback.sql +4 -0
- geovisio/migrations/20230711_01_JGSPB-inserted-at-index.sql +4 -0
- geovisio/migrations/20230720_01_EyQ0e-sequences-summary.rollback.sql +7 -0
- geovisio/migrations/20230720_01_EyQ0e-sequences-summary.sql +26 -0
- geovisio/migrations/20230803_01_aXusm-fix-sequence-computed.rollback.sql +4 -0
- geovisio/migrations/20230803_01_aXusm-fix-sequence-computed.sql +25 -0
- geovisio/migrations/20231018_01_4G3YE-pictures-exiv2.rollback.sql +165 -0
- geovisio/migrations/20231018_01_4G3YE-pictures-exiv2.sql +206 -0
- geovisio/migrations/20231103_01_ZVKEm-update-seq-on-pic-change.rollback.sql +7 -0
- geovisio/migrations/20231103_01_ZVKEm-update-seq-on-pic-change.sql +28 -0
- geovisio/migrations/20231110_01_3p070-jobs-error.rollback.sql +7 -0
- geovisio/migrations/20231110_01_3p070-jobs-error.sql +93 -0
- geovisio/migrations/20231121_01_v6oBF-more-specific-triggers.rollback.sql +36 -0
- geovisio/migrations/20231121_01_v6oBF-more-specific-triggers.sql +40 -0
- geovisio/migrations/20231121_02_1uZXT-deleted-tag.rollback.sql +19 -0
- geovisio/migrations/20231121_02_1uZXT-deleted-tag.sql +13 -0
- geovisio/migrations/20240115_01_FatLR-token-delete-cascade.rollback.sql +6 -0
- geovisio/migrations/20240115_01_FatLR-token-delete-cascade.sql +7 -0
- geovisio/migrations/20240220_01_9wZs0-sequence-current-sort.rollback.sql +6 -0
- geovisio/migrations/20240220_01_9wZs0-sequence-current-sort.sql +15 -0
- geovisio/migrations/20240223_01_LsMHB-remove-binary-fields.sql +80 -0
- geovisio/migrations/20240226_01_8iXl1-track-changes.rollback.sql +13 -0
- geovisio/migrations/20240226_01_8iXl1-track-changes.sql +123 -0
- geovisio/migrations/20240229_01_SgfQY-sequence-geom-multi-linestring.rollback.sql +67 -0
- geovisio/migrations/20240229_01_SgfQY-sequence-geom-multi-linestring.sql +101 -0
- geovisio/migrations/20240308_01_aF0Jb-migrate-sequence-geom-multi-linestring.sql +52 -0
- geovisio/migrations/20240409_01_jnhra-pictures-grid.rollback.sql +3 -0
- geovisio/migrations/20240409_01_jnhra-pictures-grid.sql +21 -0
- geovisio/migrations/20240416_01_FpyGs-pictures-stats-on-sequences.rollback.sql +7 -0
- geovisio/migrations/20240416_01_FpyGs-pictures-stats-on-sequences.sql +9 -0
- geovisio/migrations/20240416_02_A5KzC-fill-pictures-stats-on-sequences.rollback.sql +5 -0
- geovisio/migrations/20240416_02_A5KzC-fill-pictures-stats-on-sequences.sql +84 -0
- geovisio/migrations/20240507_01_eBfqZ-refresh-table.rollback.sql +5 -0
- geovisio/migrations/20240507_01_eBfqZ-refresh-table.sql +9 -0
- geovisio/migrations/20240507_02_dzVET-picture-grid-public.rollback.sql +21 -0
- geovisio/migrations/20240507_02_dzVET-picture-grid-public.sql +26 -0
- geovisio/migrations/20240514_01_IT7DD-picture-delete-cascade.rollback.sql +41 -0
- geovisio/migrations/20240514_01_IT7DD-picture-delete-cascade.sql +42 -0
- geovisio/migrations/20240611_01_jftHn-content-md5.rollback.sql +5 -0
- geovisio/migrations/20240611_01_jftHn-content-md5.sql +7 -0
- geovisio/migrations/20240612_01_yNcuE-upload-set.rollback.sql +6 -0
- geovisio/migrations/20240612_01_yNcuE-upload-set.sql +46 -0
- geovisio/migrations/20240617_01_tKtlx-md5-concurrent-index.rollback.sql +4 -0
- geovisio/migrations/20240617_01_tKtlx-md5-concurrent-index.sql +8 -0
- geovisio/migrations/20240625_01_XMZ24-fix-sequence-stat-on-pic-insertion.rollback.sql +30 -0
- geovisio/migrations/20240625_01_XMZ24-fix-sequence-stat-on-pic-insertion.sql +31 -0
- geovisio/migrations/20240708_01_Xn7IH-job-queue.rollback.sql +31 -0
- geovisio/migrations/20240708_01_Xn7IH-job-queue.sql +104 -0
- geovisio/migrations/20240715_01_Hca9V-upload-set-metadata.rollback.sql +4 -0
- geovisio/migrations/20240715_01_Hca9V-upload-set-metadata.sql +5 -0
- geovisio/migrations/20240723_01_ePGFe-upload-set-files.rollback.sql +7 -0
- geovisio/migrations/20240723_01_ePGFe-upload-set-files.sql +29 -0
- geovisio/migrations/20240729_01_HALjj-upload-set-sort.rollback.sql +18 -0
- geovisio/migrations/20240729_01_HALjj-upload-set-sort.sql +18 -0
- geovisio/migrations/20240730_01_2BaCy-improve-deletion-triggers.rollback.sql +34 -0
- geovisio/migrations/20240730_01_2BaCy-improve-deletion-triggers.sql +53 -0
- geovisio/migrations/20240730_02_aRymN-rejection-status.rollback.sql +10 -0
- geovisio/migrations/20240730_02_aRymN-rejection-status.sql +16 -0
- geovisio/migrations/20240801_01_DOqmf-reports.rollback.sql +11 -0
- geovisio/migrations/20240801_01_DOqmf-reports.sql +100 -0
- geovisio/migrations/20240801_01_uKqPo-remove-files-delete-cascade.rollback.sql +13 -0
- geovisio/migrations/20240801_01_uKqPo-remove-files-delete-cascade.sql +13 -0
- geovisio/migrations/20240813_01_T1XkO-sequences-geom-splits.rollback.sql +42 -0
- geovisio/migrations/20240813_01_T1XkO-sequences-geom-splits.sql +56 -0
- geovisio/migrations/20240820_01_aB2ZK-exclusion-zones.rollback.sql +6 -0
- geovisio/migrations/20240820_01_aB2ZK-exclusion-zones.sql +49 -0
- geovisio/migrations/20240902_01_MDqSj-user-agent.rollback.sql +5 -0
- geovisio/migrations/20240902_01_MDqSj-user-agent.sql +10 -0
- geovisio/migrations/20240904_01_gFjlV-files-rejection-msg.rollback.sql +4 -0
- geovisio/migrations/20240904_01_gFjlV-files-rejection-msg.sql +4 -0
- geovisio/migrations/20240905_01_C8F6U-conflicts.rollback.sql +13 -0
- geovisio/migrations/20240905_01_C8F6U-conflicts.sql +6 -0
- geovisio/migrations/20240905_01_E5Ki0-upload-set-delete.rollback.sql +5 -0
- geovisio/migrations/20240905_01_E5Ki0-upload-set-delete.sql +30 -0
- geovisio/migrations/20240909_01_Muc22-unique-grid-index.rollback.sql +4 -0
- geovisio/migrations/20240909_01_Muc22-unique-grid-index.sql +5 -0
- geovisio/migrations/20240912_01_dAALm-account-index.rollback.sql +4 -0
- geovisio/migrations/20240912_01_dAALm-account-index.sql +4 -0
- geovisio/migrations/20241004_01_d1zfe-pictures-grid-360.rollback.sql +22 -0
- geovisio/migrations/20241004_01_d1zfe-pictures-grid-360.sql +24 -0
- geovisio/migrations/20241011_01_e1j5C-pic-quality.rollback.sql +21 -0
- geovisio/migrations/20241011_01_e1j5C-pic-quality.sql +249 -0
- geovisio/migrations/20241017_01_GuOjF-pic-quality-update.rollback.sql +4 -0
- geovisio/migrations/20241017_01_GuOjF-pic-quality-update.sql +61 -0
- geovisio/migrations/20241017_01_RiFlm-pictures-to-delete.rollback.sql +43 -0
- geovisio/migrations/20241017_01_RiFlm-pictures-to-delete.sql +75 -0
- geovisio/migrations/20241104_01_yhRVu-rejection-details.rollback.sql +4 -0
- geovisio/migrations/20241104_01_yhRVu-rejection-details.sql +4 -0
- geovisio/migrations/20241128_01_ugthx-job-queue-args.rollback.sql +25 -0
- geovisio/migrations/20241128_01_ugthx-job-queue-args.sql +31 -0
- geovisio/migrations/20241224_01_xuN6n-delete-upload-set-on-last-picture-trg-statement.rollback.sql +27 -0
- geovisio/migrations/20241224_01_xuN6n-delete-upload-set-on-last-picture-trg-statement.sql +28 -0
- geovisio/migrations/20250102_01_EElhA-rm-cameras.rollback.sql +126 -0
- geovisio/migrations/20250102_01_EElhA-rm-cameras.sql +30 -0
- geovisio/migrations/20250107_01_EQN9v-tags-tables.rollback.sql +10 -0
- geovisio/migrations/20250107_01_EQN9v-tags-tables.sql +58 -0
- geovisio/migrations/20250109_01_4OOP4-pages.rollback.sql +4 -0
- geovisio/migrations/20250109_01_4OOP4-pages.sql +10 -0
- geovisio/migrations/20250114_01_ABaaL-collaborative-metadata-editing.rollback.sql +7 -0
- geovisio/migrations/20250114_01_ABaaL-collaborative-metadata-editing.sql +16 -0
- geovisio/migrations/20250123_01_Ececu-tos-acceptance.rollback.sql +5 -0
- geovisio/migrations/20250123_01_Ececu-tos-acceptance.sql +5 -0
- geovisio/migrations/20250206_01_PjrEL-annotation-semantics.rollback.sql +14 -0
- geovisio/migrations/20250206_01_PjrEL-annotation-semantics.sql +15 -0
- geovisio/migrations/20250306_01_58oju-semantics-views.rollback.sql +5 -0
- geovisio/migrations/20250306_01_58oju-semantics-views.sql +52 -0
- geovisio/migrations/20250318_01_pANl1-semantics-functions.rollback.sql +5 -0
- geovisio/migrations/20250318_01_pANl1-semantics-functions.sql +41 -0
- geovisio/migrations/20250331_01_kRKjo-store-detections-id.rollback.sql +7 -0
- geovisio/migrations/20250331_01_kRKjo-store-detections-id.sql +25 -0
- geovisio/migrations/20250424_01_RBGXC-semantics-indexes.rollback.sql +6 -0
- geovisio/migrations/20250424_01_RBGXC-semantics-indexes.sql +6 -0
- geovisio/migrations/20250502_01_ZNmkU-job-task-read-metadata.rollback.sql +24 -0
- geovisio/migrations/20250502_01_ZNmkU-job-task-read-metadata.sql +7 -0
- geovisio/migrations/20250509_01_kMatW-deactivate-upload-set-split-dedup.rollback.sql +12 -0
- geovisio/migrations/20250509_01_kMatW-deactivate-upload-set-split-dedup.sql +12 -0
- geovisio/migrations/20250509_01_s3hYk-semantic-delete-cascade.rollback.sql +11 -0
- geovisio/migrations/20250509_01_s3hYk-semantic-delete-cascade.sql +11 -0
- geovisio/migrations/20250513_01_8WkZC-upload-sets-default-config.rollback.sql +7 -0
- geovisio/migrations/20250513_01_8WkZC-upload-sets-default-config.sql +15 -0
- geovisio/migrations/20250523_01_b11eW-picture-update-index.rollback.sql +85 -0
- geovisio/migrations/20250523_01_b11eW-picture-update-index.sql +166 -0
- geovisio/migrations/20250523_02_5ZXXh-update-picture-updated-at.rollback.sql +4 -0
- geovisio/migrations/20250523_02_5ZXXh-update-picture-updated-at.sql +7 -0
- geovisio/migrations/20250624_01_SETp6-job-warnings.rollback.sql +4 -0
- geovisio/migrations/20250624_01_SETp6-job-warnings.sql +4 -0
- geovisio/migrations/20250701_01_kr371-upload-set-semantics.rollback.sql +6 -0
- geovisio/migrations/20250701_01_kr371-upload-set-semantics.sql +15 -0
- geovisio/migrations/20250703_01_p2WVV-sequence-upload-set-link.rollback.sql +4 -0
- geovisio/migrations/20250703_01_p2WVV-sequence-upload-set-link.sql +4 -0
- geovisio/migrations/20250703_02_q0s3D-sequence-upload-set-fill.rollback.sql +4 -0
- geovisio/migrations/20250703_02_q0s3D-sequence-upload-set-fill.sql +26 -0
- geovisio/migrations/20250716_01_f8tcJ-check-empty-annotation-late.rollback.sql +24 -0
- geovisio/migrations/20250716_01_f8tcJ-check-empty-annotation-late.sql +10 -0
- geovisio/migrations/20250728_01_2zgur-upload-set-relative-heading.rollback.sql +4 -0
- geovisio/migrations/20250728_01_2zgur-upload-set-relative-heading.sql +4 -0
- geovisio/migrations/20250825_01_nCgkN-file-deletion-after-delete-no-dup.rollback.sql +20 -0
- geovisio/migrations/20250825_01_nCgkN-file-deletion-after-delete-no-dup.sql +26 -0
- geovisio/migrations/20250902_01_k5UTq-visibility-status.rollback.sql +11 -0
- geovisio/migrations/20250902_01_k5UTq-visibility-status.sql +19 -0
- geovisio/migrations/20250904_01_3uVKX-visibility-functions.rollback.sql +20 -0
- geovisio/migrations/20250904_01_3uVKX-visibility-functions.sql +43 -0
- geovisio/migrations/20251110_01_bWEXo-report-visibility.rollback.sql +30 -0
- geovisio/migrations/20251110_01_bWEXo-report-visibility.sql +30 -0
- geovisio/migrations/20251124_01_0xdqi-page-updates.rollback.sql +6 -0
- geovisio/migrations/20251124_01_0xdqi-page-updates.sql +8 -0
- geovisio/migrations/20251127_01_S6Ci8-pictures-grid-visibility.rollback.sql +24 -0
- geovisio/migrations/20251127_01_S6Ci8-pictures-grid-visibility.sql +36 -0
- geovisio/migrations/20251202_01_Q3g59-pictures-grid-logged-only.rollback.sql +32 -0
- geovisio/migrations/20251202_01_Q3g59-pictures-grid-logged-only.sql +34 -0
- geovisio/templates/main.html +7 -58
- geovisio/templates/viewer.html +8 -15
- geovisio/translations/cy/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/cy/LC_MESSAGES/messages.po +839 -0
- geovisio/translations/en/LC_MESSAGES/messages.mo +0 -0
- geovisio/translations/en/LC_MESSAGES/messages.po +155 -149
- geovisio/translations/messages.pot +131 -150
- geovisio/utils/cql2.py +22 -17
- geovisio/utils/link.py +13 -3
- geovisio/utils/upload_set.py +42 -2
- geovisio/web/collections.py +80 -40
- geovisio/web/docs.py +1 -1
- geovisio/web/items.py +54 -4
- geovisio/web/upload_set.py +2 -3
- geovisio/web/users.py +1 -1
- {geovisio-2.11.0.dist-info → geovisio-2.12.0.dist-info}/METADATA +6 -26
- geovisio-2.12.0.dist-info/RECORD +305 -0
- geovisio-2.12.0.dist-info/entry_points.txt +3 -0
- geovisio-2.11.0.dist-info/RECORD +0 -117
- {geovisio-2.11.0.dist-info → geovisio-2.12.0.dist-info}/WHEEL +0 -0
- {geovisio-2.11.0.dist-info → geovisio-2.12.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
-- pic_quality
|
|
2
|
+
-- depends: 20240912_01_dAALm-account-index
|
|
3
|
+
|
|
4
|
+
-- NOTE : this migration is split in two files
|
|
5
|
+
-- this one, and next migration pic_quality_update
|
|
6
|
+
-- this migration is transactional, the next one is not
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
-- Add common cameras focal length (for FOV + HPixelDensity)
|
|
10
|
+
-- NOTE : cameras / sensor_data is not used anymore in versions > 2.7.1
|
|
11
|
+
-- For updating cameras metadata, please see GeoPicture Tag Reader repository
|
|
12
|
+
INSERT INTO cameras(model, sensor_width) VALUES
|
|
13
|
+
('samsung SM-A336B', 6.4),
|
|
14
|
+
('Viofo A119 Mini 2', 5.2),
|
|
15
|
+
('GoPro HERO9 Black', 6.17),
|
|
16
|
+
('GoPro HERO5 Black', 6.17),
|
|
17
|
+
('HUAWEI EML-L29', 6.29),
|
|
18
|
+
('Panasonic DC-LX100M2', 17.3),
|
|
19
|
+
('SONY FDR-X1000V', 6.19),
|
|
20
|
+
('GoPro Max', 6.17),
|
|
21
|
+
('samsung SM-G950F',7.05),
|
|
22
|
+
('GoPro HERO7 Black',6.17),
|
|
23
|
+
('Xiaomi M2101K6G',8.4),
|
|
24
|
+
('HUAWEI VOG-L29',7.3),
|
|
25
|
+
('samsung SM-A546B', 12),
|
|
26
|
+
('GoPro HERO10 Black', 6.17),
|
|
27
|
+
('Xiaomi 2107113SG',7.4),
|
|
28
|
+
('Xiaomi Redmi Note 9 Pro',7.2),
|
|
29
|
+
('GoPro HERO11 Black', 6.74),
|
|
30
|
+
('OnePlus A5000', 5.22),
|
|
31
|
+
('samsung SM-A500FU', 4.69),
|
|
32
|
+
('XIAOYI YDXJ 2', 10.2),
|
|
33
|
+
('Apple iPhone 6s', 4.8),
|
|
34
|
+
('samsung SM-G901F', 5.95),
|
|
35
|
+
('OnePlus A3003', 6.4),
|
|
36
|
+
('GoPro HERO8 Black', 6.17),
|
|
37
|
+
('XIAOYI YDXJ 1', 10.2);
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
-- Complete FOV for imported pictures
|
|
41
|
+
CREATE OR REPLACE FUNCTION missing_fov(metadata JSONB) RETURNS JSONB AS $$
|
|
42
|
+
DECLARE
|
|
43
|
+
sensor_width FLOAT;
|
|
44
|
+
BEGIN
|
|
45
|
+
IF metadata->>'field_of_view' IS NOT NULL OR metadata->>'focal_length' IS NULL OR (metadata->>'focal_length')::float = 0 THEN
|
|
46
|
+
RETURN metadata;
|
|
47
|
+
END IF;
|
|
48
|
+
|
|
49
|
+
-- Find appropriate sensor width
|
|
50
|
+
IF metadata->>'make' = 'samsung' AND metadata->>'model' = 'SM-A336B' THEN
|
|
51
|
+
sensor_width := 6.4;
|
|
52
|
+
ELSIF metadata->>'make' = 'Viofo' AND metadata->>'model' = 'A119 Mini 2' THEN
|
|
53
|
+
sensor_width := 5.2;
|
|
54
|
+
ELSIF metadata->>'make' = 'GoPro' AND metadata->>'model' = 'HERO9 Black' THEN
|
|
55
|
+
sensor_width := 6.17;
|
|
56
|
+
ELSIF metadata->>'make' = 'GoPro' AND metadata->>'model' = 'HERO5 Black' THEN
|
|
57
|
+
sensor_width := 6.17;
|
|
58
|
+
ELSIF metadata->>'make' = 'HUAWEI' AND metadata->>'model' = 'EML-L29' THEN
|
|
59
|
+
sensor_width := 6.29;
|
|
60
|
+
ELSIF metadata->>'make' = 'Panasonic' AND metadata->>'model' = 'DC-LX100M2' THEN
|
|
61
|
+
sensor_width := 17.3;
|
|
62
|
+
ELSIF metadata->>'make' = 'SONY' AND metadata->>'model' = 'FDR-X1000V' THEN
|
|
63
|
+
sensor_width := 6.19;
|
|
64
|
+
ELSIF metadata->>'make' = 'GoPro' AND metadata->>'model' = 'Max' THEN
|
|
65
|
+
sensor_width := 6.17;
|
|
66
|
+
ELSIF metadata->>'make' = 'samsung' AND metadata->>'model' = 'SM-G950F' THEN
|
|
67
|
+
sensor_width := 7.05;
|
|
68
|
+
ELSIF metadata->>'make' = 'GoPro' AND metadata->>'model' = 'HERO7 Black' THEN
|
|
69
|
+
sensor_width := 6.17;
|
|
70
|
+
ELSIF metadata->>'make' = 'Xiaomi' AND metadata->>'model' = 'M2101K6G' THEN
|
|
71
|
+
sensor_width := 8.4;
|
|
72
|
+
ELSIF metadata->>'make' = 'HUAWEI' AND metadata->>'model' = 'VOG-L29' THEN
|
|
73
|
+
sensor_width := 7.3;
|
|
74
|
+
ELSIF metadata->>'make' = 'samsung' AND metadata->>'model' = 'SM-A546B' THEN
|
|
75
|
+
sensor_width := 12;
|
|
76
|
+
ELSIF metadata->>'make' = 'GoPro' AND metadata->>'model' = 'HERO10 Black' THEN
|
|
77
|
+
sensor_width := 6.17;
|
|
78
|
+
ELSIF metadata->>'make' = 'Xiaomi' AND metadata->>'model' = '2107113SG' THEN
|
|
79
|
+
sensor_width := 7.4;
|
|
80
|
+
ELSIF metadata->>'make' = 'Xiaomi' AND metadata->>'model' = 'Redmi Note 9 Pro' THEN
|
|
81
|
+
sensor_width := 7.2;
|
|
82
|
+
ELSIF metadata->>'make' = 'GoPro' AND metadata->>'model' = 'HERO11 Black' THEN
|
|
83
|
+
sensor_width := 6.74;
|
|
84
|
+
ELSIF metadata->>'make' = 'OnePlus' AND metadata->>'model' = 'A5000' THEN
|
|
85
|
+
sensor_width := 5.22;
|
|
86
|
+
ELSIF metadata->>'make' = 'samsung' AND metadata->>'model' = 'SM-A500FU' THEN
|
|
87
|
+
sensor_width := 4.69;
|
|
88
|
+
ELSIF metadata->>'make' = 'XIAOYI' AND metadata->>'model' = 'YDXJ 2' THEN
|
|
89
|
+
sensor_width := 10.2;
|
|
90
|
+
ELSIF metadata->>'make' = 'Apple' AND metadata->>'model' = 'iPhone 6s' THEN
|
|
91
|
+
sensor_width := 4.8;
|
|
92
|
+
ELSIF metadata->>'make' = 'samsung' AND metadata->>'model' = 'SM-G901F' THEN
|
|
93
|
+
sensor_width := 5.95;
|
|
94
|
+
ELSIF metadata->>'make' = 'OnePlus' AND metadata->>'model' = 'A3003' THEN
|
|
95
|
+
sensor_width := 6.4;
|
|
96
|
+
ELSIF metadata->>'make' = 'GoPro' AND metadata->>'model' = 'HERO8 Black' THEN
|
|
97
|
+
sensor_width := 6.17;
|
|
98
|
+
ELSIF metadata->>'make' = 'XIAOYI' AND metadata->>'model' = 'YDXJ 1' THEN
|
|
99
|
+
sensor_width := 10.2;
|
|
100
|
+
-- If not found, just send back original metadata
|
|
101
|
+
ELSE
|
|
102
|
+
RETURN metadata;
|
|
103
|
+
END IF;
|
|
104
|
+
|
|
105
|
+
-- Update metadata
|
|
106
|
+
RETURN jsonb_set(
|
|
107
|
+
metadata,
|
|
108
|
+
'{field_of_view}'::text[],
|
|
109
|
+
((ROUND(DEGREES(2 * ATAN(sensor_width / (2 * (metadata->>'focal_length')::float)))))::varchar)::jsonb
|
|
110
|
+
);
|
|
111
|
+
END;
|
|
112
|
+
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
-- Read float values from EXIF
|
|
116
|
+
CREATE OR REPLACE FUNCTION get_float(val VARCHAR) RETURNS FLOAT AS $$
|
|
117
|
+
DECLARE
|
|
118
|
+
list VARCHAR[];
|
|
119
|
+
BEGIN
|
|
120
|
+
IF val ~ '^\d+\/\d+$' THEN
|
|
121
|
+
list := regexp_split_to_array(val, '/');
|
|
122
|
+
RETURN list[1]::float / list[2]::float;
|
|
123
|
+
ELSIF val ~ '^\d+(\.\d+)?$' THEN
|
|
124
|
+
RETURN val::float;
|
|
125
|
+
ELSE
|
|
126
|
+
RETURN NULL;
|
|
127
|
+
END IF;
|
|
128
|
+
END;
|
|
129
|
+
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
-- GPS accuracy function
|
|
133
|
+
CREATE OR REPLACE FUNCTION gps_accuracy(metadata JSONB, exif JSONB) RETURNS FLOAT AS $$
|
|
134
|
+
DECLARE
|
|
135
|
+
gps_dop FLOAT;
|
|
136
|
+
gps_diff INT;
|
|
137
|
+
gps_hpos_err FLOAT;
|
|
138
|
+
BEGIN
|
|
139
|
+
-- Parse GPS DOP, either float or fraction
|
|
140
|
+
gps_dop := get_float(COALESCE(exif->>'Exif.GPSInfo.GPSDOP', exif->>'Xmp.exif.GPSDOP', ''));
|
|
141
|
+
gps_diff := (COALESCE(exif->>'Exif.GPSInfo.GPSDifferential', exif->>'Xmp.exif.GPSDifferential'))::INT;
|
|
142
|
+
gps_hpos_err := get_float(COALESCE(exif->>'Exif.GPSInfo.GPSHPositioningError', exif->>'Xmp.exif.GPSHPositioningError'));
|
|
143
|
+
|
|
144
|
+
-- Direct horizontal positioning error in meters -> return as is
|
|
145
|
+
IF gps_hpos_err IS NOT NULL AND gps_hpos_err > 0 THEN
|
|
146
|
+
RETURN gps_hpos_err;
|
|
147
|
+
|
|
148
|
+
-- GPS DOP available
|
|
149
|
+
ELSIF gps_dop IS NOT NULL AND gps_dop > 0 THEN
|
|
150
|
+
-- With a DGPS -> consider GPS nominal error as 1 meter
|
|
151
|
+
IF gps_diff = 1 THEN
|
|
152
|
+
RETURN gps_dop;
|
|
153
|
+
|
|
154
|
+
-- Without DGPS -> consider GPS nominal error as 3 meters in average
|
|
155
|
+
ELSE
|
|
156
|
+
RETURN 3 * gps_dop;
|
|
157
|
+
END IF;
|
|
158
|
+
|
|
159
|
+
-- DGPS -> return 2 meters precision
|
|
160
|
+
ELSIF gps_diff = 1 THEN
|
|
161
|
+
RETURN 2;
|
|
162
|
+
|
|
163
|
+
-- Approximate guesses based on model
|
|
164
|
+
ELSIF metadata->>'make' IS NOT NULL OR metadata->>'model' IS NOT NULL THEN
|
|
165
|
+
-- Good non-diff GPS devices (best case is 3m, so setting 4 for tolerance)
|
|
166
|
+
IF lower(metadata->>'make') IN (
|
|
167
|
+
'gopro', 'insta360', 'garmin', 'viofo', 'xiaoyi', 'blackvue', 'tectectec',
|
|
168
|
+
'arashi vision'
|
|
169
|
+
)
|
|
170
|
+
OR metadata->>'model' IN ('LG-R105', 'FDR-X1000V')
|
|
171
|
+
OR metadata->>'make' ILIKE '%xiaoyi%' THEN
|
|
172
|
+
RETURN 4;
|
|
173
|
+
|
|
174
|
+
-- Diff GPS devices
|
|
175
|
+
ELSIF lower(metadata->>'make') IN ('stfmani', 'trimble', 'imajing')
|
|
176
|
+
OR metadata->>'model' IN ('LB5') OR metadata->>'model' ILIKE '%ladybug%' THEN
|
|
177
|
+
RETURN 2;
|
|
178
|
+
|
|
179
|
+
-- Smartphones or not-so-good non-diff GPS devices
|
|
180
|
+
ELSIF lower(metadata->>'make') IN (
|
|
181
|
+
'samsung', 'xiaomi', 'huawei', 'ricoh', 'lenovo', 'motorola', 'oneplus',
|
|
182
|
+
'apple', 'google', 'sony', 'wiko', 'asus', 'cubot', 'lge', 'fairphone',
|
|
183
|
+
'realme', 'symphony', 'crosscall', 'htc', 'homtom', 'hmd global', 'oppo',
|
|
184
|
+
'ulefone'
|
|
185
|
+
) THEN
|
|
186
|
+
RETURN 5;
|
|
187
|
+
|
|
188
|
+
-- Fallback for unknown make/model
|
|
189
|
+
ELSE
|
|
190
|
+
RETURN NULL;
|
|
191
|
+
END IF;
|
|
192
|
+
-- Fallback : no value
|
|
193
|
+
ELSE
|
|
194
|
+
RETURN NULL;
|
|
195
|
+
END IF;
|
|
196
|
+
END;
|
|
197
|
+
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
-- Number of pixels on horizon per FOV degree
|
|
201
|
+
CREATE OR REPLACE FUNCTION h_pixel_density(metadata JSONB) RETURNS INT AS $$
|
|
202
|
+
BEGIN
|
|
203
|
+
RETURN round((metadata->>'width')::float / (metadata->>'field_of_view')::float);
|
|
204
|
+
END;
|
|
205
|
+
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
-- Clean-up EXIF, as we're here to rewrite all pictures...
|
|
209
|
+
-- Removes all proprietary keys ending in .0x1234 (hex key)
|
|
210
|
+
CREATE OR REPLACE FUNCTION clean_exif(exif JSONB) RETURNS JSONB AS $$
|
|
211
|
+
SELECT jsonb_object_agg(key, value)
|
|
212
|
+
FROM (
|
|
213
|
+
SELECT key, value
|
|
214
|
+
FROM jsonb_each(exif)
|
|
215
|
+
WHERE key !~ '\.0x[0-9a-fA-F]+$'
|
|
216
|
+
) a;
|
|
217
|
+
$$ LANGUAGE sql IMMUTABLE PARALLEL SAFE;
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
-- Add columns for pictures
|
|
221
|
+
ALTER TABLE pictures
|
|
222
|
+
ADD COLUMN gps_accuracy_m FLOAT,
|
|
223
|
+
ADD COLUMN h_pixel_density INT;
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
-- Auto-insert pixel density/GPS accuracy for new pictures
|
|
227
|
+
CREATE OR REPLACE FUNCTION pictures_hpixdens_gpsacc() RETURNS TRIGGER AS $$
|
|
228
|
+
BEGIN
|
|
229
|
+
NEW.h_pixel_density := h_pixel_density(NEW.metadata);
|
|
230
|
+
NEW.gps_accuracy_m := gps_accuracy(NEW.metadata, NEW.exif);
|
|
231
|
+
RETURN NEW;
|
|
232
|
+
END;
|
|
233
|
+
$$ LANGUAGE plpgsql;
|
|
234
|
+
|
|
235
|
+
CREATE TRIGGER trg_pictures_hpixdens_gpsacc
|
|
236
|
+
BEFORE INSERT ON pictures
|
|
237
|
+
FOR EACH ROW
|
|
238
|
+
EXECUTE FUNCTION pictures_hpixdens_gpsacc();
|
|
239
|
+
|
|
240
|
+
CREATE TRIGGER trg_pictures_hpixdens_gpsacc_upd
|
|
241
|
+
BEFORE UPDATE OF metadata, exif ON pictures
|
|
242
|
+
FOR EACH ROW
|
|
243
|
+
EXECUTE FUNCTION pictures_hpixdens_gpsacc();
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
-- Add info on sequences too
|
|
247
|
+
ALTER TABLE sequences
|
|
248
|
+
ADD COLUMN computed_h_pixel_density INT,
|
|
249
|
+
ADD COLUMN computed_gps_accuracy FLOAT;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
-- pic_quality_update
|
|
2
|
+
-- depends: 20241011_01_e1j5C-pic-quality
|
|
3
|
+
-- transactional: false
|
|
4
|
+
|
|
5
|
+
-- Update pictures in batch
|
|
6
|
+
CREATE OR REPLACE PROCEDURE update_pictures_gps_accuracy() AS $$
|
|
7
|
+
DECLARE
|
|
8
|
+
last_inserted_at TIMESTAMPTZ;
|
|
9
|
+
BEGIN
|
|
10
|
+
SELECT min(inserted_at) - INTERVAL '1 minute' FROM pictures INTO last_inserted_at;
|
|
11
|
+
|
|
12
|
+
WHILE last_inserted_at IS NOT NULL LOOP
|
|
13
|
+
|
|
14
|
+
WITH
|
|
15
|
+
-- get a batch of 100 000 pictures to update
|
|
16
|
+
pic_to_update AS (
|
|
17
|
+
SELECT id, inserted_at from pictures where inserted_at > last_inserted_at ORDER BY inserted_at ASC LIMIT 100000
|
|
18
|
+
)
|
|
19
|
+
, updated_pic AS (
|
|
20
|
+
UPDATE pictures
|
|
21
|
+
SET gps_accuracy_m = gps_accuracy(metadata, exif),
|
|
22
|
+
h_pixel_density = h_pixel_density(missing_fov(metadata)),
|
|
23
|
+
exif = clean_exif(exif),
|
|
24
|
+
metadata = missing_fov(metadata)
|
|
25
|
+
WHERE id in (SELECT id FROM pic_to_update)
|
|
26
|
+
)
|
|
27
|
+
SELECT MAX(inserted_at) FROM pic_to_update INTO last_inserted_at;
|
|
28
|
+
|
|
29
|
+
RAISE NOTICE 'max insertion date is now %', last_inserted_at;
|
|
30
|
+
|
|
31
|
+
-- commit transaction (as a procedure is in an implicit transaction, it will start a new transaction after this)
|
|
32
|
+
COMMIT;
|
|
33
|
+
|
|
34
|
+
END LOOP;
|
|
35
|
+
RAISE NOTICE 'update finished';
|
|
36
|
+
END
|
|
37
|
+
$$ LANGUAGE plpgsql;
|
|
38
|
+
|
|
39
|
+
-- Perform pictures update
|
|
40
|
+
SET session_replication_role = replica;
|
|
41
|
+
CALL update_pictures_gps_accuracy();
|
|
42
|
+
|
|
43
|
+
-- Update sequences as well
|
|
44
|
+
UPDATE sequences s
|
|
45
|
+
SET
|
|
46
|
+
computed_h_pixel_density = CASE WHEN array_length(reshpd, 1) = 1 THEN reshpd[1] ELSE NULL END,
|
|
47
|
+
computed_gps_accuracy = gpsacc
|
|
48
|
+
FROM (
|
|
49
|
+
SELECT
|
|
50
|
+
sp.seq_id,
|
|
51
|
+
ARRAY_AGG(DISTINCT p.h_pixel_density) AS reshpd,
|
|
52
|
+
PERCENTILE_CONT(0.9) WITHIN GROUP(ORDER BY p.gps_accuracy_m) AS gpsacc
|
|
53
|
+
FROM sequences_pictures sp
|
|
54
|
+
JOIN pictures p ON sp.pic_id = p.id
|
|
55
|
+
GROUP BY sp.seq_id
|
|
56
|
+
) p
|
|
57
|
+
WHERE s.id = p.seq_id;
|
|
58
|
+
|
|
59
|
+
-- Put back triggers && cleanup
|
|
60
|
+
SET session_replication_role = DEFAULT;
|
|
61
|
+
DROP PROCEDURE update_pictures_gps_accuracy;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
-- pictures_to_delete
|
|
2
|
+
-- depends: 20240912_01_dAALm-account-index
|
|
3
|
+
|
|
4
|
+
ALTER TABLE job_queue DROP COLUMN IF EXISTS picture_to_delete_id;
|
|
5
|
+
-- ALTER TABLE job_queue DROP CONSTRAINT one_external_id;
|
|
6
|
+
ALTER TABLE job_queue ADD CONSTRAINT one_external_id CHECK (num_nonnulls(picture_id, upload_set_id, sequence_id) = 1);
|
|
7
|
+
|
|
8
|
+
ALTER TABLE job_history DROP COLUMN IF EXISTS picture_to_delete_id;
|
|
9
|
+
-- ALTER TABLE job_history DROP CONSTRAINT one_external_id;
|
|
10
|
+
ALTER TABLE job_history ADD CONSTRAINT one_external_id CHECK (num_nonnulls(picture_id, upload_set_id, sequence_id) = 1);
|
|
11
|
+
|
|
12
|
+
ALTER TABLE pictures
|
|
13
|
+
DROP CONSTRAINT upload_set_fk_id ,
|
|
14
|
+
ADD CONSTRAINT upload_set_fk_id FOREIGN KEY (upload_set_id) REFERENCES upload_sets(id);
|
|
15
|
+
|
|
16
|
+
ALTER TABLE upload_sets
|
|
17
|
+
ADD COLUMN IF NOT EXISTS deleted BOOLEAN DEFAULT FALSE;
|
|
18
|
+
|
|
19
|
+
DROP TRIGGER ask_for_all_file_deletion_after_delete_trg ON pictures;
|
|
20
|
+
|
|
21
|
+
DROP FUNCTION IF EXISTS delete_upload_set_on_last_picture CASCADE;
|
|
22
|
+
CREATE FUNCTION delete_upload_set_on_last_picture() RETURNS trigger AS $$
|
|
23
|
+
BEGIN
|
|
24
|
+
IF OLD.upload_set_id IS NOT NULL AND NOT EXISTS (
|
|
25
|
+
SELECT 1
|
|
26
|
+
FROM pictures
|
|
27
|
+
WHERE upload_set_id = OLD.upload_set_id AND id != OLD.id
|
|
28
|
+
AND status != 'waiting-for-delete'
|
|
29
|
+
LIMIT 1
|
|
30
|
+
) THEN
|
|
31
|
+
-- if it's the last picture of an upload set, we delete the upload set
|
|
32
|
+
UPDATE upload_sets SET deleted = true WHERE id = OLD.upload_set_id AND not deleted;
|
|
33
|
+
INSERT INTO job_queue AS j (upload_set_id, task) VALUES (OLD.upload_set_id, 'delete') ON CONFLICT (upload_set_id) DO UPDATE SET task = 'delete' WHERE j.task != 'delete';
|
|
34
|
+
END IF;
|
|
35
|
+
RETURN NULL;
|
|
36
|
+
|
|
37
|
+
END $$ LANGUAGE plpgsql;
|
|
38
|
+
|
|
39
|
+
CREATE TRIGGER delete_upload_set_on_last_picture_trg
|
|
40
|
+
AFTER DELETE ON pictures
|
|
41
|
+
REFERENCING OLD TABLE AS deleted_pictures
|
|
42
|
+
FOR EACH ROW -- a picture is always deleted by the workers, one by one, so it's useless to run this trigger for each statement
|
|
43
|
+
EXECUTE FUNCTION delete_upload_set_on_last_picture();
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
-- pictures_to_delete
|
|
2
|
+
-- depends: 20240912_01_dAALm-account-index
|
|
3
|
+
|
|
4
|
+
-- Add another column to the job_queue tabel to store the pictures to delete without using a foreign key,
|
|
5
|
+
-- so that we can keep them even after their deletion
|
|
6
|
+
ALTER TABLE job_queue DROP CONSTRAINT one_external_id;
|
|
7
|
+
|
|
8
|
+
ALTER TABLE job_queue ADD COLUMN IF NOT EXISTS picture_to_delete_id UUID;
|
|
9
|
+
|
|
10
|
+
ALTER TABLE job_queue ADD CONSTRAINT one_external_id CHECK (num_nonnulls(picture_id, upload_set_id, sequence_id, picture_to_delete_id) = 1);
|
|
11
|
+
|
|
12
|
+
ALTER TABLE job_history ADD COLUMN IF NOT EXISTS picture_to_delete_id UUID;
|
|
13
|
+
ALTER TABLE job_history DROP CONSTRAINT one_external_id;
|
|
14
|
+
ALTER TABLE job_history ADD CONSTRAINT one_external_id CHECK (num_nonnulls(picture_id, upload_set_id, sequence_id, picture_to_delete_id) = 1);
|
|
15
|
+
|
|
16
|
+
-- when an upload set is deleted, all it's pictures are added to the deletion queue, and deleted from the database (since there is a `ON DELETE CASCADE`)
|
|
17
|
+
|
|
18
|
+
ALTER TABLE pictures
|
|
19
|
+
DROP CONSTRAINT upload_set_fk_id ,
|
|
20
|
+
ADD CONSTRAINT upload_set_fk_id FOREIGN KEY (upload_set_id) REFERENCES upload_sets(id) ON DELETE CASCADE;
|
|
21
|
+
|
|
22
|
+
-- also update how the last picture trigger the deletion of its upload set
|
|
23
|
+
DROP FUNCTION IF EXISTS delete_upload_set_on_last_picture CASCADE;
|
|
24
|
+
CREATE FUNCTION delete_upload_set_on_last_picture() RETURNS trigger AS $$
|
|
25
|
+
BEGIN
|
|
26
|
+
IF OLD.upload_set_id IS NOT NULL AND NOT EXISTS (
|
|
27
|
+
SELECT 1
|
|
28
|
+
FROM pictures
|
|
29
|
+
WHERE upload_set_id = OLD.upload_set_id AND id != OLD.id
|
|
30
|
+
AND status != 'waiting-for-delete' -- Note: this status is deprecated, but we need to consider it anyway since it can remain some pictures with this status in the database for the migration
|
|
31
|
+
LIMIT 1
|
|
32
|
+
) THEN
|
|
33
|
+
-- if it's the last picture of an upload set, we delete the upload set
|
|
34
|
+
DELETE FROM upload_sets WHERE id = OLD.upload_set_id;
|
|
35
|
+
END IF;
|
|
36
|
+
RETURN NULL;
|
|
37
|
+
|
|
38
|
+
END $$ LANGUAGE plpgsql;
|
|
39
|
+
|
|
40
|
+
CREATE TRIGGER delete_upload_set_on_last_picture_trg
|
|
41
|
+
AFTER DELETE ON pictures
|
|
42
|
+
REFERENCING OLD TABLE AS deleted_pictures
|
|
43
|
+
FOR EACH ROW -- a picture is always deleted by the workers, one by one, so it's useless to run this trigger for each statement
|
|
44
|
+
EXECUTE FUNCTION delete_upload_set_on_last_picture();
|
|
45
|
+
|
|
46
|
+
-- Add to the new queue all pictures marked and waiting for delete and all pictures from deleted upload sets
|
|
47
|
+
INSERT INTO job_queue(picture_to_delete_id, task)
|
|
48
|
+
SELECT p.id, 'delete'
|
|
49
|
+
FROM pictures p
|
|
50
|
+
LEFT JOIN upload_sets us ON us.id = p.upload_set_id
|
|
51
|
+
WHERE p.status = 'waiting-for-delete' OR us.deleted;
|
|
52
|
+
|
|
53
|
+
-- cleanup all pictures/uploadset, as we don't need them anymore since they are added to the new deletion queue
|
|
54
|
+
DELETE FROM pictures WHERE status = 'waiting-for-delete';
|
|
55
|
+
DELETE FROM upload_sets WHERE deleted;
|
|
56
|
+
|
|
57
|
+
-- the deleted column of upload_sets is now useless, we can delete them right away
|
|
58
|
+
ALTER TABLE upload_sets DROP COLUMN deleted;
|
|
59
|
+
|
|
60
|
+
DROP FUNCTION IF EXISTS ask_for_all_file_deletion_after_delete;
|
|
61
|
+
CREATE FUNCTION ask_for_all_file_deletion_after_delete() RETURNS trigger AS $$
|
|
62
|
+
BEGIN
|
|
63
|
+
INSERT INTO job_queue(picture_to_delete_id, task)
|
|
64
|
+
SELECT p.id, 'delete' FROM deleted_pictures p;
|
|
65
|
+
RETURN NULL;
|
|
66
|
+
|
|
67
|
+
END $$ LANGUAGE plpgsql;
|
|
68
|
+
|
|
69
|
+
CREATE TRIGGER ask_for_all_file_deletion_after_delete_trg
|
|
70
|
+
AFTER DELETE ON pictures
|
|
71
|
+
REFERENCING OLD TABLE AS deleted_pictures
|
|
72
|
+
FOR EACH STATEMENT
|
|
73
|
+
EXECUTE FUNCTION ask_for_all_file_deletion_after_delete();
|
|
74
|
+
|
|
75
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- job_queue_args
|
|
2
|
+
-- depends: 20241017_01_GuOjF-pic-quality-update 20241104_01_yhRVu-rejection-details
|
|
3
|
+
|
|
4
|
+
ALTER TABLE job_queue DROP COLUMN args;
|
|
5
|
+
ALTER TABLE job_history DROP COLUMN args;
|
|
6
|
+
|
|
7
|
+
-- put trigger as old value
|
|
8
|
+
DROP TRIGGER trigger_process_picture_insertion ON pictures;
|
|
9
|
+
|
|
10
|
+
CREATE OR REPLACE FUNCTION picture_insertion() RETURNS TRIGGER AS
|
|
11
|
+
$BODY$
|
|
12
|
+
BEGIN
|
|
13
|
+
INSERT INTO
|
|
14
|
+
pictures_to_process(picture_id)
|
|
15
|
+
VALUES
|
|
16
|
+
(new.id);
|
|
17
|
+
RETURN new;
|
|
18
|
+
END;
|
|
19
|
+
$BODY$
|
|
20
|
+
language plpgsql;
|
|
21
|
+
|
|
22
|
+
CREATE TRIGGER trigger_process_picture_insertion
|
|
23
|
+
AFTER INSERT ON pictures
|
|
24
|
+
FOR EACH ROW
|
|
25
|
+
EXECUTE PROCEDURE picture_insertion();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
-- job_queue_args
|
|
2
|
+
-- depends: 20241017_01_GuOjF-pic-quality-update 20241104_01_yhRVu-rejection-details
|
|
3
|
+
|
|
4
|
+
ALTER TABLE job_queue ADD COLUMN IF NOT EXISTS args JSONB;
|
|
5
|
+
ALTER TABLE job_history ADD COLUMN IF NOT EXISTS args JSONB;
|
|
6
|
+
|
|
7
|
+
-- Update the trigger that insert each new picture into pictures_to_process
|
|
8
|
+
-- Now we check if the picture has been blurred before upload, to skip the blurring process
|
|
9
|
+
DROP TRIGGER trigger_process_picture_insertion ON pictures;
|
|
10
|
+
CREATE OR REPLACE FUNCTION picture_insertion() RETURNS TRIGGER AS
|
|
11
|
+
$BODY$
|
|
12
|
+
DECLARE
|
|
13
|
+
args JSONB;
|
|
14
|
+
BEGIN
|
|
15
|
+
IF new.metadata->>'blurredByAuthor' THEN
|
|
16
|
+
args := jsonb_build_object('skip_blurring', true);
|
|
17
|
+
END IF;
|
|
18
|
+
|
|
19
|
+
INSERT INTO
|
|
20
|
+
job_queue(picture_id, task, args)
|
|
21
|
+
VALUES
|
|
22
|
+
(new.id, 'prepare', args);
|
|
23
|
+
RETURN new;
|
|
24
|
+
END;
|
|
25
|
+
$BODY$
|
|
26
|
+
language plpgsql;
|
|
27
|
+
|
|
28
|
+
CREATE TRIGGER trigger_process_picture_insertion
|
|
29
|
+
AFTER INSERT ON pictures
|
|
30
|
+
FOR EACH ROW
|
|
31
|
+
EXECUTE PROCEDURE picture_insertion();
|
geovisio/migrations/20241224_01_xuN6n-delete-upload-set-on-last-picture-trg-statement.rollback.sql
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- delete_upload_set_on_last_picture_trg_statement
|
|
2
|
+
-- depends: 20241128_01_ugthx-job-queue-args
|
|
3
|
+
|
|
4
|
+
--put back old trigger
|
|
5
|
+
|
|
6
|
+
DROP FUNCTION IF EXISTS delete_upload_set_on_last_picture CASCADE;
|
|
7
|
+
CREATE FUNCTION delete_upload_set_on_last_picture() RETURNS trigger AS $$
|
|
8
|
+
BEGIN
|
|
9
|
+
IF OLD.upload_set_id IS NOT NULL AND NOT EXISTS (
|
|
10
|
+
SELECT 1
|
|
11
|
+
FROM pictures
|
|
12
|
+
WHERE upload_set_id = OLD.upload_set_id AND id != OLD.id
|
|
13
|
+
AND status != 'waiting-for-delete'
|
|
14
|
+
LIMIT 1
|
|
15
|
+
) THEN
|
|
16
|
+
-- if it's the last picture of an upload set, we delete the upload set
|
|
17
|
+
DELETE FROM upload_sets WHERE id = OLD.upload_set_id;
|
|
18
|
+
END IF;
|
|
19
|
+
RETURN NULL;
|
|
20
|
+
|
|
21
|
+
END $$ LANGUAGE plpgsql;
|
|
22
|
+
|
|
23
|
+
CREATE TRIGGER delete_upload_set_on_last_picture_trg
|
|
24
|
+
AFTER DELETE ON pictures
|
|
25
|
+
REFERENCING OLD TABLE AS deleted_pictures
|
|
26
|
+
FOR EACH ROW
|
|
27
|
+
EXECUTE FUNCTION delete_upload_set_on_last_picture();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- delete_upload_set_on_last_picture_trg_statement
|
|
2
|
+
-- depends: 20241128_01_ugthx-job-queue-args
|
|
3
|
+
|
|
4
|
+
-- Change the trigger to run for each statement, so that we run it once when deleting an uploadset
|
|
5
|
+
|
|
6
|
+
DROP FUNCTION IF EXISTS delete_upload_set_on_last_picture CASCADE;
|
|
7
|
+
CREATE FUNCTION delete_upload_set_on_last_picture() RETURNS trigger AS $$
|
|
8
|
+
BEGIN
|
|
9
|
+
WITH upload_sets AS (
|
|
10
|
+
SELECT distinct(upload_set_id)
|
|
11
|
+
FROM deleted_pictures
|
|
12
|
+
WHERE NOT EXISTS (
|
|
13
|
+
SELECT 1
|
|
14
|
+
FROM pictures p
|
|
15
|
+
WHERE p.upload_set_id = upload_set_id AND p.id NOT in (SELECT id FROM deleted_pictures)
|
|
16
|
+
LIMIT 1
|
|
17
|
+
)
|
|
18
|
+
)
|
|
19
|
+
DELETE FROM upload_sets WHERE id IN (SELECT upload_set_id FROM upload_sets);
|
|
20
|
+
RETURN NULL;
|
|
21
|
+
|
|
22
|
+
END $$ LANGUAGE plpgsql;
|
|
23
|
+
|
|
24
|
+
CREATE TRIGGER delete_upload_set_on_last_picture_trg
|
|
25
|
+
AFTER DELETE ON pictures
|
|
26
|
+
REFERENCING OLD TABLE AS deleted_pictures
|
|
27
|
+
FOR EACH STATEMENT
|
|
28
|
+
EXECUTE FUNCTION delete_upload_set_on_last_picture();
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
-- rm-cameras
|
|
2
|
+
-- depends: 20241128_01_ugthx-job-queue-args
|
|
3
|
+
|
|
4
|
+
DROP TRIGGER trg_pictures_hpixdens_upd ON pictures;
|
|
5
|
+
DROP TRIGGER trg_pictures_hpixdens ON pictures;
|
|
6
|
+
DROP FUNCTION pictures_hpixdens;
|
|
7
|
+
|
|
8
|
+
-- Read float values from EXIF
|
|
9
|
+
CREATE OR REPLACE FUNCTION get_float(val VARCHAR) RETURNS FLOAT AS $$
|
|
10
|
+
DECLARE
|
|
11
|
+
list VARCHAR[];
|
|
12
|
+
BEGIN
|
|
13
|
+
IF val ~ '^\d+\/\d+$' THEN
|
|
14
|
+
list := regexp_split_to_array(val, '/');
|
|
15
|
+
RETURN list[1]::float / list[2]::float;
|
|
16
|
+
ELSIF val ~ '^\d+(\.\d+)?$' THEN
|
|
17
|
+
RETURN val::float;
|
|
18
|
+
ELSE
|
|
19
|
+
RETURN NULL;
|
|
20
|
+
END IF;
|
|
21
|
+
END;
|
|
22
|
+
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
-- GPS accuracy function
|
|
26
|
+
CREATE OR REPLACE FUNCTION gps_accuracy(metadata JSONB, exif JSONB) RETURNS FLOAT AS $$
|
|
27
|
+
DECLARE
|
|
28
|
+
gps_dop FLOAT;
|
|
29
|
+
gps_diff INT;
|
|
30
|
+
gps_hpos_err FLOAT;
|
|
31
|
+
BEGIN
|
|
32
|
+
-- Parse GPS DOP, either float or fraction
|
|
33
|
+
gps_dop := get_float(COALESCE(exif->>'Exif.GPSInfo.GPSDOP', exif->>'Xmp.exif.GPSDOP', ''));
|
|
34
|
+
gps_diff := (COALESCE(exif->>'Exif.GPSInfo.GPSDifferential', exif->>'Xmp.exif.GPSDifferential'))::INT;
|
|
35
|
+
gps_hpos_err := get_float(COALESCE(exif->>'Exif.GPSInfo.GPSHPositioningError', exif->>'Xmp.exif.GPSHPositioningError'));
|
|
36
|
+
|
|
37
|
+
-- Direct horizontal positioning error in meters -> return as is
|
|
38
|
+
IF gps_hpos_err IS NOT NULL AND gps_hpos_err > 0 THEN
|
|
39
|
+
RETURN gps_hpos_err;
|
|
40
|
+
|
|
41
|
+
-- GPS DOP available
|
|
42
|
+
ELSIF gps_dop IS NOT NULL AND gps_dop > 0 THEN
|
|
43
|
+
-- With a DGPS -> consider GPS nominal error as 1 meter
|
|
44
|
+
IF gps_diff = 1 THEN
|
|
45
|
+
RETURN gps_dop;
|
|
46
|
+
|
|
47
|
+
-- Without DGPS -> consider GPS nominal error as 3 meters in average
|
|
48
|
+
ELSE
|
|
49
|
+
RETURN 3 * gps_dop;
|
|
50
|
+
END IF;
|
|
51
|
+
|
|
52
|
+
-- DGPS -> return 2 meters precision
|
|
53
|
+
ELSIF gps_diff = 1 THEN
|
|
54
|
+
RETURN 2;
|
|
55
|
+
|
|
56
|
+
-- Approximate guesses based on model
|
|
57
|
+
ELSIF metadata->>'make' IS NOT NULL OR metadata->>'model' IS NOT NULL THEN
|
|
58
|
+
-- Good non-diff GPS devices (best case is 3m, so setting 4 for tolerance)
|
|
59
|
+
IF lower(metadata->>'make') IN (
|
|
60
|
+
'gopro', 'insta360', 'garmin', 'viofo', 'xiaoyi', 'blackvue', 'tectectec',
|
|
61
|
+
'arashi vision'
|
|
62
|
+
)
|
|
63
|
+
OR metadata->>'model' IN ('LG-R105', 'FDR-X1000V')
|
|
64
|
+
OR metadata->>'make' ILIKE '%xiaoyi%' THEN
|
|
65
|
+
RETURN 4;
|
|
66
|
+
|
|
67
|
+
-- Diff GPS devices
|
|
68
|
+
ELSIF lower(metadata->>'make') IN ('stfmani', 'trimble', 'imajing')
|
|
69
|
+
OR metadata->>'model' IN ('LB5') OR metadata->>'model' ILIKE '%ladybug%' THEN
|
|
70
|
+
RETURN 2;
|
|
71
|
+
|
|
72
|
+
-- Smartphones or not-so-good non-diff GPS devices
|
|
73
|
+
ELSIF lower(metadata->>'make') IN (
|
|
74
|
+
'samsung', 'xiaomi', 'huawei', 'ricoh', 'lenovo', 'motorola', 'oneplus',
|
|
75
|
+
'apple', 'google', 'sony', 'wiko', 'asus', 'cubot', 'lge', 'fairphone',
|
|
76
|
+
'realme', 'symphony', 'crosscall', 'htc', 'homtom', 'hmd global', 'oppo',
|
|
77
|
+
'ulefone'
|
|
78
|
+
) THEN
|
|
79
|
+
RETURN 5;
|
|
80
|
+
|
|
81
|
+
-- Fallback for unknown make/model
|
|
82
|
+
ELSE
|
|
83
|
+
RETURN NULL;
|
|
84
|
+
END IF;
|
|
85
|
+
-- Fallback : no value
|
|
86
|
+
ELSE
|
|
87
|
+
RETURN NULL;
|
|
88
|
+
END IF;
|
|
89
|
+
END;
|
|
90
|
+
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
|
91
|
+
|
|
92
|
+
-- Function should not be used in rollback, created a mock one for db_migration tests
|
|
93
|
+
CREATE OR REPLACE FUNCTION missing_fov(metadata JSONB) RETURNS JSONB AS $$
|
|
94
|
+
DECLARE
|
|
95
|
+
sensor_width FLOAT;
|
|
96
|
+
BEGIN
|
|
97
|
+
RETURN metadata;
|
|
98
|
+
END;
|
|
99
|
+
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
|
100
|
+
|
|
101
|
+
-- Auto-insert pixel density/GPS accuracy for new pictures
|
|
102
|
+
CREATE OR REPLACE FUNCTION pictures_hpixdens_gpsacc() RETURNS TRIGGER AS $$
|
|
103
|
+
BEGIN
|
|
104
|
+
NEW.h_pixel_density := h_pixel_density(NEW.metadata);
|
|
105
|
+
NEW.gps_accuracy_m := gps_accuracy(NEW.metadata, NEW.exif);
|
|
106
|
+
RETURN NEW;
|
|
107
|
+
END;
|
|
108
|
+
$$ LANGUAGE plpgsql;
|
|
109
|
+
|
|
110
|
+
CREATE TRIGGER trg_pictures_hpixdens_gpsacc
|
|
111
|
+
BEFORE INSERT ON pictures
|
|
112
|
+
FOR EACH ROW
|
|
113
|
+
EXECUTE FUNCTION pictures_hpixdens_gpsacc();
|
|
114
|
+
|
|
115
|
+
CREATE TRIGGER trg_pictures_hpixdens_gpsacc_upd
|
|
116
|
+
BEFORE UPDATE OF metadata, exif ON pictures
|
|
117
|
+
FOR EACH ROW
|
|
118
|
+
EXECUTE FUNCTION pictures_hpixdens_gpsacc();
|
|
119
|
+
|
|
120
|
+
-- Mock table for db_migration tests
|
|
121
|
+
CREATE TABLE cameras(
|
|
122
|
+
model VARCHAR PRIMARY KEY,
|
|
123
|
+
sensor_width FLOAT NOT NULL
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
CREATE INDEX cameras_model_idx ON cameras USING GIST(model gist_trgm_ops);
|