From d97d92b0ffd1d96fa9b1fc4911be89a5c039ec6a Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 24 Sep 2024 13:56:09 +0200 Subject: [PATCH] Add properties support --- qsa-api/qsa_api/api/projects.py | 49 ++++++++++++++++++++- qsa-api/qsa_api/project.py | 22 ++++++++-- qsa-api/qsa_api/properties.py | 77 +++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 qsa-api/qsa_api/properties.py diff --git a/qsa-api/qsa_api/api/projects.py b/qsa-api/qsa_api/api/projects.py index d0f456e..94e2edc 100644 --- a/qsa-api/qsa_api/api/projects.py +++ b/qsa-api/qsa_api/api/projects.py @@ -48,6 +48,53 @@ def project_info(name: str): return {"error": "internal server error"}, 415 +@projects.get("//properties") +def project_properties(name: str): + log_request() + try: + psql_schema = request.args.get("schema", default="public") + project = QSAProject(name, psql_schema) + + if project.exists(): + return jsonify(project.properties) + return {"error": "Project does not exist"}, 415 + except Exception as e: + logger().exception(str(e)) + return {"error": "internal server error"}, 415 + + +@projects.post("//properties") +def project_properties_update(name: str): + log_request() + try: + schema = { + "type": "object", + "required": [], + "properties": { + }, + } + + psql_schema = request.args.get("schema", default="public") + project = QSAProject(name, psql_schema) + if project.exists(): + data = request.get_json() + try: + validate(data, schema) + except ValidationError as e: + return {"error": e.message}, 415 + + rc, err = project.properties_update(data) + if rc: + return jsonify(rc), 201 + else: + return {"error": err}, 415 + else: + return {"error": "Project does not exist"}, 415 + except Exception as e: + logger().exception(str(e)) + return {"error": "internal server error"}, 415 + + @projects.post("/") def project_add(): log_request() @@ -156,7 +203,7 @@ def project_del_style(name, style): return {"error": "Project does not exist"}, 415 except Exception as e: logger().exception(str(e)) - return {"error": "internal server error"}, 415 + return {"error": "internal server error"}, 414 @projects.post("//layers//style") diff --git a/qsa-api/qsa_api/project.py b/qsa-api/qsa_api/project.py index 6e36c30..f22f5c3 100644 --- a/qsa-api/qsa_api/project.py +++ b/qsa-api/qsa_api/project.py @@ -29,14 +29,12 @@ ) from .mapproxy import QSAMapProxy +from .properties import ProjectProperties from .vector import VectorSymbologyRenderer from .utils import StorageBackend, config, logger from .raster import RasterSymbologyRenderer, RasterOverview -RENDERER_TAG_NAME = "renderer-v2" # constant from core/symbology/renderer.h - - class QSAProject: def __init__(self, name: str, schema: str = "public") -> None: self.name: str = name @@ -63,6 +61,12 @@ def sqlite_db(self) -> Path: con.close() return p + @property + def properties(self) -> dict: + props = ProjectProperties() + props.read(self.project) + return props.to_json() + @staticmethod def projects(schema: str = "") -> list: p = [] @@ -136,6 +140,18 @@ def metadata(self) -> dict: return m + def properties_update(self, data) -> bool: + project = self.project + + properties = ProjectProperties() + properties.read(project) + rc, err = properties.update(data) + if rc: + properties.write(project) + project.write() + + return rc, err + def cache_metadata(self) -> (dict, str): if self._mapproxy_enabled: return QSAMapProxy(self.name).metadata(), "" diff --git a/qsa-api/qsa_api/properties.py b/qsa-api/qsa_api/properties.py new file mode 100644 index 0000000..7837847 --- /dev/null +++ b/qsa-api/qsa_api/properties.py @@ -0,0 +1,77 @@ +# coding: utf8 + +from qgis.core import QgsProject + + +class _Property: + def __init__(self, scope: str, key: str, entry_type, default): + self.scope = scope + self.key = key + self.type = entry_type + self.default = default + self._value = self.default + + @property + def value(self): + return self._value + + @value.setter + def value(self, v): + self._value = self.type(v) + + def update(self, project): + meth = None + if self.type == bool: + meth = project.readBoolEntry + elif self.type == int: + meth = project.readNumEntry + + if meth: + self.value = meth(self.scope, self.key, self.default)[0] + + +class ProjectProperties: + def __init__(self) -> None: + self.props = {} + + # WMS project properties + wms = {} + + prop = _Property("WMSAddWktGeometry", "/", bool, False) + wms["getfeatureinfo_geometry"] = prop + + prop = _Property("WMSPrecision", "/", int, 8) + wms["getfeatureinfo_geometry_precision"] = prop + + self.props["wms"] = wms + + def to_json(self): + d = {} + d["wms"] = {} + for key in self.props["wms"]: + d["wms"][key] = self.props["wms"][key].value + return d + + def read(self, project): + for key in self.props: + for subkey in self.props[key]: + self.props[key][subkey].update(project) + + def update(self, data) -> (bool, str): + for key in data: + if key not in self.props: + return False, f"Unsupported key '{key}'" + + for subkey in data[key]: + if subkey not in self.props[key]: + return False, f"Unsupported key '{subkey}'" + + self.props[key][subkey].value = data[key][subkey] + + return True, "" + + def write(self, project) -> (bool, str): + for key in self.props: + for subkey in self.props[key]: + prop = self.props[key][subkey] + project.writeEntry(prop.scope, prop.key, prop.value)