diff options
| author | Daniel Schadt <kingdread@gmx.de> | 2026-10-15 17:11:06 +0200 |
|---|---|---|
| committer | Daniel Schadt <kingdread@gmx.de> | 2026-10-15 17:11:06 +0200 |
| commit | bba5d88563488db62173b47b1924d97ea6a401e8 (patch) | |
| tree | b1ced1f9be96f0b4b7ef86a617c205a52831e9dd | |
| parent | f80658b0ae172e8a2cbe57642b68f41dc3be4e8f (diff) | |
| download | fietsboek-bba5d88563488db62173b47b1924d97ea6a401e8.tar.gz fietsboek-bba5d88563488db62173b47b1924d97ea6a401e8.tar.bz2 fietsboek-bba5d88563488db62173b47b1924d97ea6a401e8.zip | |
add update to run smooth-elevation on all tracks
in order to enable this, some of the logic from
actions.execute_transformers has been moved to
Track.execute_transformers, as we did not previously have a method to
run the transformers that were set on a track.
| -rw-r--r-- | fietsboek/actions.py | 20 | ||||
| -rw-r--r-- | fietsboek/models/track.py | 40 | ||||
| -rw-r--r-- | fietsboek/transformers/__init__.py | 12 | ||||
| -rw-r--r-- | fietsboek/transformers/elevation.py | 5 | ||||
| -rw-r--r-- | fietsboek/updater/scripts/upd_20261015_gg7gfmpk4nxl062z.py | 69 |
5 files changed, 125 insertions, 21 deletions
diff --git a/fietsboek/actions.py b/fietsboek/actions.py index a50884f..adc0616 100644 --- a/fietsboek/actions.py +++ b/fietsboek/actions.py @@ -216,27 +216,11 @@ def execute_transformers(request: Request, track: models.Track): LOGGER.debug("Applied transformations match on %d, skipping", track.id) return - # We always start with the backup, that way we don't get "deepfried GPX" - # files by having the same filters run multiple times on the same input. - # They are not idempotent after all. - manager = request.data_manager.open(track.id) - backup_bytes = manager.decompress_backup() - reloaded = convert.smart_convert(backup_bytes) - path = reloaded.path() - - for transformer in settings: - LOGGER.debug("Running %s with %r", transformer, transformer.parameters) - transformer.execute(path) - - track.fast_set_path(path) - LOGGER.debug("Saving new transformers on %d", track.id) track.transformers = serialized - LOGGER.debug("Rebuilding cache for %d", track.id) - request.dbsession.delete(track.cache) - track.cache = None - track.ensure_cache() + track.execute_transformers(request.data_manager) + request.dbsession.add(track.cache) diff --git a/fietsboek/models/track.py b/fietsboek/models/track.py index 6272aef..13787c8 100644 --- a/fietsboek/models/track.py +++ b/fietsboek/models/track.py @@ -54,6 +54,7 @@ from sqlalchemy import ( from sqlalchemy.orm import Mapped, mapped_column, relationship from .. import geo, util +from ..data import DataManager from .meta import Base if TYPE_CHECKING: @@ -626,6 +627,45 @@ class Track(Base): return settings return None + def execute_transformers(self, data_manager: DataManager): + """Makes sure that the transformers applied to this track match what they should be. + + The track's data is loaded fresh from the backup (as given by the data manager). + """ + # The conversion returns a Track, so it imports this model. + # pylint: disable=import-outside-toplevel,cyclic-import + from .. import convert, transformers + + if self.transformers is None: + return + + manager = data_manager.open(self.id) + backup_bytes = manager.decompress_backup() + reloaded = convert.smart_convert(backup_bytes) + path = reloaded.path() + + if not isinstance(self.transformers, list): + raise ValueError("Track.transformers should be a list or None") + + for transformer_id, parameters in self.transformers: + if not isinstance(parameters, dict): + LOGGER.debug("Invalid parameters on track %s: %r", self.id, parameters) + continue + transformer_cls = transformers.get_transformer(transformer_id) + if transformer_cls is None: + LOGGER.info("Unknown transformer set in track %s: %s", self.id, transformer_id) + continue + transformer = transformer_cls() + transformer.parameters = transformer.parameter_model()(**parameters) + LOGGER.debug("Running %s with %r", transformer, transformer.parameters) + transformer.execute(path) + + self.fast_set_path(path) + + LOGGER.debug("Rebuilding cache for %d", self.id) + self.cache = None + self.ensure_cache() + class TrackWithMetadata: """A class to add metadata to a :class:`Track`. diff --git a/fietsboek/transformers/__init__.py b/fietsboek/transformers/__init__.py index c6e159c..4542721 100644 --- a/fietsboek/transformers/__init__.py +++ b/fietsboek/transformers/__init__.py @@ -150,6 +150,18 @@ def list_transformers() -> list[type[Transformer]]: ] +def get_transformer(identifier: str) -> type[Transformer] | None: + """Retrieve the transformer with the given identifier. + + :param identifier: The identifier of the transformer to retrieve. + :return: The class of the transformer. + """ + for transformer in list_transformers(): + if transformer.identifier() == identifier: + return transformer + return None + + def extract_from_request(request: Request) -> list[Transformer]: """Extracts the list of transformers to execute from the given request. diff --git a/fietsboek/transformers/elevation.py b/fietsboek/transformers/elevation.py index 15a5061..67d3ecf 100644 --- a/fietsboek/transformers/elevation.py +++ b/fietsboek/transformers/elevation.py @@ -226,15 +226,14 @@ class SmoothElevation(Transformer): def _snr(self, path: geo.Path) -> float: window_size = 5 - errors = [] + errors: list[float] = [] for start in range(0, len(path.points) - window_size): elevations = [point.elevation for point in path.points[start : start + window_size]] xs = list(range(len(elevations))) sl, intercept = statistics.linear_regression(xs, elevations) errors.extend( - abs(elevation - (x * sl + intercept)) - for x, elevation in enumerate(elevations) + abs(elevation - (x * sl + intercept)) for x, elevation in enumerate(elevations) ) return statistics.mean(errors) diff --git a/fietsboek/updater/scripts/upd_20261015_gg7gfmpk4nxl062z.py b/fietsboek/updater/scripts/upd_20261015_gg7gfmpk4nxl062z.py new file mode 100644 index 0000000..4168c5d --- /dev/null +++ b/fietsboek/updater/scripts/upd_20261015_gg7gfmpk4nxl062z.py @@ -0,0 +1,69 @@ +"""Revision upgrade script gg7gfmpk4nxl062z + +Active the "smooth elevation" transformer. + +Date created: 2026-10-15 14:22:06.047695 +""" +from pathlib import Path + +from sqlalchemy import select +from sqlalchemy.orm import Session +from sqlalchemy.orm.attributes import flag_modified + +from fietsboek import models +from fietsboek.data import DataManager +from fietsboek.updater.script import UpdateScript + +update_id = 'gg7gfmpk4nxl062z' +previous = [ + '7fa8kflikvnlvuqe', +] +alembic_revision = 'b73ed8b45f67' + + +class Up(UpdateScript): + def pre_alembic(self, config): + pass + + def post_alembic(self, config): + data_manager = DataManager(Path(config["fietsboek.data_dir"])) + engine = self.dbengine(config) + session = Session(engine) + + with Session(engine) as session: + query = select(models.Track) + for track in session.execute(query).scalars(): + if track.type != models.track.TrackType.ORGANIC: + continue + self.tell(f"Applying smoothing to track {track.id}") + track.transformers.append(["smooth-elevation", {}]) + flag_modified(track, "transformers") + track.execute_transformers(data_manager) + session.add(track) + session.add(track.cache) + session.commit() + + +class Down(UpdateScript): + def pre_alembic(self, config): + pass + + def post_alembic(self, config): + data_manager = DataManager(Path(config["fietsboek.data_dir"])) + engine = self.dbengine(config) + session = Session(engine) + + with Session(engine) as session: + query = select(models.Track) + for track in session.execute(query).scalars(): + if track.type != models.track.TrackType.ORGANIC: + continue + self.tell(f"Removing smoothing from track {track.id}") + unsmoothed = [[k, v] for k, v in track.transformers if k != "smooth-elevation"] + if unsmoothed == track.transformers: + continue + track.transformers = unsmoothed + track.execute_transformers(data_manager) + session.add(track) + session.add(track.cache) + session.commit() |
