Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.10.0"
".": "0.10.1"
}
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ desde los Conventional Commits y bumpea `pyproject.toml`. Al mergear ese PR se c
`vX.Y.Z` y el GitHub Release. Las secciones por debajo de `[0.3.0]` son el historial previo a
la conexión del tooling (se mantuvieron a mano); de acá en adelante las gestiona el bot.

## [0.10.1](https://github.com/complexluise/bib2graph/compare/v0.10.0...v0.10.1) (2026-06-30)


### Bug Fixes

* **export:** escribir CSV en utf-8-sig (BOM) para Excel-Windows ([#216](https://github.com/complexluise/bib2graph/issues/216)) ([66339a7](https://github.com/complexluise/bib2graph/commit/66339a787debb04e88413147cfc8d7e22916ae2f))

## [0.10.0](https://github.com/complexluise/bib2graph/compare/v0.9.0...v0.10.0) (2026-06-28)


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ build-backend = "hatchling.build"

[project]
name = "bib2graph"
version = "0.10.0" # Hitos 7 (dedup) + 8 (co-citación). De acá en adelante el bump
version = "0.10.1" # Hitos 7 (dedup) + 8 (co-citación). De acá en adelante el bump
# lo maneja release-please vía PR de release (CI ya conectado).
description = "Librería de Python para transformar corpus bibliográficos en redes bibliométricas reproducibles (co-citación, colaboración, co-ocurrencia)."
readme = "README.md"
Expand Down
6 changes: 4 additions & 2 deletions src/bib2graph/exporters/csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def export(

def _write_aristas(self, g: _Graph, out_path: Path) -> None:
"""Escribe aristas.csv con columnas source,target,weight (D5)."""
with open(out_path / "aristas.csv", "w", newline="", encoding="utf-8") as f:
# utf-8-sig (BOM) para que Excel-Windows autodetecte UTF-8 y no rompa tildes (#214)
with open(out_path / "aristas.csv", "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["source", "target", "weight"])
# Orden determinista: aristas ordenadas por (source, target)
Expand Down Expand Up @@ -85,7 +86,8 @@ def _write_nodos(
metric_cols = sorted(metric_keys)
header = ["id", "label", *attr_cols, *metric_cols]

with open(out_path / "nodos.csv", "w", newline="", encoding="utf-8") as f:
# utf-8-sig (BOM) para que Excel-Windows autodetecte UTF-8 y no rompa tildes (#214)
with open(out_path / "nodos.csv", "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(header)
# Orden determinista: nodos ordenados por id como string
Expand Down
26 changes: 25 additions & 1 deletion tests/unit/test_exporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,30 @@ def test_csv_aristas_columnas_correctas(
"""aristas.csv tiene columnas source,target,weight (D5)."""
CsvExporter().export(simple_graph, results_dict, tmp_path)

content = (tmp_path / "aristas.csv").read_text(encoding="utf-8")
content = (tmp_path / "aristas.csv").read_text(encoding="utf-8-sig")
header = content.splitlines()[0]
assert header == "source,target,weight"


@pytest.mark.unit
def test_csv_escribe_con_bom_utf8_para_excel(tmp_path: Path) -> None:
"""Los CSV se escriben en utf-8-sig (BOM) para Excel-Windows (#214).

Sin BOM, Excel asume cp1252 y rompe las tildes (Valoración → Valoración).
Regresión: el export debe empezar con el BOM UTF-8 y las tildes deben
sobrevivir el round-trip.
"""
g = nx.Graph()
g.add_node("doi:abc", label="Valoración estética (Müller, 1987)")
g.add_node("doi:def", label="Crítica de la razón")
g.add_edge("doi:abc", "doi:def", weight=3)

CsvExporter().export(g, {"grado": dict(g.degree())}, tmp_path)

for name in ("nodos.csv", "aristas.csv"):
raw = (tmp_path / name).read_bytes()
assert raw.startswith(b"\xef\xbb\xbf"), f"{name} debe empezar con BOM UTF-8"

# Las tildes sobreviven el round-trip leyendo utf-8-sig
nodos = (tmp_path / "nodos.csv").read_text(encoding="utf-8-sig")
assert "Valoración estética (Müller, 1987)" in nodos
Loading