ZivKassnerNK's picture
Deploy Streamlit dashboard via Docker Space
5d68221 verified
Raw
History Blame Contribute Delete
3.82 kB
from __future__ import annotations
import re
from typing import Iterable
import pandas as pd
HEADING_RE = re.compile(r"^\s{0,3}(#{1,6})\s+(.*?)\s*$")
TABLE_DIVIDER_CELL_RE = re.compile(r"^:?-{3,}:?$")
def extract_markdown_section(markdown: str, heading: str) -> str | None:
lines = markdown.splitlines()
heading_normalized = heading.strip().lower()
start_index: int | None = None
heading_level: int | None = None
for index, line in enumerate(lines):
match = HEADING_RE.match(line)
if not match:
continue
title = match.group(2).strip().lower()
if title == heading_normalized:
start_index = index + 1
heading_level = len(match.group(1))
break
if start_index is None or heading_level is None:
return None
end_index = len(lines)
for index in range(start_index, len(lines)):
match = HEADING_RE.match(lines[index])
if not match:
continue
next_heading_level = len(match.group(1))
if next_heading_level <= heading_level:
end_index = index
break
section = "\n".join(lines[start_index:end_index]).strip()
return section or None
def extract_performance_metrics_table(markdown: str, heading: str = "Performance Metrics") -> pd.DataFrame | None:
section = extract_markdown_section(markdown, heading)
if not section:
return None
blocks = _extract_table_blocks(section)
for block in blocks:
table = _parse_markdown_table_block(block)
if table is None or table.empty:
continue
normalized_columns = {str(col).strip().lower() for col in table.columns}
if "class" in normalized_columns:
return table
if blocks:
fallback = _parse_markdown_table_block(blocks[0])
if fallback is not None and not fallback.empty:
return fallback
return None
def _extract_table_blocks(text: str) -> list[list[str]]:
lines = text.splitlines()
blocks: list[list[str]] = []
current_block: list[str] = []
for line in lines:
if "|" in line:
current_block.append(line)
continue
if len(current_block) >= 2:
blocks.append(current_block)
current_block = []
if len(current_block) >= 2:
blocks.append(current_block)
return blocks
def _parse_markdown_table_block(lines: Iterable[str]) -> pd.DataFrame | None:
rows = [_split_markdown_row(line) for line in lines if line.strip()]
if len(rows) < 2:
return None
header_index = None
for index in range(len(rows) - 1):
if _is_divider_row(rows[index + 1]):
header_index = index
break
if header_index is None:
return None
header = [cell.strip() for cell in rows[header_index]]
if not header or len(header) < 2:
return None
data_rows = rows[header_index + 2 :]
normalized_rows: list[list[str]] = []
for row in data_rows:
if _is_divider_row(row):
continue
padded = row[: len(header)] + [""] * max(0, len(header) - len(row))
if any(cell.strip() for cell in padded):
normalized_rows.append(padded)
if not normalized_rows:
return None
dataframe = pd.DataFrame(normalized_rows, columns=header)
dataframe.columns = [str(column).strip() for column in dataframe.columns]
return dataframe
def _split_markdown_row(line: str) -> list[str]:
raw = line.strip().strip("|")
return [cell.strip() for cell in raw.split("|")]
def _is_divider_row(row: Iterable[str]) -> bool:
cells = [cell.strip() for cell in row]
if not cells:
return False
return all(TABLE_DIVIDER_CELL_RE.match(cell) for cell in cells if cell)