Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(project read): must check if layer is available in qgs project #126

Merged
merged 2 commits into from
Nov 29, 2024
Merged
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
9 changes: 7 additions & 2 deletions menu_from_project/logic/layer_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ def addLayer(
node = getFirstChildByTagNameValue(
doc.documentElement(), "maplayer", "id", layerId
)
node = node.cloneNode()
if node:
node = node.cloneNode()
idNode = node.namedItem("id")
layerType = node.toElement().attribute("type", "vector")
# give it a new id (for multiple import)
Expand Down Expand Up @@ -173,7 +173,10 @@ def addLayer(
return newLayer, relationsToBuild

else:
self.log("{} not found".format(layerId), indent=loop)
self.log(
"Layer {} not found. Can't add layer to QGIS.".format(layerId),
indent=loop,
)

return None, None

Expand Down Expand Up @@ -285,6 +288,8 @@ def fixForm(
layerNode = getFirstChildByTagNameValue(
doc.documentElement(), "maplayer", "id", oldLayerId
)
if not layerNode:
self.log("{} not found for form relation fix".format(oldLayerId))

nodes = layerNode.toElement().elementsByTagName("attributeEditorForm")
if nodes.count() == 0:
Expand Down
162 changes: 85 additions & 77 deletions menu_from_project/logic/project_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
from pathlib import Path
from typing import Dict, Optional, Tuple

from qgis.core import QgsMapLayerType, QgsMessageLog, QgsWkbTypes

# PyQGIS
from qgis.core import QgsMapLayerType, QgsMessageLog, QgsWkbTypes
from qgis.PyQt import QtXml
from qgis.PyQt.QtCore import QFileInfo

Expand Down Expand Up @@ -148,7 +147,7 @@ def get_layer_menu_config(
qgs_dom_manager: QgsDomManager,
init_filename: str,
absolute_project: bool,
) -> MenuLayerConfig:
) -> Optional[MenuLayerConfig]:
"""Get layer menu configuration from a xml node

:param node: xml node
Expand All @@ -163,7 +162,7 @@ def get_layer_menu_config(
:param absolute_project: True if project is absolute, False otherwise
:type absolute_project: bool
:return: layer menu configuration
:rtype: MenuLayerConfig
:rtype: Optional[MenuLayerConfig]
"""

embedded, filename = read_embedded_properties(
Expand All @@ -175,40 +174,43 @@ def get_layer_menu_config(

if embedded:
ml = qgs_dom_manager.getMapLayerDomFromQgs(filename, layer_id)
else:
elif layer_id in maplayer_dict:
ml = maplayer_dict[layer_id]
else:
ml = None

if ml:
# Metadata infos
md = ml.namedItem("resourceMetadata")
metadata_title = md.namedItem("title").firstChild().toText().data()
metadata_abstract = md.namedItem("abstract").firstChild().toText().data()

# Layer info
title = ml.namedItem("title").firstChild().toText().data()
abstract = ml.namedItem("abstract").firstChild().toText().data()

# Layer notes
layer_notes = ""
elt_note = ml.namedItem("userNotes")
if elt_note.toElement().hasAttribute("value"):
layer_notes = elt_note.toElement().attribute("value")

# Geometry and layer type
ml_elem = ml.toElement()
geometry_type_str = ml_elem.attribute("geometry")
if geometry_type_str == "":
# A TMS has not a geometry attribute.
# Let's read the "type"
geometry_type_str = ml_elem.attribute("type")
layer_type, geometry_type, is_spatial = get_layer_type_from_geometry_str(
geometry_type_str
if not ml:
QgsMessageLog.logMessage(
f"Menu from layer: Can't find layer {layer_id} in qgs project. Layer won't be added to project.",
__title__,
notifyUser=True,
)
else:
metadata_abstract, metadata_title, title, abstract, layer_notes = ""
layer_type = None
geometry_type = None
is_spatial = False
return None
# Metadata infos
md = ml.namedItem("resourceMetadata")
metadata_title = md.namedItem("title").firstChild().toText().data()
metadata_abstract = md.namedItem("abstract").firstChild().toText().data()

# Layer info
title = ml.namedItem("title").firstChild().toText().data()
abstract = ml.namedItem("abstract").firstChild().toText().data()

# Layer notes
layer_notes = ""
elt_note = ml.namedItem("userNotes")
if elt_note.toElement().hasAttribute("value"):
layer_notes = elt_note.toElement().attribute("value")

# Geometry and layer type
ml_elem = ml.toElement()
geometry_type_str = ml_elem.attribute("geometry")
if geometry_type_str == "":
# A TMS has not a geometry attribute.
# Let's read the "type"
geometry_type_str = ml_elem.attribute("type")
layer_type, geometry_type, is_spatial = get_layer_type_from_geometry_str(
geometry_type_str
)

return MenuLayerConfig(
name=element.attribute("name"),
Expand Down Expand Up @@ -324,15 +326,15 @@ def get_group_menu_config(
)
)
elif child.nodeName() == "layer-tree-layer":
childs.append(
get_layer_menu_config(
node=child,
maplayer_dict=maplayer_dict,
qgs_dom_manager=qgs_dom_manager,
init_filename=init_filename,
absolute_project=absolute_project,
)
layer_config = get_layer_menu_config(
node=child,
maplayer_dict=maplayer_dict,
qgs_dom_manager=qgs_dom_manager,
init_filename=init_filename,
absolute_project=absolute_project,
)
if layer_config:
childs.append(layer_config)

return MenuGroupConfig(
name=name, embedded=embedded, filename=filename, childs=childs
Expand All @@ -352,39 +354,45 @@ def get_project_menu_config(
:return: Optional menu project configuration
:rtype: Optional[MenuProjectConfig]
"""
try:
# Get path to QgsProject file, local / downloaded / from postgres database
uri = project.file
qgs_dom_manager.set_project(project)
doc, filename = qgs_dom_manager.getQgsDoc(uri)

# Define project name
name = project.name
if name == "":
name = get_project_title(doc)
if name == "":
name = Path(filename).stem

# Get layer tree root
layer_tree_roots = doc.elementsByTagName("layer-tree-group")
if layer_tree_roots.length() > 0:
if node := layer_tree_roots.item(0):
# Create dict of maplayer nodes
maplayer_dict = create_map_layer_dict(doc)
# Parse node for group and layers
menu_project_config = MenuProjectConfig(
project_name=name,
filename=filename,
uri=uri,
root_group=get_group_menu_config(
node=node,
maplayer_dict=maplayer_dict,
qgs_dom_manager=qgs_dom_manager,
init_filename=filename,
absolute_project=is_absolute(doc),
),
)

# Get path to QgsProject file, local / downloaded / from postgres database
uri = project.file
qgs_dom_manager.set_project(project)
doc, filename = qgs_dom_manager.getQgsDoc(uri)

# Define project name
name = project.name
if name == "":
name = get_project_title(doc)
if name == "":
name = Path(filename).stem

# Get layer tree root
layer_tree_roots = doc.elementsByTagName("layer-tree-group")
if layer_tree_roots.length() > 0:
if node := layer_tree_roots.item(0):
# Create dict of maplayer nodes
maplayer_dict = create_map_layer_dict(doc)
# Parse node for group and layers
menu_project_config = MenuProjectConfig(
project_name=name,
filename=filename,
uri=uri,
root_group=get_group_menu_config(
node=node,
maplayer_dict=maplayer_dict,
qgs_dom_manager=qgs_dom_manager,
init_filename=filename,
absolute_project=is_absolute(doc),
),
)

qgs_dom_manager.set_project(None)
return menu_project_config
qgs_dom_manager.set_project(None)
return menu_project_config
except Exception as e:
QgsMessageLog.logMessage(
f"Menu from layer: Can't parse qgs project for {project.name} : {e}",
__title__,
notifyUser=True,
)
return None
4 changes: 3 additions & 1 deletion menu_from_project/logic/qgs_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ def getQgsDoc(self, uri: str) -> Tuple[QtXml.QDomDocument, str]:

return doc, project_path

def getMapLayerDomFromQgs(self, fileName: str, layerId: str) -> QtXml.QDomNode:
def getMapLayerDomFromQgs(
self, fileName: str, layerId: str
) -> Optional[QtXml.QDomNode]:
"""Return the maplayer node in a project filepath given a maplayer ID.

:param fileName: The project filepath on the filesystem.
Expand Down
4 changes: 3 additions & 1 deletion menu_from_project/logic/xml_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

# PyQGIS
from qgis.PyQt.QtXml import QDomNode

Expand All @@ -17,7 +19,7 @@ def getFirstChildByAttrValue(elt, tagName, key, value):
return None


def getFirstChildByTagNameValue(elt, tagName, key, value):
def getFirstChildByTagNameValue(elt, tagName, key, value) -> Optional[QDomNode]:
nodes = elt.elementsByTagName(tagName)
for node in (nodes.at(i) for i in range(nodes.size())):
nd = node.namedItem(key)
Expand Down
15 changes: 11 additions & 4 deletions menu_from_project/menu_from_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,17 @@ def load_all_project_config(
if not project_config:
# Create project menu configuration from QgsProject
project_config = get_project_menu_config(project, self.qgs_dom_manager)
# Save in cache
cache_manager.save_project_menu_config(project, project_config)

result.append((project, project_config))
if project_config:
# Save in cache
cache_manager.save_project_menu_config(project, project_config)
if project_config:
result.append((project, project_config))
else:
self.log(
self.tr(
f"Can't define project configuration for project {project.name}"
)
)
return result

def project_config_loaded(
Expand Down