Skip to content

Commit

Permalink
Release v0.8.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sco1 authored Oct 23, 2024
2 parents 8aac5fc + c07fe49 commit 13db281
Show file tree
Hide file tree
Showing 43 changed files with 800 additions and 391 deletions.
10 changes: 10 additions & 0 deletions .bumper.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[tool.bumper]
current_version = "0.8.0"

[[tool.bumper.files]]
file = "./pyproject.toml"
search = 'version = "{current_version}"'

[[tool.bumper.files]]
file = "./README.md"
search = "pyflysight/{current_version}"
12 changes: 0 additions & 12 deletions .bumpversion.cfg

This file was deleted.

6 changes: 3 additions & 3 deletions .github/workflows/lint_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v4

- name: Set up uv
run: curl -LsSf https://astral.sh/uv/0.3.5/install.sh | sh
run: curl -LsSf https://astral.sh/uv/0.4.25/install.sh | sh

- name: Set up Python
uses: actions/setup-python@v5
Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13-dev"]
python-version: ["3.11", "3.12", "3.13"]
fail-fast: false

env:
Expand All @@ -59,7 +59,7 @@ jobs:
- uses: actions/checkout@v4

- name: Set up uv
run: curl -LsSf https://astral.sh/uv/0.3.5/install.sh | sh
run: curl -LsSf https://astral.sh/uv/0.4.25/install.sh | sh

- name: Set up (release) Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/pypi_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ jobs:
build:
name: Build dist & publish
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/pyflysight
permissions:
contents: write
id-token: write

steps:
- uses: actions/checkout@v4

- name: Set up uv
run: curl -LsSf https://astral.sh/uv/0.3.5/install.sh | sh
run: curl -LsSf https://astral.sh/uv/0.4.25/install.sh | sh

- name: Set up Python
uses: actions/setup-python@v5
Expand All @@ -26,10 +30,9 @@ jobs:
run: uvx --from build pyproject-build --installer uv

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1.9
uses: pypa/gh-action-pypi-publish@release/v1.10
with:
user: __token__
password: ${{ secrets.pypi_api_token }}
print-hash: true

- name: Upload wheel to release
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
uses: actions/configure-pages@v5

- name: Set up uv
run: curl -LsSf https://astral.sh/uv/0.3.5/install.sh | sh
run: curl -LsSf https://astral.sh/uv/0.4.25/install.sh | sh

- name: Set up Python
uses: actions/setup-python@v5
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ci:

repos:
- repo: https://github.com/psf/black
rev: 24.8.0
rev: 24.10.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
Expand Down Expand Up @@ -35,6 +35,6 @@ repos:
- id: python-check-blanket-type-ignore
- id: python-use-type-annotations
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
rev: v0.7.0
hooks:
- id: ruff
3 changes: 3 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ ignore = [
"pyflysight/config_params.py" = [
"D101",
]
"pyflysight/exceptions.py" = [
"D101",
]

[lint.flake8-bugbear]
extend-immutable-calls = [
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Changelog
Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (`<major>`.`<minor>`.`<patch>`)

## [0.8.0]
### Added
* #35 Add a `prefer_processed` kwarg to the `pyflysight.flysight_proc.parse_v2_log_directory` helper pipeline to prefer loading a serialized `pyflysight.flysight_proc.FlysightV2FlightLog` instance, if detected, rather than parsing the raw data files

### Changed
* (Internal) Move some commonly used/caught exceptions to `pyflysight.exceptions` for more granular exception handling
* #41 `pyflysight log_convert single` and `pyflysight trim single` are now more tolerant of directory specification when provided a top-level directory containing only one child logging session; the child directory should now automatically be resolved prior to processing

## [0.7.0]
### Added
* #15 Add `pyflysight.flysight_proc.FlysightV1` and `pyflysight.flysight_proc.FlysightV1FlightLog` container classes for interfacing with FlySight V1 track data & metadata
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# pyflysight
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyflysight/0.7.0?logo=python&logoColor=FFD43B)](https://pypi.org/project/pyflysight/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyflysight/0.8.0?logo=python&logoColor=FFD43B)](https://pypi.org/project/pyflysight/)
[![PyPI](https://img.shields.io/pypi/v/pyflysight?logo=Python&logoColor=FFD43B)](https://pypi.org/project/pyflysight/)
[![PyPI - License](https://img.shields.io/pypi/l/pyflysight?color=magenta)](https://github.com/sco1/pyflysight/blob/main/LICENSE)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/sco1/pyflysight/main.svg)](https://results.pre-commit.ci/latest/github/sco1/pyflysight/main)
Expand Down
26 changes: 24 additions & 2 deletions pyflysight/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from pyflysight import FlysightType
from pyflysight.config_utils import FlysightConfig, FlysightV1Config, FlysightV2Config
from pyflysight.exceptions import MultipleChildLogsError, NoLogsFoundError
from pyflysight.flysight_proc import parse_v2_log_directory
from pyflysight.flysight_utils import (
classify_hardware_type,
Expand All @@ -18,7 +19,7 @@
wait_for_flysight,
write_config,
)
from pyflysight.log_utils import classify_log_dir, iter_log_dirs
from pyflysight.log_utils import classify_log_dir, iter_log_dirs, locate_log_subdir
from pyflysight.trim_app import windowtrim_flight_log

pyflysight_cli = typer.Typer(add_completion=False)
Expand Down Expand Up @@ -265,7 +266,7 @@ def _check_log_dir(log_dir: Path, v2_only: bool = False) -> None:
"""
try:
flysight_type = classify_log_dir(log_dir)
except ValueError:
except NoLogsFoundError:
_abort_with_message("Error: No log files found in provided log directory.")

if v2_only:
Expand All @@ -281,6 +282,25 @@ def _trim_pipeline(log_dir: Path, normalize_gps: bool) -> None:
print("Done!")


def _try_resolve_single_log(top_dir: Path, flysight_type: FlysightType) -> Path:
"""
Helper function for single log pipelines to resolve the log directory.
This helps allow the user to more coarsely select the processing directory if it is known that
the tree only contains a single logging session.
A resolved directory is only provided if it is the only child logging session in the tree,
otherwise it is passed through unchanged.
Note:
Directories containing trimmed V2 log data are currently not considered.
"""
try:
return locate_log_subdir(top_dir, flysight_type=flysight_type)
except (NoLogsFoundError, MultipleChildLogsError):
return top_dir


@trim_app.command()
def single(
log_dir: Path = typer.Option(None, exists=True, file_okay=False, dir_okay=True),
Expand All @@ -295,6 +315,7 @@ def single(
if log_dir is None:
log_dir = prompt_for_dir(title="Select Log Directory For Processing")

log_dir = _try_resolve_single_log(log_dir, flysight_type=FlysightType.VERSION_2)
_check_log_dir(log_dir, v2_only=True)
_trim_pipeline(log_dir, normalize_gps=normalize_gps)

Expand Down Expand Up @@ -341,6 +362,7 @@ def single_convert(
if log_dir is None:
log_dir = prompt_for_dir(title="Select Log Directory For Processing")

log_dir = _try_resolve_single_log(log_dir, flysight_type=FlysightType.VERSION_2)
_check_log_dir(log_dir, v2_only=True)
_v2_log_parse2csv_pipeline(log_dir, normalize_gps=normalize_gps)

Expand Down
13 changes: 13 additions & 0 deletions pyflysight/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class NoProcessedFlightLogError(Exception): ...


class MultipleChildLogsError(Exception): ...


class NoLogsFoundError(Exception): ...


class NoDeviceStateError(Exception): ...


class UnknownDeviceError(Exception): ...
20 changes: 18 additions & 2 deletions pyflysight/flysight_proc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from polars.exceptions import ShapeError

from pyflysight import FlysightType, HEADER_PARTITION_KEYWORD, NUMERIC_T
from pyflysight.exceptions import MultipleChildLogsError, NoProcessedFlightLogError
from pyflysight.log_utils import get_idx, normalize_gps_location

GPS_EPOCH = dt.datetime(year=1980, month=1, day=6)
Expand Down Expand Up @@ -644,10 +645,14 @@ def from_csv(cls, base_dir: Path) -> FlysightV2FlightLog:
"""
device_info_filepath = tuple(base_dir.rglob("device_info.json"))
if not device_info_filepath:
raise ValueError("No device info JSON found in the given base dir or its children.")
raise NoProcessedFlightLogError(
"No device info JSON found in the given base dir or its children."
)

if len(device_info_filepath) != 1:
raise ValueError("Must specify a base dir with only one child data directory.")
raise MultipleChildLogsError(
"Must specify a base dir with only one child data directory."
)

with device_info_filepath[0].open() as f:
raw_device_info = json.load(f)
Expand Down Expand Up @@ -718,6 +723,7 @@ def _add_sync_column(track_data: polars.DataFrame, track_offset: float) -> polar

def parse_v2_log_directory(
log_directory: Path,
prefer_processed: bool = False,
normalize_gps: bool = False,
sensor_filename: str = "SENSOR.CSV",
track_filename: str = "TRACK.CSV",
Expand All @@ -735,8 +741,18 @@ def parse_v2_log_directory(
When utilizing this pipeline, an `elapsed_time_sensor` column is added to the track `DataFrame`,
providing a synchronized elapsed time that can be used to align the sensor & track `DataFrame`s.
If `prefer_processed` is `True`, if a serialized `FlysightV2FlightLog` instance is discovered in
the target directory it will be loaded rather than parsing the raw data files.
If `normalize_gps` is `True`, the GPS track data is normalized to start at `(0, 0)`
"""
if prefer_processed:
try:
return FlysightV2FlightLog.from_csv(log_directory)
except NoProcessedFlightLogError as e:
print(e)
print("Attempting to parse raw logging session data instead...")

sensor_filepath = log_directory / sensor_filename
if not sensor_filepath.exists():
raise ValueError(f"Could not locate 'SENSOR.CSV` in directory: '{log_directory}'")
Expand Down
7 changes: 1 addition & 6 deletions pyflysight/flysight_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,10 @@

from pyflysight import FlysightType
from pyflysight.config_utils import FlysightConfig, parse_config_params
from pyflysight.exceptions import NoDeviceStateError, UnknownDeviceError
from pyflysight.log_utils import iter_log_dirs


class NoDeviceStateError(ValueError): ... # noqa: D101


class UnknownDeviceError(ValueError): ... # noqa: D101


def iter_flysight_drives() -> abc.Generator[Path, None, None]:
"""
Iterate through the system's mounted disk partitions and yield likely FlySight devices.
Expand Down
11 changes: 7 additions & 4 deletions pyflysight/log_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import polars

from pyflysight import FlysightType, HEADER_PARTITION_KEYWORD, NUMERIC_T
from pyflysight.exceptions import MultipleChildLogsError, NoLogsFoundError


def get_idx(log_data: polars.DataFrame, query: NUMERIC_T, ref_col: str = "elapsed_time") -> int:
Expand Down Expand Up @@ -37,7 +38,7 @@ def classify_log_dir(log_dir: Path) -> FlysightType:
"""
csv_stems = {file.stem for file in log_dir.glob("*.CSV")}
if not csv_stems:
raise ValueError("No log files found in provided log directory.")
raise NoLogsFoundError("No log files found in provided log directory.")

if "SENSOR" in csv_stems:
return FlysightType.VERSION_2
Expand All @@ -53,7 +54,7 @@ def locate_log_subdir(top_dir: Path, flysight_type: FlysightType) -> Path:
It is assumed that the provided `top_dir` contains only one valid directory of log files.
Note:
Directories containing trimmed log data are currently not considered.
Directories containing trimmed V2 log data are currently not considered.
"""
if flysight_type == FlysightType.VERSION_1:
query = "*.CSV"
Expand All @@ -62,9 +63,11 @@ def locate_log_subdir(top_dir: Path, flysight_type: FlysightType) -> Path:

found_files = tuple(top_dir.rglob(query))
if not found_files:
raise ValueError("No log files found in directory or its children.")
raise NoLogsFoundError("No log files found in directory or its children.")
elif len(found_files) > 1:
raise ValueError(f"Multiple matching log directories found. Found: {len(found_files)}")
raise MultipleChildLogsError(
f"Multiple matching log directories found. Found: {len(found_files)}"
)

return found_files[0].parent

Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pyflysight"
version = "0.7.0"
version = "0.8.0"
description = "Helper library for processing FlySight GPS flight logs."
authors = [
{name = "sco1", email = "[email protected]"}
Expand Down Expand Up @@ -44,7 +44,6 @@ pyflysight = "pyflysight.cli:pyflysight_cli"
[tool.uv]
dev-dependencies = [
"black~=24.8",
"bump2version~=1.0",
"cogapp~=3.4",
"flake8~=7.1",
"flake8-annotations~=3.1",
Expand All @@ -53,14 +52,15 @@ dev-dependencies = [
"mkdocs~=1.6",
"mkdocstrings-python~=1.10",
"mypy~=1.11",
"pre-commit~=3.8",
"pre-commit~=4.0",
"pymdown-extensions~=10.9",
"pytest-check~=2.4",
"pytest-cov~=5.0",
"pytest-mock~=3.14",
"pytest-randomly~=3.15",
"pytest~=8.3",
"ruff~=0.6",
"sco1-bumper~=1.0",
"tox~=4.18",
"tox-uv~=1.11",
]
Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from pathlib import Path

SAMPLE_DATA_DIR = Path(__file__).parent / "sample_data"
SAMPLE_DATA_DIR = Path(__file__).parent / "test_data"
Loading

0 comments on commit 13db281

Please sign in to comment.