modelwright 0.1.0a1__tar.gz → 0.1.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {modelwright-0.1.0a1/src/modelwright.egg-info → modelwright-0.1.0a2}/PKG-INFO +2 -2
  2. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/README.md +1 -1
  3. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/pyproject.toml +1 -1
  4. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/__init__.py +1 -1
  5. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/extraction.py +24 -11
  6. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/generation.py +339 -91
  7. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/graph.py +42 -14
  8. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/validation.py +44 -2
  9. {modelwright-0.1.0a1 → modelwright-0.1.0a2/src/modelwright.egg-info}/PKG-INFO +2 -2
  10. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_dependency_graph.py +49 -0
  11. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_formula_translation.py +50 -0
  12. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_import.py +1 -1
  13. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_openpyxl_extraction.py +25 -1
  14. modelwright-0.1.0a2/tests/test_python_generation.py +1192 -0
  15. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_scalar_comparison.py +16 -0
  16. modelwright-0.1.0a1/tests/test_python_generation.py +0 -538
  17. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/LICENSE +0 -0
  18. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/setup.cfg +0 -0
  19. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/cli.py +0 -0
  20. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/conversion.py +0 -0
  21. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/evaluation.py +0 -0
  22. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/execution.py +0 -0
  23. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/formulas.py +0 -0
  24. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/formulas_oracle.py +0 -0
  25. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/oracle_validation.py +0 -0
  26. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/oracles.py +0 -0
  27. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright/references.py +0 -0
  28. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright.egg-info/SOURCES.txt +0 -0
  29. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright.egg-info/dependency_links.txt +0 -0
  30. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright.egg-info/entry_points.txt +0 -0
  31. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright.egg-info/requires.txt +0 -0
  32. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/src/modelwright.egg-info/top_level.txt +0 -0
  33. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_cli.py +0 -0
  34. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_conversion_plan.py +0 -0
  35. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_evaluation_orchestration.py +0 -0
  36. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_extraction_records.py +0 -0
  37. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_formula_expressions.py +0 -0
  38. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_formulas_oracle.py +0 -0
  39. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_generated_execution.py +0 -0
  40. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_generation_contract.py +0 -0
  41. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_materialize_fable_benchmarks.py +0 -0
  42. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_oracle_backed_validation.py +0 -0
  43. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_oracle_interface.py +0 -0
  44. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_public_api.py +0 -0
  45. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_references.py +0 -0
  46. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_supported_semantics_fixture.py +0 -0
  47. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_synthetic_fixture.py +0 -0
  48. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_validation.py +0 -0
  49. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_validation_regression.py +0 -0
  50. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_validation_report_builder.py +0 -0
  51. {modelwright-0.1.0a1 → modelwright-0.1.0a2}/tests/test_validation_scenario.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modelwright
3
- Version: 0.1.0a1
3
+ Version: 0.1.0a2
4
4
  Summary: Tools for converting spreadsheet workbooks into transparent Python models.
5
5
  Author: UBC FRESH Lab
6
6
  License-Expression: MIT
@@ -135,7 +135,7 @@ Restore the public external FABLE benchmark workbooks into ignored local paths:
135
135
  scripts/bootstrap_dev_env.sh --benchmarks
136
136
  ```
137
137
 
138
- `modelwright` is pre-release. The first planned alpha line is `0.1.0a1`; alpha releases must not be described as full-workbook conversion guarantees.
138
+ `modelwright` is pre-release. The current alpha line is `0.1.0a2`; alpha releases must not be described as full-workbook conversion guarantees.
139
139
 
140
140
  Check release artifacts locally:
141
141
 
@@ -84,7 +84,7 @@ Restore the public external FABLE benchmark workbooks into ignored local paths:
84
84
  scripts/bootstrap_dev_env.sh --benchmarks
85
85
  ```
86
86
 
87
- `modelwright` is pre-release. The first planned alpha line is `0.1.0a1`; alpha releases must not be described as full-workbook conversion guarantees.
87
+ `modelwright` is pre-release. The current alpha line is `0.1.0a2`; alpha releases must not be described as full-workbook conversion guarantees.
88
88
 
89
89
  Check release artifacts locally:
90
90
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "modelwright"
7
- version = "0.1.0a1"
7
+ version = "0.1.0a2"
8
8
  description = "Tools for converting spreadsheet workbooks into transparent Python models."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -81,7 +81,7 @@ from modelwright.validation import (
81
81
  load_validation_scenario,
82
82
  )
83
83
 
84
- __version__ = "0.1.0a1"
84
+ __version__ = "0.1.0a2"
85
85
 
86
86
  __all__ = [
87
87
  "CellRecord",
@@ -280,7 +280,7 @@ def extract_workbook(path: str | Path, progress: Callable[[str], None] | None =
280
280
 
281
281
  cell_records: list[CellRecord] = []
282
282
  for index, worksheet in enumerate(workbook.worksheets, start=1):
283
- populated_cells = _populated_cells(worksheet)
283
+ populated_cells = _populated_cells(worksheet, cached_workbook[worksheet.title])
284
284
  _progress(
285
285
  progress,
286
286
  f"sheet cells start index={index}/{len(workbook.worksheets)} populated={len(populated_cells)}",
@@ -462,22 +462,24 @@ def _extract_sheet_cells(
462
462
  worksheet: Any,
463
463
  cached_worksheet: Any,
464
464
  *,
465
- populated_cells: tuple[Any, ...] | None = None,
465
+ populated_cells: tuple[str, ...] | None = None,
466
466
  ) -> tuple[CellRecord, ...]:
467
467
  records: list[CellRecord] = []
468
- for cell in populated_cells if populated_cells is not None else _populated_cells(worksheet):
469
- if cell.value is None:
468
+ for coordinate in populated_cells if populated_cells is not None else _populated_cells(worksheet, cached_worksheet):
469
+ cell = worksheet[coordinate]
470
+ cached_value = cached_worksheet[coordinate].value
471
+ raw_value = cell.value if cell.value is not None else cached_value
472
+ if raw_value is None:
470
473
  continue
471
474
 
472
- cell_ref = _cell_ref(worksheet.title, cell.coordinate)
473
- cached_value = cached_worksheet[cell.coordinate].value
475
+ cell_ref = _cell_ref(worksheet.title, coordinate)
474
476
  if cell.data_type == "f":
475
477
  formula = _extract_formula(cell_ref, str(cell.value), cached_value)
476
478
  records.append(
477
479
  CellRecord(
478
480
  cell_ref=cell_ref,
479
481
  kind="formula",
480
- raw_value=_json_value(cell.value),
482
+ raw_value=_json_value(raw_value),
481
483
  data_type=cell.data_type,
482
484
  cached_value=_json_value(cached_value),
483
485
  formula=formula,
@@ -489,7 +491,7 @@ def _extract_sheet_cells(
489
491
  CellRecord(
490
492
  cell_ref=cell_ref,
491
493
  kind="value",
492
- raw_value=_json_value(cell.value),
494
+ raw_value=_json_value(raw_value),
493
495
  data_type=cell.data_type,
494
496
  cached_value=_json_value(cached_value),
495
497
  formula=None,
@@ -498,12 +500,23 @@ def _extract_sheet_cells(
498
500
  return tuple(records)
499
501
 
500
502
 
501
- def _populated_cells(worksheet: Any) -> tuple[Any, ...]:
503
+ def _populated_cells(worksheet: Any, cached_worksheet: Any | None = None) -> tuple[str, ...]:
504
+ coordinates = _worksheet_populated_coordinates(worksheet)
505
+ if cached_worksheet is not None:
506
+ coordinates = coordinates | _worksheet_populated_coordinates(cached_worksheet)
507
+ return tuple(sorted(coordinates, key=_coordinate_sort_key))
508
+
509
+
510
+ def _worksheet_populated_coordinates(worksheet: Any) -> set[str]:
502
511
  cells = getattr(worksheet, "_cells", None)
503
512
  if isinstance(cells, dict):
504
- return tuple(cell for _, cell in sorted(cells.items()))
513
+ return {cell.coordinate for cell in cells.values()}
514
+ return {cell.coordinate for row in worksheet.iter_rows() for cell in row if cell.value is not None}
515
+
505
516
 
506
- return tuple(cell for row in worksheet.iter_rows() for cell in row)
517
+ def _coordinate_sort_key(coordinate: str) -> tuple[int, int]:
518
+ min_col, min_row, _max_col, _max_row = range_boundaries(coordinate)
519
+ return min_row, min_col
507
520
 
508
521
 
509
522
  def _progress(progress: Callable[[str], None] | None, message: str) -> None: