heurist-api 0.1.4__tar.gz → 0.2.1__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.

Potentially problematic release.


This version of heurist-api might be problematic. Click here for more details.

Files changed (130) hide show
  1. {heurist_api-0.1.4 → heurist_api-0.2.1}/PKG-INFO +1 -1
  2. {heurist_api-0.1.4 → heurist_api-0.2.1}/pyproject.toml +2 -1
  3. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/cli/load.py +23 -4
  4. heurist_api-0.2.1/src/heurist/cli/parse_log.py +27 -0
  5. heurist_api-0.2.1/src/heurist/log/__init__.py +3 -0
  6. heurist_api-0.2.1/src/heurist/log/constants.py +4 -0
  7. heurist_api-0.2.1/src/heurist/log/iterator.py +16 -0
  8. heurist_api-0.2.1/src/heurist/log/model.py +42 -0
  9. heurist_api-0.2.1/src/heurist/log/summary.py +35 -0
  10. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/validators/record_validator.py +1 -3
  11. heurist_api-0.2.1/tests/unit/log_test.py +44 -0
  12. {heurist_api-0.1.4 → heurist_api-0.2.1}/uv.lock +16 -19
  13. {heurist_api-0.1.4 → heurist_api-0.2.1}/.github/workflows/pypi-release.yml +0 -0
  14. {heurist_api-0.1.4 → heurist_api-0.2.1}/.github/workflows/python-package.yml +0 -0
  15. {heurist_api-0.1.4 → heurist_api-0.2.1}/.gitignore +0 -0
  16. {heurist_api-0.1.4 → heurist_api-0.2.1}/.pre-commit-config.yaml +0 -0
  17. {heurist_api-0.1.4 → heurist_api-0.2.1}/LICENSE +0 -0
  18. {heurist_api-0.1.4 → heurist_api-0.2.1}/README.md +0 -0
  19. {heurist_api-0.1.4 → heurist_api-0.2.1}/demos/pandas_dataframe.ipynb +0 -0
  20. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/assets/coverage-badge.svg +0 -0
  21. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/assets/erc-logo.png +0 -0
  22. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/assets/heurist-admin-panel-users.png +0 -0
  23. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/assets/logo-transparent-1.png +0 -0
  24. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/assets/logo-transparent.png +0 -0
  25. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/assets/logo.png +0 -0
  26. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/assets/tests-badge.svg +0 -0
  27. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/development/code_of_conduct.md +0 -0
  28. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/development/contributing.md +0 -0
  29. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/development/coverage.md +0 -0
  30. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/development/heuristdb/temporal.md +0 -0
  31. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/development/publishing.md +0 -0
  32. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/index.md +0 -0
  33. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/legal.md +0 -0
  34. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/download/date_validation.md +0 -0
  35. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/download/export_csv.md +0 -0
  36. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/download/group_types.md +0 -0
  37. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/download/index.md +0 -0
  38. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/download/logs.md +0 -0
  39. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/download/user_filter.md +0 -0
  40. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/index.md +0 -0
  41. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/module.md +0 -0
  42. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/records.md +0 -0
  43. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/rstudio.md +0 -0
  44. {heurist_api-0.1.4 → heurist_api-0.2.1}/docs/usage/schema.md +0 -0
  45. {heurist_api-0.1.4 → heurist_api-0.2.1}/mkdocs.yml +0 -0
  46. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/__init__.py +0 -0
  47. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/__init__.py +0 -0
  48. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/client.py +0 -0
  49. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/connection.py +0 -0
  50. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/constants.py +0 -0
  51. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/credentials.py +0 -0
  52. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/exceptions.py +0 -0
  53. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/url_builder.py +0 -0
  54. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/api/utils.py +0 -0
  55. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/cli/__init__.py +0 -0
  56. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/cli/__main__.py +0 -0
  57. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/cli/records.py +0 -0
  58. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/cli/schema.py +0 -0
  59. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/database/__init__.py +0 -0
  60. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/database/basedb.py +0 -0
  61. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/database/database.py +0 -0
  62. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/__init__.py +0 -0
  63. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/dynamic/__init__.py +0 -0
  64. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/dynamic/annotation.py +0 -0
  65. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/dynamic/create_model.py +0 -0
  66. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/dynamic/date.py +0 -0
  67. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/dynamic/type.py +0 -0
  68. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/DetailTypes.py +0 -0
  69. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/RecStructure.py +0 -0
  70. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/RecTypeGroups.py +0 -0
  71. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/RecTypes.py +0 -0
  72. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/Terms.py +0 -0
  73. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/__init__.py +0 -0
  74. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/dty.py +0 -0
  75. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/hml_structure.py +0 -0
  76. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/rst.py +0 -0
  77. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/rtg.py +0 -0
  78. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/rty.py +0 -0
  79. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/trm.py +0 -0
  80. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/models/structural/utils.py +0 -0
  81. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/schema/__init__.py +0 -0
  82. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/schema/models.py +0 -0
  83. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/schema/rel_to_dict.py +0 -0
  84. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/sql/__init__.py +0 -0
  85. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/sql/joinRecordTypeIDNameByGroupType.sql +0 -0
  86. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/sql/joinRecordTypeMetadata.sql +0 -0
  87. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/sql/selectRecordTypeSchema.sql +0 -0
  88. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/sql/sql_safety.py +0 -0
  89. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/utils/constants.py +0 -0
  90. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/utils/rel_to_dict_array.py +0 -0
  91. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/validators/__init__.py +0 -0
  92. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/validators/detail_validator.py +0 -0
  93. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/validators/exceptions.py +0 -0
  94. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/validators/parse_heurist_date.py +0 -0
  95. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/workflows/__init__.py +0 -0
  96. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/heurist/workflows/etl.py +0 -0
  97. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/__init__.py +0 -0
  98. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/blocktext/__init__.py +0 -0
  99. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/blocktext/single.py +0 -0
  100. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/date/__init__.py +0 -0
  101. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/date/compound_repeated.py +0 -0
  102. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/date/compound_single.py +0 -0
  103. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/date/simple_single.py +0 -0
  104. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/date/timestamp_repeated.py +0 -0
  105. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/enum/__init__.py +0 -0
  106. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/enum/repeated.py +0 -0
  107. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/enum/single.py +0 -0
  108. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/file/__init__.py +0 -0
  109. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/file/single.py +0 -0
  110. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/float/__init__.py +0 -0
  111. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/float/single.py +0 -0
  112. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/freetext/__init__.py +0 -0
  113. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/freetext/single.py +0 -0
  114. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/geo/__init__.py +0 -0
  115. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/geo/single.py +0 -0
  116. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/resource/__init__.py +0 -0
  117. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/resource/repeated.py +0 -0
  118. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/mock_data/resource/single.py +0 -0
  119. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/scripts/gen_badges.sh +0 -0
  120. {heurist_api-0.1.4 → heurist_api-0.2.1}/src/scripts/gen_ref_pages.py +0 -0
  121. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/e2e/download_test.py +0 -0
  122. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/e2e/schema_test.py +0 -0
  123. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/integration/api/client_test.py +0 -0
  124. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/integration/api/connection_test.py +0 -0
  125. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/unit/database/database_test.py +0 -0
  126. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/unit/database/modeling_test.py +0 -0
  127. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/unit/database/skeleton_test.py +0 -0
  128. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/unit/schema/get_db_schema_test.py +0 -0
  129. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/unit/validators/detail_validation_test.py +0 -0
  130. {heurist_api-0.1.4 → heurist_api-0.2.1}/tests/unit/validators/repeated_enum_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: heurist-api
3
- Version: 0.1.4
3
+ Version: 0.2.1
4
4
  Dynamic: Description
5
5
  Dynamic: Description-Content-Type
6
6
  Summary: API wrapper and CLI for Heurist database.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "heurist-api"
3
- version = "0.1.4"
3
+ version = "0.2.1"
4
4
  description = "API wrapper and CLI for Heurist database."
5
5
  keywords = [ "duckdb", "etl" ]
6
6
  dynamic = [ "readme" ]
@@ -56,6 +56,7 @@ dev = [
56
56
 
57
57
  [project.scripts]
58
58
  heurist = "heurist.cli.__main__:cli"
59
+ heurist-log = "heurist.cli.parse_log:cli"
59
60
 
60
61
  [tool.isort]
61
62
  profile = "black"
@@ -7,8 +7,14 @@ from pathlib import Path
7
7
  import duckdb
8
8
  from heurist.api.connection import HeuristAPIConnection
9
9
  from heurist.api.credentials import CredentialHandler
10
+ from heurist.log import log_summary
11
+ from heurist.log.constants import VALIDATION_LOG
10
12
  from heurist.utils.constants import DEFAULT_RECORD_GROUPS
11
13
  from heurist.workflows import extract_transform_load
14
+ from rich.columns import Columns
15
+ from rich.console import Console, Group
16
+ from rich.padding import Padding
17
+ from rich.panel import Panel
12
18
 
13
19
 
14
20
  def load_command(
@@ -37,10 +43,11 @@ def load_command(
37
43
  )
38
44
 
39
45
  # Show the results of the created DuckDB database
40
- with duckdb.connect(duckdb_database_connection_path) as new_conn:
41
- tables = new_conn.sql("show tables;")
42
- print("\nCreated the following tables")
43
- print(tables)
46
+ with duckdb.connect(duckdb_database_connection_path, read_only=True) as new_conn:
47
+ tables = [t[0] for t in new_conn.sql("show tables;").fetchall()]
48
+ with open(VALIDATION_LOG) as f:
49
+ log = f.readlines()
50
+ show_summary_in_console(tables=tables, log_lines=log)
44
51
 
45
52
  # If writing to CSV files, write only tables of record types
46
53
  if outdir:
@@ -53,3 +60,15 @@ def load_command(
53
60
  continue
54
61
  fp = outdir.joinpath(f"{table_name}.csv")
55
62
  new_conn.table(table_name).sort("H-ID").write_csv(str(fp))
63
+
64
+
65
+ def show_summary_in_console(tables: list[str], log_lines: list[str]):
66
+ console = Console()
67
+ t0 = Panel(
68
+ Columns(tables, equal=True, expand=True),
69
+ title="SQL Tables",
70
+ subtitle="Saved in DuckDB database file.",
71
+ )
72
+ t1, t2 = log_summary(lines=log_lines)
73
+ panel_group = Group(Padding(t0, 1), t1, Padding(t2, 1))
74
+ console.print(panel_group)
@@ -0,0 +1,27 @@
1
+ import csv
2
+ from pathlib import Path
3
+
4
+ import click
5
+ from heurist.log import LogDetail, yield_log_blocks
6
+ from heurist.log.constants import VALIDATION_LOG
7
+
8
+ log_detail_fieldnames = list(LogDetail.__annotations__.keys())
9
+
10
+
11
+ @click.command()
12
+ @click.option("-l", "--log-file", required=None, default=VALIDATION_LOG)
13
+ @click.option("-o", "--outfile", required=None, default="invalid_records.csv")
14
+ def cli(log_file, outfile):
15
+ logfile = Path(log_file)
16
+ if not logfile.is_file():
17
+ raise FileNotFoundError(log_file)
18
+ with open(logfile) as f, open(outfile, "w") as of:
19
+ writer = csv.DictWriter(of, fieldnames=log_detail_fieldnames)
20
+ writer.writeheader()
21
+ lines = f.readlines()
22
+ for block in yield_log_blocks(lines):
23
+ writer.writerow(block.__dict__)
24
+
25
+
26
+ if __name__ == "__main__":
27
+ cli()
@@ -0,0 +1,3 @@
1
+ from .iterator import yield_log_blocks as yield_log_blocks
2
+ from .model import LogDetail as LogDetail
3
+ from .summary import log_summary as log_summary
@@ -0,0 +1,4 @@
1
+ from pathlib import Path
2
+
3
+ VALIDATION_LOG = Path.cwd().joinpath("validation.log")
4
+ VALIDATION_LOG.touch(exist_ok=True)
@@ -0,0 +1,16 @@
1
+ from typing import Generator
2
+
3
+ from .model import LogDetail
4
+
5
+
6
+ def yield_log_blocks(lines: list[str]) -> Generator[LogDetail, None, None]:
7
+ line_iterator = iter(lines)
8
+ l1 = next(line_iterator, None)
9
+ while l1 is not None:
10
+ if l1 and not l1.startswith("\t"):
11
+ l2 = next(line_iterator)
12
+ l3 = next(line_iterator)
13
+ l4 = next(line_iterator)
14
+ l5 = next(line_iterator)
15
+ yield LogDetail.load_lines(l1, l2, l3, l4, l5)
16
+ l1 = next(line_iterator, None)
@@ -0,0 +1,42 @@
1
+ import re
2
+ from dataclasses import dataclass
3
+
4
+
5
+ @dataclass
6
+ class LogDetail:
7
+ time: str
8
+ level: str
9
+ recType: int
10
+ recID: int
11
+ rule: str
12
+ problem: str
13
+
14
+ @classmethod
15
+ def load_lines(cls, *block_lines) -> "LogDetail":
16
+ l1, l2, l3, l4, l5 = block_lines
17
+ for indented_line in [l2, l3, l4, l5]:
18
+ assert indented_line.startswith("\t")
19
+ return LogDetail(
20
+ time=cls.parse_time(l1),
21
+ level=cls.parse_level(l1),
22
+ recType=cls.parse_number(l2),
23
+ recID=cls.parse_number(l3),
24
+ rule=l4.removeprefix("\t").strip(),
25
+ problem=l5.removeprefix("\t").strip(),
26
+ )
27
+
28
+ @staticmethod
29
+ def parse_number(line) -> int:
30
+ parts = line.split()
31
+ suffix: str = parts[-1]
32
+ number = suffix.removesuffix("]")
33
+ return int(number)
34
+
35
+ @staticmethod
36
+ def parse_time(l1: str) -> str:
37
+ parts = l1.split(" - ")
38
+ return parts[0].strip()
39
+
40
+ @staticmethod
41
+ def parse_level(l1: str) -> str:
42
+ return re.search(r"[A-Z]+", l1).group(0).strip()
@@ -0,0 +1,35 @@
1
+ from collections import Counter
2
+
3
+ from heurist.log import yield_log_blocks
4
+ from rich.table import Table
5
+
6
+
7
+ def log_summary(lines: list[str]) -> tuple[Table, Table]:
8
+ rectypes = []
9
+ recs = []
10
+ for block in yield_log_blocks(lines):
11
+ rectypes.append(block.recType)
12
+ recs.append(block.recID)
13
+
14
+ rectype_counter = Counter(rectypes)
15
+ rec_counter = Counter(recs)
16
+
17
+ rec_table = Table(
18
+ title="Most problematic records",
19
+ caption="Note: Invalid records are not saved in the DuckDB database.",
20
+ )
21
+ rec_table.add_column("Record ID", style="red")
22
+ rec_table.add_column("Number of problems")
23
+ for rec, count in rec_counter.most_common(10):
24
+ rec_table.add_row(str(rec), str(count))
25
+
26
+ type_table = Table(
27
+ title="Types of invalid records",
28
+ caption="Note: Invalid records are not saved in the DuckDB database.",
29
+ )
30
+ type_table.add_column("Record Type", style="red")
31
+ type_table.add_column("Number of records")
32
+ for rec, count in rectype_counter.items():
33
+ type_table.add_row(str(rec), str(count))
34
+
35
+ return type_table, rec_table
@@ -1,15 +1,13 @@
1
1
  import logging
2
2
  import os
3
- from pathlib import Path
4
3
 
4
+ from heurist.log.constants import VALIDATION_LOG
5
5
  from heurist.models.dynamic.annotation import PydanticField
6
6
  from heurist.models.dynamic.type import FieldType
7
7
  from heurist.validators.detail_validator import DetailValidator
8
8
  from heurist.validators.exceptions import RepeatedValueInSingularDetailType
9
9
  from pydantic import BaseModel
10
10
 
11
- VALIDATION_LOG = Path.cwd().joinpath("validation.log")
12
-
13
11
  handlers = [logging.FileHandler(filename=VALIDATION_LOG, mode="w", delay=True)]
14
12
  if os.getenv("HEURIST_STREAM_LOG") == "True":
15
13
  handlers.append(logging.StreamHandler())
@@ -0,0 +1,44 @@
1
+ import unittest
2
+ from pathlib import Path
3
+
4
+ from heurist.log import yield_log_blocks, LogDetail
5
+
6
+
7
+ LOG_FILE = Path(__file__).parent.joinpath("mock_log.txt")
8
+
9
+ LINES = [
10
+ "2025-05-26 18:50 - WARNING - ",
11
+ " [rec_Type 104]",
12
+ " [rec_ID 834]",
13
+ " The detail 'note' is limited to a maximum of 1 values.",
14
+ " Count of values = 3.",
15
+ "2025-05-26 18:50 - WARNING - ",
16
+ " [rec_Type 104]",
17
+ " [rec_ID 834]",
18
+ " The detail 'contam' is limited to a maximum of 1 values.",
19
+ " Count of values = 2.",
20
+ ]
21
+
22
+
23
+ class TestLog(unittest.TestCase):
24
+ def assess_log_block(self, log: LogDetail):
25
+ self.assertEqual(log.level, "WARNING")
26
+ self.assertIsNotNone(log.rule)
27
+ self.assertIsNotNone(log.problem)
28
+ self.assertGreater(log.recID, 100)
29
+ self.assertGreater(log.recType, 99)
30
+
31
+ def test_parsed_lines(self):
32
+ for log in yield_log_blocks(LINES):
33
+ self.assess_log_block(log)
34
+
35
+ def test_log_file(self):
36
+ print(LOG_FILE)
37
+ with open(LOG_FILE) as f:
38
+ lines = f.readlines()
39
+ for log in yield_log_blocks(lines):
40
+ self.assess_log_block(log)
41
+
42
+
43
+ if __name__ == "__main__":
44
+ unittest.main()
@@ -421,7 +421,7 @@ wheels = [
421
421
 
422
422
  [[package]]
423
423
  name = "heurist-api"
424
- version = "0.1.3"
424
+ version = "0.2.1"
425
425
  source = { editable = "." }
426
426
  dependencies = [
427
427
  { name = "click" },
@@ -438,7 +438,7 @@ dependencies = [
438
438
  { name = "tenacity" },
439
439
  ]
440
440
 
441
- [package.dev-dependencies]
441
+ [package.optional-dependencies]
442
442
  dev = [
443
443
  { name = "coverage" },
444
444
  { name = "genbadge", extra = ["coverage"] },
@@ -458,35 +458,32 @@ dev = [
458
458
  [package.metadata]
459
459
  requires-dist = [
460
460
  { name = "click", specifier = ">=8.2.0" },
461
+ { name = "coverage", marker = "extra == 'dev'", specifier = ">=7.8.0" },
461
462
  { name = "duckdb", specifier = ">=1.2.2" },
463
+ { name = "genbadge", extras = ["coverage"], marker = "extra == 'dev'", specifier = ">=1.1.2" },
464
+ { name = "isort", marker = "extra == 'dev'", specifier = ">=6.0.1" },
462
465
  { name = "lxml", specifier = ">=5.4.0" },
466
+ { name = "mkdocs", marker = "extra == 'dev'", specifier = ">=1.6.1" },
467
+ { name = "mkdocs-gen-files", marker = "extra == 'dev'", specifier = ">=0.5.0" },
468
+ { name = "mkdocs-literate-nav", marker = "extra == 'dev'", specifier = ">=0.6.2" },
469
+ { name = "mkdocs-material", extras = ["imaging"], marker = "extra == 'dev'", specifier = ">=9.6.14" },
470
+ { name = "mkdocstrings-python", marker = "extra == 'dev'", specifier = ">=1.16.10" },
463
471
  { name = "pandas", specifier = ">=2.2.3" },
464
472
  { name = "polars", specifier = ">=1.29.0" },
473
+ { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.2.0" },
465
474
  { name = "pyarrow", specifier = ">=20.0.0" },
466
475
  { name = "pydantic", specifier = ">=2.11.4" },
467
476
  { name = "pydantic-xml", specifier = ">=2.16.0" },
477
+ { name = "pymdown-extensions", marker = "extra == 'dev'", specifier = ">=10.15" },
478
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.5" },
468
479
  { name = "python-dotenv", specifier = ">=1.1.0" },
469
480
  { name = "requests", specifier = ">=2.32.3" },
470
481
  { name = "rich", specifier = ">=14.0.0" },
482
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.11.10" },
471
483
  { name = "tenacity", specifier = ">=9.1.2" },
484
+ { name = "uv", marker = "extra == 'dev'", specifier = ">=0.7.5" },
472
485
  ]
473
-
474
- [package.metadata.requires-dev]
475
- dev = [
476
- { name = "coverage", specifier = ">=7.8.0" },
477
- { name = "genbadge", extras = ["coverage"], specifier = ">=1.1.2" },
478
- { name = "isort", specifier = ">=6.0.1" },
479
- { name = "mkdocs", specifier = ">=1.6.1" },
480
- { name = "mkdocs-gen-files", specifier = ">=0.5.0" },
481
- { name = "mkdocs-literate-nav", specifier = ">=0.6.2" },
482
- { name = "mkdocs-material", extras = ["imaging"], specifier = ">=9.6.14" },
483
- { name = "mkdocstrings-python", specifier = ">=1.16.10" },
484
- { name = "pre-commit", specifier = ">=4.2.0" },
485
- { name = "pymdown-extensions", specifier = ">=10.15" },
486
- { name = "pytest", specifier = ">=8.3.5" },
487
- { name = "ruff", specifier = ">=0.11.10" },
488
- { name = "uv", specifier = ">=0.7.5" },
489
- ]
486
+ provides-extras = ["dev"]
490
487
 
491
488
  [[package]]
492
489
  name = "identify"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes