From edf13b44cc8f9d73b7be4574affe57faf5d07e6d Mon Sep 17 00:00:00 2001 From: Thomas André Date: Wed, 4 Jun 2025 10:27:12 +0200 Subject: New version for the export: creation of Finds and Context records layers adapted to the data source and new automations --- ishtar_common/forms_common.py | 6 +- ishtar_common/models_imports.py | 6 +- ishtar_common/qfield/model/Context_records.gpkg | Bin 0 -> 106496 bytes ishtar_common/qfield/model/Finds.gpkg | Bin 106496 -> 106496 bytes ishtar_common/qfield/model/Prospections.qgs | 3180 ++++++++++++++++---- .../qfield/model/Prospections_attachments.zip | Bin 1124 -> 1124 bytes ishtar_common/qfield/model/Prospections_qfield.zip | Bin 1219 -> 1219 bytes ishtar_common/tests.py | 179 +- ishtar_common/views.py | 4 +- ishtar_common/views_item.py | 522 +++- 10 files changed, 3051 insertions(+), 846 deletions(-) create mode 100644 ishtar_common/qfield/model/Context_records.gpkg (limited to 'ishtar_common') diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 5789f1e8d..13c96e38a 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -405,7 +405,7 @@ class NewImportForm(BaseImportForm): raise forms.ValidationError( _('"Associated images" field must be a valid zip file.') ) - types = [".csv", ".ods", ".xls", ".xlsx", ".xlsm"] + types = [".csv", ".ods", ".xls", ".xlsx", ".xlsm", ".zip", ".gpkg"] self._clean_imported_file(types=types) archive_required = self._need_archive(data) if archive_required and ( @@ -447,7 +447,7 @@ class NewImportForm(BaseImportForm): return item -class NewImportGISForm(BaseImportForm): +class NewImportGISForm(NewImportForm): error_css_class = "error" required_css_class = "required" importer_type = "gis" @@ -462,6 +462,8 @@ class NewImportGISForm(BaseImportForm): "skip_lines", "encoding", "csv_sep", + "imported_images", + "imported_media_link", ) HEADERS = { diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py index 6a9b8b145..2a7039d51 100644 --- a/ishtar_common/models_imports.py +++ b/ishtar_common/models_imports.py @@ -2325,7 +2325,7 @@ class Import(BaseImport): return name = self.imported_file.name.lower() ext = name.split(".")[-1] - if ext not in ("ods", "xls", "xlsx", "xlsm"): + if ext not in ("ods", "xls", "xlsx", "xlsm", "zip", "gpkg"): return imported_file_path = os.path.abspath(self.imported_file.path) media_root = os.path.abspath(settings.MEDIA_ROOT) @@ -2672,6 +2672,10 @@ class Import(BaseImport): if tmp_dir: shutil.rmtree(tmp_dir) + if imported_values.endswith(".gpkg"): + print(data) + del data[0][0] + print(data) return data @property diff --git a/ishtar_common/qfield/model/Context_records.gpkg b/ishtar_common/qfield/model/Context_records.gpkg new file mode 100644 index 000000000..0d00f6158 Binary files /dev/null and b/ishtar_common/qfield/model/Context_records.gpkg differ diff --git a/ishtar_common/qfield/model/Finds.gpkg b/ishtar_common/qfield/model/Finds.gpkg index a439d366d..79c5e7073 100644 Binary files a/ishtar_common/qfield/model/Finds.gpkg and b/ishtar_common/qfield/model/Finds.gpkg differ diff --git a/ishtar_common/qfield/model/Prospections.qgs b/ishtar_common/qfield/model/Prospections.qgs index 5049b2e4e..4e042d631 100644 --- a/ishtar_common/qfield/model/Prospections.qgs +++ b/ishtar_common/qfield/model/Prospections.qgs @@ -1,5 +1,5 @@ - + @@ -30,28 +30,48 @@ false - + - + - + - + + + + + + + + + + + + + + + + + - + @@ -61,11 +81,17 @@ Finds_7595ca92_4128_456d_a700_416cedf9aaa4 OSM_Standard_88a32e94_cd29_412c_8b66_ee44fe338fab Ortho_20_cm_f8497a68_e819_4170_a2ce_17cc699ac2d2 + Parcelles_b677c8a6_df4d_4e0a_9f9f_42287fd8263e + Communes_4866eaff_96f1_4af8_820c_6a6e1c2a757b + Context_records_94782d53_b0c1_4448_9720_44f58c1a13fa - + - + + + + @@ -73,10 +99,10 @@ degrees - -4.83189219737137599 - 43.70747210619575895 - 8.19138905262862771 - 49.86105416580328864 + -4.78929591895689732 + 43.59536848099315165 + 8.23398533104295716 + 49.74895054059680177 0 @@ -97,26 +123,43 @@ - - - + Annotations_61e154b2_8160_463f_af4c_71565a806b89 @@ -177,13 +220,13 @@ - - Finds_7595ca92_4128_456d_a700_416cedf9aaa4 - ./Finds.gpkg|layername=Finds + + Communes_4866eaff_96f1_4af8_820c_6a6e1c2a757b + pagingEnabled='default' preferCoordinatesForWfsT11='false' restrictToRequestBBOX='1' srsname='EPSG:4326' typename='ADMINEXPRESS-COG.2024:commune' url='https://data.geopf.fr/wfs/ows' url='https://data.geopf.fr/wfs/ows?VERSION=2.0.0' version='auto' - Finds + Communes GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] @@ -201,46 +244,29 @@ - dataset + - - - - - - - - - - GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - EPSG:7030 - true + + + 0 + 0 + + + + + false - - - - - - - - - + - ogr + WFS @@ -256,181 +282,173 @@ 1 0 - + - + - + - - - + - - - + - - - + - + - - - + - - - @@ -516,224 +525,193 @@ 0 0 1 - + - + + + - + - + - + - + - + - - + + - + - - + + - + - + - + + + + + + + - + - + - + - + - - + + - - + - + - + - + - + - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - + - + - @@ -765,256 +743,1807 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - "champ_id" + "nom" - - - -20037508.34278924390673637 - -20037508.34278924763202667 - 20037508.34278924390673637 - 20037508.34278924763202667 - - - -180 - -85.05112877980660357 - 180 - 85.05112877980660357 - - OSM_Standard_88a32e94_cd29_412c_8b66_ee44fe338fab - crs=EPSG:3857&format&type=xyz&url=http://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0 + + Context_records_94782d53_b0c1_4448_9720_44f58c1a13fa + ./Context_records.gpkg|layername=Context_records - © OpenStreetMap contributors, CC-BY-SA - OSM Standard + Context_records - PROJCRS["WGS 84 / Pseudo-Mercator",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["Popular Visualisation Pseudo-Mercator",METHOD["Popular Visualisation Pseudo Mercator",ID["EPSG",1024]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",0,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Web mapping and visualisation."],AREA["World between 85.06°S and 85.06°N."],BBOX[-85.06,-180,85.06,180]],ID["EPSG",3857]] - +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo-Mercator - merc + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat EPSG:7030 - false + true - Tuiles OpenStreetMap + dataset - Tuiles OpenStreetMap - OpenStreetMap est construit par une communauté de cartographes qui contribuent et maintiennent des données sur les routes, les sentiers, les cafés, les gares, et bien plus encore, dans le monde entier. - - - + + + + + + + + + + + + - Fond de carte et données d’OpenStreetMap et de la Fondation OpenStreetMap (CC-BY-SA). © les contributeurs de https://www.openstreetmap.org. - Open Data Commons Open Database License (ODbL) - Creative Commons Attribution-ShareAlike (CC-BY-SA) - PROJCRS["WGS 84 / Pseudo-Mercator",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["Popular Visualisation Pseudo-Mercator",METHOD["Popular Visualisation Pseudo Mercator",ID["EPSG",1024]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",0,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Web mapping and visualisation."],AREA["World between 85.06°S and 85.06°N."],BBOX[-85.06,-180,85.06,180]],ID["EPSG",3857]] - +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo-Mercator - merc + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat EPSG:7030 - false + true - + + + + + + + - wms - - - + ogr + + + + + 1 1 - 0 + 1 0 - + - + - + - - - + - - + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "description" + + + + Finds_7595ca92_4128_456d_a700_416cedf9aaa4 + ./Finds.gpkg|layername=Finds + + + + Finds + + + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + EPSG:7030 + true + + + + + + + dataset + + + + + + + + + + + + + + + + + + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + EPSG:7030 + true + + + + + + + + + + + + + ogr + + + + + + + + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "champ_id" + + + + + -20037508.34278924390673637 + -20037508.34278924763202667 + 20037508.34278924390673637 + 20037508.34278924763202667 + + + -180 + -85.05112877980660357 + 180 + 85.05112877980660357 + + OSM_Standard_88a32e94_cd29_412c_8b66_ee44fe338fab + crs=EPSG:3857&format&type=xyz&url=http://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0 + + + + © OpenStreetMap contributors, CC-BY-SA + OSM Standard + + + PROJCRS["WGS 84 / Pseudo-Mercator",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["Popular Visualisation Pseudo-Mercator",METHOD["Popular Visualisation Pseudo Mercator",ID["EPSG",1024]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",0,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Web mapping and visualisation."],AREA["World between 85.06°S and 85.06°N."],BBOX[-85.06,-180,85.06,180]],ID["EPSG",3857]] + +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + EPSG:7030 + false + + + + Tuiles OpenStreetMap + + + dataset + Tuiles OpenStreetMap + OpenStreetMap est construit par une communauté de cartographes qui contribuent et maintiennent des données sur les routes, les sentiers, les cafés, les gares, et bien plus encore, dans le monde entier. + + + + + + Fond de carte et données d’OpenStreetMap et de la Fondation OpenStreetMap (CC-BY-SA). © les contributeurs de https://www.openstreetmap.org. + Open Data Commons Open Database License (ODbL) + Creative Commons Attribution-ShareAlike (CC-BY-SA) + + + + PROJCRS["WGS 84 / Pseudo-Mercator",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["Popular Visualisation Pseudo-Mercator",METHOD["Popular Visualisation Pseudo Mercator",ID["EPSG",1024]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",0,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Web mapping and visualisation."],AREA["World between 85.06°S and 85.06°N."],BBOX[-85.06,-180,85.06,180]],ID["EPSG",3857]] + +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + EPSG:7030 + false + + + + + + + wms + + + + + + + + + 1 + 1 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + resamplingFilter + + 0 + + + + -20037508.34278924390673637 + -15538711.09630922041833401 + 20037508.34278924390673637 + 15538711.09630922041833401 + + + -180 + -80 + 180 + 80 + + Ortho_20_cm_f8497a68_e819_4170_a2ce_17cc699ac2d2 + crs=EPSG:3857&dpiMode=7&featureCount=10&format=image/jpeg&layers=HR.ORTHOIMAGERY.ORTHOPHOTOS&styles=normal&tileMatrixSet=PM_6_19&tilePixelRatio=0&url=https://data.geopf.fr/wmts?SERVICE%3DWMTS%26VERSION%3D1.0.0%26REQUEST%3DGetCapabilities + + + + Ortho 20 cm + + + PROJCRS["WGS 84 / Pseudo-Mercator",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["Popular Visualisation Pseudo-Mercator",METHOD["Popular Visualisation Pseudo Mercator",ID["EPSG",1024]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",0,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Web mapping and visualisation."],AREA["World between 85.06°S and 85.06°N."],BBOX[-85.06,-180,85.06,180]],ID["EPSG",3857]] + +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + EPSG:7030 + false + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + wms + + + + + + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + None @@ -1025,43 +2554,31 @@ def my_form_open(dialog, layer, feature): 2 - - + + resamplingFilter 0 - - - -20037508.34278924390673637 - -15538711.09630922041833401 - 20037508.34278924390673637 - 15538711.09630922041833401 - - - -180 - -80 - 180 - 80 - - Ortho_20_cm_f8497a68_e819_4170_a2ce_17cc699ac2d2 - crs=EPSG:3857&dpiMode=7&featureCount=10&format=image/jpeg&layers=HR.ORTHOIMAGERY.ORTHOPHOTOS&styles=normal&tileMatrixSet=PM_6_19&tilePixelRatio=0&url=https://data.geopf.fr/wmts?SERVICE%3DWMTS%26VERSION%3D1.0.0%26REQUEST%3DGetCapabilities + + Parcelles_b677c8a6_df4d_4e0a_9f9f_42287fd8263e + pagingEnabled='default' preferCoordinatesForWfsT11='false' restrictToRequestBBOX='1' srsname='EPSG:4326' typename='BDPARCELLAIRE-VECTEUR_WLD_BDD_WGS84G:parcelle' url='https://data.geopf.fr/wfs/ows' url='https://data.geopf.fr/wfs/ows?VERSION=2.0.0' version='auto' - Ortho 20 cm + Parcelles - PROJCRS["WGS 84 / Pseudo-Mercator",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["Popular Visualisation Pseudo-Mercator",METHOD["Popular Visualisation Pseudo Mercator",ID["EPSG",1024]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",0,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Web mapping and visualisation."],AREA["World between 85.06°S and 85.06°N."],BBOX[-85.06,-180,85.06,180]],ID["EPSG",3857]] - +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo-Mercator - merc + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat EPSG:7030 - false + true @@ -1090,13 +2607,15 @@ def my_form_open(dialog, layer, feature): - wms - - - + WFS + + + + + 1 @@ -1104,144 +2623,479 @@ def my_form_open(dialog, layer, feature): 1 0 - + - + - + - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - None - WholeRaster - Estimated - 0.02 - 0.98 - 2 - - - - - - resamplingFilter - 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "nom" + + + + @@ -1301,9 +3155,9 @@ def my_form_open(dialog, layer, feature): @@ -1317,7 +3171,7 @@ def my_form_open(dialog, layer, feature): - + Thomas ANDRE 2025-04-29T16:38:23 @@ -1327,9 +3181,9 @@ def my_form_open(dialog, layer, feature): - + - + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] +proj=longlat +datum=WGS84 +no_defs @@ -1343,40 +3197,40 @@ def my_form_open(dialog, layer, feature): - + - + - + - + @@ -1394,7 +3248,7 @@ def my_form_open(dialog, layer, feature): - + diff --git a/ishtar_common/qfield/model/Prospections_attachments.zip b/ishtar_common/qfield/model/Prospections_attachments.zip index 3932d291c..5648d20d8 100644 Binary files a/ishtar_common/qfield/model/Prospections_attachments.zip and b/ishtar_common/qfield/model/Prospections_attachments.zip differ diff --git a/ishtar_common/qfield/model/Prospections_qfield.zip b/ishtar_common/qfield/model/Prospections_qfield.zip index ee82cc69e..f748c79f9 100644 Binary files a/ishtar_common/qfield/model/Prospections_qfield.zip and b/ishtar_common/qfield/model/Prospections_qfield.zip differ diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index c3ca7cfd6..909380e77 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -29,7 +29,6 @@ from time import time import zipfile from io import StringIO from unittest.runner import TextTestRunner, TextTestResult -from osgeo import ogr, osr from zipfile import ZipFile from django.apps import apps @@ -97,6 +96,7 @@ from ishtar_common.utils import ( ) from ishtar_common.tasks import launch_export from ishtar_common import utils_secretary +from ishtar_common import views_item LIB_BASE_PATH = settings.LIB_BASE_PATH FIXTURE_AUTH_PATH = settings.FIXTURE_AUTH_PATH @@ -2782,12 +2782,8 @@ class BaseImportTest(TestCase): root = os.path.join(settings.LIB_BASE_PATH, "ishtar_common") folder = os.path.join(root, "qfield", "model") dir_list = os.listdir(folder) - try: - self.assertEqual(len(dir_list), 4) - self.assertEqual(dir_list,['Finds.gpkg', 'Prospections_attachments.zip', 'Prospections_qfield.zip', 'Prospections.qgs']) - except: - self.assertEqual(len(dir_list), 5) - self.assertEqual(dir_list,['Finds.gpkg', 'Prospections_attachments.zip', 'Prospections.qgs~', 'Prospections_qfield.zip', 'Prospections.qgs']) + self.assertEqual(len(dir_list), 6) + self.assertEqual(dir_list,['Finds.gpkg', 'Prospections_attachments.zip', 'Context_records.gpkg', 'Prospections.qgs~', 'Prospections_qfield.zip', 'Prospections.qgs']) with ZipFile(os.path.join(folder, "Prospections_qfield.zip"), 'r') as zip_file: # Verification of the number of files in the .zip self.assertEqual(len(zip_file.namelist()),1) @@ -2820,73 +2816,130 @@ class BaseImportTest(TestCase): os.remove(new_file) - def test_export_qfield(self): + def export_qfield(self, mode, root, table_cols, col_names, datas): """ - :function: Test for the creation of a .gpkg, the modification of the .qgs and creation of a .zip test + :function: Core part of the test for the creation of a .gpkg, the modification of the .qgs and creation of a .zip test """ - # Verification of the original files - self.test_verify_qfield_folder() - # Preparation for the .gpkg - driver = ogr.GetDriverByName("GPKG") - root = settings.LIB_BASE_PATH + "ishtar_common/qfield/" - filename = os.path.join(root, "export", "Finds.gpkg") - # Verification to delete it if already existing - if os.path.exists(filename): - os.remove(filename) - datasource = driver.CreateDataSource(filename) - srs = osr.SpatialReference() - srs.ImportFromEPSG(4326) - # Preparation for the copy of the .qgs - qgs_path = os.path.join(root, "model", "Prospections.qgs") - new_qgs = os.path.join(root, "export", "Prospections.qgs") - # Preparation for the copy of the .zip - project = os.path.join(root, "model", "Prospections_qfield.zip") - duplicate = os.path.join(root, "export", "Prospections_qfield_export.zip") - # Verification to delete it if already existing + # 2) Creation of the .gpkg + finds, cr, list_ope, list_crea = views_item.gpkg_creation(root, table_cols, col_names, datas) + # 3) Preparations for the modification of the style in the .qgs file + qgs_path = os.path.join(root, 'model', 'Prospections.qgs') + new_qgs = os.path.join(root, 'tests', str(mode), 'Prospections.qgs') + if os.path.exists(new_qgs): + os.remove(new_qgs) + text = views_item.modification_style(qgs_path, table_cols, col_names, list_ope, list_crea) + with open(new_qgs, 'w', encoding='utf-8') as file: + file.write(text) + + # II. Duplication of the .zip for export + project = os.path.join(root, 'model', 'Prospections_qfield.zip') + duplicate = os.path.join(root, 'tests', str(mode), 'Prospections_qfield_export.zip') if os.path.exists(duplicate): os.remove(duplicate) shutil.copyfile(project, duplicate) - layer = datasource.CreateLayer("Finds", srs, ogr.wkbPoint) - list = ["x", "y", "z"] - for elem in list: - layer.CreateField(ogr.FieldDefn(elem, ogr.OFTReal)) - feature = ogr.Feature(layer.GetLayerDefn()) - for elem in list: - feature.SetField(elem, 14.0) - layer.CreateFeature(feature) - feature = None - datasource = None - # Verification of the modifications - text = open(qgs_path, encoding='utf-8').read() - for elem in list: - text = text.replace(f"champ_{elem}", elem) - with open(new_qgs, 'w', encoding='utf-8') as file: - file.write(text) - # Creation of the new .zip + + # III. Moving the .gpkg in a copy of the Qfield test project with ZipFile(duplicate, 'a') as zip_file: - zip_file.write(filename, os.path.basename(filename)) + # Adding the .gpkg to the .zip + zip_file.write(finds, os.path.basename(finds)) + zip_file.write(cr, os.path.basename(cr)) zip_file.write(new_qgs, os.path.basename(new_qgs)) + # Closing of the .zip zip_file.close() - # Verification of the content of the .zip - folder = os.path.join(root, "export") + + # IV. Verification phase + folder = os.path.join(root, 'tests', str(mode)) dir_list = os.listdir(folder) - self.assertEqual(len(dir_list), 3) - self.assertEqual(dir_list, ['Finds.gpkg', 'Prospections_qfield_export.zip','Prospections.qgs']) - with open(new_qgs, 'r', encoding='utf-8') as file: - style = file.read() - for elem in list: - bool = f'"{elem}"' in style - self.assertEqual(bool, True) - bool = f'champ_{elem}' in style - self.assertEqual(bool, False) - with ZipFile(os.path.join(folder, "Prospections_qfield_export.zip"), 'r') as zip_file: - self.assertEqual(len(zip_file.namelist()),3) - list_files = ["Prospections_attachments.zip", "Finds.gpkg", "Prospections.qgs"] + # Verification if the file was well created + self.assertEqual(len(dir_list), 2) + self.assertEqual(dir_list, ['Prospections_qfield_export.zip', 'Prospections.qgs']) + # Verification of the content of the .zip + with ZipFile(os.path.join(folder, 'Prospections_qfield_export.zip'), 'r') as zip_file: + self.assertEqual(len(zip_file.namelist()), 4) + list_files = ['Prospections_attachments.zip', 'Finds.gpkg', 'Context_records.gpkg', 'Prospections.qgs'] self.assertEqual(zip_file.namelist(), list_files) zip_file.close() - os.remove(filename) - os.remove(new_qgs) - os.remove(duplicate) + # List of modified and new values to verify the absence/presence depending on the situation + if mode == 1: + old = ['champ_id', 'champ_ue', 'champ_x', 'champ_y', 'champ_z', 'champ_wkt_modif', 'champ_nom', 'champ_ope', + 'champ_insee', 'champ_parc', 'champ_type', 'champ_occup', 'champ_desc', 'champ_loca', 'champ_media_cr', + 'champ_periode', 'champ_acti', 'champ_geom'] + new = ['Identifiant', 'UE', 'X', 'Y', 'Z', 'WKT_point', 'Unité_Enregistrement', 'Opération', 'INSEE_Commune', + 'Parcelle', 'Type', 'Interprétation', 'Description', 'Localisation', 'Media', 'Periode', 'Type_Activité', 'WKT'] + if mode == 2: + old = ['champ_id', 'champ_ue', 'champ_wkt_simple', 'champ_nom', 'champ_ope', 'champ_insee', 'champ_parc', + 'champ_type', 'champ_occup', 'champ_desc', 'champ_loca', 'champ_media_cr', 'champ_periode', 'champ_acti', 'champ_geom'] + new = ['Identifiant', 'UE', 'WKT_point', 'Unité_Enregistrement', 'Opération', 'INSEE_Commune', 'Parcelle', + 'Type', 'Interprétation', 'Description', 'Localisation', 'Media', 'Periode', 'Type_Activité', 'WKT'] + if mode == 3: + old = ['champ_nom', 'champ_ope', 'champ_geom', 'champ_id', 'champ_ue', 'champ_date', 'champ_x', 'champ_y', + 'champ_z', 'champ_matériaux', 'champ_desc', 'champ_media_finds', 'champ_wkt_modif'] + new = ['Nom', 'Opération', 'WKT_polygon', 'Identifiant', 'UE', 'Date', 'X', 'Y', 'Z', 'Description', 'Media', 'WKT_point'] + with open(new_qgs, 'r', encoding='utf-8') as file: + style = file.read() + for elem in old: + print(mode, elem) + bool = f'name="{elem}"' in style + self.assertEqual(bool, False) + for elem in new: + bool = f'name="{elem}"' in style + if not bool: + print(elem) + self.assertEqual(bool, True) + for file in dir_list: + os.remove(os.path.join(folder, file)) + + + def test_export_qfield(self): + """ + :function: Test for the creation of a .gpkg, the modification of the .qgs and creation of a .zip test + """ + # Verification of the original files + self.test_verify_qfield_folder() + + # I. Preparations + root = settings.LIB_BASE_PATH + 'ishtar_common/qfield/' + # 1) Creating the necessary datas for the test + Operation = apps.get_model('archaeological_operations', 'Operation') + OperationType = apps.get_model('ishtar_common', 'OperationType') + ope_type = OperationType.objects.create( + label='Diag', + ) + ope, __ = Operation.objects.get_or_create( + code_patriarche='Ope_Test', + operation_type=ope_type) + ContextRecord = apps.get_model('archaeological_context_records', 'ContextRecord') + cr, __ = ContextRecord.objects.get_or_create( + operation=ope, + label='Test_UE') + BaseFind = apps.get_model('archaeological_finds', 'BaseFind') + base_find, __ = BaseFind.objects.get_or_create( + context_record=cr, + label='Test_Mobilier', + external_id='Test_Mobilier', + auto_external_id=False, + x=0.7, + y=1.4, + z=2.1, + ) + + for mode in range(1, 4): + if mode == 1: + table_cols = ['label', 'context_record__label', 'geodata__x', 'geodata__y', 'geodata__z', 'geodata__point_3d'] + col_names = ['Identifiant', 'UE', 'X', 'Y', 'Z', 'WKT_point'] + datas = [['', 'Test_Mobilier', 'Test_UE', 7.0, 1.4, 2.1, 'Point Z (0.7 1.4 2.1)']] + self.export_qfield(mode, root, table_cols, col_names, datas) + if mode == 2: + table_cols = ['label', 'context_record__label', 'geodata__point_3d'] + col_names = ['Identifiant', 'UE', 'WKT_point'] + datas = [['', 'Test_Mobilier', 'Test_UE', 'POINT Z (0.7 1.4 0)']] + self.export_qfield(mode, root, table_cols, col_names, datas) + if mode == 3: + table_cols = [['label'], ['operation__code_patriarche'], ['geodata__multi_polygon']] + col_names = ['Nom', 'Opération', 'WKT_polygon'] + datas = [['', 'Test_UE', 'Ope_Test', 'MultiPolygon (((0.0 0.0, 1.0 1.0, 2.0 2.0)))']] + self.export_qfield(mode, root, table_cols, col_names, datas) + class ImportTestInterface(BaseImportTest): diff --git a/ishtar_common/views.py b/ishtar_common/views.py index a6010dfa6..b765c8e37 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -873,8 +873,8 @@ def get_by_importer( if importer.export_format: data_type = importer.export_format dct["type"] = importer.export_format - print(data_type) - print(dct) + #print(data_type) # debugtest + #print(dct) # debugtest if data_type == "csv" or dct.get("type", "") == "csv": obj_name = importer.name elif data_type == "gpkg" or dct.get("type", "") == "gpkg": diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index bd8780ea9..e697a2435 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -3221,38 +3221,9 @@ def get_item( writer.writerow(vals) return response elif data_type == "gpkg": - # Work in progress # I. Preparations - driver = ogr.GetDriverByName("GPKG") root = settings.LIB_BASE_PATH + "ishtar_common/qfield/" - # 1) Creation of the .gpkg - filename = os.path.join(root, "export", "Finds.gpkg") - # Verification to delete it if already existing - if os.path.exists(filename): - os.remove(filename) - datasource = driver.CreateDataSource(filename) - srs = osr.SpatialReference() - srs.ImportFromEPSG(4326) - # 2) Preparations for the modification of the style in the .qgs file - qgs_path = os.path.join(root, "model", "Prospections.qgs") - new_qgs = os.path.join(root, "export", "Prospections.qgs") - if os.path.exists(new_qgs): - os.remove(new_qgs) - # 3) Duplication of the .zip for export - project = os.path.join(root, "model", "Prospections_qfield.zip") - duplicate = os.path.join(root, "export", "Prospections_qfield_export.zip") - if os.path.exists(duplicate): - os.remove(duplicate) - shutil.copyfile(project, duplicate) - # II. Populating of the .gpkg - # 1) Layer creation with verification of the type of geometry to create - if "base_finds__point_2d" in table_cols: - layer = datasource.CreateLayer("Finds", srs, ogr.wkbPoint) - elif any(elem in table_cols for elem in ["base_finds__point_3d", "_z"]): - layer = datasource.CreateLayer("Finds", srs, ogr.wkbPoint25D) - else: - layer = datasource.CreateLayer("Finds", srs, ogr.wkbPolygon) - # 2) Getting all the column names (copy from below) + # 1) Getting all the column names (copy from below) if col_names: col_names = [name for name in col_names] else: @@ -3277,105 +3248,426 @@ def get_item( ) continue col_names.append(str(field.verbose_name)) - # 3) Creation of the attributes - print("II.3)") - for idx in range(0, len(col_names)): - if any(elem in table_cols[idx] for elem in ["index", "order", "quantity", "taq", "tpq", "year"]): - layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTInteger64)) - elif any(elem in table_cols[idx] for elem in ["_x", "_y", "_z", "circumference", "cost", "depth", "diameter", "height", "length", "number", "surface", "side", "thickness", "value", "volume", "weight", "width"]): - layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTReal)) - elif "_date" in table_cols[idx]: - layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTDate)) - elif "_datetime" in table_cols[idx]: - layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTDateTime)) - elif any(elem in table_cols[idx] for elem in ["large_area_prescription", "is_complete", "executed"]): - layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTBinary)) - else: - layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTString)) - idx += 1 - max = len(col_names) - # Looping on all the datas extracted to create features - for data in datas: - # Creation of a new feature - feature = ogr.Feature(layer.GetLayerDefn()) - # Preparations for the geometry - point = "" - geom_x = "" - geom_y = "" - geom_z = "" - # Looping on the attributes to add them to the feature - for idx in range (0, max) : - # 4) Completion of the attributes - if any(elem == table_cols[idx] for elem in ["_date", "_datetime"]): - # Preparations for specific values for the date and date_time - try: - # First version if it has all the data necessary for an ogr.OFTDateTime - # +1 because the first value in the attributes is '' - feature.SetField(col_names[idx], data[idx + 1]) - except: - # Second version if some values are missing - # +1 because the first value in the attributes is '' - feature.SetField(col_names[idx], data[idx + 1].year, data[idx + 1].month, data[idx + 1].day, 0, 0, 0) - else: - # +1 because the first value in the attributes is '' - feature.SetField(col_names[idx], str(data[idx + 1])) - # 5) Gestion of the geometry - if any(elem in table_cols for elem in ["base_finds__point_2d", "base_finds__point_3d", "_line", "_points", "_polygon"]): - if table_cols[idx] in ["base_finds__point_2d", "base_finds__point_3d", "_line", "_points", "_polygon"]: - try: - point = ogr.CreateGeometryFromWkt(data[idx + 1].split(";")[1]) - except: - pass - else: - if "base_finds__x" and "base_finds__y" in table_cols: - if table_cols[idx] == "base_finds__x": - geom_x = data[idx + 1] - elif table_cols[idx] == "base_finds__y": - geom_y = data[idx + 1] - if "base_finds__z" in table_cols: - if table_cols[idx] == "base_finds__z": - geom_z = data[idx + 1] - # Prevent problems when both x,y and geometry are present - if point == "" and geom_x != "" and geom_y != "": - if geom_z != "": - point = ogr.Geometry(ogr.wkbPoint25D) - point.AddPoint(float(geom_x), float(geom_y), float(geom_z)) - else: - point = ogr.Geometry(ogr.wkbPoint) - point.AddPoint(float(geom_x), float(geom_y)) - if point != "": - feature.SetGeometry(point) - layer.CreateFeature(feature) - feature = None - datasource = None - # 6) Modification of the style - list_ref = ["champ_id", "champ_date", "champ_datetime", "champ_x", "champ_y", "champ_z", "champ_media", "champ_wkt_2d", "champ_wkt_3d"] - list_search = ["label", "_date", "_datetime", "base_finds__x", "base_finds__y", "base_finds__z", "_image", "__point_2d", "__point_2d"] - text = open(qgs_path, encoding='utf-8').read() - for elem in list_search: - for col in table_cols: - if elem in col: - id_old = list_search.index(elem) - id_new = table_cols.index(col) - text = text.replace(list_ref[id_old], col_names[id_new]) - else: - pass + # 2) Creation of the .gpkg + finds, cr, list_ope, list_crea = gpkg_creation(root, table_cols, col_names, datas) + # 3) Preparations for the modification of the style in the .qgs file + qgs_path = os.path.join(root, 'model', 'Prospections.qgs') + new_qgs = os.path.join(root, 'export', 'Prospections.qgs') + if os.path.exists(new_qgs): + os.remove(new_qgs) + text = modification_style(qgs_path, table_cols, col_names, list_ope, list_crea) with open(new_qgs, 'w', encoding='utf-8') as file: file.write(text) + # II. Duplication of the .zip for export + project = os.path.join(root, 'model', 'Prospections_qfield.zip') + duplicate = os.path.join(root, 'export', 'Prospections_qfield_export.zip') + if os.path.exists(duplicate): + os.remove(duplicate) + shutil.copyfile(project, duplicate) # III. Moving the .gpkg in a copy of the Qfield test project with ZipFile(duplicate, 'a') as zip_file: # Adding the .gpkg to the .zip - zip_file.write(filename, os.path.basename(filename)) + zip_file.write(finds, os.path.basename(finds)) + zip_file.write(cr, os.path.basename(cr)) zip_file.write(new_qgs, os.path.basename(new_qgs)) # Closing of the .zip zip_file.close() response = HttpResponse(open(duplicate, 'rb'), content_type='application/zip') response['Content-Disposition'] = 'attachment; filename="Qfield_prospections.zip"' return response - return HttpResponse("{}", content_type="text/plain") + return HttpResponse('{}', content_type='text/plain') return func +def gpkg_creation(root, table_cols, col_names, datas): + # 1) Creation of the Geopackage + finds = os.path.join(root, 'export', 'Finds.gpkg') + # Verification to delete it if already existing + if os.path.exists(finds): + os.remove(finds) + driver = ogr.GetDriverByName('GPKG') + datasource = driver.CreateDataSource(finds) + srs = osr.SpatialReference() + srs.ImportFromEPSG(4326) + # 2) Verufucation of the origin of the export + origin = geometry_attribution(table_cols, datas) + # 3a) Case where the extraction come from Finds + if origin == 'finds': + # 4a) Creation of the finds layer and its attributes + layer = datasource.CreateLayer('Finds', srs, ogr.wkbPoint25D) + layer = attributes_creation_finds_a(layer, col_names, table_cols) + # 5a) Populating the finds layer with the datas + list_cr = populating_layer_finds(layer,table_cols,col_names,datas) + datasource = None + # 6a) Creation of the Context Records file + cr = os.path.join(root, 'export', 'Context_records.gpkg') + # Verification to delete it if already existing + if os.path.exists(cr): + os.remove(cr) + datasource = driver.CreateDataSource(cr) + srs = osr.SpatialReference() + srs.ImportFromEPSG(4326) + # 7a) Creation of the cr's layer and a list of default attrbutes + layer = datasource.CreateLayer('Context_records', srs, ogr.wkbMultiPolygon) + list_crea = ['Unité_Enregistrement', 'Opération', 'INSEE_Commune', 'Parcelle', 'Type', 'Interprétation', + 'Description', 'Localisation', 'Media', 'Periode', 'Type_Activité', 'WKT'] + layer = attributes_creation_cr_a(layer, list_crea) + # 8a) Populating the cr's layer with datas from the cr of the extracted finds + list_ope = populating_layer_cr_a(layer, list_crea, list_cr) + datasource = None + # 9a) Preparation of a list of the attributes names for the style modifications + list_crea = ['cr', list_crea] + else: + # 4b) Creation of the finds layer and a list of default attrbutes + layer = datasource.CreateLayer('Finds', srs, ogr.wkbPoint25D) + list_crea = ['Identifiant', 'UE', 'Date', 'X', 'Y', 'Z', 'Matériaux', 'Description', 'Media', 'WKT_point'] + attributes_creation_finds_b(layer, list_crea) + datasource = None + # 5b) Creation of the Context Records file + cr = os.path.join(root, 'export', 'Context_records.gpkg') + # Verification to delete it if already existing + if os.path.exists(cr): + os.remove(cr) + datasource = driver.CreateDataSource(cr) + srs = osr.SpatialReference() + srs.ImportFromEPSG(4326) + # 6a) Creation of the cr's layer and its attributes + layer = datasource.CreateLayer('Context_records', srs, ogr.wkbMultiPolygon) + layer = attributes_creation_cr_b(layer, col_names, table_cols) + # 7b) Populating the cr's layer with the datas + list_ope = populating_layer_cr_b(layer, table_cols, col_names, datas) + # 8b) Preparation of a list of the attributes names for the style modifications + list_crea = ['finds', list_crea] + return finds, cr, list_ope, list_crea + + +def geometry_attribution (table_cols, datas): + # Getting the name of the first element, two cases because in the case of Context Records, all elements are in lists + try: + id_label = table_cols.index('label') + name = datas[0][id_label + 1] + except: + id_label = table_cols.index(['label']) + name = datas[0][id_label + 1][0] + # Recuperation of the BaseFind datas + BaseFind = apps.get_model('archaeological_finds', 'BaseFind') + references = BaseFind.objects.order_by('-pk') + # Verification if the extracted elements come from BaseFind or, by default, from Context Records + if any(elem.label == name for elem in references): + origin = 'finds' + else: + origin = 'cr' + return origin + + +def attributes_creation_finds_a(layer, col_names, table_cols): + # print(table_cols) # debugtest + # print(col_names) # debugtest + # print(datas) # debugtest + # Looping on all the attributes + for idx in range(0, len(col_names)): + # Prevent missing values (case in some .gpkg) + if table_cols[idx] != '': + # print(table_cols[idx]) # debugtest + # print(col_names[idx]) # debugtest + # Gestion of specific formats of attributes + if any(elem in table_cols[idx] for elem in ['index', 'order', 'quantity', 'taq', 'tpq', 'year']): + layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTInteger64)) + elif any(elem in table_cols[idx] for elem in + ['_x', '_y', '_z', 'circumference', 'cost', 'depth', 'diameter', 'height', 'length', 'number', + 'surface', 'side', 'thickness', 'value', 'volume', 'weight', 'width']): + layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTReal)) + elif '_date' in table_cols[idx]: + layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTDate)) + elif '_datetime' in table_cols[idx]: + layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTDateTime)) + elif any(elem in table_cols[idx] for elem in ['large_area_prescription', 'is_complete', 'executed']): + layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTBinary)) + else: + layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTString)) + return layer + + +def populating_layer_finds(layer, table_cols, col_names, datas): + max = len(col_names) + list_cr = [] + # Looping on all the datas extracted to create features + for data in datas: + # Creation of a new feature + feature = ogr.Feature(layer.GetLayerDefn()) + # Preparations for the geometry + point = '' + # Looping on the attributes to add them to the feature + for idx in range(0, max): + if col_names[idx] != '': + # print(col_names[idx]) # debugtest + # print(data[idx + 1]) # debugtest + # 4) Completion of the attributes + if any(elem == table_cols[idx] for elem in ['_date', '_datetime']): + # Preparations for specific values for the date and date_time + try: + # First version if it has all the data necessary for an ogr.OFTDateTime + # +1 because the first value in the attributes is '' + feature.SetField(col_names[idx], data[idx + 1]) + except: + # Second version if some values are missing + # +1 because the first value in the attributes is '' + feature.SetField(col_names[idx], data[idx + 1].year, data[idx + 1].month, data[idx + 1].day, 0, + 0, 0) + elif 'context_record__label' in table_cols[idx] and data[idx + 1] not in list_cr: + list_cr.append(data[idx + 1]) + feature.SetField(col_names[idx], str(data[idx + 1])) + else: + # +1 because the first value in the attributes is '' + feature.SetField(col_names[idx], str(data[idx + 1])) + # Gestion of the geometry + id_label = table_cols.index('label') + BaseFind = apps.get_model('archaeological_finds', 'BaseFind') + name = data[id_label + 1] + object, __ = BaseFind.objects.get_or_create( + label=name, + ) + try: + point = ogr.Geometry(ogr.wkbPoint25D) + point.AddPoint(float(object.x), float(object.y), float(object.z)) + except: + try: + point = ogr.Geometry(ogr.wkbPoint25D) + point.AddPoint(float(object.x), float(object.y), float(0.0)) + except: + pass + if point != '': + feature.SetGeometry(point) + # Addition of the new feature + layer.CreateFeature(feature) + feature = None + return list_cr + + +def attributes_creation_finds_b(layer, list_crea): + # Gestion of specific types of attributes for the default values + for attribute in list_crea: + if attribute == 'Date': + layer.CreateField(ogr.FieldDefn(attribute, ogr.OFTDate)) + elif attribute in ['X', 'Y', 'Z']: + layer.CreateField(ogr.FieldDefn(attribute, ogr.OFTReal)) + else: + layer.CreateField(ogr.FieldDefn(attribute, ogr.OFTString)) + return layer + + +def attributes_creation_cr_a(layer, list_crea): + for idx in range(0, len(list_crea)): + layer.CreateField(ogr.FieldDefn(list_crea[idx], ogr.OFTString)) + return layer + + +def populating_layer_cr_a(layer, list_crea, list_cr): + list_ope = [] + # Query in the DataBase to get information on the Context Records of the Finds exported + ContextRecord = apps.get_model('archaeological_context_records', 'ContextRecord') + for name in list_cr: + cr, __ = ContextRecord.objects.get_or_create( + label=name + ) + list_attributes = [] + try:list_attributes.append(cr.label) + except:list_attributes.append('Null') + try:list_attributes.append(str(cr.operation.code_patriarche)) + except:list_attributes.append('Null') + try:list_attributes.append(cr.town.numero_insee) + except:list_attributes.append('Null') + try:list_attributes.append(cr.parcel.external_id) + except:list_attributes.append('Null') + try:list_attributes.append(str(cr.unit)) + except:list_attributes.append('Null') + try:list_attributes.append(cr.operation.interpretation) + except:list_attributes.append('Null') + try:list_attributes.append(cr.description) + except:list_attributes.append('Null') + try:list_attributes.append(cr.location) + except:list_attributes.append('Null') + try:list_attributes.append(cr.documents.image) + except:list_attributes.append('Null') + try:list_attributes.append(cr.datings.period) + except:list_attributes.append('Null') + try:list_attributes.append(str(cr.activity)) + except:list_attributes.append('Null') + try:list_attributes.append(str(cr.main_geodata.multi_polygon)) + except:list_attributes.append('Null') + # Creation of a new feature + feature = ogr.Feature(layer.GetLayerDefn()) + for idx in range(0, len(list_crea)-1): + try: + feature.SetField(list_crea[idx], list_attributes[idx]) + except: + pass + if list_attributes[1] not in list_ope: + list_ope.append(list_attributes[1]) + try: + geom = ogr.CreateGeometryFromWkt(str(list_attributes[-1]).split(';')[-1]) + feature.SetGeometry(geom) + except: + pass + layer.CreateFeature(feature) + feature = None + return list_ope + + +def attributes_creation_cr_b(layer, col_names, table_cols): + for idx in range(0, len(col_names)): + if table_cols[idx] != '': + # print(table_cols[idx]) # debugtest + # print(col_names[idx]) # debugtest + layer.CreateField(ogr.FieldDefn(col_names[idx], ogr.OFTString)) + return layer + + +def populating_layer_cr_b(layer, table_cols, col_names, datas): + #print(table_cols) #debugtest + #print(col_names) #debugtest + #print(datas) #debugtest + list_ope = [] + geom = '' + max = len(col_names) + for data in datas: + # Creation of a new feature + feature = ogr.Feature(layer.GetLayerDefn()) + for idx in range(0, max): + if col_names[idx] != '': + if 'operation__code_patriarche' in table_cols[idx] and data[idx + 1] not in list_ope: + list_ope.append(data[idx + 1]) + feature.SetField(col_names[idx], str(data[idx + 1])) + else: + feature.SetField(col_names[idx], str(data[idx + 1])) + id_label = table_cols.index(['label']) + name = datas[0][id_label + 1] + ContextRecord = apps.get_model('archaeological_context_records', 'ContextRecord') + cr, __ = ContextRecord.objects.get_or_create( + label=name + ) + print(str(cr.multi_polygon)) + try: + geom = ogr.CreateGeometryFromWkt(str(cr.geodata.multi_polygon).split(';')[-1]) + except: + try: + geom = ogr.CreateGeometryFromWkt(str(cr.main_geodata.multi_polygon).split(';')[-1]) + except: + pass + if geom != '': + feature.SetGeometry(geom) + layer.CreateFeature(feature) + feature = None + return list_ope + + +def modification_style(qgs_path, table_cols, col_names, list_ope, list_crea): + # Lists of default names in the style, attribut names of the datas and new default names + list_ref = ['champ_date', 'champ_datetime', 'champ_x', 'champ_y', 'champ_z', 'champ_ue', + 'champ_ope', 'champ_insee', 'champ_parc', 'champ_geom'] + list_search = ['_date', '_datetime', 'base_finds__x', 'base_finds__y', 'base_finds__z', 'context_record__label', + 'operation__code_patriarche', 'town__numero_insee', 'parcel__external_id', + 'geodata__multi_polygon'] + list_auto = ['Date', 'Date/Temps', 'X', 'Y', 'Z', 'UE', 'Opération', 'INSEE_Commune', 'Parcelle', 'WKT'] + # Opening of the style + text = open(qgs_path, encoding='utf-8').read() + # Adding the different Operations linked of the Contexts Records and/or Finds exported to a list of possible values + if len(list_ope) > 0: + new_text = "" + for ope in list_ope: + choice = ' \n'.format(ope, ope) + new_text += choice + old_text = ' \n' + text = text.replace(old_text, new_text) + else: + text = text.replace("Test_choice", "Null") + # Specifics modifications if the datas don't come from Finds + if list_crea[0] == 'finds': + print(table_cols) + for ref in list_ref: + id_ref = list_ref.index(ref) + new = '' + for col in table_cols: + if list_search[id_ref] in col[0]: + print('A') + id_new = table_cols.index(col) + new = col_names[id_new] + print(col, new) + if new == '': + print('B') + new = list_auto[id_ref] + print(new) + print(ref, new) + text = text.replace(ref, new) + # List of corresponding default names in the style linked to the default names used for the Finds + list_corr = ['champ_id', 'champ_ue', 'champ_date', 'champ_x', 'champ_y', 'champ_z', 'champ_matériaux', + 'champ_desc', 'champ_media_finds', 'champ_wkt_modif'] + # Gestion of the link between the Finds and Context Records layers + id_label = table_cols.index(['label']) + new = col_names[id_label] + text = text.replace("champ_nom", new) + if ['documents__image'] in table_cols: + id_media = table_cols.index(['documents__image']) + # Gestion of the link between the Finds and Context Records layers + new = col_names[id_media] + text = text.replace("champ_media_cr", new) + # Replacement of the values from the default names used for the Finds + n = 0 + for elem in list_crea[1]: + old = list_corr[n] + text = text.replace(old, elem) + n += 1 + # Specifics modifications if the datas don't come from Context_Records + elif list_crea[0] == 'cr': + for ref in list_ref: + id_ref = list_ref.index(ref) + new = '' + for col in table_cols: + if list_search[id_ref] in col: + id_new = table_cols.index(col) + if new == '': + new = list_auto[id_ref] + text = text.replace(ref, new) + # List of corresponding default names in the style linked to the default names used for the Finds + list_corr = ['champ_nom', 'champ_ope', 'champ_insee', 'champ_parc', 'champ_type', 'champ_occup', 'champ_desc', + 'champ_loca', 'champ_media_cr', 'champ_periode', 'champ_acti', 'champ_geom'] + # Test in case the all names of attributes are in lists + try: + id_label = table_cols.index(['label']) + except: + id_label = table_cols.index('label') + # Gestion of the link between the Finds and Context Records layers + new = col_names[id_label] + text = text.replace('champ_id', new) + if 'documents__image' in table_cols: + try: + id_media = table_cols.index(['documents__image']) + except: + id_media = table_cols.index('documents__image') + # Gestion of the link between the Finds and Context Records layers + new = col_names[id_media] + text = text.replace("champ_media_finds", new) + # Specific case to assure the good registration of the z coordinate + if 'geodata__point_3d' in table_cols: + id_new = table_cols.index('geodata__point_3d') + if any('__z' in elem for elem in table_cols): + ref = "champ_wkt_modif" + new = col_names[id_new] + else: + ref = "champ_wkt_simple" + new = col_names[id_new] + text = text.replace(ref, new) + # Replacement of the values from the default names used for the Context Records + n = 0 + for elem in list_crea[1]: + old = list_corr[n] + text = text.replace(old, elem) + n += 1 + else: + pass + return text + + def adapt_distant_search(params, src, model): if "search_vector" in params and params["search_vector"]: search_vector = params["search_vector"][0] -- cgit v1.2.3