From 0c247e34e79978ba910c01c56528718cbce4e2d5 Mon Sep 17 00:00:00 2001 From: Thomas André Date: Fri, 2 May 2025 18:13:45 +0200 Subject: QField project export done and tested + saving some of the specifics importer for QField --- archaeological_finds/tests.py | 1 + archaeological_finds/tests/Finds.gpkg | Bin 98304 -> 0 bytes ishtar_common/qfield/model/Finds.gpkg | Bin 0 -> 106496 bytes ishtar_common/qfield/model/Prospections.qgs | 1400 ++++++++++++++++++++ .../qfield/model/Prospections_attachments.zip | Bin 0 -> 1124 bytes ishtar_common/qfield/model/Prospections_qfield.zip | Bin 0 -> 1219 bytes ishtar_common/tests.py | 115 +- ishtar_common/tests/Finds.gpkg | Bin 98304 -> 0 bytes ishtar_common/views.py | 6 +- ishtar_common/views_item.py | 152 ++- 10 files changed, 1634 insertions(+), 40 deletions(-) delete mode 100644 archaeological_finds/tests/Finds.gpkg create mode 100644 ishtar_common/qfield/model/Finds.gpkg create mode 100644 ishtar_common/qfield/model/Prospections.qgs create mode 100644 ishtar_common/qfield/model/Prospections_attachments.zip create mode 100644 ishtar_common/qfield/model/Prospections_qfield.zip delete mode 100644 ishtar_common/tests/Finds.gpkg diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index 87ed1f179..b5f7ee8b7 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -1283,6 +1283,7 @@ class ImportFindTest(BaseImportFindTest): layer.CreateFeature(feature) feature = None datasource = None + os.remove(gpkg) class ExportTest(FindInit, TestCase): diff --git a/archaeological_finds/tests/Finds.gpkg b/archaeological_finds/tests/Finds.gpkg deleted file mode 100644 index 1b71e2aa6..000000000 Binary files a/archaeological_finds/tests/Finds.gpkg and /dev/null differ diff --git a/ishtar_common/qfield/model/Finds.gpkg b/ishtar_common/qfield/model/Finds.gpkg new file mode 100644 index 000000000..a439d366d Binary files /dev/null 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 new file mode 100644 index 000000000..5049b2e4e --- /dev/null +++ b/ishtar_common/qfield/model/Prospections.qgs @@ -0,0 +1,1400 @@ + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + Finds_7595ca92_4128_456d_a700_416cedf9aaa4 + OSM_Standard_88a32e94_cd29_412c_8b66_ee44fe338fab + Ortho_20_cm_f8497a68_e819_4170_a2ce_17cc699ac2d2 + + + + + + + + + + + degrees + + -4.83189219737137599 + 43.70747210619575895 + 8.19138905262862771 + 49.86105416580328864 + + 0 + + + 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 + + + + + + + + + + + + + + + + + + + Annotations_61e154b2_8160_463f_af4c_71565a806b89 + + + + + Annotations + + + 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 + + + + + + + 1 + 1 + 1 + 0 + + + + 1 + 0 + + + + + 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 + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + resamplingFilter + + 0 + + + + + + + + + + + 0 + + + 255 + 255 + 255 + 255 + 0 + 255 + 255 + + + false + + + EPSG:7030 + + + m2 + meters + + + 5 + 2.5 + false + false + false + 1 + 0 + false + false + true + 0 + 255,0,0,255,rgb:1,0,0,1 + + + false + + + true + 2 + + + {} + /home/thomas/StageM2/ishtar/ishtar_common/qfield/model + + + 1 + + + . + + + + + + + + + + + + + + + + + + + Thomas ANDRE + 2025-04-29T16:38:23 + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + diff --git a/ishtar_common/qfield/model/Prospections_attachments.zip b/ishtar_common/qfield/model/Prospections_attachments.zip new file mode 100644 index 000000000..3932d291c Binary files /dev/null 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 new file mode 100644 index 000000000..ee82cc69e Binary files /dev/null and b/ishtar_common/qfield/model/Prospections_qfield.zip differ diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index d14c861bd..fd56b1a83 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -29,7 +29,8 @@ 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 @@ -2773,6 +2774,118 @@ class BaseImportTest(TestCase): return impt + def test_verify_qfield_folder(self): + """ + :function: Verify if all folders necessary for QField are here + """ + # Definition of the path to test importer data for GIS data + 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']) + 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) + # Verification of the names of the files in the .zip + list_files = ["Prospections_attachments.zip"] + self.assertEqual(zip_file.namelist(), list_files) + # Closing of the .zip + zip_file.close() + + + def test_modification_xml(self): + """ + :function: Test to modify the content of a style file for QGIS + """ + # Definition of the path to test importer data for GIS data + root = os.path.join(settings.LIB_BASE_PATH, "ishtar_common") + filename = os.path.join(root, "qfield", "model", "Prospections.qgs") + new_file = os.path.join(root, "qfield", "model", "Test.qgs") + with open(filename, 'r', encoding ='utf-8') as file: + style = file.read() + modifications = style.replace('champ_','test_modif_') + with open(new_file, 'w', encoding ='utf-8') as file: + file.write(modifications) + with open(new_file, 'r', encoding='utf-8') as file: + style = file.read() + bool = 'test_modif_' in style + self.assertEqual(bool, True) + bool = 'champ_' in style + self.assertEqual(bool, False) + os.remove(new_file) + + + def test_export_qfield(self): + # 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 + 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 + with ZipFile(duplicate, 'a') as zip_file: + zip_file.write(filename, os.path.basename(filename)) + zip_file.write(new_qgs, os.path.basename(new_qgs)) + zip_file.close() + # Verification of the content of the .zip + folder = os.path.join(root, "export") + 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"] + self.assertEqual(zip_file.namelist(), list_files) + zip_file.close() + os.remove(filename) + os.remove(new_qgs) + os.remove(duplicate) + + class ImportTestInterface(BaseImportTest): def setUp(self): diff --git a/ishtar_common/tests/Finds.gpkg b/ishtar_common/tests/Finds.gpkg deleted file mode 100644 index 5000f2234..000000000 Binary files a/ishtar_common/tests/Finds.gpkg and /dev/null differ diff --git a/ishtar_common/views.py b/ishtar_common/views.py index a2c041030..a6010dfa6 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -870,9 +870,13 @@ def get_by_importer( importer = q.all()[0] importer_class = importer.get_importer_class() cols, col_names = importer.get_columns(importer_class=importer_class) + if importer.export_format: + data_type = importer.export_format + dct["type"] = importer.export_format + print(data_type) + print(dct) if data_type == "csv" or dct.get("type", "") == "csv": obj_name = importer.name - print(obj_name) elif data_type == "gpkg" or dct.get("type", "") == "gpkg": obj_name = importer.name else: diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 4126219fd..bd8780ea9 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -32,6 +32,7 @@ import subprocess # nosec from tempfile import NamedTemporaryFile from osgeo import ogr, osr import shutil +from zipfile import ZipFile from django.apps import apps from django.conf import settings @@ -2374,7 +2375,7 @@ def get_item( EMPTY = "[]" if not return_query and data_type not in ( - "json", "csv", "json-image", "json-map", "json-stats"): + "json", "csv", "json-image", "json-map", "json-stats", "gpkg"): return HttpResponse(EMPTY, content_type="text/plain") if data_type == "json-stats" and len(model.STATISTIC_MODALITIES) < 2: @@ -2991,7 +2992,7 @@ def get_item( items, query_table_cols, my_extra_request_keys, geo_fields=geo_fields ) - elif data_type != "csv" and getattr(model, "NEW_QUERY_ENGINE", False): + elif data_type not in ["csv", "gpkg"] and getattr(model, "NEW_QUERY_ENGINE", False): datas = _get_data_from_query(items, query_table_cols, my_extra_request_keys) else: datas = _get_data_from_query_old( @@ -3218,22 +3219,40 @@ def get_item( except UnicodeEncodeError: vals.append(unidecode(v).encode(ENCODING).decode(ENCODING)) writer.writerow(vals) - #return response + return response elif data_type == "gpkg": # Work in progress - # Creation of the .gpkg + # I. Preparations driver = ogr.GetDriverByName("GPKG") - root = settings.LIB_BASE_PATH + "ishtar_common/tests/" - filename = os.path.join(root, "Finds.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) - # Layer creation - layer = datasource.CreateLayer("Finds", srs, ogr.wkbPoint) - # Getting all the column names (copy from below) + # 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) if col_names: col_names = [name for name in col_names] else: @@ -3258,44 +3277,101 @@ def get_item( ) continue col_names.append(str(field.verbose_name)) - # Creation of the columns - for name in col_names: - layer.CreateField(ogr.FieldDefn(name, ogr.OFTString)) + # 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 + # 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 n in range (0, max) : - # +1 because the first value in the attributes is '' - m = n + 1 - feature.SetField(col_names[n], str(data[m])) - # First version to create the geometry of the feature - # Work in progress - if "x" in col_names[n] or "X" in col_names[n]: - try: - float(data[m]) - geom_x = data[m] - except: - pass - if "y" in col_names[n] or "Y" in col_names[n]: + 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: - float(data[m]) - geom_y = data[m] + # 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: - pass - try: - point = ogr.Geometry(ogr.wkbPoint) - point.AddPoint(float(geom_x), float(geom_y)) - feature.SetGeometry(point) - layer.CreateFeature(feature) - except: - pass + # 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 - # Missing : Part where the new .gpkg is moved to a copy of the QField folder - # Work in progress + # 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 + with open(new_qgs, 'w', encoding='utf-8') as file: + file.write(text) + # 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(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 func -- cgit v1.2.3