Fuentes (siembra)¶
La costura de ingesta de doble puerta: Source y sus implementaciones
siembran el corpus inicial desde una ecuación de búsqueda (OpenAlex) o un archivo
BibTeX. Ambas puertas son primarias.
Source
¶
Bases: Protocol
Convierte una entrada externa en un Corpus.
El acceso a campos es DEFENSIVO (sin KeyError). Debe entregar al
menos el MÍNIMO UNIVERSAL (id, title, year, authors_raw,
keywords_raw); el enriquecimiento (refs/citantes/afiliaciones/
instituciones) es OPCIONAL (ADR 0018).
Methods:
| Name | Description |
|---|---|
seed |
Siembra desde una ecuación de búsqueda. |
load |
Siembra desde un archivo (export/pearls). |
seed
¶
seed(query: str) -> SeedResult
Siembra desde una ecuación de búsqueda.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Ecuación de búsqueda (WoS-style o nativa OpenAlex). |
required |
Returns:
| Type | Description |
|---|---|
SeedResult
|
|
SeedResult
|
de traducción. |
OpenAlexSource
¶
Siembra un Corpus desde la API de OpenAlex.
Ejemplo::
source = OpenAlexSource(email="yo@example.com")
result = source.seed('"unequal exchange" AND trade')
print(result.executed_query)
print(result.translation_report)
Credenciales (ADR 0012): email y api_key se inyectan; nunca
embebidos en el código. Resolución: argumento > OPENALEX_API_KEY >
ausencia → polite pool.
El transport permite pasar un httpx.MockTransport en tests (sin
red en CI).
Methods:
| Name | Description |
|---|---|
seed |
Siembra un |
fetch_citing |
Trae los works que citan al paper con |
fetch_dois_for |
Resuelve una lista de IDs de OpenAlex a sus DOIs. |
fetch_dois_to_openalex_ids |
Resuelve una lista de DOIs a sus IDs cortos de OpenAlex ( |
fetch_works_by_ids |
Trae works completos de OpenAlex a partir de una lista de IDs. |
fetch_citing_batch |
Trae en lote los citantes de varios papers usando |
fetch_citing_batch_with_works |
Como |
load |
Carga un export JSON de OpenAlex como |
seed
¶
seed(
query: str,
*,
native: bool = False,
exclude: list[str] | None = None,
min_year: int | None = None,
max_year: int | None = None,
) -> SeedResult
Siembra un Corpus desde una ecuación de búsqueda.
cited_by_id queda [] en el corpus sembrado: OpenAlex no lo
entrega inline; lo pueblan el Forager/Enricher en Hito 5/8.
references_id SÍ se trae inline (referenced_works).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Ecuación de búsqueda (WoS-style o nativa OpenAlex). |
required |
native
|
bool
|
Si es |
False
|
exclude
|
list[str] | None
|
Lista de términos a excluir de título/abstract (#30).
Cada término genera |
None
|
min_year
|
int | None
|
Año mínimo de publicación (filtro de rango OpenAlex).
Genera |
None
|
max_year
|
int | None
|
Año máximo de publicación (filtro de rango OpenAlex).
Genera |
None
|
Returns:
| Type | Description |
|---|---|
SeedResult
|
|
fetch_citing
¶
Trae los works que citan al paper con openalex_id dado.
Usa GET /works?filter=cites:{openalex_id} con paginación por
cursor, reutilizando el cliente httpx y _work_to_row. Es el
mecanismo del forward chaining (Hito 5, ADR 0008).
R5: agrega retry/backoff ante 429/5xx (_fetch_all_with_retry).
Sin esto, un corpus mediano falla en el forward chaining con un rate
limit de OpenAlex (Nota 06, RAÍZ 3).
Calcula el id canónico (D1) de cada citante antes de devolverlo,
de modo que los consumidores de estas filas (Forager,
compute_forward_scent) pueden identificar los candidatos.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
openalex_id
|
str
|
ID corto de OpenAlex (p. ej. |
required |
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
Lista de dicts con el schema canónico de filas del Corpus |
list[dict[str, Any]]
|
(misma estructura que produce |
list[dict[str, Any]]
|
con |
fetch_dois_for
¶
Resuelve una lista de IDs de OpenAlex a sus DOIs.
Batchea la consulta en lotes de hasta 100 IDs por request, usando el
filtro openalex_id:W1|W2|... con select=id,doi. Reutiliza el
retry/backoff de _fetch_all_with_retry para resiliencia ante 429/5xx.
Diseñado para el OpenAlexEnricher (Hito 8a): mantiene la frontera
núcleo/costura — el Source hace la I/O; el Enricher orquesta.
Hito 8b (futuro): el mismo patrón sirve para resolver
cited_by_id; el Enricher solo necesita un método distinto o un
argumento adicional.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids
|
list[str]
|
Lista de IDs cortos de OpenAlex (p. ej. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
Dict |
dict[str, str]
|
sin DOI en OpenAlex simplemente no aparecen en el resultado. |
fetch_dois_to_openalex_ids
¶
Resuelve una lista de DOIs a sus IDs cortos de OpenAlex (W…).
Batchea la consulta en lotes de hasta 100 DOIs por request, usando el
filtro doi:d1|d2|... con select=id,doi. Reutiliza el
retry/backoff de _fetch_batch_select para resiliencia ante 429/5xx.
Diseñado para service.resolve (ADR 0035): espeja la dirección
INVERSA de fetch_dois_for (ids→dois) pero va en la dirección
dois→source_id. Mismo patrón de batching/select/retry/polite-pool.
Normaliza los DOIs de entrada (minúsculas, sin prefijo URL) con
_normalize_doi antes de armar el filtro. El dict resultado usa
también el DOI normalizado como clave.
DOIs no encontrados en OpenAlex simplemente no aparecen en el resultado (no son error).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dois
|
list[str]
|
Lista de DOIs (con o sin prefijo URL, mayúsculas/minúsculas). |
required |
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
Dict |
dict[str, str]
|
Los DOIs sin match en OpenAlex no aparecen en el resultado. |
fetch_works_by_ids
¶
fetch_works_by_ids(ids: list[str]) -> Corpus
Trae works completos de OpenAlex a partir de una lista de IDs.
Batchea la consulta en lotes de hasta 100 IDs por request, usando el
filtro openalex_id:W1|W2|... con select=_FIELDS (todos los
campos del schema canónico). Reutiliza _fetch_batch_select y
el retry/backoff de la infraestructura existente.
Los works resultantes se marcan como is_seed=False (son candidatos,
no semillas de una ecuación) con curation_status=CANDIDATE y
provenance[action="fetched_by_id"].
IDs inexistentes en OpenAlex son simplemente omitidos sin error: el filtro OR devuelve solo los works encontrados.
Las filas del Corpus retornado se ordenan por id canónico (D1)
para garantizar determinismo entre corridas (ADR 0017).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids
|
list[str]
|
Lista de IDs de OpenAlex (p. ej. |
required |
Returns:
| Type | Description |
|---|---|
Corpus
|
|
Corpus
|
|
Corpus
|
Vacío si ningún ID es encontrado. |
fetch_citing_batch
¶
fetch_citing_batch(
ids: list[str],
*,
max_per_paper: int | None = None,
since: date | None = None,
) -> dict[str, list[str]]
Trae en lote los citantes de varios papers usando cites:W1|W2|....
Reemplaza N llamadas individuales a fetch_citing (patrón N+1) por una
sola request por lote (≤50 IDs, límite empírico de OpenAlex para OR en
cites:). Preserva el retry/backoff de _fetch_all_with_retry.
Presupuesto por semilla (anti-starvation): pagina con cursor sobre el
filtro OR del lote y, página a página, atribuye cada citante a las semillas
objetivo cruzando references_id del citante con el set de IDs. Lleva
un contador por semilla y deja de paginar cuando TODAS las semillas del
lote alcanzaron max_per_paper citantes (o se agota la paginación).
Así la semilla más citada no consume el presupuesto de las demás.
El filtro cites:W1|W2 es un OR válido en la API de OpenAlex: devuelve
todos los works que citan al menos uno de los IDs listados.
Thin wrapper sobre _fetch_citing_pages que descarta el mapa de works
para mantener la firma/contrato actual (usado por el OpenAlexEnricher,
Hito 8b).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids
|
list[str]
|
Lista de IDs cortos de OpenAlex (p. ej. |
required |
max_per_paper
|
int | None
|
Presupuesto máximo de citantes a recolectar por semilla.
|
None
|
Returns:
| Type | Description |
|---|---|
dict[str, list[str]]
|
Dict |
dict[str, list[str]]
|
ya están atribuidos (cruzando |
dict[str, list[str]]
|
acotados a |
dict[str, list[str]]
|
de OpenAlex. Orden determinista (alfabético). |
fetch_citing_batch_with_works
¶
fetch_citing_batch_with_works(
ids: list[str],
*,
max_per_paper: int | None = None,
since: date | None = None,
) -> tuple[dict[str, list[str]], dict[str, dict[str, Any]]]
Como fetch_citing_batch pero conserva los objetos JSON completos.
Misma lógica de paginación, atribución y presupuesto que
fetch_citing_batch, sin red extra: los works ya vienen en las
páginas que se traen para la atribución. Reutiliza _fetch_citing_pages
(no duplica la lógica).
Diseñado para el forward chaining del Forager (#78, opción A1):
permite materializar filas con metadata real (título/año/autores) en vez
de placeholders [candidate:W...].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids
|
list[str]
|
Lista de IDs cortos de OpenAlex (p. ej. |
required |
max_per_paper
|
int | None
|
Presupuesto máximo de citantes por semilla.
|
None
|
Returns:
| Type | Description |
|---|---|
dict[str, list[str]]
|
Tupla |
dict[str, dict[str, Any]]
|
|
tuple[dict[str, list[str]], dict[str, dict[str, Any]]]
|
retorno de |
tuple[dict[str, list[str]], dict[str, dict[str, Any]]]
|
|
tuple[dict[str, list[str]], dict[str, dict[str, Any]]]
|
completo (campos |
tuple[dict[str, list[str]], dict[str, dict[str, Any]]]
|
etc.) de cada citante distinto traído en las páginas de la |
tuple[dict[str, list[str]], dict[str, dict[str, Any]]]
|
atribución. |
load
¶
load(path: str) -> Corpus
Carga un export JSON de OpenAlex como Corpus.
Cada objeto del array JSON se trata como un Work de OpenAlex y se
mapea al schema canónico con is_seed=True.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Ruta al archivo JSON (array de Works). |
required |
Returns:
| Type | Description |
|---|---|
Corpus
|
|
BibtexSource
¶
Siembra un Corpus desde un archivo BibTeX.
BibTeX es Source secundaria (ADR 0007): entrega el mínimo universal
(título, año, autores, keywords) pero típicamente no referencias ni
citantes.
El import de bibtexparser es perezoso: se instala con el extra
[bibtex] (pip install "bib2graph[bibtex]").
Example::
source = BibtexSource()
corpus = source.load("semillas.bib")
Methods:
| Name | Description |
|---|---|
seed |
No implementado: BibTeX no siembra por ecuación. |
load |
Carga un archivo .bib como |
seed
¶
seed(query: str) -> SeedResult
No implementado: BibTeX no siembra por ecuación.
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
Siempre. Usa |
load
¶
load(path: str) -> Corpus
Carga un archivo .bib como Corpus.
Todos los papers se marcan con is_seed=True y
curation_status='candidate'. Campos faltantes quedan en None
(sin KeyError, bugfix T1).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Ruta al archivo |
required |
Returns:
| Type | Description |
|---|---|
Corpus
|
|
Raises:
| Type | Description |
|---|---|
ImportError
|
Si |
SeedResult
¶
Bases: BaseModel
Resultado de Source.seed().
Agrupa el corpus sembrado, la query exacta ejecutada (consciencia de traducción, ADR 0007) y el reporte de mapeo (qué mapeó limpio, qué se aproximó, qué se descartó).
corpus se valida en runtime (arbitrary_types_allowed porque Corpus
no es un BaseModel). No hay circularidad: corpus.py no importa
sources.