diff --git a/libqfieldsync/offline_converter.py b/libqfieldsync/offline_converter.py index 3551ea9..a9cd191 100644 --- a/libqfieldsync/offline_converter.py +++ b/libqfieldsync/offline_converter.py @@ -19,7 +19,6 @@ ***************************************************************************/ """ -import os import sys from enum import Enum from pathlib import Path @@ -84,7 +83,7 @@ class OfflineConverter(QObject): def __init__( self, project: QgsProject, - export_folder: str, + export_filename: str, area_of_interest_wkt: str, area_of_interest_crs: Union[str, QgsCoordinateReferenceSystem], attachment_dirs: List[str], @@ -92,6 +91,7 @@ def __init__( export_type: ExportType = ExportType.Cable, create_basemap: bool = True, dirs_to_copy: Optional[Dict[str, bool]] = None, + export_title: str = "", ): super(OfflineConverter, self).__init__(parent=None) self.__max_task_progress = 0 @@ -102,7 +102,11 @@ def __init__( # elipsis workaround self.trUtf8 = self.tr - self.export_folder = Path(export_folder) + if not export_filename: + raise Exception("Empty export filename provided!") + + self._export_filename = Path(export_filename) + self._export_title = export_title self.export_type = export_type self.create_basemap = create_basemap self.area_of_interest = QgsPolygon() @@ -188,7 +192,7 @@ def _convert(self, project: QgsProject) -> None: # NOTE force delete the `QgsProject`, otherwise the `QgsApplication` might be deleted by the time the project is garbage collected del tmp_project - self.export_folder.mkdir(parents=True, exist_ok=True) + self._export_filename.parent.mkdir(parents=True, exist_ok=True) self.total_progress_updated.emit(0, 100, self.trUtf8("Converting project…")) project_layers: List[QgsMapLayer] = list(project.mapLayers().values()) @@ -266,15 +270,16 @@ def _convert(self, project: QgsProject) -> None: elif ( layer_action == SyncAction.COPY or layer_action == SyncAction.NO_ACTION ): - copied_files = layer_source.copy(self.export_folder, copied_files) + copied_files = layer_source.copy( + self._export_filename.parent, + copied_files, + ) elif layer_action == SyncAction.KEEP_EXISTENT: - layer_source.copy(self.export_folder, copied_files, True) + layer_source.copy(self._export_filename.parent, copied_files, True) elif layer_action == SyncAction.REMOVE: project.removeMapLayer(layer) - export_project_filename = self.export_folder.joinpath( - f"{self.original_filename.stem}_qfield.qgs" - ) + export_project_filename = self._export_filename # save the original project path self.project_configuration.original_project_path = str(self.original_filename) @@ -306,7 +311,6 @@ def _convert(self, project: QgsProject) -> None: plugin_file, export_project_filename.parent.joinpath(plugin_file.name) ) - gpkg_filename = str(self.export_folder.joinpath("data.gpkg")) if offline_layers: bbox = None if self.project_configuration.offline_copy_only_aoi: @@ -317,9 +321,10 @@ def _convert(self, project: QgsProject) -> None: ).transformBoundingBox(self.area_of_interest.boundingBox()) is_success = self.offliner.convert_to_offline( - gpkg_filename, + str(self._export_filename.with_name("data.gpkg")), offline_layers, bbox, + self._export_title, ) if not is_success: @@ -543,7 +548,7 @@ def _export_basemap(self) -> bool: "TILE_SIZE": self.project_configuration.base_map_tile_size, "MAP_UNITS_PER_PIXEL": self.project_configuration.base_map_mupp, "MAKE_BACKGROUND_TRANSPARENT": False, - "OUTPUT": os.path.join(self.export_folder, "basemap.gpkg"), + "OUTPUT": str(self._export_filename.with_name("basemap.gpkg")), } if base_map_type == ProjectProperties.BaseMapType.SINGLE_LAYER: diff --git a/libqfieldsync/offliners.py b/libqfieldsync/offliners.py index 8404381..1a06153 100644 --- a/libqfieldsync/offliners.py +++ b/libqfieldsync/offliners.py @@ -53,6 +53,7 @@ def convert_to_offline( offline_db_filename: str, layers: List[QgsMapLayer], bbox: Optional[QgsRectangle], + exported_project_title: str = "", ) -> bool: raise NotImplementedError( "Expected `BaseOffliner` to be extended by a class that implements `convert_to_offline`." @@ -86,6 +87,7 @@ def convert_to_offline( offline_db_filename: str, layers: List[QgsMapLayer], bbox: Optional[QgsRectangle], + exported_project_title: str = "", ) -> bool: project = QgsProject.instance() offline_db_path = Path(offline_db_filename).parent @@ -124,6 +126,9 @@ def convert_to_offline( if layer.selectedFeatureCount() == 0: layer.selectByIds([FID_NULL]) + if exported_project_title: + project.setTitle(exported_project_title) + is_success = self.offliner.convertToOfflineProject( str(offline_db_path), str(offline_db_filename), @@ -144,8 +149,14 @@ def convert_to_offline( offline_db_filename: str, layers: List[QgsMapLayer], bbox: Optional[QgsRectangle], + exported_project_title: str = "", ) -> bool: - self._convert_to_offline_project(str(offline_db_filename), layers, bbox) + self._convert_to_offline_project( + str(offline_db_filename), + layers, + bbox, + exported_project_title, + ) return True def ogr_field_type(self, field: QgsField) -> ogr.FieldDefn: @@ -351,6 +362,7 @@ def _convert_to_offline_project( offline_gpkg_path: str, offline_layers: Optional[List[QgsMapLayer]], bbox: Optional[QgsRectangle], + exported_project_title: str = "", ) -> None: """Converts the currently loaded QgsProject to an offline project. Offline layers are written to ``offline_gpkg_path``. Only valid vector layers are written. @@ -444,12 +456,18 @@ class LayerInfo(NamedTuple): self.update_data_provider(layer_info.layer, source) layer_info.layer.setSubsetString(layer_info.subset_string) - project_title = project.title() - if not project_title: - project_title = QFileInfo(project.fileName()).baseName() + if exported_project_title: + project_title = exported_project_title + else: + project_title = project.title() + + if not project_title: + project_title = QFileInfo(project.fileName()).baseName() + + project_title += f"{project_title} (offline)" - project_title += f"{project_title} (offline)" project.setTitle(project_title) + project.writeEntry( PROJECT_ENTRY_SCOPE_OFFLINE, PROJECT_ENTRY_KEY_OFFLINE_DB_PATH, diff --git a/tests/test_offline_converter_minioffliner.py b/tests/test_offline_converter_minioffliner.py index b1045a8..0d5a907 100644 --- a/tests/test_offline_converter_minioffliner.py +++ b/tests/test_offline_converter_minioffliner.py @@ -73,7 +73,7 @@ def test_copy(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -116,7 +116,7 @@ def test_primary_keys_custom_property(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -141,7 +141,7 @@ def test_geometryless_layers(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -195,7 +195,7 @@ def test_localized_layers(self): offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -219,7 +219,7 @@ def test_cloud_packaging_pk(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], diff --git a/tests/test_offline_converter_qgiscore.py b/tests/test_offline_converter_qgiscore.py index 4cf6344..85e76b1 100644 --- a/tests/test_offline_converter_qgiscore.py +++ b/tests/test_offline_converter_qgiscore.py @@ -70,7 +70,7 @@ def test_copy(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -113,7 +113,7 @@ def test_primary_keys_custom_property(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -138,7 +138,7 @@ def test_geometryless_layers(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -192,7 +192,7 @@ def test_localized_layers(self): offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"], @@ -216,7 +216,7 @@ def test_cloud_packaging_pk(self): ) offline_converter = OfflineConverter( project, - str(self.target_dir), + self.target_dir.joinpath("project_qfield.qgs"), "POLYGON((1 1, 5 0, 5 5, 0 5, 1 1))", QgsProject.instance().crs().authid(), ["DCIM"],