diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ac3a34c --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,59 @@ +name: docs + +# Construye el sitio MkDocs Material y lo publica en GitHub Pages. +# Publica desde `main` (docs estables, alineadas con el último release). Se puede +# disparar a mano con "Run workflow" (workflow_dispatch) para previsualizar antes +# de un release. Requiere: Settings → Pages → Source = "GitHub Actions" (una vez). + +on: + push: + branches: [main] + paths: + - "docs/**" + - "mkdocs.yml" + - "*.md" + - ".github/workflows/docs.yml" + workflow_dispatch: + +# Permisos mínimos para el deploy a Pages vía OIDC. +permissions: + contents: read + pages: write + id-token: write + +# Un solo deploy a la vez; no cancela uno en curso. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + python-version: "3.11" + - name: Sync (grupo docs) + run: uv sync --group docs + - name: Build (mkdocs) + run: uv run mkdocs build --clean + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + name: deploy + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index b2b938a..5f6aaf4 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,13 @@ valoraciones_* examples/**/*.duckdb +# Docs (MkDocs) — output de build local; el sitio se publica vía CI (docs.yml), +# nunca se commitea. +site/ + +# Borradores y exploración local (no va al repo) +scratch/ + # Logs logs *.log diff --git a/docs/Notas/22-frontera-y-alcance-de-bib2graph.md b/docs/Notas/22-frontera-y-alcance-de-bib2graph.md new file mode 100644 index 0000000..569a5b9 --- /dev/null +++ b/docs/Notas/22-frontera-y-alcance-de-bib2graph.md @@ -0,0 +1,60 @@ +# 22 — Frontera y alcance de bib2graph (nota de separación) + +> Qué es bib2graph y dónde se detiene **a propósito**. Esta nota existe para ser transparentes con +> quien la usa: dejar la frontera escrita, sin letra chica. Es la versión que habla al usuario; la +> decisión, una vez asentada, se gradúa a un ADR que habla a quien decide el código. Fecha: 2026-06-28. + +--- + +> **bib2graph convierte una búsqueda en un corpus, redes de citación y un orden de lectura priorizado —deterministas, reproducibles y con procedencia—. Te lleva hasta la lectura y se detiene ahí: no interpreta, no gestiona tu investigación ni usa IA; el juicio queda tuyo.** + +--- + +## Qué es + +bib2graph hace **una** cosa, y la hace bien: toma una búsqueda —una ecuación sobre OpenAlex o un archivo `.bib`— y la transforma en material estructurado para empezar a leer con criterio. Concretamente, te entrega: + +- **Un corpus** que crece siguiendo las citaciones (forward y backward chaining) y que **curás vos** —aceptar, rechazar, filtrar—. +- **Redes de citación** (acoplamiento, co-citación, co-autoría, instituciones, co-keywords), listas para Gephi, Cytoscape o networkx. +- **Un orden de lectura priorizado**: qué leer primero, según cuánto se acopla, co-cita o es central cada pieza dentro de tu corpus. +- **Procedencia** de todo: la cadena de búsqueda exacta, la fecha, los topes, la versión — para reconstruir y reportar cómo llegaste a lo que llegaste. + +Tres propiedades lo definen, y ninguna es decorativa: + +- **Determinista** — mismo input, mismo output. No hay azar ni modelo opaco en el medio. +- **Reproducible** — cualquiera puede correr lo mismo y obtener lo mismo. Eso lo vuelve defendible ante un revisor. +- **Con procedencia** — cómo lo hallaste viaja pegado al resultado. No hay magia que no puedas auditar. + +## Qué no es (y por qué, a propósito) + +Tan importante como lo que hace es lo que **elige no hacer**: + +- **No interpreta.** No te dice qué significan los clusters, no decide qué deberías citar, no escribe tu estado del arte. Te muestra la estructura; el sentido lo construís vos leyendo. +- **No gestiona tu investigación.** No tiene usuarios, ni proyectos, ni colecciones, ni anotaciones. No es un Zotero ni un Mendeley. Administra **un** corpus reproducible, no tu biblioteca a través de toda tu carrera. +- **No usa IA.** El descubrimiento es **estructura bibliométrica determinista** —acoplamiento, co-citación, centralidad—, no un modelo de lenguaje. Por eso es reproducible y reportable, y por eso no puede "alucinar" un paper que no existe. + +Esto no son carencias: son **la frontera**. Una herramienta que se limita a propósito es una herramienta en la que podés confiar y que podés reportar con honestidad. El día que bib2graph empezara a decidir por vos —qué es relevante, qué significa, qué citar— dejaría de ser auditable y se volvería justo lo que la buena investigación debe evitar: una caja que disfraza una elección de autoridad. + +## Por qué esta frontera + +- **Una cosa bien hecha.** Es más útil un motor afilado y predecible que una suite que hace de todo a medias. Si querés encadenarlo con otras herramientas —tuyas, de terceros, o las que vengan— podés, porque su salida es limpia, abierta y versionada. +- **El juicio es tuyo, y eso es deliberado.** bib2graph asiste el trabajo *mecánico* —seguir citas a mano agota y se hace mal justo cuando estás cansado—. El trabajo *de criterio* —formular la pregunta, dejar que mute al leer, interpretar las tensiones, decidir qué entra— sigue siendo humano. La herramienta te lleva hasta la lectura; el trabajo de verdad, el de pensar, empieza ahí y es tuyo. +- **Honestidad reportable.** Una corrida de bib2graph **no es** una revisión sistemática PRISMA, y no pretende serlo. Es un barrido exploratorio, reproducible y documentado, que reduce el sesgo de empezar tu lectura solo por lo que ya recordabas. Reportarlo así —con modestia y con la procedencia a la vista— es más fuerte, no más débil. + +## El principio de diseño: la frontera se sostiene sola + +Esta nota no enumera una lista cerrada de exclusiones; declara un **principio**. + +> Si una funcionalidad es muy distinta de *"convertir una búsqueda en estructura navegable"* —aunque sea una parte legítima del ciclo de investigación— **no entra en bib2graph: se construye al lado, como otra herramienta**. + +La frontera no se ensancha para acomodar lo nuevo. Lo nuevo se construye afuera y, si hace falta, se conecta consumiendo la salida limpia y versionada de bib2graph. Gestionar una biblioteca a través de proyectos, interpretar las tensiones de un campo, asistir la escritura: son trabajo real y parte del ciclo, pero son **otras** cosas, y mezclarlas adentro degradaría lo único que bib2graph promete ser —un motor determinista y confiable—. + +El principio es **fractal**: la misma disciplina que separa bib2graph de lo demás separa después módulo de módulo dentro del código, y esta nota (que habla al usuario) del ADR (que habla a quien decide el código). Trazar la línea con claridad, en cada nivel, es lo que mantiene cada pieza comprensible y confiable. + +## Dónde encaja en tu ciclo + +El ciclo de exploración bibliográfica es iterativo: sembrás, seguís citas, la pregunta muta, volvés a sembrar; después organizás, interpretás, escribís. bib2graph vive en el tramo del **forrajeo** —de la semilla a un corpus navegable con su orden de lectura— y se retira antes de la interpretación y la escritura. No te acompaña en todo el ciclo a propósito: hace bien su tramo y te entrega el material limpio para el tuyo. Lo que sigue —pensar, enmarcar, escribir— es tuyo, con tu juicio y con las herramientas que elijas. + +--- + +*bib2graph es software libre (GPL-3.0). El código es la fuente de verdad; esta nota fija la intención.* diff --git a/docs/ai-disclosure.md b/docs/ai-disclosure.md new file mode 100644 index 0000000..319dc5f --- /dev/null +++ b/docs/ai-disclosure.md @@ -0,0 +1,5 @@ +--- +title: IA en el desarrollo +--- + +{% include-markdown "../AI_DISCLOSURE.md" %} diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..6b9015b --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,5 @@ +--- +title: Changelog +--- + +{% include-markdown "../CHANGELOG.md" %} diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..1fd8277 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,5 @@ +--- +title: Contribuir +--- + +{% include-markdown "../CONTRIBUTING.md" %} diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000..c2129c1 --- /dev/null +++ b/docs/getting-started/installation.md @@ -0,0 +1,39 @@ +--- +title: Instalación +--- + +# Instalación + +bib2graph necesita **Python ≥ 3.11**. + +## Con uv (recomendado) + +Recomendamos [**uv**](https://docs.astral.sh/uv/) para gestionar el entorno: + +```bash +uv add bib2graph +``` + +## Con pip + +```bash +pip install bib2graph +``` + +## Extras opcionales + +Algunas capacidades viven en extras opt-in para mantener el núcleo liviano: + +| Extra | Habilita | Instalar | +|-------|----------|----------| +| `bibtex` | Sembrar el corpus desde archivos `.bib` | `uv add "bib2graph[bibtex]"` | +| `gui` | Interfaz local (FastAPI + SPA), aún en construcción | `uv add "bib2graph[gui]"` | + +## Verificá la instalación + +```bash +b2g --help +``` + +Deberías ver el grupo de comandos del CLI `b2g`. Si es así, seguí con el +[Quickstart](quickstart.md). diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md new file mode 100644 index 0000000..136b0d8 --- /dev/null +++ b/docs/getting-started/quickstart.md @@ -0,0 +1,57 @@ +--- +title: Quickstart +--- + +# Quickstart + +De una ecuación de búsqueda a un GraphML, sin escribir código. + +## Desde el CLI + + + +``` +$ b2g init mi-investigacion +Workspace creado en ./mi-investigacion +$ cd mi-investigacion +$ b2g seed --equation '"unequal ecological exchange"' --max-results 50 +Sembrando desde OpenAlex... +---> 100% +52 papers en el corpus +$ b2g build +Construyendo las redes... +---> 100% +Listas: acoplamiento · co-citación · co-autoría · instituciones · co-keywords +$ b2g export --format graphml +Exportadas a ./redes/*.graphml +``` + +En tres pasos pasaste de una ecuación a redes en GraphML, sin escribir código. + +Cada comando acepta `--json` para orquestarlo desde scripts o agentes. Para la +lista completa de comandos y opciones, corré `b2g --help` o mirá la +[referencia del CLI `b2g`](../reference/cli.md). + +!!! tip "Workspace por investigación" + `b2g init` crea una carpeta autocontenida (`workspace.json` + base de datos + + redes/snapshots/exports). Todos los comandos resuelven el workspace desde el + directorio actual — por eso el `cd` después de `init`. + +## Desde Python + +```python +from pathlib import Path +from bib2graph import OpenAlexSource, DuckDBStore, Networks, GraphMLExporter + +corpus = OpenAlexSource().seed('"unequal ecological exchange"').corpus +store = DuckDBStore(Path("biblioteca.duckdb")) +store.persist(corpus) + +for red in Networks.quick(store.load()): + GraphMLExporter().export(red.graph, red.metrics, out_dir=Path(f"redes/{red.spec.kind}")) +``` + +## Siguiente paso + +- Profundizá en flujos concretos en las [Guías](../guias/index.md). +- Entendé el modelo por dentro en [Arquitectura](../ARCHITECTURE.md). diff --git a/docs/guias/index.md b/docs/guias/index.md new file mode 100644 index 0000000..db61584 --- /dev/null +++ b/docs/guias/index.md @@ -0,0 +1,28 @@ +--- +title: Guías (how-to) +--- + +# Guías (how-to) + +Las **guías** son recetas orientadas a una tarea concreta: ya sabés qué querés +hacer y buscás los pasos para lograrlo. Si en cambio querés *aprender* desde +cero, empezá por los [Tutoriales](../tutoriales/index.md). + +!!! note "En construcción" + Estamos escribiendo las guías. Seguimiento en el + [issue #208](https://github.com/complexluise/bib2graph/issues/208). + Planificado: + + - Cómo elaborar y refinar una ecuación de búsqueda. + - Cómo expandir el corpus por citaciones (forrajeo) sin perder el foco. + - Cómo aplicar filtros PRISMA y curar (aceptar/rechazar) de forma versionable. + - Cómo interpretar las 5 redes y exportarlas a Gephi / Cytoscape. + - Cómo hacer reproducible un análisis (snapshots e historia del corpus). + +## Mientras tanto + +- [Quickstart](../getting-started/quickstart.md) — el camino más corto a un + GraphML. +- [Referencia del CLI `b2g`](../reference/cli.md) — todos los comandos y opciones. +- [API de Python](../reference/python-api.md) — para uso como librería. +- [Arquitectura](../ARCHITECTURE.md) — el modelo mental del ciclo y las redes. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..8757ee3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,74 @@ +--- +title: Inicio +--- + +# bib2graph + +> De una búsqueda bibliográfica a **redes de citación reproducibles** — una +> biblioteca de literatura que curás vos, sin servidores ni planillas. + +**bib2graph** toma una ecuación de búsqueda (o un archivo `.bib`), arma un corpus +de papers desde [OpenAlex](https://openalex.org), te deja **curarlo** y lo +proyecta a **redes bibliométricas** listas para analizar en Gephi, Cytoscape, +networkx o donde quieras: acoplamiento bibliográfico, co-citación, co-autoría, +colaboración institucional y co-ocurrencia de keywords. + +El corpus **persiste y crece** entre sesiones, y el resultado es **reproducible**: +mismo input, mismas redes. + +!!! warning "Alpha" + Mientras la versión sea `0.x`, la API puede cambiar entre releases menores. + Úsalo para explorar y validar, no como dependencia estable de producción + todavía. + +## Empezá acá + +
+ +- :material-download: **[Instalación](getting-started/installation.md)** + + Instalá `bib2graph` con `uv` o `pip` en un minuto. + +- :material-rocket-launch: **[Quickstart](getting-started/quickstart.md)** + + De una ecuación a un GraphML, sin escribir código. + +- :material-book-open-variant: **[Guías](guias/index.md)** + + Tutoriales y recetas para tareas concretas de investigación. + +- :material-api: **[Referencia](reference/cli.md)** + + El CLI `b2g` y la API de Python, autogeneradas desde el código. + +
+ +## Qué hace + +- **Siembra** desde una ecuación de búsqueda (OpenAlex) o un archivo BibTeX. +- **Expande** el corpus siguiendo citaciones, rankeando candidatos por + estructura — sin IA. +- **Curás vos:** aceptar/rechazar papers, filtros PRISMA, todo versionable en CSV. +- **5 redes bibliométricas:** acoplamiento, co-citación, co-autoría, + instituciones, co-keywords. +- **Sub-redes temáticas** filtrando por keyword. +- **Biblioteca persistente** (DuckDB) que crece entre sesiones. +- **Reproducible:** mismo corpus → mismas redes y comunidades (hash de contenido). +- **Dos interfaces:** CLI scriptable (`b2g`, salida `--json`) y librería de Python. +- **Exporta** a GraphML/CSV para Gephi, Cytoscape, networkx, etc. + +## Cómo se construye (y la IA) + +bib2graph se desarrolla **con la IA en el lazo**: una persona plantea el problema, +decide y **aprueba cada cambio**; modelos de IA implementan el código, los tests y +la documentación bajo esa dirección. **El producto en sí no usa IA generativa** — +el ranking del forrajeo es estructura bibliométrica determinista (acoplamiento, +co-citación, centralidad), sin LLM ni embeddings, y la curación es 100% humana. El +detalle está en [IA en el desarrollo](ai-disclosure.md). + +## Licencia + +[GPL-3.0-or-later](https://github.com/complexluise/bib2graph/blob/main/LICENSE) — +software libre con copyleft fuerte: cualquier derivado que se distribuya debe +seguir siendo libre y de código abierto. Es deliberado: esta herramienta queda +para la comunidad y no puede cerrarse en un producto propietario. diff --git a/docs/reference/cli.md b/docs/reference/cli.md new file mode 100644 index 0000000..dd7c5b0 --- /dev/null +++ b/docs/reference/cli.md @@ -0,0 +1,22 @@ +--- +title: CLI b2g +--- + +# CLI `b2g` + +Referencia completa del CLI agente-native `b2g`: el grupo principal y todos sus +subcomandos, con sus opciones y textos de ayuda. Se **autogenera desde el grupo +Click** (mkdocs-click): es la misma información que `b2g --help`, siempre en +sincronía con el código. + +!!! info "Salida JSON" + Cada subcomando acepta `--json` (envelope versionado) y respeta los exit + codes 0–5. Los detalles del contrato (envelope, exit codes, FSM) están en + [Contratos detallados](../API.md). + +::: mkdocs-click + :module: bib2graph.cli + :command: b2g + :prog_name: b2g + :depth: 1 + :style: table diff --git a/docs/reference/python-api.md b/docs/reference/python-api.md new file mode 100644 index 0000000..3f05ae8 --- /dev/null +++ b/docs/reference/python-api.md @@ -0,0 +1,63 @@ +--- +title: API de Python +--- + +# API de Python + +Referencia de la **superficie pública de la librería** `bib2graph`, agrupada por +tema. Se **autogenera desde los docstrings** del código (mkdocstrings): si cambia +el código, cambia esta página. + +Para usar la herramienta desde la terminal, mirá la [referencia del CLI `b2g`](cli.md). + +## Corpus y persistencia + +::: bib2graph.Corpus +::: bib2graph.Manifest +::: bib2graph.CorpusSnapshot +::: bib2graph.SchemaError +::: bib2graph.TabularBackend +::: bib2graph.InMemoryBackend +::: bib2graph.stores.duckdb.DuckDBStore +::: bib2graph.backends.duckdb.DuckDBBackend + +## Fuentes (siembra) + +::: bib2graph.Source +::: bib2graph.OpenAlexSource +::: bib2graph.BibtexSource +::: bib2graph.SeedResult + +## Forrajeo y curación + +::: bib2graph.Forager +::: bib2graph.RankedCandidates +::: bib2graph.GrowthPreview +::: bib2graph.Preprocessor +::: bib2graph.apply_filters +::: bib2graph.FilterCriterion + +## Redes (proyección) + +::: bib2graph.Networks +::: bib2graph.NetworkArtifact +::: bib2graph.BibliographicCouplingProjector +::: bib2graph.CoCitationProjector +::: bib2graph.AuthorCollaborationProjector +::: bib2graph.InstitutionCollaborationProjector +::: bib2graph.KeywordCoOccurrenceProjector + +## Análisis de redes + +::: bib2graph.network_metrics +::: bib2graph.centrality +::: bib2graph.detect_communities +::: bib2graph.assortativity +::: bib2graph.community_composition +::: bib2graph.cocitation_quality_report +::: bib2graph.QualityThresholds + +## Exportadores + +::: bib2graph.GraphMLExporter +::: bib2graph.CsvExporter diff --git a/docs/tutoriales/index.md b/docs/tutoriales/index.md new file mode 100644 index 0000000..f059454 --- /dev/null +++ b/docs/tutoriales/index.md @@ -0,0 +1,32 @@ +--- +title: Tutoriales +--- + +# Tutoriales + +Los **tutoriales** te enseñan a usar bib2graph paso a paso, partiendo de cero y +llegando a un resultado concreto. Son para *aprender* (a diferencia de las +[Guías](../guias/index.md), que son para *resolver una tarea puntual*). + +!!! note "En construcción" + El tutorial end-to-end está en preparación. Hoy, el camino guiado más corto + es el [Quickstart](../getting-started/quickstart.md). + + Planificado (seguimiento en el + [issue #208](https://github.com/complexluise/bib2graph/issues/208)): + + **De una pregunta de investigación a un mapa de citación.** Un caso real, + de punta a punta, reusando el corpus de ejemplo `examples/valoraciones/`: + + 1. Plantear la pregunta y la ecuación de búsqueda. + 2. Sembrar el corpus (`seed`). + 3. Expandir por citaciones (`chain`). + 4. Curar: aceptar/rechazar, filtros PRISMA. + 5. Construir las redes (`build`) y leerlas (`read`). + 6. Exportar a GraphML y abrir en Gephi. + +## Mientras tanto + +- [Quickstart](../getting-started/quickstart.md) — el ciclo mínimo en 2 minutos. +- [Guías (how-to)](../guias/index.md) — recetas para tareas concretas. +- [Referencia del CLI `b2g`](../reference/cli.md) — todos los comandos. diff --git a/docs/versioning.md b/docs/versioning.md new file mode 100644 index 0000000..4406d93 --- /dev/null +++ b/docs/versioning.md @@ -0,0 +1,5 @@ +--- +title: Versionado +--- + +{% include-markdown "../VERSIONING.md" %} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..b5f678a --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,152 @@ +# mkdocs.yml — sitio de documentación de bib2graph +# +# Fuente: docs/ (mismo árbol que ya mantenemos). El sitio NO duplica contenido: +# reusa API.md, ARCHITECTURE.md, los ADRs (docs/decisiones/) y, vía +# include-markdown, los .md de la raíz (CONTRIBUTING, CHANGELOG, AI_DISCLOSURE). +# Lo interno (Notas/, _archivo/, features/, ROADMAP/, borradores) queda fuera del +# sitio público — ver `exclude_docs` más abajo. +# +# Build local: uv sync --group docs && uv run mkdocs serve +# Deploy: .github/workflows/docs.yml → GitHub Pages (push a main) + +site_name: bib2graph +site_description: >- + De una búsqueda bibliográfica a redes de citación reproducibles — una + biblioteca de literatura que curás vos, sin servidores ni planillas. +site_author: Equipo bib2graph +site_url: https://complexluise.github.io/bib2graph/ +copyright: Copyright © 2026 Equipo bib2graph (complexluise) — GPL-3.0-or-later + +repo_url: https://github.com/complexluise/bib2graph +repo_name: complexluise/bib2graph +edit_uri: edit/dev/docs/ + +docs_dir: docs + +# Validación de enlaces. Muchos .md fuente —los ADRs (inmutables) y los archivos +# de raíz como CONTRIBUTING/VERSIONING/AGENTS— enlazan entre sí con rutas +# relativas que son correctas en GitHub (su render principal) pero caen fuera de +# docs/ en el sitio. Los degradamos a `info` para no ahogar el build con ruido +# intencional, PERO mantenemos `anchors: warn` para seguir cazando anclas rotas +# reales en las páginas que sí escribimos. +validation: + links: + not_found: info + anchors: warn + absolute_links: ignore + unrecognized_links: ignore + +theme: + name: material + language: es + icon: + repo: fontawesome/brands/github + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: teal + accent: deep orange + toggle: + icon: material/weather-night + name: Cambiar a modo oscuro + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: teal + accent: deep orange + toggle: + icon: material/weather-sunny + name: Cambiar a modo claro + features: + - navigation.tabs + - navigation.sections + - navigation.top + - navigation.instant + - navigation.tracking + - navigation.indexes + - toc.follow + - search.suggest + - search.highlight + - content.code.copy + - content.action.edit + +plugins: + - search: + lang: es + - include-markdown + # Anima sesiones de terminal: bloques precedidos por . + - termynal + # Referencia de la librería autogenerada desde los docstrings (src layout). + - mkdocstrings: + handlers: + python: + paths: [src] + options: + docstring_style: google + show_root_heading: true + show_root_full_path: false + show_source: false + show_signature_annotations: true + separate_signature: true + members_order: source + heading_level: 2 + filters: ["!^_"] + +markdown_extensions: + - admonition + - attr_list + - md_in_html + - tables + - toc: + permalink: true + - pymdownx.details + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + # Referencia del CLI b2g autogenerada desde el grupo Click. + - mkdocs-click + +# Páginas que se construyen y son enlazables por URL, pero NO aparecen en el +# sidebar (los 38 ADRs viven en su índice docs/decisiones/README.md). +not_in_nav: | + /decisiones/0*.md + /decisiones/registro-ia.md + +# Contenido interno: no se publica en el sitio. Si algo de acá hay que mostrar, +# se promueve explícitamente a una página del nav. +exclude_docs: | + Notas/ + _archivo/ + features/ + ROADMAP/ + RELEASE_TEMPLATE.md + +# Estructura Diátaxis (https://diataxis.fr): Empezar/Tutoriales (aprender), +# Guías (resolver una tarea), Referencia (consultar), Explicación (entender). +nav: + - Inicio: index.md + - Empezar: + - Instalación: getting-started/installation.md + - Quickstart: getting-started/quickstart.md + - Tutoriales: + - tutoriales/index.md + - Guías (how-to): + - guias/index.md + - Referencia: + - API de Python: reference/python-api.md + - CLI b2g: reference/cli.md + - Contratos detallados: API.md + - Explicación: + - Arquitectura: ARCHITECTURE.md + - Decisiones de diseño (ADRs): decisiones/README.md + - Requisitos del producto (PRD): PRD.md + - Proyecto: + - Contribuir: contributing.md + - Versionado: versioning.md + - IA en el desarrollo: ai-disclosure.md + - Changelog: changelog.md diff --git a/pyproject.toml b/pyproject.toml index 5f70e6a..eb7389c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -127,6 +127,19 @@ dev = [ "types-pyyaml>=6.0.12.20260518", ] +# Sitio de documentación (MkDocs Material). Solo para construir docs en local y +# en CI (.github/workflows/docs.yml); no se publica a PyPI. `uv sync --group docs`. +# La referencia de API/CLI se autogenera del código: mkdocstrings (librería, +# desde docstrings) y mkdocs-click (CLI b2g, desde el grupo Click). termynal +# anima las sesiones de terminal del quickstart. +docs = [ + "mkdocs-material>=9.5", + "mkdocs-include-markdown-plugin>=6", + "mkdocstrings[python]>=0.24", + "mkdocs-click>=0.8", + "termynal>=0.12", +] + # ----- Tooling ------------------------------------------------------------ [tool.ruff] diff --git a/src/bib2graph/sources/openalex.py b/src/bib2graph/sources/openalex.py index 15133c7..f15ee56 100644 --- a/src/bib2graph/sources/openalex.py +++ b/src/bib2graph/sources/openalex.py @@ -1084,12 +1084,13 @@ def fetch_citing_batch_with_works( ``None`` = sin tope. Returns: - Tupla ``(attribution, works_map)`` donde: - - ``attribution``: ``{seed_id: [citer_id, ...]}``, orden alfabético, - idéntico al retorno de ``fetch_citing_batch`` con los mismos args. - - ``works_map``: ``{citer_id: work_json}`` con el objeto JSON completo - (campos ``_FIELDS``: título, año, autores, referencias, etc.) de - cada citante distinto traído en las páginas de la atribución. + Tupla ``(attribution, works_map)``. ``attribution`` es + ``{seed_id: [citer_id, ...]}`` en orden alfabético, idéntico al + retorno de ``fetch_citing_batch`` con los mismos argumentos. + ``works_map`` es ``{citer_id: work_json}`` con el objeto JSON + completo (campos ``_FIELDS``: título, año, autores, referencias, + etc.) de cada citante distinto traído en las páginas de la + atribución. """ if not ids: return {}, {} diff --git a/uv.lock b/uv.lock index 25e4f8f..12632f5 100644 --- a/uv.lock +++ b/uv.lock @@ -77,9 +77,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, ] +[[package]] +name = "babel" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, +] + +[[package]] +name = "backrefs" +version = "7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" }, + { url = "https://files.pythonhosted.org/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491, upload-time = "2026-04-28T16:28:00.928Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5c/fb93d3092640a24dfb7bd7727a24016d7c01774ca013e60efd3f683c8002/backrefs-7.0-py314-none-any.whl", hash = "sha256:a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9", size = 412349, upload-time = "2026-04-28T16:28:02.412Z" }, +] + [[package]] name = "bib2graph" -version = "0.9.0" +version = "0.10.0" source = { editable = "." } dependencies = [ { name = "click" }, @@ -111,6 +133,13 @@ dev = [ { name = "types-pyyaml" }, { name = "types-tqdm" }, ] +docs = [ + { name = "mkdocs-click" }, + { name = "mkdocs-include-markdown-plugin" }, + { name = "mkdocs-material" }, + { name = "mkdocstrings", extra = ["python"] }, + { name = "termynal" }, +] [package.metadata] requires-dist = [ @@ -140,6 +169,13 @@ dev = [ { name = "types-pyyaml", specifier = ">=6.0.12.20260518" }, { name = "types-tqdm" }, ] +docs = [ + { name = "mkdocs-click", specifier = ">=0.8" }, + { name = "mkdocs-include-markdown-plugin", specifier = ">=6" }, + { name = "mkdocs-material", specifier = ">=9.5" }, + { name = "mkdocstrings", extras = ["python"], specifier = ">=0.24" }, + { name = "termynal", specifier = ">=0.12" }, +] [[package]] name = "bibtexparser" @@ -150,6 +186,15 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/44/1c/577d3ce406e88f370e80a6ebf76ae52a2866521e0b585e8ec612759894f1/bibtexparser-1.4.4.tar.gz", hash = "sha256:093b6c824f7a71d3a748867c4057b71f77c55b8dbc07efc993b781771520d8fb", size = 55594, upload-time = "2026-01-29T18:58:01.366Z" } +[[package]] +name = "bracex" +version = "2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/7c/a2a8a52db0ee751007507ddad3a1ddf1b0f763de546c588e7a828579bdad/bracex-2.7.tar.gz", hash = "sha256:4cb5d415a707f6beeb2779099486090bf98cbd8b7edbdfcb7cbea2f5fe6bdb48", size = 42150, upload-time = "2026-06-28T18:48:39.276Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/24/67865d7a710d86de496c7984e06023aa3656b5fae16ee229a530b57c0491/bracex-2.7-py3-none-any.whl", hash = "sha256:025043774188f8a05db36de9e3d4f7d82a8509a41a115cc134c44a60c36375eb", size = 11508, upload-time = "2026-06-28T18:48:38.138Z" }, +] + [[package]] name = "certifi" version = "2026.5.20" @@ -480,6 +525,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/13/37/a065dc3bd6e49423a6532c642ca7378d3f467b1ef44c2800c937af7f9739/filelock-3.29.4-py3-none-any.whl", hash = "sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767", size = 42757, upload-time = "2026-06-13T16:11:59.582Z" }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, +] + +[[package]] +name = "griffelib" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/e4/8d187ea29c2e30b3a09505c567513077d6117861bde1fbd997a167f262ec/griffelib-2.1.0.tar.gz", hash = "sha256:762a186d2c6fd6794d4ea20d428d597ffb857cb56b66421651cbba15bdd5e813", size = 216234, upload-time = "2026-06-19T12:05:42.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/d3/5268aeabf2ad82658c4e2ff3a060648d0f02f3926cb53247c0e4d0dab49e/griffelib-2.1.0-py3-none-any.whl", hash = "sha256:cc7b3d2d2865ad0b909fcc38086e3f554b5ea7acbaa7bbb7ecaa3f5dfb7d9f00", size = 142560, upload-time = "2026-06-19T12:05:38.742Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -629,6 +695,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, ] +[[package]] +name = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -703,6 +778,160 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588, upload-time = "2026-02-10T15:23:55.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530, upload-time = "2026-02-10T15:23:53.817Z" }, +] + +[[package]] +name = "mkdocs-click" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "markdown" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/c7/8c25f3a3b379def41e6d0bb5c4beeab7aa8a394b17e749f498504102cfa5/mkdocs_click-0.9.0.tar.gz", hash = "sha256:6050917628d4740517541422b607404d044117bc31b770c4f9e9e1939a50c908", size = 18720, upload-time = "2025-04-07T16:59:36.387Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/fc/9124ab36e2341e78d8d9c669511bd70f52ea0de8105760c31fabec1f9396/mkdocs_click-0.9.0-py3-none-any.whl", hash = "sha256:5208e828f4f68f63c847c1ef7be48edee9964090390afc8f5b3d4cbe5ea9bbed", size = 15104, upload-time = "2025-04-07T16:59:34.807Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, +] + +[[package]] +name = "mkdocs-include-markdown-plugin" +version = "7.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/2b/c788fc5c39ccba342eb9067be637327faebbc0e47da41ea79dc2526e693a/mkdocs_include_markdown_plugin-7.3.0.tar.gz", hash = "sha256:2800126746452e31c2e321bbd43c8190b356e0de353e20cbc16a34a3c3d6796c", size = 25527, upload-time = "2026-05-15T18:16:13.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/84/c0109c5991b89cbf028b8597f73fa7bd6a6b092407be2369a354b52737ab/mkdocs_include_markdown_plugin-7.3.0-py3-none-any.whl", hash = "sha256:5b5c99b5d3c9b9ce0114a9e60353bbafb6be53a26c2d3b74ec6b767a7a8e55ca", size = 29675, upload-time = "2026-05-15T18:16:12.654Z" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.7.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, +] + +[[package]] +name = "mkdocstrings" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "pymdown-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/5d/f888d4d3eb31359b327bc9b17a212d6ef03fe0b0682fbb3fc2cb849fb12b/mkdocstrings-1.0.4.tar.gz", hash = "sha256:3969a6515b77db65fd097b53c1b7aa4ae840bd71a2ee62a6a3e89503446d7172", size = 100088, upload-time = "2026-04-15T09:16:53.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/94/be70f8ee9c45f2f62b39a1f0e9303bc20e138a8f3b8e50ffd89498e177e1/mkdocstrings-1.0.4-py3-none-any.whl", hash = "sha256:63464b4b29053514f32a1dbbf604e52876d5e638111b0c295ab7ed3cac73ca9b", size = 35560, upload-time = "2026-04-15T09:16:51.436Z" }, +] + +[package.optional-dependencies] +python = [ + { name = "mkdocstrings-python" }, +] + +[[package]] +name = "mkdocstrings-python" +version = "2.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffelib" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocstrings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/b6/e858701499d57eee8b3fd8e78168083956c6683ddbe727b46758b19e1119/mkdocstrings_python-2.0.5.tar.gz", hash = "sha256:3a4d92556ad39637e88af94a5374213af9a8e3040c3824ceaed04b486c017594", size = 199578, upload-time = "2026-06-19T10:41:08.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/fc/10ab7e80650a9c9e8f4f1105f8c8e73567f88ed0c06ada589ab81d38687c/mkdocstrings_python-2.0.5-py3-none-any.whl", hash = "sha256:30c837bbff016549f659fcba6539ac351303f0fd7e713c89a040611072236e9d", size = 104951, upload-time = "2026-06-19T10:41:07.378Z" }, +] + [[package]] name = "mypy" version = "2.1.0" @@ -869,6 +1098,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, +] + [[package]] name = "pathspec" version = "1.1.1" @@ -1100,6 +1338,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pymdown-extensions" +version = "11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/67/f1e79672a5f91985577c7984c9709ca110e4fd37fe7fd167b60422e6ccc2/pymdown_extensions-11.0.tar.gz", hash = "sha256:8269cef0247f9e2d0a62fcea10860aba05c1cbab5470fd4b63230b96434dc589", size = 857049, upload-time = "2026-06-23T02:27:45.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/b6/1ae53367e28b9cffa3be7574e13fbe4589694272fd47710fbdbafd3d63c6/pymdown_extensions-11.0-py3-none-any.whl", hash = "sha256:fbc4acb641814fa9d17521bbd21a5240ef739a662f11c06330c4b78c93e954d6", size = 269415, upload-time = "2026-06-23T02:27:43.826Z" }, +] + [[package]] name = "pyparsing" version = "3.3.2" @@ -1139,6 +1390,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "python-discovery" version = "1.4.2" @@ -1217,6 +1480,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + [[package]] name = "questionary" version = "2.1.1" @@ -1308,6 +1583,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/b5/363906b1064fc6fe611783a61764927bbd91919aaaabe8cba82151ca93ef/rapidfuzz-3.14.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:dfef96543ced67d9513a422755db422ae1dc34dade0a1485e0b43e7342ed3ebf", size = 1509889, upload-time = "2026-04-07T11:16:28.487Z" }, ] +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + [[package]] name = "ruff" version = "0.15.17" @@ -1333,6 +1623,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "termcolor" version = "3.3.0" @@ -1342,6 +1641,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, ] +[[package]] +name = "termynal" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/8c/ea13868e6008ed964837db011648e3e7766e0ecfa1b42d7b9a4cf0c228e2/termynal-0.14.0.tar.gz", hash = "sha256:2918a1a6cf468497fbb3a72dc3726743988829eef1136d0c78a81df70ec869f6", size = 178022, upload-time = "2026-03-16T16:56:21.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/8f/c089f7c9f9fc1da25e6271386e5df84e1f6e0b04d2f0b408939b51b2876f/termynal-0.14.0-py3-none-any.whl", hash = "sha256:b5147545e29256d35148251f5c2d3d23dec1d2fb0b4ba464ff330a7de8b56fc6", size = 11754, upload-time = "2026-03-16T16:56:22.76Z" }, +] + [[package]] name = "tomli" version = "2.4.1" @@ -1507,6 +1818,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/87/b0667ede418386ab631e48924b845d326f366d61e6bd08fe68a748fae4d4/virtualenv-21.5.0-py3-none-any.whl", hash = "sha256:8f7c38605023688c89789f566959006af6d61c99eeeb9e58342eb780c5761e5e", size = 4557937, upload-time = "2026-06-13T20:36:42.967Z" }, ] +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + +[[package]] +name = "wcmatch" +version = "10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e47680bd5803a07cb3dfd17de84cde92dd217dcb9e05253/wcmatch-10.1.tar.gz", hash = "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af", size = 117421, upload-time = "2025-06-22T19:14:02.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854, upload-time = "2025-06-22T19:14:00.978Z" }, +] + [[package]] name = "wcwidth" version = "0.8.1"