diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe4a294..2a229cc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: python-version: 3.12 - name: Install APT dependencies run: | - sudo apt-get install -y git build-essential apt-utils wget libfreetype6 libpng-dev libopenblas-dev gcc gfortran libsnappy-dev + sudo apt-get install -y git build-essential apt-utils wget libfreetype6 libpng-dev libopenblas-dev gcc gfortran libsnappy-dev graphviz - name: Install dependencies run: | python -m pip install --upgrade pip poetry diff --git a/Makefile b/Makefile index 61f9c85..c7e9419 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ help: @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " redirectcheck to check that deleted files have proper redirects" @echo " dummy to check syntax errors of document sources" .PHONY: clean @@ -243,8 +244,14 @@ pseudoxml: @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." +.PHONY: redirectcheck +redirectcheck: + $(SPHINXBUILD) -b rediraffecheckdiff $(ALLSPHINXOPTS) $(BUILDDIR)/rediraffe + @echo + @echo "Redirect check complete." + .PHONY: dummy dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo - @echo "Build finished. Dummy builder generates no files." + @echo "Build finished. Dummy builder generates no files." \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index b114802..040bbe0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -6,6 +6,7 @@ version = "0.0.4" description = "A collection of accessible pygments styles" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "accessible-pygments-0.0.4.tar.gz", hash = "sha256:e7b57a9b15958e9601c7e9eb07a440c813283545a20973f2574a5f453d0e953e"}, {file = "accessible_pygments-0.0.4-py2.py3-none-any.whl", hash = "sha256:416c6d8c1ea1c5ad8701903a20fcedf953c6e720d64f33dc47bfb2d3f2fa4e8d"}, @@ -20,6 +21,7 @@ version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, @@ -31,6 +33,7 @@ version = "9.0.1" description = "A library for parsing ISO 8601 strings." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, @@ -45,18 +48,19 @@ version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] [[package]] name = "aw-client" @@ -64,6 +68,7 @@ version = "0.5.14" description = "Client library for ActivityWatch" optional = false python-versions = "^3.8" +groups = ["main"] files = [] develop = false @@ -87,6 +92,7 @@ version = "0.5.17" description = "Core library for ActivityWatch" optional = false python-versions = "^3.8" +groups = ["main"] files = [] develop = false @@ -113,6 +119,7 @@ version = "0.13.2" description = "Server for ActivityWatch" optional = false python-versions = "^3.8" +groups = ["main"] files = [] develop = false @@ -137,6 +144,7 @@ version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, @@ -154,6 +162,7 @@ version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" +groups = ["main"] files = [ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, @@ -175,6 +184,7 @@ version = "1.8.2" description = "Fast, simple object-to-object and broadcast signaling" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, @@ -186,6 +196,7 @@ version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, @@ -197,6 +208,7 @@ version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" +groups = ["main"] files = [ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, @@ -311,6 +323,7 @@ version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, @@ -325,6 +338,7 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -336,6 +350,7 @@ version = "0.9.1" description = "Python parser for the CommonMark Markdown spec" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, @@ -350,6 +365,7 @@ version = "2.1.0" description = "A library to handle automated deprecations" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, @@ -364,6 +380,7 @@ version = "0.17.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, @@ -375,6 +392,7 @@ version = "2.3.3" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"}, {file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"}, @@ -398,6 +416,7 @@ version = "5.0.0" description = "A Flask extension adding a decorator for CORS support" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc"}, {file = "flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef"}, @@ -412,6 +431,7 @@ version = "1.3.0" description = "Fully featured framework for fast, easy and documented API development with Flask" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "flask-restx-1.3.0.tar.gz", hash = "sha256:4f3d3fa7b6191fcc715b18c201a12cd875176f92ba4acc61626ccfd571ee1728"}, {file = "flask_restx-1.3.0-py2.py3-none-any.whl", hash = "sha256:636c56c3fb3f2c1df979e748019f084a938c4da2035a3e535a4673e4fc177691"}, @@ -436,6 +456,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -450,6 +471,7 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -461,6 +483,8 @@ version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.10\"" files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, @@ -470,12 +494,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -484,6 +508,7 @@ version = "6.4.5" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, @@ -493,7 +518,7 @@ files = [ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -506,6 +531,7 @@ version = "2.1.0" description = "Simple module to parse ISO 8601 dates" optional = false python-versions = ">=3.7,<4.0" +groups = ["main"] files = [ {file = "iso8601-2.1.0-py3-none-any.whl", hash = "sha256:aac4145c4dcb66ad8b648a02830f5e2ff6c24af20f4f482689be402db2429242"}, {file = "iso8601-2.1.0.tar.gz", hash = "sha256:6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df"}, @@ -517,6 +543,7 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -528,6 +555,7 @@ version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, @@ -545,6 +573,7 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -568,6 +597,7 @@ version = "2023.12.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, @@ -583,6 +613,7 @@ version = "2.7.0" description = "Python LiveReload is an awesome tool for web developers" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "livereload-2.7.0-py3-none-any.whl", hash = "sha256:19bee55aff51d5ade6ede0dc709189a0f904d3b906d3ea71641ed548acff3246"}, {file = "livereload-2.7.0.tar.gz", hash = "sha256:f4ba199ef93248902841e298670eebfe1aa9e148e19b343bc57dbf1b74de0513"}, @@ -597,6 +628,7 @@ version = "0.3.2" description = "Markdown and reStructuredText in a single file." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, @@ -612,6 +644,7 @@ version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, @@ -636,6 +669,7 @@ version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, @@ -705,6 +739,7 @@ version = "0.3.5" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdit-py-plugins-0.3.5.tar.gz", hash = "sha256:eee0adc7195e5827e17e02d2a258a2ba159944a0748f59c5099a4a27f78fcf6a"}, {file = "mdit_py_plugins-0.3.5-py3-none-any.whl", hash = "sha256:ca9a0714ea59a24b2b044a1831f48d817dd0c817e84339f20e7889f392d77c4e"}, @@ -724,6 +759,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -735,6 +771,7 @@ version = "0.8.4" description = "The fastest markdown parser in pure Python" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, @@ -746,6 +783,7 @@ version = "0.18.1" description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "myst-parser-0.18.1.tar.gz", hash = "sha256:79317f4bb2c13053dd6e64f9da1ba1da6cd9c40c8a430c447a7b146a594c246d"}, {file = "myst_parser-0.18.1-py3-none-any.whl", hash = "sha256:61b275b85d9f58aa327f370913ae1bec26ebad372cc99f3ab85c8ec3ee8d9fb8"}, @@ -772,6 +810,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -783,6 +822,7 @@ version = "3.17.8" description = "a little orm" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "peewee-3.17.8.tar.gz", hash = "sha256:ce1d05db3438830b989a1b9d0d0aa4e7f6134d5f6fd57686eeaa26a3e6485a8c"}, ] @@ -793,6 +833,7 @@ version = "1.0.0" description = "A thread-safe disk based persistent queue in Python." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "persist-queue-1.0.0.tar.gz", hash = "sha256:3ffb746902d3023fd09eb46897609fdee6c77b1641f19e2fc8d98d744bdfc845"}, {file = "persist_queue-1.0.0-py3-none-any.whl", hash = "sha256:81bb20030b480fcacecc3abe6261480c818246f4d838fdf0217e36c2552a5f3a"}, @@ -807,6 +848,8 @@ version = "1.3.10" description = "Resolve a name to an object." optional = false python-versions = ">=3.6" +groups = ["main"] +markers = "python_version == \"3.8\"" files = [ {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, @@ -818,6 +861,7 @@ version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, @@ -833,6 +877,7 @@ version = "0.13.3" description = "Bootstrap-based Sphinx theme from the PyData community" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pydata_sphinx_theme-0.13.3-py3-none-any.whl", hash = "sha256:bf41ca6c1c6216e929e28834e404bfc90e080b51915bbe7563b5e6fda70354f0"}, {file = "pydata_sphinx_theme-0.13.3.tar.gz", hash = "sha256:827f16b065c4fd97e847c11c108bf632b7f2ff53a3bca3272f63f3f3ff782ecc"}, @@ -859,6 +904,7 @@ version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, @@ -873,6 +919,7 @@ version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, @@ -884,6 +931,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -946,6 +994,7 @@ version = "0.7.1" description = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, @@ -962,6 +1011,7 @@ version = "0.35.1" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, @@ -977,6 +1027,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -998,6 +1049,7 @@ version = "0.1.4" description = "A pure python RFC3339 validator" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, @@ -1012,6 +1064,7 @@ version = "0.20.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "rpds_py-0.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a649dfd735fff086e8a9d0503a9f0c7d01b7912a333c7ae77e1515c08c146dad"}, {file = "rpds_py-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f16bc1334853e91ddaaa1217045dd7be166170beec337576818461268a3de67f"}, @@ -1124,19 +1177,20 @@ version = "75.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.5.2) ; sys_platform != \"cygwin\""] +core = ["importlib-metadata (>=6) ; python_version < \"3.10\"", "importlib-resources (>=5.10.2) ; python_version < \"3.9\"", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.12.*)", "pytest-mypy"] [[package]] name = "six" @@ -1144,6 +1198,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1155,6 +1210,7 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -1166,6 +1222,7 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -1177,6 +1234,7 @@ version = "4.5.0" description = "Python documentation generator" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"}, {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"}, @@ -1204,7 +1262,7 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast ; python_version < \"3.8\""] [[package]] name = "sphinx-autobuild" @@ -1212,6 +1270,7 @@ version = "2021.3.14" description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, @@ -1231,6 +1290,7 @@ version = "1.0.1" description = "A clean book theme for scientific explanations and documentation with Sphinx" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sphinx_book_theme-1.0.1-py3-none-any.whl", hash = "sha256:d15f8248b3718a9a6be0ba617a32d1591f9fa39c614469bface777ba06a73b75"}, {file = "sphinx_book_theme-1.0.1.tar.gz", hash = "sha256:927b399a6906be067e49c11ef1a87472f1b1964075c9eea30fb82c64b20aedee"}, @@ -1251,6 +1311,7 @@ version = "4.4.0" description = "Sphinx extension that automatically documents click applications" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sphinx-click-4.4.0.tar.gz", hash = "sha256:cc67692bd28f482c7f01531c61b64e9d2f069bfcf3d24cbbb51d4a84a749fa48"}, {file = "sphinx_click-4.4.0-py3-none-any.whl", hash = "sha256:2821c10a68fc9ee6ce7c92fad26540d8d8c8f45e6d7258f0e4fb7529ae8fab49"}, @@ -1267,6 +1328,7 @@ version = "1.3.0" description = "Read the Docs theme for Sphinx" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main"] files = [ {file = "sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0"}, {file = "sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931"}, @@ -1286,6 +1348,7 @@ version = "3.4.7" description = "Tabbed views for Sphinx" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sphinx-tabs-3.4.7.tar.gz", hash = "sha256:991ad4a424ff54119799ba1491701aa8130dd43509474aef45a81c42d889784d"}, {file = "sphinx_tabs-3.4.7-py3-none-any.whl", hash = "sha256:c12d7a36fd413b369e9e9967a0a4015781b71a9c393575419834f19204bd1915"}, @@ -1306,6 +1369,7 @@ version = "1.0.4" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, @@ -1321,6 +1385,7 @@ version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, @@ -1336,6 +1401,7 @@ version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, @@ -1351,6 +1417,7 @@ version = "4.1" description = "Extension to include jQuery on newer Sphinx releases" optional = false python-versions = ">=2.7" +groups = ["main"] files = [ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, @@ -1365,6 +1432,7 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -1379,6 +1447,7 @@ version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, @@ -1394,6 +1463,7 @@ version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, @@ -1403,12 +1473,28 @@ files = [ lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] +[[package]] +name = "sphinxext-rediraffe" +version = "0.2.7" +description = "Sphinx Extension that redirects non-existent pages to working pages." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "sphinxext-rediraffe-0.2.7.tar.gz", hash = "sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d"}, + {file = "sphinxext_rediraffe-0.2.7-py3-none-any.whl", hash = "sha256:9e430a52d4403847f4ffb3a8dd6dfc34a9fe43525305131f52ed899743a5fd8c"}, +] + +[package.dependencies] +sphinx = ">=2.0" + [[package]] name = "strict-rfc3339" version = "0.7" description = "Strict, simple, lightweight RFC3339 functions" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, ] @@ -1419,6 +1505,7 @@ version = "0.9.0" description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -1433,6 +1520,7 @@ version = "0.1.2" description = "Data type for representing time slots with a start and end." optional = false python-versions = ">=3.6,<4.0" +groups = ["main"] files = [ {file = "timeslot-0.1.2-py3-none-any.whl", hash = "sha256:2f8efaec7b0a4c1e56a92ec05533219332dd9d8b577539077664c233996911b5"}, {file = "timeslot-0.1.2.tar.gz", hash = "sha256:a2ac998657e3f3b9ca928757b4906add2c05390c5fc14ed792bb9028d08547b1"}, @@ -1444,6 +1532,7 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -1455,6 +1544,7 @@ version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, @@ -1475,6 +1565,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1486,13 +1577,14 @@ version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1503,6 +1595,7 @@ version = "2.3.8" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "werkzeug-2.3.8-py3-none-any.whl", hash = "sha256:bba1f19f8ec89d4d607a3bd62f1904bd2e609472d93cd85e9d4e178f472c3748"}, {file = "werkzeug-2.3.8.tar.gz", hash = "sha256:554b257c74bbeb7a0d254160a4f8ffe185243f52a52035060b761ca62d977f03"}, @@ -1520,20 +1613,22 @@ version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.10\"" files = [ {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.8" -content-hash = "0b54c89bda705fc95aa9147bce154897aa8cda5d77766ed52f292ae128ecd00c" +content-hash = "91cbb54bf1cb4ea223bd5c7b029b80f778ad808b19af786ce5b0fe8196472f49" diff --git a/pyproject.toml b/pyproject.toml index 0ae54c3..7095a58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ m2r2 = "*" myst-parser = "*" recommonmark = "^0.7.0" setuptools = "*" # needed in Python 3.12+: https://github.com/CrossNox/m2r2/issues/72 +sphinxext-rediraffe = "^0.2.7" [build-system] requires = ["poetry-core"] diff --git a/src/conf.py b/src/conf.py index d7d14c8..536fd32 100644 --- a/src/conf.py +++ b/src/conf.py @@ -41,6 +41,7 @@ "sphinx.ext.extlinks", "sphinx_tabs.tabs", "sphinx_click", + "sphinxext.rediraffe", ] extlinks = { @@ -50,6 +51,10 @@ "gh-aw": ("https://github.com/ActivityWatch/%s", ""), } +# Redirects for moved pages +rediraffe_redirects = "redirects.txt" +rediraffe_branch = "HEAD~1" + # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -391,4 +396,4 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. # -# texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False \ No newline at end of file diff --git a/src/examples.rst b/src/examples.rst index c5dcda9..6eb5113 100644 --- a/src/examples.rst +++ b/src/examples.rst @@ -1,9 +1,54 @@ Examples ======== -For more examples, see the examples in the aw-client repo: https://github.com/ActivityWatch/aw-client +This section provides practical examples for working with ActivityWatch, from retrieving your data to extending functionality with custom watchers. + +Getting Your Data Out +---------------------- + +**Most users should start here:** + +.. toctree:: + :maxdepth: 1 + + examples/working-with-data + +This comprehensive guide covers: + +* **Canonical Events** - Get processed activity data (what the web UI uses) +* **Custom Queries** - Write your own analysis using the query language +* **Raw Events** - Advanced direct access to bucket data +* **Safety Best Practices** - Avoiding data corruption with proper testing and dry-run modes + +The guide includes links to production-ready examples from the `aw-client repository `_. + +Writing Your Own Watchers +------------------------- + +Want to collect custom data? **See:** .. toctree:: - examples/querying-data + :maxdepth: 1 + examples/writing-watchers - examples/extending + +These guides cover: + +* **Minimal watcher example** - Get started with a simple template +* **Full-featured watcher** - Complete example with heartbeats and proper structure +* **Best practices** - Error handling, bucket management, and testing modes +* **Rust examples** - Alternative implementation for performance-critical watchers + +Watchers are small programs that collect data and send it to ActivityWatch. You can track anything with a timestamp! + +Example Code Repository +----------------------- + +The `aw-client examples `_ contain comprehensive, well-documented examples including: + +* **Time analysis** - ``time_spent_today.py``, ``working_hours.py`` +* **Data export** - ``load_dataframe.py`` for pandas integration +* **Data management** - ``redact_sensitive.py`` with safe dry-run mode +* **Advanced analysis** - ``suggest_categories.py`` with AI categorization + +All examples follow safety best practices with testing modes and error handling. diff --git a/src/examples/extending.rst b/src/examples/extending.rst deleted file mode 100644 index 82c5d4d..0000000 --- a/src/examples/extending.rst +++ /dev/null @@ -1,27 +0,0 @@ -Extending ActivityWatch -======================= - -So, you want to do something more with ActivityWatch? Great! - -We've tried to make things easy for you (and ourselves) so here's some advice on how to get started. - - -Collecting more data --------------------- - -ActivityWatch is written to be flexible to be able to gather most types of data. -Except for the included aw-watcher-window and aw-watcher-afk which tracks your application usage, there are additional so-called :doc:`../watchers` for activitywatch. -Watchers are small programs that collect data and send it off to the server. -The only requirement for what kind of data is sent to aw-server as an event is that it has to contain a starttime (and preferably a duration aswell) so it can fit on a timeline. - -If you want to write a watcher of your own, see :doc:`writing-watchers`. - - -Fetching Data -------------- - -If you want to fetch data from aw-server for visualization, exporting, backup or something we have not yet thought of, there are a few ways you can do this: - -* `Exporting a Bucket <../features/exporting-data>` If you want a complete dump of all events of bucket -* `Bucket REST API <../api/rest>` If you want to export raw events in a specific time interval from a bucket -* `Writing a Query ` If you want to summarize/aggregate one or more buckets into more easily readable data diff --git a/src/examples/querying-data.rst b/src/examples/querying-data.rst deleted file mode 100644 index 2652ae9..0000000 --- a/src/examples/querying-data.rst +++ /dev/null @@ -1,92 +0,0 @@ -Querying Data -============= - -There are a couple of ways to query data in ActivityWatch. - -aw-server supplies a "/query" endpoint (also accessible via aw-client's query method) which supplies a basic scripting language which you can utilize to do transformations on the server-side. -This option is good for basic analysis and for lightweight clients (such as aw-webui). - -Another option is to fetch events from the "/buckets/bucketname/events" endpoint (also accessible via aw-client's get_events method) and either program your own transformations or use transformation methods available in the aw-analysis python library (which includes all transformations available in the query endpoint). This requires a lot of more work since you will likely have to reprogram transformations already available in the query API, but on the other hand it is much more flexible. - - -Writing a Query ---------------- - -.. note:: - This section is still WIP. - There is still no documentation of all the transform functions, but for most simple queries these examples should be enough. - -Queries are the easiest yet advanced way to get events from aw-server buckets in a format which fits most needs. -Queries can be done by doing a POST request to aw-server either manually or with the aw-client library. - -For an incomplete API reference of the transform functions, see the API reference for :py:mod:`aw_transform` and :py:mod:`aw_query`. - -In a query you start by getting events from a bucket and assign that collection of events to a variable, then there are multiple transform functions which you can use to for example filter, limit, sort, and merge events from a bucket. -After that you assign what you want to receive from the request to the RETURN variable. - -Magic Variables: - There is a magic variable ``__CATEGORIES__`` you can use in the web UI's Query Explorer to include your configured categories in your query. - - Here's an example of using this variable to find all events categorized as "Web Browsing" - - .. code-block:: python - - events = flood(query_bucket(find_bucket("aw-watcher-window_"))); - not_afk = flood(query_bucket(find_bucket("aw-watcher-afk_"))); - not_afk = filter_keyvals(not_afk, "status", ["not-afk"]); - events = filter_period_intersect(events, not_afk); - events = categorize(events, __CATEGORIES__); - events = filter_keyvals(events, "$category", [["Work"]]); - RETURN = sort_by_duration(events); - -Minimal example: - Minimal query which only gets events from a bucket and returns it: - - .. code-block:: bash - - events = query_bucket("my_bucket"); - RETURN = events; - - -Example which arranges a hierarchy: - A query which merges events from a bucket in a key1->key2 hierarchy: - - .. code-block:: bash - - events = query_bucket("my_bucket"); - events = merge_events_by_keys(events, "merged_key1", "merged_key2"); - RETURN = events; - - -Example combining window and AFK events: - A simplified query example of how to summarize what programs used while not afk. - The query intersects the not-afk events from the afk bucket with the events from the window bucket, merges keys from the result and sorts by duration. - - .. code-block:: bash - - afk_events = query_bucket(find_bucket("aw-watcher-afk_")); - window_events = query_bucket(find_bucket("aw-watcher-window_")); - window_events = filter_period_intersect(window_events, filter_keyvals(afk_events, "status", ["not-afk"])); - merged_events = merge_events_by_keys(window_events, ["app", "title"]); - RETURN = sort_by_duration(merged_events); - -Example including aw-client: - This is an example of how you can do analysis and aggregation with the query method in Python with aw-client. - You probably need to install the client library by following the instructions in its `repository `_. - - .. note:: This example runs the client in *testing* mode, which means that it will try to connect to an aw-server in testing mode on the port 5666 instead of the normal 5600. - - .. literalinclude:: query_client.py - -Fetching Raw Events -------------------- -It is possible to fetch the raw events from a bucket. This is useful if you want to do your own analysis on the data, or if you want to use the aw-analysis library to do transformations on the data. - -Example fetching raw events from the "aw-watcher-window_" bucket: - This is an example that you can run in a Python to fetch raw events posted by the window watcher. - The scripts sums the time spent on each window title and showcases a data redaction use case. - - .. literalinclude:: raw_events.py - - -.. TODO `Bucket REST API <./rest.html#get-events>`_ diff --git a/src/examples/raw_events.py b/src/examples/raw_events.py deleted file mode 100644 index 93013e3..0000000 --- a/src/examples/raw_events.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -from aw_client import ActivityWatchClient -from aw_core.models import Event -import re -from typing import Pattern, cast, List, Set -from copy import deepcopy - -client = ActivityWatchClient("test-client", testing=True) - -# get the buckets -buckets = client.get_buckets() - -bucket_id = "{}_{}".format("aw-watcher-window", client.client_hostname) - -# fetches 1000 events from the bucket -events = client.get_events(bucket_id, limit=1000) - -# sum the time spent on each app -time_spent = dict() -for event in events: - app = event.data["app"] - if app in time_spent: - time_spent[app] += event.duration - else: - time_spent[app] = event.duration - -print(time_spent) - -# sensitive data pattern -pattern = re.compile(r"Binance|Metamask|TrustWallet|Trust Wallet") - -# what to replace sensitive data with -REDACTED = "REDACTED" - -def _redact_event(e: Event, pattern: Pattern) -> Event: - e = deepcopy(e) - for k, v in e.data.items(): - if isinstance(v, str): - if pattern.findall(v.lower()): - e.data[k] = REDACTED - return e - -def _find_sensitive(el: List[Event], pattern: Pattern) -> Set: - sensitive_ids = set() - for e in el: - if _check_event(e, pattern): - sensitive_ids.add(e.id) - return sensitive_ids - -def _check_event(e: Event, pattern: Pattern) -> bool: - for k, v in e.data.items(): - if isinstance(v, str): - if pattern.findall(v.lower()): - return True - return False - -sensitive_ids = _find_sensitive(events, pattern) -for e in events: - print(f"Event id: {e.id}") - if e.id in sensitive_ids: - e_before = e - e = _redact_event(e, pattern) - print(f"\nData before: {e_before.data}") - print(f"Data after: {e.data}") - - client.delete_event(bucket_id, cast(int, e_before.id)) - client.insert_event(bucket_id, e) - print("Redacted event") diff --git a/src/examples/working-with-data.rst b/src/examples/working-with-data.rst new file mode 100644 index 0000000..cf93a56 --- /dev/null +++ b/src/examples/working-with-data.rst @@ -0,0 +1,250 @@ +Working with ActivityWatch Data +=============================== + +This guide covers how to retrieve and work with your ActivityWatch data, from simple exports to advanced custom analysis. + +Most users will want to start with canonical events, which provide processed, meaningful activity data using the same logic as the web UI. + +.. contents:: + :local: + + +Getting Started: Canonical Events +---------------------------------- + +**Canonical events** are processed activity data that combine raw events from multiple sources (window tracking, AFK detection, etc.) into meaningful activity summaries. This is what the ActivityWatch web UI uses and what most users should start with. + +The canonical query automatically handles: + +* AFK detection from multiple sources (keyboard/mouse, audio, custom patterns) +* Multi-device activity merging with priority handling +* Event categorization using your configured rules +* Activity summarization and time calculations + +Python Implementation +~~~~~~~~~~~~~~~~~~~~~ + +The easiest way to get canonical events is using the Python client: + +.. code-block:: python + + from aw_client import ActivityWatchClient + from aw_client.queries import canonicalEvents + from datetime import datetime, timedelta + + # Connect to ActivityWatch (use testing=True for safety during development) + client = ActivityWatchClient("my-analysis", testing=False) + + # Get canonical events for today + start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + end = start + timedelta(days=1) + + # Get processed activity data (same as web UI) + events = canonicalEvents(client, start, end) + + # events now contains categorized activity data with durations + for event in events: + print(f"{event.data['app']}: {event.duration.total_seconds()/3600:.1f} hours") + +Reference Implementations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +For more advanced usage, see the actual canonical query implementations: + +* `Python canonical queries `_ - Production-ready functions +* `Web UI implementation `_ - TypeScript version used by the interface +* `Query examples `_ - Human-readable query snapshots + + +Comprehensive Examples +----------------------- + +The `aw-client repository `_ contains comprehensive, safe examples for working with ActivityWatch data: + +**Getting Started:** + * `time_spent_today.py `_ - Simple canonical events example + * `load_dataframe.py `_ - Load data into pandas for analysis + +**Advanced Analysis:** + * `working_hours.py `_ - Complex analysis using canonical queries + * `suggest_categories.py `_ - AI-powered category suggestions + +**Data Management:** + * `redact_sensitive.py `_ - Safely redact sensitive data (dry-run by default) + * `merge_buckets.py `_ - Combine data from multiple buckets + +.. note:: + These examples follow best practices including safety checks, dry-run modes, and comprehensive error handling. + + +Custom Queries +-------------- + +For specialized analysis beyond canonical events, you can write custom queries using ActivityWatch's query language. + +Query Language Basics +~~~~~~~~~~~~~~~~~~~~~~ + +Queries use a simple scripting language for server-side data processing: + +.. code-block:: javascript + + // Get events from a bucket + events = query_bucket("aw-watcher-window_hostname"); + + // Filter to only active periods + afk_events = query_bucket("aw-watcher-afk_hostname"); + not_afk = filter_keyvals(afk_events, "status", ["not-afk"]); + events = filter_period_intersect(events, not_afk); + + // Group by application + events = merge_events_by_keys(events, ["app"]); + + // Sort by time spent + RETURN = sort_by_duration(events); + +Key Transform Functions +~~~~~~~~~~~~~~~~~~~~~~~ + +Common query functions include: + +* ``query_bucket(bucket_id)`` - Get events from a bucket +* ``filter_keyvals(events, key, values)`` - Filter events by data values +* ``filter_period_intersect(events1, events2)`` - Only keep overlapping time periods +* ``merge_events_by_keys(events, keys)`` - Group events by specified keys +* ``categorize(events, categories)`` - Apply category rules +* ``sort_by_duration(events)`` - Sort by time spent + +Magic Variables +~~~~~~~~~~~~~~~ + +The web UI provides special variables you can use: + +* ``__CATEGORIES__`` - Your configured categorization rules +* ``find_bucket(pattern)`` - Find bucket names matching a pattern + +Example using categories to filter work activities: + +.. code-block:: javascript + + events = flood(query_bucket(find_bucket("aw-watcher-window_"))); + not_afk = flood(query_bucket(find_bucket("aw-watcher-afk_"))); + not_afk = filter_keyvals(not_afk, "status", ["not-afk"]); + events = filter_period_intersect(events, not_afk); + events = categorize(events, __CATEGORIES__); + events = filter_keyvals(events, "$category", [["Work"]]); + RETURN = sort_by_duration(events); + +Query Examples +~~~~~~~~~~~~~~ + +**Minimal example** - Get events from a bucket: + +.. code-block:: javascript + + events = query_bucket("my_bucket"); + RETURN = events; + +**Hierarchy example** - Create app→title hierarchy: + +.. code-block:: javascript + + events = query_bucket("my_bucket"); + events = merge_events_by_keys(events, ["app", "title"]); + RETURN = events; + +**Practical Python example** - The `query_client.py `_ file demonstrates: + +* Creating test buckets and data +* Writing queries with transformations +* Using merge_events_by_keys for grouping +* Time period filtering +* Safe cleanup with testing mode + +Complete Example +~~~~~~~~~~~~~~~~ + +Here's a complete custom query for analyzing productivity: + +.. code-block:: javascript + + // Get window and AFK events + events = flood(query_bucket(find_bucket("aw-watcher-window_"))); + not_afk = flood(query_bucket(find_bucket("aw-watcher-afk_"))); + not_afk = filter_keyvals(not_afk, "status", ["not-afk"]); + + // Only count active time + events = filter_period_intersect(events, not_afk); + + // Apply categories + events = categorize(events, __CATEGORIES__); + + // Filter to work categories only + events = filter_keyvals(events, "$category", [["Work"]]); + + // Group by application and sort + events = merge_events_by_keys(events, ["app"]); + RETURN = sort_by_duration(events); + +For more transform functions, see the API reference for :py:mod:`aw_transform` and :py:mod:`aw_query`. + + +Raw Events (Advanced) +---------------------- + +For maximum flexibility, you can work directly with raw events from buckets. This is useful for: + +* Custom analysis requiring full event details +* Building your own processing pipeline +* Using the aw-analysis library directly + +.. warning:: + Working with raw events requires more care: + + * Always test with ``testing=True`` first + * Back up your data before any modifications + * Use dry-run modes for destructive operations + * Raw events don't include AFK filtering or categorization + +Basic Raw Event Access +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from aw_client import ActivityWatchClient + + client = ActivityWatchClient("my-app", testing=True) # Safe mode + + # Get buckets + buckets = client.get_buckets() + print("Available buckets:", list(buckets.keys())) + + # Get raw events from a bucket + bucket_id = "aw-watcher-window_" + client.client_hostname + events = client.get_events(bucket_id, limit=100) + + # Process events manually + for event in events: + print(f"Time: {event.timestamp}, Duration: {event.duration}") + print(f"Data: {event.data}") + + +Exporting Data +-------------- + +For simple data export, see :doc:`../features/exporting-data` which covers: + +* Exporting complete buckets to JSON +* Using the web UI export feature +* Backup and restore procedures + +The web UI's export feature uses canonical events by default, providing clean, processed data suitable for external analysis. + + +API Reference +------------- + +For low-level API access, see: + +* :doc:`../api/rest` - HTTP REST API documentation +* :doc:`../api/python` - Python client library API \ No newline at end of file diff --git a/src/index.rst b/src/index.rst index 8cd3c15..52cba29 100644 --- a/src/index.rst +++ b/src/index.rst @@ -39,9 +39,6 @@ Table of contents architecture buckets-and-events examples - examples/querying-data - examples/writing-watchers - examples/extending api security server-compare @@ -62,5 +59,4 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` -* :ref:`search` - +* :ref:`search` \ No newline at end of file diff --git a/src/redirects.txt b/src/redirects.txt new file mode 100644 index 0000000..c0d0da3 --- /dev/null +++ b/src/redirects.txt @@ -0,0 +1,3 @@ +# Redirects for moved/deleted pages +examples/extending.rst examples.rst +examples/querying-data.rst examples/working-with-data.rst