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

Style unit suffix validation #706

Merged
merged 19 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
262 changes: 195 additions & 67 deletions qgreenland/ancillary/styles/ice_sheet_velocity.qml

Large diffs are not rendered by default.

1,180 changes: 654 additions & 526 deletions qgreenland/ancillary/styles/ice_thickness_change.qml

Large diffs are not rendered by default.

260 changes: 194 additions & 66 deletions qgreenland/ancillary/styles/racmo_wind_speed.qml

Large diffs are not rendered by default.

1,182 changes: 655 additions & 527 deletions qgreenland/ancillary/styles/sea_ice_concentration.qml

Large diffs are not rendered by default.

1,180 changes: 654 additions & 526 deletions qgreenland/ancillary/styles/seawater_temperature.qml

Large diffs are not rendered by default.

180 changes: 154 additions & 26 deletions qgreenland/ancillary/styles/vertical_velocity.qml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 20 additions & 48 deletions qgreenland/models/config/layer.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from pathlib import Path
from typing import Any, Optional, Union
from xml.etree import ElementTree

from pydantic import Field, validator
from pydantic import Field

import qgreenland.exceptions as exc
from qgreenland.constants.paths import ANCILLARY_DIR
from qgreenland.models.base_model import QgrBaseModel
from qgreenland.models.config.dataset import AnyAsset, Dataset
from qgreenland.models.config.step import AnyStep
from qgreenland.util.layer_style import get_style_filepath
from qgreenland.util.model_validators import reusable_validator, validate_paragraph_text
from qgreenland.util.model_validators.layer_style import (
validate_style_file_exists,
validate_style_file_only_contains_allowed_fonts,
validate_style_file_unit_suffixes,
)


class LayerInput(QgrBaseModel):
Expand All @@ -23,10 +26,6 @@ class LayerInput(QgrBaseModel):
"""The actual input asset (file or files)."""


def _style_filepath(style_name: str) -> Path:
return ANCILLARY_DIR / "styles" / (style_name + ".qml")


class Layer(QgrBaseModel):
id: str
"""Unique identifier."""
Expand Down Expand Up @@ -57,53 +56,26 @@ class Layer(QgrBaseModel):
steps: Optional[list[AnyStep]]

_validate_description = reusable_validator("description", validate_paragraph_text)

@validator("style")
@classmethod
def style_file_only_contains_allowed_fonts(cls, value):
"""Ensure only fonts that can be downloaded by QGIS are in our style files.

This ensures we don't re-trigger an old issue:
https://github.com/nsidc/qgreenland/issues/515
"""
# TODO: Is the full list of supported fonts available in PyQGIS' API? I think
# this is the complete list, but haven't found it in the Python API yet:
# https://github.com/qgis/QGIS/blob/a7b31c7db29328fc44966a854d22c452f58c77c1/src/core/textrenderer/qgsfontmanager.cpp#L203-L925
allowed_fonts = ["Open Sans"]
if value:
style_filepath = _style_filepath(value)
tree = ElementTree.parse(style_filepath)
for elem in tree.getroot().iter():
if font_family := elem.attrib.get("fontFamily", False):
if font_family not in allowed_fonts:
raise exc.QgrInvalidConfigError(
f"Style {style_filepath} contains disallowed font:"
f" '{font_family}'."
f" Only the following fonts are allowed: {allowed_fonts}."
)

return value

@validator("style")
@classmethod
def style_file_exists(cls, value):
"""Ensure the QML style file exists in the configuration."""
if value:
style_filepath = _style_filepath(value)
if not style_filepath.is_file():
raise exc.QgrInvalidConfigError(
f"Style file does not exist: {style_filepath}"
)

return value
_validate_style_file_exists = reusable_validator(
"style",
validate_style_file_exists,
)
_validate_style_file_only_contains_allowed_fonts = reusable_validator(
"style",
validate_style_file_only_contains_allowed_fonts,
)
_validate_style_file_unit_suffixes = reusable_validator(
"style",
validate_style_file_unit_suffixes,
)

@property
def style_filepath(self) -> Union[Path, None]:
"""Full filepath to the QML style file."""
if self.style is None:
return None

return _style_filepath(self.style)
return get_style_filepath(self.style)

def __json__(self) -> dict[Any, Any]:
"""Limit child models that are output when dumping JSON.
Expand Down
8 changes: 8 additions & 0 deletions qgreenland/util/layer_style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pathlib import Path

from qgreenland.constants.paths import ANCILLARY_DIR


def get_style_filepath(style_name: str) -> Path:
"""Generate a Path object for style file represented by `style_name`."""
return ANCILLARY_DIR / "styles" / (style_name + ".qml")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migrated from _style_filepath in config/layer.py

Loading
Loading