Skip to content

Commit 946ecce

Browse files
authored
Merge pull request #2 from harumiWeb/feature
PyPIパッケージ更新
2 parents d567e45 + db08b63 commit 946ecce

5 files changed

Lines changed: 68 additions & 61 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
ExStruct reads Excel workbooks and outputs structured data (cells, table candidates, shapes, charts, print areas/views, auto page-break areas, hyperlinks) as JSON by default, with optional YAML/TOON formats. It targets both COM/Excel environments (rich extraction) and non-COM environments (cells + table candidates + print areas), with tunable detection heuristics and multiple output modes to fit LLM/RAG pipelines.
88

9+
[日本版README](README.ja.md)
10+
911
## Features
1012

1113
- **Excel → Structured JSON**: cells, shapes, charts, table candidates, print areas/views, and auto page-break areas per sheet.

docs/agents/FEATURE_SPEC.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66

77
## リファクタリング案
88

9-
- PrintAreaView 判定と出力フラグの分散
10-
11-
- 事象: shapes/charts のフィルタとサイズ出力フラグが io と engine の両方に散在し、呼び出し側の引数も増えてきた。
12-
- 対策案: PrintAreaView のビルドをサービスクラス/モジュールに切り出し、(include_shapes, include_charts, include_shape_size, include_chart_size) を一つの設定オブジェクトにまとめる。エンジンからは mode に基づくプリセットを渡すだけにする。
13-
149
- ドキュメントとコードの同期コスト
1510

1611
- 事象: DATA_MODEL/README/api が手書きで分散している。モデル更新時の漏れリスクが高い。

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "exstruct"
3-
version = "0.2.51"
3+
version = "0.2.60"
44
description = "Excel to structured JSON (tables, shapes, charts) for LLM/RAG pipelines"
55
readme = "README.md"
66
license = { file = "LICENSE" }

src/exstruct/core/integrate.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import os
55
from pathlib import Path
6-
from typing import Literal
6+
from typing import Any, Literal, cast
77

88
from openpyxl import load_workbook
99
from openpyxl.utils import range_boundaries
@@ -203,7 +203,7 @@ def _compute_auto_page_break_areas(workbook: xw.Book) -> dict[str, list[PrintAre
203203
results: dict[str, list[PrintArea]] = {}
204204
for sheet in workbook.sheets:
205205
try:
206-
ws_api = sheet.api
206+
ws_api = cast(Any, sheet.api) # xlwings COM API; treated as Any
207207
original_display: bool | None = ws_api.DisplayPageBreaks
208208
ws_api.DisplayPageBreaks = True
209209
print_area = ws_api.PageSetup.PrintArea or ws_api.UsedRange.Address
@@ -213,26 +213,30 @@ def _compute_auto_page_break_areas(workbook: xw.Book) -> dict[str, list[PrintAre
213213
rng = _normalize_area_for_sheet(part, sheet.name)
214214
if rng:
215215
area_parts.append(rng)
216-
hpb = ws_api.HPageBreaks
217-
vpb = ws_api.VPageBreaks
216+
hpb = cast(Any, ws_api.HPageBreaks)
217+
vpb = cast(Any, ws_api.VPageBreaks)
218218
h_break_rows = [
219219
hpb.Item(i).Location.Row for i in range(1, int(hpb.Count) + 1)
220220
]
221221
v_break_cols = [
222222
vpb.Item(i).Location.Column for i in range(1, int(vpb.Count) + 1)
223223
]
224224
for addr in area_parts:
225-
rng = ws_api.Range(addr)
226-
min_row = int(rng.Row)
227-
max_row = min_row + int(rng.Rows.Count) - 1
228-
min_col = int(rng.Column)
229-
max_col = min_col + int(rng.Columns.Count) - 1
230-
rows = [min_row] + [
231-
r for r in h_break_rows if min_row < r <= max_row
232-
] + [max_row + 1]
233-
cols = [min_col] + [
234-
c for c in v_break_cols if min_col < c <= max_col
235-
] + [max_col + 1]
225+
range_obj = cast(Any, ws_api.Range(addr))
226+
min_row = int(range_obj.Row)
227+
max_row = min_row + int(range_obj.Rows.Count) - 1
228+
min_col = int(range_obj.Column)
229+
max_col = min_col + int(range_obj.Columns.Count) - 1
230+
rows = (
231+
[min_row]
232+
+ [r for r in h_break_rows if min_row < r <= max_row]
233+
+ [max_row + 1]
234+
)
235+
cols = (
236+
[min_col]
237+
+ [c for c in v_break_cols if min_col < c <= max_col]
238+
+ [max_col + 1]
239+
)
236240
for i in range(len(rows) - 1):
237241
r1, r2 = rows[i], rows[i + 1] - 1
238242
for j in range(len(cols) - 1):
@@ -361,7 +365,9 @@ def _cells_and_tables_only(reason: str) -> WorkbookData:
361365
wb,
362366
mode=mode,
363367
print_area_data=print_area_data if include_print_areas else None,
364-
auto_page_break_data=auto_page_break_data if include_auto_page_breaks else None,
368+
auto_page_break_data=auto_page_break_data
369+
if include_auto_page_breaks
370+
else None,
365371
)
366372
return WorkbookData(book_name=file_path.name, sheets=merged)
367373
except Exception as e:

src/exstruct/engine.py

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ class ExStructEngine:
217217
export(workbook, ...)
218218
- Writes to file/stdout; optionally per-sheet and per-print-area files
219219
process(file_path, ...)
220-
- One-shot extractexport (CLI equivalent), with optional PDF/PNG
220+
- One-shot extract->export (CLI equivalent), with optional PDF/PNG
221221
"""
222222

223223
def __init__(
@@ -320,9 +320,7 @@ def _filter_workbook(
320320
self, wb: WorkbookData, *, include_auto_override: bool | None = None
321321
) -> WorkbookData:
322322
filtered = {
323-
name: self._filter_sheet(
324-
sheet, include_auto_override=include_auto_override
325-
)
323+
name: self._filter_sheet(sheet, include_auto_override=include_auto_override)
326324
for name, sheet in wb.sheets.items()
327325
}
328326
return WorkbookData(book_name=wb.book_name, sheets=filtered)
@@ -331,14 +329,15 @@ def extract(
331329
self, file_path: str | Path, *, mode: ExtractionMode | None = None
332330
) -> WorkbookData:
333331
"""
334-
ワークブックを抽出して WorkbookData を返す。
332+
Extract a workbook and return normalized workbook data.
335333
336334
Args:
337-
file_path: .xlsx/.xlsm/.xls のパス
338-
mode: light/standard/verbose(未指定ならエンジンの StructOptions.mode)
339-
- light: COM なし。セル+テーブル+印刷範囲のみ。
340-
- standard: テキスト付き図形+矢印+チャート。印刷範囲あり。サイズは保持するがデフォルト出力では非表示。
341-
- verbose: 全図形(サイズ付き)+チャート(サイズ付き)。
335+
file_path: Path to the .xlsx/.xlsm/.xls file to extract.
336+
mode: Extraction mode; defaults to the engine's StructOptions.mode.
337+
- light: COM-free; cells, table candidates, and print areas only.
338+
- standard: Shapes with text/arrows plus charts; print areas included;
339+
size fields retained but hidden from default output.
340+
- verbose: All shapes (with size) and charts (with size).
342341
"""
343342
chosen_mode = mode or self.options.mode
344343
if chosen_mode not in ("light", "standard", "verbose"):
@@ -348,7 +347,7 @@ def extract(
348347
if self.options.include_cell_links is not None
349348
else chosen_mode == "verbose"
350349
)
351-
include_print_areas = True # lightでも印刷範囲は抽出する
350+
include_print_areas = True # Extract print areas even in light mode
352351
include_auto_page_breaks = (
353352
self.output.filters.include_auto_print_areas
354353
or self.output.destinations.auto_page_breaks_dir is not None
@@ -371,11 +370,13 @@ def serialize(
371370
indent: int | None = None,
372371
) -> str:
373372
"""
374-
WorkbookData を include/exclude フィルタ適用後に文字列化する。
373+
Serialize a workbook after applying include/exclude filters.
375374
376375
Args:
377-
fmt: json/yaml/yml/toon(未指定なら OutputOptions.fmt)
378-
pretty/indent: JSON 整形オプション
376+
data: Workbook to serialize after filtering.
377+
fmt: Serialization format; defaults to OutputOptions.fmt.
378+
pretty: Whether to pretty-print JSON output.
379+
indent: Indentation to use when pretty-printing JSON.
379380
"""
380381
filtered = self._filter_workbook(data)
381382
use_fmt = fmt or self.output.format.fmt
@@ -399,19 +400,22 @@ def export(
399400
stream: TextIO | None = None,
400401
) -> None:
401402
"""
402-
WorkbookData をファイルまたは標準出力に書き出す。
403+
Write filtered workbook data to a file or stream.
403404
404-
- include_* フィルタ後のデータを使用
405-
- sheets_dir を指定するとシートごとの個別ファイルも出力
406-
- print_areas_dir を指定すると印刷範囲ごとの個別ファイルも出力(light モードではデフォルト無効)
405+
Includes optional per-sheet and per-print-area outputs when destinations are
406+
provided.
407407
408408
Args:
409-
output_path: None なら標準出力、それ以外はファイル書き込み
410-
fmt/pretty/indent: シリアライズ設定(未指定は OutputOptions から)
411-
sheets_dir: シートごとの出力先ディレクトリ
412-
print_areas_dir: 印刷範囲ごとの出力先ディレクトリ
413-
auto_page_breaks_dir: 自動改ページ範囲の出力先ディレクトリ(COM 環境のみ)
414-
stream: output_path が None のときに上書きしたい IO
409+
data: Workbook to serialize and write.
410+
output_path: Target file path; writes to stdout when None.
411+
fmt: Serialization format; defaults to OutputOptions.fmt.
412+
pretty: Whether to pretty-print JSON output.
413+
indent: Indentation to use when pretty-printing JSON.
414+
sheets_dir: Directory for per-sheet outputs when provided.
415+
print_areas_dir: Directory for per-print-area outputs when provided.
416+
auto_page_breaks_dir: Directory for auto page-break outputs (COM
417+
environments only).
418+
stream: Stream override when output_path is None.
415419
"""
416420
text = self.serialize(data, fmt=fmt, pretty=pretty, indent=indent)
417421
target_stream = stream or self.output.destinations.stream
@@ -470,9 +474,7 @@ def export(
470474

471475
if chosen_auto_page_breaks_dir is not None:
472476
include_shape_size, include_chart_size = self._resolve_size_flags()
473-
filtered = self._filter_workbook(
474-
data, include_auto_override=True
475-
)
477+
filtered = self._filter_workbook(data, include_auto_override=True)
476478
save_auto_page_break_views(
477479
filtered,
478480
chosen_auto_page_breaks_dir,
@@ -505,20 +507,22 @@ def process(
505507
stream: TextIO | None = None,
506508
) -> None:
507509
"""
508-
抽出→出力の一括実行ラッパー(CLI 相当)。必要なら PDF/PNG も出力。
510+
One-shot extract->export wrapper (CLI equivalent) with optional PDF/PNG output.
509511
510512
Args:
511-
file_path: 入力 Excel
512-
output_path: None なら標準出力、それ以外はファイル
513-
out_fmt: json/yaml/yml/toon
514-
image/pdf: True で PNG/PDF を追加出力(Excel + pypdfium2 が必要)
515-
dpi: 画像出力時の DPI
516-
mode: 抽出モード(未指定ならエンジンの StructOptions.mode)
517-
pretty/indent: JSON 整形
518-
sheets_dir: シートごとの出力先
519-
print_areas_dir: 印刷範囲ごとの出力先
520-
auto_page_breaks_dir: 自動改ページ範囲の出力先
521-
stream: 標準出力時の IO を上書きしたい場合
513+
file_path: Input Excel workbook path.
514+
output_path: Target file path; writes to stdout when None.
515+
out_fmt: Serialization format for structured output.
516+
image: Whether to export PNGs alongside structured output.
517+
pdf: Whether to export a PDF snapshot alongside structured output.
518+
dpi: DPI to use when rendering images.
519+
mode: Extraction mode; defaults to the engine's StructOptions.mode.
520+
pretty: Whether to pretty-print JSON output.
521+
indent: Indentation to use when pretty-printing JSON.
522+
sheets_dir: Directory for per-sheet structured outputs.
523+
print_areas_dir: Directory for per-print-area structured outputs.
524+
auto_page_breaks_dir: Directory for auto page-break outputs.
525+
stream: Stream override when writing to stdout.
522526
"""
523527
wb = self.extract(file_path, mode=mode)
524528
chosen_fmt = out_fmt or self.output.format.fmt

0 commit comments

Comments
 (0)