specmod 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specmod-0.6.0 → specmod-0.7.0}/.github/workflows/build.yml +2 -0
- {specmod-0.6.0 → specmod-0.7.0}/.github/workflows/docs.yml +2 -0
- {specmod-0.6.0 → specmod-0.7.0}/.github/workflows/release.yml +2 -0
- {specmod-0.6.0 → specmod-0.7.0}/.github/workflows/test.yml +6 -0
- specmod-0.7.0/.release-please-manifest.json +3 -0
- {specmod-0.6.0 → specmod-0.7.0}/CHANGELOG.md +21 -0
- {specmod-0.6.0 → specmod-0.7.0}/PKG-INFO +1 -1
- {specmod-0.6.0 → specmod-0.7.0}/docs/_builders/specmod_tutorial.py +6 -4
- {specmod-0.6.0 → specmod-0.7.0}/docs/releasing-data.md +7 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/roadmap.md +26 -4
- {specmod-0.6.0 → specmod-0.7.0}/docs/upgrading.md +71 -6
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/acquire.py +6 -3
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/cli.py +23 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/config/sections.py +9 -4
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/datasets.py +142 -4
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/utcdatetime.pyi +1 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_acquire.py +28 -5
- specmod-0.7.0/tests/test_path_names.py +220 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_release_config.py +32 -0
- {specmod-0.6.0 → specmod-0.7.0}/tutorial/specmod-tutorial.ipynb +6 -4
- specmod-0.6.0/.release-please-manifest.json +0 -3
- {specmod-0.6.0 → specmod-0.7.0}/.git-blame-ignore-revs +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/.github/workflows/pr-title.yml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/.gitignore +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/.pre-commit-config.yaml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/.readthedocs.yaml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/AGENTS.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/CITATION.cff +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/CLAUDE.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/CONTRIBUTING.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/LICENSE +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/README.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/datasets/magna_2020.toml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/datasets/pnr_2019.toml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/REFACTOR_PLAN.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_builders/_notebook.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_builders/choosing_a_transform.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/academic.css +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/OFL-fira-code.txt +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/OFL-merriweather.txt +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/OFL-open-sans.txt +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/README.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/fira-code-400-greek.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/fira-code-400-latin-ext.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/fira-code-400-latin.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/merriweather-latin-ext.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/merriweather-latin.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/open-sans-greek.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/open-sans-latin-ext.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts/open-sans-latin.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/fonts.css +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/LICENSE +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/README.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/katex/katex.min.css +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic-dark-mark.svg +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic-dark.svg +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic-mark.svg +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/_static/specmod-logo-academic.svg +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/api.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/branding.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/choosing-a-transform.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/conf.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/contributing.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/development.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/documentation.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/getting-started.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/guides.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/index.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/notebooks/choosing-a-transform.ipynb +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/notes/api-audit.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/notes/window-position.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/pick-formats.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/processing.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/docs/releasing.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/pyproject.toml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/release-please-config.json +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/requirements.txt +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/_vendor/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/_vendor/qiinv.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/api.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/config/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/config/layers.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/config/provenance.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/config/serialize.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/core/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/core/bandwidth.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/core/collection.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/core/noise.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/core/scalogram.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/core/spectrum.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/core/units.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/distance.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/exceptions.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/fitting/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/fitting/base.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/fitting/event.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/fitting/guess.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/fitting/spectrum.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/io/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/io/backend.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/io/hdf5.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/io/layout.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/magnitude.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/picks/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/picks/base.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/picks/delimited.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/picks/events.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/picks/resolution.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/picks/snuffler.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/pipeline.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/plotting/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/plotting/grid.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/plotting/pair.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/plotting/style.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/preprocess.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/smoothing/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/smoothing/base.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/smoothing/konno_ohmachi.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/smoothing/log_bins.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/smoothing/log_window.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/smoothing/savitzky_golay.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/sources/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/sources/attenuation.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/sources/composite.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/sources/motion.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/sources/source.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/spreading.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/staged.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/tables.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/transforms/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/transforms/base.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/transforms/cwt.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/transforms/fft.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/transforms/multitaper.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/transforms/prieto.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/transforms/quadratic.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/src/specmod/utils.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/README.md +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/lmfit/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/lmfit/model.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/lmfit/parameter.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/clients/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/clients/fdsn/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/event.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/inventory.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/stream.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/trace.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/util/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/core/util/base.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/geodetics/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/signal/__init__.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/stubs/obspy/signal/konnoohmachismoothing.pyi +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/studies/magna_2020_paper.toml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/__init__.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/conftest.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/golden/motion_reference.json +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/golden/pipeline_reference.json +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/golden/reference.toml +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/golden/window_reference.json +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_ambient_state.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_api_surface.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_collection.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_config.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_config_pinning.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_cwt.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_datasets.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_docs_are_current.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_end_to_end.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_fitting_defaults.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_golden_reference.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_import.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_io_and_plotting.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_legacy_fixes.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_magnitude.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_make_golden.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_pick_plugins.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_pick_readers.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_picks.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_pipeline.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_pipeline_smoke.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_pr_title.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_preprocess.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_prieto.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_quadratic.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_smoothing.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_sources.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_spectral_wiring.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_staged.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_stubs.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_tools_track_the_api.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_transforms.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_tutorial.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_typing_backlog.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_utils.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tests/test_versioning.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/check_built_version.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/check_floors.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/check_pr_title.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/make_golden.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/make_logo.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/measure_docs.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/vendor_fonts.py +0 -0
- {specmod-0.6.0 → specmod-0.7.0}/tools/vendor_katex.py +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/picks/2019-08-26T07:30:47.000000Z.picks → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/picks/20190826T073047.000000Z.picks +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/picks/2019-08-26T07:30:47.000000Z.xml → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/picks/20190826T073047.000000Z.xml +0 -0
- {specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z → specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z}/stations/inventory.xml +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L001..HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L001..HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L001..HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L002..HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L002..HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L002..HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L006..HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L006..HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L006..HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L007..HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L007..HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L007..HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L008..HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L008..HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L008..HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L009..HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L009..HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.L009..HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH1_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.LD06..HH1_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH2_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.LD06..HH2_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH3_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/LV.LD06..HH3_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ01.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ01.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ01.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ02.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ02.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ03.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ03.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ03.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ04.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ04.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ04.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ05.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ05.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ05.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ06.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ06.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ06.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ07.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ07.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ07.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ09.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ09.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ09.00.HHZ_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHE_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ10.00.HHE_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHN_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ10.00.HHN_20190826T073047.000000Z +0 -0
- /specmod-0.6.0/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHZ_2019-08-26T07:30:47.000000Z → /specmod-0.7.0/tutorial/data/events/20190826T073047.000000Z/waveforms/UR.AQ10.00.HHZ_20190826T073047.000000Z +0 -0
|
@@ -18,6 +18,8 @@ jobs:
|
|
|
18
18
|
# fails every job at "Getting action download info", before a step runs.
|
|
19
19
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
20
20
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
21
|
+
with:
|
|
22
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
21
23
|
- run: uv python install 3.11
|
|
22
24
|
|
|
23
25
|
- name: Build sdist and wheel
|
|
@@ -32,6 +32,8 @@ jobs:
|
|
|
32
32
|
# fails every job at "Getting action download info", before a step runs.
|
|
33
33
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
34
34
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
35
|
+
with:
|
|
36
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
35
37
|
- run: uv python install 3.11
|
|
36
38
|
- run: uv venv # create a venv to install into
|
|
37
39
|
|
|
@@ -90,6 +90,8 @@ jobs:
|
|
|
90
90
|
# fails every job at "Getting action download info", before a step runs.
|
|
91
91
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
92
92
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
93
|
+
with:
|
|
94
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
93
95
|
- run: uv python install 3.11
|
|
94
96
|
- run: uv build
|
|
95
97
|
|
|
@@ -21,6 +21,7 @@ jobs:
|
|
|
21
21
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
22
22
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
23
23
|
with:
|
|
24
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
24
25
|
enable-cache: true
|
|
25
26
|
- run: uv python install 3.11
|
|
26
27
|
- run: uv venv
|
|
@@ -41,6 +42,7 @@ jobs:
|
|
|
41
42
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
42
43
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
43
44
|
with:
|
|
45
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
44
46
|
enable-cache: true
|
|
45
47
|
- run: uv python install 3.11
|
|
46
48
|
- run: uv venv
|
|
@@ -65,6 +67,7 @@ jobs:
|
|
|
65
67
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
66
68
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
67
69
|
with:
|
|
70
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
68
71
|
enable-cache: true
|
|
69
72
|
- run: uv python install ${{ matrix.python-version }}
|
|
70
73
|
- run: uv venv
|
|
@@ -93,6 +96,8 @@ jobs:
|
|
|
93
96
|
# fails every job at "Getting action download info", before a step runs.
|
|
94
97
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
95
98
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
99
|
+
with:
|
|
100
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
96
101
|
- run: uv python install 3.11
|
|
97
102
|
- run: uv venv
|
|
98
103
|
# Exercise the declared minimums. CI otherwise installs the newest of
|
|
@@ -117,6 +122,7 @@ jobs:
|
|
|
117
122
|
# Bump this deliberately; do not "tidy" it to `@v10`.
|
|
118
123
|
- uses: astral-sh/setup-uv@v10.0.1
|
|
119
124
|
with:
|
|
125
|
+
version: "0.13.7" # see UV_VERSION in tests/test_release_config.py
|
|
120
126
|
enable-cache: true
|
|
121
127
|
- run: uv python install 3.11
|
|
122
128
|
- run: uv venv
|
|
@@ -9,6 +9,27 @@ release could have this problem — every later one has a real predecessor tag
|
|
|
9
9
|
so leave subsequent headings as generated. release-please only prepends to this
|
|
10
10
|
file, so this edit survives. -->
|
|
11
11
|
|
|
12
|
+
## [0.7.0](https://github.com/sgjholt/SpecMod/compare/v0.6.0...v0.7.0) (2026-11-08)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### ⚠ BREAKING CHANGES
|
|
16
|
+
|
|
17
|
+
* **datasets:** event directories, waveform files and pick files are named in ISO 8601 basic format (`20190826T073047.000000Z`) instead of extended format (`2019-08-26T07:30:47.000000Z`), and `specmod fetch` writes the new names. Run `specmod migrate-names <dir>` on data fetched before 0.7; `Event.directory()` reads the old names with a DeprecationWarning until 0.8. Code that built a path from `Event.origin` or `SpectrumSet.event` should call `specmod.datasets.path_name` on it. See docs/upgrading.md, From 0.6 to 0.7.
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **datasets:** name times on disk without colons, and migrate existing data ([52168de](https://github.com/sgjholt/SpecMod/commit/52168dea5cd9ffd8ff16dd9dd70996824c0bbb93))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Code Refactoring
|
|
25
|
+
|
|
26
|
+
* **config:** narrow the default_factory sentinel instead of suppressing mypy ([961da50](https://github.com/sgjholt/SpecMod/commit/961da50c28aa8ba785879a2b2287c219f64ec216))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Documentation
|
|
30
|
+
|
|
31
|
+
* **roadmap:** file the default change and the config pin as shipped in v0.6.0 ([92eff1a](https://github.com/sgjholt/SpecMod/commit/92eff1af91dceea5ee65da5a55b460c026cb75de))
|
|
32
|
+
|
|
12
33
|
## [0.6.0](https://github.com/sgjholt/SpecMod/compare/v0.5.0...v0.6.0) (2026-09-27)
|
|
13
34
|
|
|
14
35
|
<!-- One Documentation line below was removed by hand, the third edit in this
|
|
@@ -91,13 +91,15 @@ from pandas.core.series import Series
|
|
|
91
91
|
import specmod.preprocess as pre
|
|
92
92
|
import specmod.utils as ut
|
|
93
93
|
from specmod.core.collection import SpectrumPair, SpectrumSet
|
|
94
|
+
from specmod.datasets import path_name
|
|
94
95
|
from specmod.pipeline import spectrum_set_from_streams
|
|
95
96
|
from specmod.staged import StagedFit
|
|
96
97
|
|
|
97
98
|
# Relative to this notebook, so it runs from wherever it is opened.
|
|
98
99
|
EVENTS = Path("data/events")
|
|
99
100
|
EVENT = "2019-08-26T07:30:47.000000Z" # Event origin time
|
|
100
|
-
|
|
101
|
+
# Named without colons, which Windows does not allow: 20190826T073047.000000Z.
|
|
102
|
+
DATA: Path = EVENTS / path_name(EVENT)
|
|
101
103
|
WAVEFORMS: Path = DATA / "waveforms"
|
|
102
104
|
STATIONS: Path = DATA / "stations" / "*.xml"
|
|
103
105
|
PICKS: Path = DATA / "picks"
|
|
@@ -1004,7 +1006,7 @@ it, because journal supplements want one.
|
|
|
1004
1006
|
code("""
|
|
1005
1007
|
from specmod.io import load, save
|
|
1006
1008
|
|
|
1007
|
-
path: Path = save(path=OUTPUT / spectra.event, spectra=spectra)
|
|
1009
|
+
path: Path = save(path=OUTPUT / path_name(spectra.event), spectra=spectra)
|
|
1008
1010
|
print(f"{path} ({path.stat().st_size // 1024} KB)")
|
|
1009
1011
|
|
|
1010
1012
|
back: SpectrumSet = load(path)
|
|
@@ -1016,8 +1018,8 @@ print(
|
|
|
1016
1018
|
|
|
1017
1019
|
|
|
1018
1020
|
code("""
|
|
1019
|
-
FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{spectra.event}.parquet", fits=fits)
|
|
1020
|
-
FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{spectra.event}.csv", fits=fits)
|
|
1021
|
+
FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{path_name(spectra.event)}.parquet", fits=fits)
|
|
1022
|
+
FitSpectra.write_flatfile(path=OUTPUT / "flatfiles" / f"{path_name(spectra.event)}.csv", fits=fits)
|
|
1021
1023
|
sorted(p.name for p in (OUTPUT / "flatfiles").iterdir())
|
|
1022
1024
|
""")
|
|
1023
1025
|
|
|
@@ -30,6 +30,13 @@ station wildcards against the data centre, cuts each channel to the window
|
|
|
30
30
|
relative to origin, and writes an `EventDirectory` with a `manifest.json`
|
|
31
31
|
beside it.
|
|
32
32
|
|
|
33
|
+
The event directory and each waveform file carry the origin in ISO 8601 basic
|
|
34
|
+
format — `20200318T130931.000000Z`, not `2020-03-18T13:09:31.000000Z` — because
|
|
35
|
+
the colons are not legal in a Windows file name, and a source distribution or
|
|
36
|
+
archive containing one is refused by uv 0.13. A fetch made before 0.7 has the
|
|
37
|
+
old names: run `specmod migrate-names build/magna_2020` before archiving it,
|
|
38
|
+
which renames them and updates the manifest to match.
|
|
39
|
+
|
|
33
40
|
Expect it to take a few minutes — Magna is 88 stations across seven networks.
|
|
34
41
|
|
|
35
42
|
**Check the manifest before going further.** It records what came back, not
|
|
@@ -191,9 +191,7 @@ found by a live fetch rather than by the suite:
|
|
|
191
191
|
as two independent records of one ground motion. `channel_priorities` and
|
|
192
192
|
`location_priorities` rank them, first match wins per station.
|
|
193
193
|
|
|
194
|
-
##
|
|
195
|
-
|
|
196
|
-
Merged on `main`, and it will name its version when a release goes out.
|
|
194
|
+
## Shipped in v0.6.0
|
|
197
195
|
|
|
198
196
|
### The default is FFT with Konno-Ohmachi smoothing
|
|
199
197
|
|
|
@@ -227,6 +225,22 @@ indistinguishable from a regression. They now carry their own settings in
|
|
|
227
225
|
`tests/golden/reference.toml`, and a default change leaves every one of them
|
|
228
226
|
untouched.
|
|
229
227
|
|
|
228
|
+
## In progress — not yet released
|
|
229
|
+
|
|
230
|
+
Merged on `main`, and it will name its version when a release goes out.
|
|
231
|
+
|
|
232
|
+
### Names on disk carry no colons
|
|
233
|
+
|
|
234
|
+
Event directories and the files in them were named by the origin as ObsPy
|
|
235
|
+
prints it, `2019-08-26T07:30:47.000000Z`. Windows does not allow the colons,
|
|
236
|
+
and uv 0.13 refuses to build a source distribution containing them, which
|
|
237
|
+
stopped the release. Names are now ISO 8601 basic format,
|
|
238
|
+
`20190826T073047.000000Z`, from `specmod.datasets.path_name`, and
|
|
239
|
+
`specmod fetch` writes them. `specmod migrate-names` renames data written
|
|
240
|
+
earlier and keeps its manifest verifying; until 0.8, data that has not been
|
|
241
|
+
migrated is still found, with a warning.
|
|
242
|
+
[Upgrading](upgrading.md#from-06-to-07) has the steps.
|
|
243
|
+
|
|
230
244
|
## Planned
|
|
231
245
|
|
|
232
246
|
### 1.0 — the API stops moving
|
|
@@ -268,7 +282,15 @@ Twice in a row makes it worth naming: what keeps moving is not the API, it is
|
|
|
268
282
|
what the API *does* with a setting that was already there. A deprecation cycle
|
|
269
283
|
does not protect against that, since there is nothing to deprecate; the
|
|
270
284
|
[upgrading guide](upgrading.md) is the only thing that does, and 1.0 will not
|
|
271
|
-
change that.
|
|
285
|
+
change that.
|
|
286
|
+
|
|
287
|
+
**v0.6.0 is not clean either, and is the first to break on purpose.** Its
|
|
288
|
+
default estimator and smoother changed, so every result moves unless a
|
|
289
|
+
configuration pinned both; the change is marked breaking, and
|
|
290
|
+
[Upgrading](upgrading.md#from-05-to-06) says what to pin. One signature moved
|
|
291
|
+
as well: the first argument of `load_config` is positional-only, so
|
|
292
|
+
`load_config(start=path)` is now read as a `[start]` configuration section and
|
|
293
|
+
refused. The count stands at zero.
|
|
272
294
|
|
|
273
295
|
## After 1.0
|
|
274
296
|
|
|
@@ -1,10 +1,70 @@
|
|
|
1
1
|
# Upgrading
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Six moves are documented here. Most readers porting code want `0.2` to `0.3`,
|
|
4
4
|
which renamed ten functions in `specmod.preprocess` and changed what they do
|
|
5
|
-
with the stream they are given.
|
|
6
|
-
change what a setting does, or which one is the default, so the break is
|
|
7
|
-
numbers rather than in the call.
|
|
5
|
+
with the stream they are given. Three of the moves above it rename nothing:
|
|
6
|
+
they change what a setting does, or which one is the default, so the break is
|
|
7
|
+
in the numbers rather than in the call. `0.6` to `0.7` renames files on disk,
|
|
8
|
+
and comes with a command that does it for you.
|
|
9
|
+
|
|
10
|
+
## From 0.6 to 0.7
|
|
11
|
+
|
|
12
|
+
**Times in file and directory names no longer contain colons.** An event
|
|
13
|
+
directory and the files inside it were named by the origin as ObsPy prints it,
|
|
14
|
+
`2019-08-26T07:30:47.000000Z`. They are now named in ISO 8601 basic format,
|
|
15
|
+
`20190826T073047.000000Z`, through `specmod.datasets.path_name`. Nothing in any
|
|
16
|
+
file changes, only names; no result moves.
|
|
17
|
+
|
|
18
|
+
The old names had two problems. Windows does not allow a colon in a file name,
|
|
19
|
+
so the repository could not be checked out there, and uv 0.13 refuses to build
|
|
20
|
+
a source distribution containing one, which stopped the release.
|
|
21
|
+
|
|
22
|
+
| | Before 0.7 | From 0.7 |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Event directory | `events/2019-08-26T07:30:47.000000Z/` | `events/20190826T073047.000000Z/` |
|
|
25
|
+
| Waveform file | `LV.L001..HHE_2019-08-26T07:30:47.000000Z` | `LV.L001..HHE_20190826T073047.000000Z` |
|
|
26
|
+
| Pick file | `picks/2019-08-26T07:30:47.000000Z.picks` | `picks/20190826T073047.000000Z.picks` |
|
|
27
|
+
| `manifest.json` `files` keys | the old paths | the new paths |
|
|
28
|
+
|
|
29
|
+
**`specmod fetch` writes the new names.** Data you fetched with an earlier
|
|
30
|
+
version keeps the old ones until you migrate it.
|
|
31
|
+
|
|
32
|
+
**To migrate a directory**, point the command at anything that contains event
|
|
33
|
+
data — one fetch, a tree of them, or a checkout's `tutorial/data/events`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
specmod migrate-names build/magna_2020 --dry-run # list what would change
|
|
37
|
+
specmod migrate-names build/magna_2020
|
|
38
|
+
specmod fetch datasets/magna_2020.toml -o build/magna_2020 --verify
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
It rewrites every extended-format time in every name under the directory, and
|
|
42
|
+
the `files` keys of any `manifest.json` there, so `fetch --verify` still passes:
|
|
43
|
+
the hashes are of file contents, which the migration does not touch. From
|
|
44
|
+
Python it is `specmod.datasets.migrate_names(root, dry_run=False)`, which
|
|
45
|
+
returns the `(old, new)` pairs. Running it twice is safe; the second run finds
|
|
46
|
+
nothing to do. It refuses, before renaming anything, when a new name is
|
|
47
|
+
already taken, which happens when one event exists in both layouts. Which copy
|
|
48
|
+
to keep is yours to decide.
|
|
49
|
+
|
|
50
|
+
**Unmigrated data is still read, until 0.8.** `Event.directory()` looks for the
|
|
51
|
+
new name first and falls back to the old one with a `DeprecationWarning` naming
|
|
52
|
+
the command to run. The fallback goes in 0.8, under the usual one-minor
|
|
53
|
+
deprecation.
|
|
54
|
+
|
|
55
|
+
**Code that built a path from the origin string** has to call `path_name`
|
|
56
|
+
instead:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from specmod.datasets import path_name
|
|
60
|
+
|
|
61
|
+
event_dir = out / path_name(event.origin) # was: out / event.origin
|
|
62
|
+
save(spectra_dir / path_name(spectra.event), s) # was: spectra_dir / spectra.event
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The origin itself is unchanged everywhere it is a time rather than a name:
|
|
66
|
+
`Event.origin`, `datasets/*.toml`, the manifest's `resolved.origin`, and
|
|
67
|
+
`SpectrumSet.event` all keep the extended format.
|
|
8
68
|
|
|
9
69
|
`0.x` ships breaking changes in minor bumps without a deprecation cycle,
|
|
10
70
|
because shimming an API still being worked out costs more than it protects.
|
|
@@ -13,8 +73,8 @@ See [Releasing the software](releasing.md#what-a-version-number-means-while-this
|
|
|
13
73
|
## From 0.5 to 0.6
|
|
14
74
|
|
|
15
75
|
**The default estimator is `fft` and the default smoother is `konno_ohmachi`.**
|
|
16
|
-
They were `multitaper` and `log_bins`. Nothing is renamed
|
|
17
|
-
|
|
76
|
+
They were `multitaper` and `log_bins`. Nothing is renamed; **every result
|
|
77
|
+
changes** unless your configuration already pinned both.
|
|
18
78
|
|
|
19
79
|
The two do different jobs. The estimator sets the spectrum that is fitted.
|
|
20
80
|
The smoother sets the band the fit is confined to — the signal-to-noise ratio
|
|
@@ -72,6 +132,11 @@ with config.using("studies/my_study.toml"):
|
|
|
72
132
|
The local file and the environment are excluded inside the block, and an
|
|
73
133
|
explicit `load_config(project_file=...)` still wins.
|
|
74
134
|
|
|
135
|
+
**The first argument of `load_config` is positional-only.**
|
|
136
|
+
`load_config(start=path)` now reads `start` as a configuration section and
|
|
137
|
+
raises `ValueError: Configuration section [start] must be a table`; write
|
|
138
|
+
`load_config(path)`. The keyword arguments are unchanged.
|
|
139
|
+
|
|
75
140
|
## From 0.4 to 0.5
|
|
76
141
|
|
|
77
142
|
**`max_radius_km` and `min_radius_km` now mean kilometres.** They were converted
|
|
@@ -38,7 +38,7 @@ from pathlib import Path
|
|
|
38
38
|
from typing import Any
|
|
39
39
|
|
|
40
40
|
from . import __version__
|
|
41
|
-
from .datasets import Event, EventDirectory
|
|
41
|
+
from .datasets import Event, EventDirectory, path_name
|
|
42
42
|
|
|
43
43
|
__all__ = [
|
|
44
44
|
"AcquisitionConfig",
|
|
@@ -567,7 +567,10 @@ def fetch(
|
|
|
567
567
|
bulk = _bulk_request(inventory, config, start, end)
|
|
568
568
|
stream = client.get_waveforms_bulk(bulk)
|
|
569
569
|
|
|
570
|
-
|
|
570
|
+
# Named by `path_name`, never by the origin string itself: its colons are
|
|
571
|
+
# not legal in a Windows name, and a source distribution carrying one is
|
|
572
|
+
# refused by uv 0.13.
|
|
573
|
+
paths = EventDirectory(Path(out) / path_name(event.origin))
|
|
571
574
|
paths.waveforms.mkdir(parents=True, exist_ok=True)
|
|
572
575
|
paths.stations.mkdir(parents=True, exist_ok=True)
|
|
573
576
|
paths.picks.mkdir(parents=True, exist_ok=True)
|
|
@@ -576,7 +579,7 @@ def fetch(
|
|
|
576
579
|
for trace in stream:
|
|
577
580
|
# One file per channel, named as the shipped data is, so a fetched
|
|
578
581
|
# event and a committed one are read by the same code.
|
|
579
|
-
name = f"{trace.id}_{trace.stats.starttime}"
|
|
582
|
+
name = f"{trace.id}_{path_name(trace.stats.starttime)}"
|
|
580
583
|
target = paths.waveforms / name
|
|
581
584
|
trace.write(str(target), format="MSEED")
|
|
582
585
|
written[str(target.relative_to(Path(out)))] = _sha256(target)
|
|
@@ -19,6 +19,7 @@ from .acquire import fetch, verify
|
|
|
19
19
|
from .config import load_config
|
|
20
20
|
from .config.provenance import Provenance
|
|
21
21
|
from .config.serialize import to_toml
|
|
22
|
+
from .datasets import migrate_names
|
|
22
23
|
|
|
23
24
|
#: Shared by every command that resolves configuration, so the layering flags
|
|
24
25
|
#: cannot drift apart between them.
|
|
@@ -135,5 +136,27 @@ def fetch_command(config_file: str, out: str, verify_only: bool) -> None:
|
|
|
135
136
|
)
|
|
136
137
|
|
|
137
138
|
|
|
139
|
+
@main.command("migrate-names")
|
|
140
|
+
@click.argument("root", type=click.Path(exists=True, file_okay=False))
|
|
141
|
+
@click.option(
|
|
142
|
+
"--dry-run",
|
|
143
|
+
is_flag=True,
|
|
144
|
+
help="List what would be renamed, and rename nothing.",
|
|
145
|
+
)
|
|
146
|
+
def migrate_names_command(root: str, dry_run: bool) -> None:
|
|
147
|
+
"""Rename event data written before 0.7 to colon-free names.
|
|
148
|
+
|
|
149
|
+
Every time in a file or directory name under ROOT is rewritten from
|
|
150
|
+
2019-08-26T07:30:47.000000Z to 20190826T073047.000000Z, and any
|
|
151
|
+
manifest.json there is updated to match, so `fetch --verify` still
|
|
152
|
+
passes. File contents are untouched. Safe to run twice.
|
|
153
|
+
"""
|
|
154
|
+
renames = migrate_names(root, dry_run=dry_run)
|
|
155
|
+
for old, new in renames:
|
|
156
|
+
click.echo(f"{old} -> {new.name}")
|
|
157
|
+
verb = "would rename" if dry_run else "renamed"
|
|
158
|
+
click.echo(f"{root}: {verb} {len(renames)} path(s)")
|
|
159
|
+
|
|
160
|
+
|
|
138
161
|
if __name__ == "__main__": # pragma: no cover
|
|
139
162
|
main()
|
|
@@ -12,7 +12,7 @@ values used for the published Magna run. Those live in
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
-
from dataclasses import dataclass, field, fields, is_dataclass
|
|
15
|
+
from dataclasses import MISSING, dataclass, field, fields, is_dataclass
|
|
16
16
|
from typing import Any, Literal, Self
|
|
17
17
|
|
|
18
18
|
__all__ = [
|
|
@@ -396,9 +396,14 @@ class Config:
|
|
|
396
396
|
f"Valid sections are {sorted(known)}."
|
|
397
397
|
)
|
|
398
398
|
kwargs: dict[str, Any] = {}
|
|
399
|
-
for
|
|
400
|
-
if name in data:
|
|
401
|
-
|
|
399
|
+
for f in fields(cls):
|
|
400
|
+
if f.name in data:
|
|
401
|
+
# Every section field has a default_factory; the check narrows
|
|
402
|
+
# the MISSING sentinel away for the type checker.
|
|
403
|
+
assert f.default_factory is not MISSING
|
|
404
|
+
kwargs[f.name] = _build_section(
|
|
405
|
+
f.default_factory(), data[f.name], f.name
|
|
406
|
+
)
|
|
402
407
|
return cls(**kwargs)
|
|
403
408
|
|
|
404
409
|
|
|
@@ -4,7 +4,7 @@ The layout follows ObsPy's ``mass_downloader``, which writes ``waveforms/``
|
|
|
4
4
|
and ``stations/`` beneath a per-event directory. This adds the three things a
|
|
5
5
|
spectral workflow needs alongside them::
|
|
6
6
|
|
|
7
|
-
tutorial/data/events/<origin>/
|
|
7
|
+
tutorial/data/events/<path_name(origin)>/
|
|
8
8
|
event.xml # QuakeML: origin, magnitudes, uncertainties
|
|
9
9
|
waveforms/ # one miniSEED file per channel
|
|
10
10
|
stations/inventory.xml # StationXML for those channels
|
|
@@ -20,6 +20,14 @@ Datasets that ship with the repository have their own loader —
|
|
|
20
20
|
:func:`load_pnr_2019` — and need no network. Published ones are fetched by
|
|
21
21
|
:func:`load`, cached by pooch and pinned by hash. :mod:`specmod.acquire`
|
|
22
22
|
produces both.
|
|
23
|
+
|
|
24
|
+
A time in a file or directory name is written by :func:`path_name`, in ISO 8601
|
|
25
|
+
basic format — ``20190826T073047.000000Z`` rather than
|
|
26
|
+
``2019-08-26T07:30:47.000000Z``. The extended form's colons are not legal in a
|
|
27
|
+
Windows file name, and uv 0.13 refuses to build a source distribution that
|
|
28
|
+
contains one. Directories written before 0.7 used the extended form;
|
|
29
|
+
:func:`migrate_names` renames them, and :meth:`Event.directory` still finds an
|
|
30
|
+
unmigrated one until 0.8.
|
|
23
31
|
"""
|
|
24
32
|
|
|
25
33
|
from __future__ import annotations
|
|
@@ -27,6 +35,8 @@ from __future__ import annotations
|
|
|
27
35
|
import glob
|
|
28
36
|
import json
|
|
29
37
|
import os
|
|
38
|
+
import re
|
|
39
|
+
import warnings
|
|
30
40
|
from dataclasses import dataclass
|
|
31
41
|
from pathlib import Path
|
|
32
42
|
from typing import Any
|
|
@@ -42,11 +52,45 @@ __all__ = [
|
|
|
42
52
|
"data_dir",
|
|
43
53
|
"load",
|
|
44
54
|
"load_pnr_2019",
|
|
55
|
+
"migrate_names",
|
|
56
|
+
"path_name",
|
|
45
57
|
]
|
|
46
58
|
|
|
47
59
|
#: Event directories, relative to the repository root.
|
|
48
60
|
EVENTS = Path("tutorial") / "data" / "events"
|
|
49
61
|
|
|
62
|
+
#: A time in ISO 8601 extended format, as names were written before 0.7: the
|
|
63
|
+
#: form :func:`str` gives an ``obspy.UTCDateTime``. The colons are what
|
|
64
|
+
#: :func:`migrate_names` exists to remove, so a time without them is left alone.
|
|
65
|
+
_EXTENDED_TIME = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def path_name(time: Any) -> str:
|
|
69
|
+
"""``time`` as it is written in a file or directory name.
|
|
70
|
+
|
|
71
|
+
ISO 8601 basic format with microseconds, ``20190826T073047.000000Z``: no
|
|
72
|
+
colons, which Windows does not allow in a name and uv 0.13 does not allow
|
|
73
|
+
in a source distribution, and the same fixed width for every time, so names
|
|
74
|
+
sort chronologically. Microseconds are kept so that two events in the same
|
|
75
|
+
second do not share a directory.
|
|
76
|
+
|
|
77
|
+
Takes anything ``obspy.UTCDateTime`` accepts, including a name this
|
|
78
|
+
function produced, so applying it twice changes nothing.
|
|
79
|
+
|
|
80
|
+
>>> path_name("2019-08-26T07:30:47.000000Z")
|
|
81
|
+
'20190826T073047.000000Z'
|
|
82
|
+
>>> path_name("2019-08-26T07:49:24.2")
|
|
83
|
+
'20190826T074924.200000Z'
|
|
84
|
+
"""
|
|
85
|
+
import obspy # noqa: PLC0415
|
|
86
|
+
|
|
87
|
+
return obspy.UTCDateTime(time).strftime("%Y%m%dT%H%M%S.%fZ")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _renamed(name: str) -> str:
|
|
91
|
+
"""``name`` with every extended-format time in it rewritten by :func:`path_name`."""
|
|
92
|
+
return _EXTENDED_TIME.sub(lambda match: path_name(match.group(0)), name)
|
|
93
|
+
|
|
50
94
|
|
|
51
95
|
@dataclass(frozen=True)
|
|
52
96
|
class EventDirectory:
|
|
@@ -133,13 +177,32 @@ class Event:
|
|
|
133
177
|
catalogue_magnitude_type: str | None = None
|
|
134
178
|
|
|
135
179
|
def directory(self, project_root: Path | str) -> EventDirectory:
|
|
136
|
-
"""Locate this event beneath ``project_root``, the repository root.
|
|
137
|
-
|
|
180
|
+
"""Locate this event beneath ``project_root``, the repository root.
|
|
181
|
+
|
|
182
|
+
The directory is named by :func:`path_name`. One written before 0.7,
|
|
183
|
+
named by the origin itself, is still found when the new name is
|
|
184
|
+
absent, with a :class:`DeprecationWarning`; that fallback is removed
|
|
185
|
+
in 0.8. ``specmod migrate-names`` renames it.
|
|
186
|
+
"""
|
|
187
|
+
events = Path(project_root) / EVENTS
|
|
188
|
+
current = events / path_name(self.origin)
|
|
189
|
+
legacy = events / self.origin
|
|
190
|
+
if not current.exists() and legacy.is_dir():
|
|
191
|
+
warnings.warn(
|
|
192
|
+
f"{legacy} is named in the pre-0.7 layout, whose colons are "
|
|
193
|
+
f"not legal on Windows; it is read for now, and will not be "
|
|
194
|
+
f"found from 0.8. Run `specmod migrate-names {events}` to "
|
|
195
|
+
f"rename it to {current.name}.",
|
|
196
|
+
DeprecationWarning,
|
|
197
|
+
stacklevel=2,
|
|
198
|
+
)
|
|
199
|
+
return EventDirectory(legacy)
|
|
200
|
+
return EventDirectory(current)
|
|
138
201
|
|
|
139
202
|
|
|
140
203
|
#: Preston New Road, 26 August 2019 — the induced event the tutorial and both
|
|
141
204
|
#: golden references are built around, and the largest of the PNR-2 sequence.
|
|
142
|
-
#: The origin time
|
|
205
|
+
#: The origin time, through :func:`path_name`, is the directory name.
|
|
143
206
|
#:
|
|
144
207
|
#: ``Mw 2.9`` is from the PNR-2 catalogue published with Cuadrilla's
|
|
145
208
|
#: hydraulic-fracture monitoring (NGDC, `709cbc2f-af5c-4d09-a4ea-6deb5aa8c5d8
|
|
@@ -314,3 +377,78 @@ def load(name: str, *, downloader: Any = None) -> Dataset:
|
|
|
314
377
|
json.loads(manifest_file.read_text()) if manifest_file.is_file() else None
|
|
315
378
|
)
|
|
316
379
|
return Dataset(event=spec.event, paths=paths, manifest=manifest)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
# --------------------------------------------------------------- migrating
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def migrate_names(
|
|
386
|
+
root: Path | str, *, dry_run: bool = False
|
|
387
|
+
) -> list[tuple[Path, Path]]:
|
|
388
|
+
"""Rename every file and directory under ``root`` to the 0.7 layout.
|
|
389
|
+
|
|
390
|
+
Each ISO 8601 extended-format time in a name is rewritten by
|
|
391
|
+
:func:`path_name`, so ``2019-08-26T07:30:47.000000Z/`` becomes
|
|
392
|
+
``20190826T073047.000000Z/`` and a waveform file
|
|
393
|
+
``LV.L001..HHE_2019-08-26T07:30:47.000000Z`` becomes
|
|
394
|
+
``LV.L001..HHE_20190826T073047.000000Z``. Nothing else in a name changes,
|
|
395
|
+
and no file's contents change, so every hash a manifest records stays
|
|
396
|
+
valid. What a ``manifest.json`` under ``root`` changes is its ``files``
|
|
397
|
+
keys, which are paths: they are rewritten the same way, so
|
|
398
|
+
``specmod fetch --verify`` still passes afterwards.
|
|
399
|
+
|
|
400
|
+
Returns ``(old, new)`` for every rename, deepest first, which is the order
|
|
401
|
+
they are made in. With ``dry_run`` nothing is renamed or rewritten. Running
|
|
402
|
+
it again on a migrated tree finds nothing to do.
|
|
403
|
+
|
|
404
|
+
Refuses before renaming anything if a new name is already taken, so a tree
|
|
405
|
+
that holds both layouts of one event is left for a person to reconcile
|
|
406
|
+
rather than half-migrated.
|
|
407
|
+
"""
|
|
408
|
+
root = Path(root)
|
|
409
|
+
if not root.is_dir():
|
|
410
|
+
raise NotADirectoryError(f"{root} is not a directory")
|
|
411
|
+
|
|
412
|
+
# Deepest first, so a directory is renamed after everything inside it and
|
|
413
|
+
# no path in the plan is invalidated by an earlier rename.
|
|
414
|
+
renames: list[tuple[Path, Path]] = []
|
|
415
|
+
for path in sorted(root.rglob("*"), key=lambda p: len(p.parts), reverse=True):
|
|
416
|
+
new_name = _renamed(path.name)
|
|
417
|
+
if new_name != path.name:
|
|
418
|
+
renames.append((path, path.with_name(new_name)))
|
|
419
|
+
|
|
420
|
+
targets = [new for _, new in renames]
|
|
421
|
+
taken = [new for new in targets if new.exists()]
|
|
422
|
+
if len(set(targets)) != len(targets) or taken:
|
|
423
|
+
clash = taken or [t for t in targets if targets.count(t) > 1]
|
|
424
|
+
raise FileExistsError(
|
|
425
|
+
f"migrating {root} would overwrite {sorted(set(map(str, clash)))}. "
|
|
426
|
+
f"Nothing was renamed: this tree holds both layouts of the same "
|
|
427
|
+
f"data, and which copy to keep is not a choice to make silently."
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
# Where each manifest will be once its directories are renamed.
|
|
431
|
+
manifests = [
|
|
432
|
+
root.joinpath(*(_renamed(part) for part in path.relative_to(root).parts))
|
|
433
|
+
for path in root.rglob("manifest.json")
|
|
434
|
+
if path.is_file()
|
|
435
|
+
]
|
|
436
|
+
if dry_run:
|
|
437
|
+
return renames
|
|
438
|
+
|
|
439
|
+
for old, new in renames:
|
|
440
|
+
old.rename(new)
|
|
441
|
+
|
|
442
|
+
for manifest_file in manifests:
|
|
443
|
+
manifest = json.loads(manifest_file.read_text())
|
|
444
|
+
files = manifest.get("files")
|
|
445
|
+
if not isinstance(files, dict):
|
|
446
|
+
continue
|
|
447
|
+
migrated = {
|
|
448
|
+
"/".join(_renamed(part) for part in name.split("/")): digest
|
|
449
|
+
for name, digest in files.items()
|
|
450
|
+
}
|
|
451
|
+
if migrated != files:
|
|
452
|
+
manifest["files"] = dict(sorted(migrated.items()))
|
|
453
|
+
manifest_file.write_text(json.dumps(manifest, indent=1) + "\n")
|
|
454
|
+
return renames
|