Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions Mergin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,24 +944,31 @@ def unhandled_exception_message(error_details, dialog_title, error_text, mm_clie


def write_project_variables(project_name, project_full_name, version, role=""):
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "mergin_project_name", project_name)
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "mergin_project_full_name", project_full_name)
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "mergin_project_version", int_version(version))
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "mm_project_name", project_name)
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "mm_project_full_name", project_full_name)
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "mm_project_version", int_version(version))
project = QgsProject.instance()
# these variables are stored in the project file, so writing them marks the project as modified;
was_dirty = project.isDirty()
QgsExpressionContextUtils.setProjectVariable(project, "mergin_project_name", project_name)
QgsExpressionContextUtils.setProjectVariable(project, "mergin_project_full_name", project_full_name)
QgsExpressionContextUtils.setProjectVariable(project, "mergin_project_version", int_version(version))
QgsExpressionContextUtils.setProjectVariable(project, "mm_project_name", project_name)
QgsExpressionContextUtils.setProjectVariable(project, "mm_project_full_name", project_full_name)
QgsExpressionContextUtils.setProjectVariable(project, "mm_project_version", int_version(version))
# projects downloaded with an older client have no role in their metadata
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "mm_project_role", role or "")
QgsExpressionContextUtils.setProjectVariable(project, "mm_project_role", role or "")
project.setDirty(was_dirty)


def remove_project_variables():
QgsExpressionContextUtils.removeProjectVariable(QgsProject.instance(), "mergin_project_name")
QgsExpressionContextUtils.removeProjectVariable(QgsProject.instance(), "mergin_project_full_name")
QgsExpressionContextUtils.removeProjectVariable(QgsProject.instance(), "mergin_project_version")
QgsExpressionContextUtils.removeProjectVariable(QgsProject.instance(), "mm_project_name")
QgsExpressionContextUtils.removeProjectVariable(QgsProject.instance(), "mm_project_full_name")
QgsExpressionContextUtils.removeProjectVariable(QgsProject.instance(), "mm_project_version")
QgsExpressionContextUtils.removeProjectVariable(QgsProject.instance(), "mm_project_role")
project = QgsProject.instance()
was_dirty = project.isDirty()
QgsExpressionContextUtils.removeProjectVariable(project, "mergin_project_name")
QgsExpressionContextUtils.removeProjectVariable(project, "mergin_project_full_name")
QgsExpressionContextUtils.removeProjectVariable(project, "mergin_project_version")
QgsExpressionContextUtils.removeProjectVariable(project, "mm_project_name")
QgsExpressionContextUtils.removeProjectVariable(project, "mm_project_full_name")
QgsExpressionContextUtils.removeProjectVariable(project, "mm_project_version")
QgsExpressionContextUtils.removeProjectVariable(project, "mm_project_role")
project.setDirty(was_dirty)


def pretty_summary(summary):
Expand Down
18 changes: 18 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,21 @@ def layer_field_filter(test_data_path: Path) -> QgsVectorLayer:
layer = QgsVectorLayer(str(test_data_path / "data_field_filter.gpkg"), "field filter layer", "ogr")
assert layer.isValid()
return layer


@pytest.fixture
def mergin_project_dir(tmp_path: Path) -> Path:
"""Fixture for a downloaded Mergin Maps project directory with its metadata."""
metadata = {
"id": "b2e0b0b0-0000-4000-8000-000000000000",
"name": "survey",
"workspace": {"id": 1, "name": "Lutra Consulting"},
"version": "v3",
"role": "editor",
"files": [],
}
meta_dir = tmp_path / ".mergin"
meta_dir.mkdir()
with open(meta_dir / "mergin.json", "w") as f:
json.dump(metadata, f)
return tmp_path
68 changes: 68 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@


import copy
import json
import tempfile
from pathlib import Path
from typing import Dict
Expand All @@ -13,6 +14,7 @@
QgsCoordinateReferenceSystem,
QgsCoordinateTransformContext,
QgsDatumTransform,
QgsExpressionContextUtils,
QgsProject,
QgsSymbolLayer,
QgsVectorLayer,
Expand All @@ -25,7 +27,10 @@
create_tracking_layer,
get_datum_shift_grids,
is_valid_name,
remove_project_variables,
same_schema,
set_qgis_project_mergin_variables,
write_project_variables,
)


Expand Down Expand Up @@ -229,3 +234,66 @@ def test_create_map_sketches_layer():
sl = layer.renderer().symbol().symbolLayer(0)
assert sl.dataDefinedProperties().property(QgsSymbolLayer.PropertyStrokeColor).expressionString() == '"color"'
assert sl.dataDefinedProperties().property(QgsSymbolLayer.PropertyStrokeWidth).expressionString() == '"width"'


def test_writing_variables_keeps_a_clean_project_clean(project_dir: Path):
"""Variables live in the project file, so writing them must not leave the user unsaved changes."""
proj = QgsProject.instance()
proj.setDirty(False)

write_project_variables("survey", "Lutra Consulting/survey", "v3", "editor")

assert not proj.isDirty()
scope = QgsExpressionContextUtils.projectScope(proj)
assert scope.variable("mm_project_version") == 3
assert scope.variable("mm_project_role") == "editor"

# a later version really does reach expressions, it is only the dirty flag we suppress
write_project_variables("survey", "Lutra Consulting/survey", "v4", "editor")
assert QgsExpressionContextUtils.projectScope(proj).variable("mm_project_version") == 4
assert not proj.isDirty()

remove_project_variables()
assert not proj.isDirty()


def test_writing_variables_keeps_unsaved_user_changes_flagged(project_dir: Path):
"""The user's own unsaved work must still prompt them to save."""
proj = QgsProject.instance()
proj.setDirty(False)
proj.setTitle("edited by the user")
assert proj.isDirty()

write_project_variables("survey", "Lutra Consulting/survey", "v3", "editor")

assert proj.isDirty()
assert proj.title() == "edited by the user"

remove_project_variables()
assert proj.isDirty()


def project_variable(name: str):
return QgsExpressionContextUtils.projectScope(QgsProject.instance()).variable(name)


def test_project_role_variable(mergin_project_dir: Path):
set_qgis_project_mergin_variables(str(mergin_project_dir))
assert project_variable("mm_project_full_name") == "Lutra Consulting/survey"
assert project_variable("mm_project_role") == "editor"

remove_project_variables()
assert project_variable("mm_project_role") is None


def test_project_role_variable_missing_in_metadata(mergin_project_dir: Path):
metadata_path = mergin_project_dir / ".mergin" / "mergin.json"
with open(metadata_path) as f:
metadata = json.load(f)
del metadata["role"]
with open(metadata_path, "w") as f:
json.dump(metadata, f)

set_qgis_project_mergin_variables(str(mergin_project_dir))
assert project_variable("mm_project_role") == ""
remove_project_variables()
Loading