mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 21:07:15 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
leetcode-coach pre-commit / pre-commit (push) Waiting to run
leetcode-coach tests / test (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
Todo: fda71ed1
72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
"""SQL problems state the OUTPUT SHAPE (columns, rows, grouping) before any code.
|
|
|
|
The SQL counterpart of the ``req:`` gate: a ``shape:`` message is judged
|
|
mechanically (no model, nothing to fail open), and the verdict rides the same
|
|
``req`` / ``req_check`` log events (``via: "shape"``), so readers need no change.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Final, NamedTuple
|
|
|
|
from leetcode_coach import req_check
|
|
|
|
|
|
class Facet(NamedTuple):
|
|
"""One part of an output shape: how to spot it, and what to ask for."""
|
|
|
|
pattern: re.Pattern[str]
|
|
hint: str
|
|
|
|
|
|
VIA: Final = "shape"
|
|
SHAPE_RE: Final = re.compile(r"^\s*shape\s*:", re.IGNORECASE)
|
|
SQL_LANGS: Final = frozenset(
|
|
{"mysql", "mssql", "oraclesql", "postgresql", "pythondata"}
|
|
)
|
|
LANGUAGES_RE: Final = re.compile(r"^Languages available:(.*)$", re.MULTILINE)
|
|
TAGS_RE: Final = re.compile(r"^Tags:(.*)$", re.MULTILINE)
|
|
FACETS: Final = {
|
|
"columns": Facet(
|
|
re.compile(r"\b(?:columns?|fields?|headers?|named)\b", re.IGNORECASE),
|
|
"which columns come back, named how",
|
|
),
|
|
"rows": Facet(
|
|
re.compile(r"\b(?:rows?|records?|per)\b", re.IGNORECASE),
|
|
"what one row stands for (one row per ...)",
|
|
),
|
|
"grouping": Facet(
|
|
re.compile(
|
|
r"\b(?:group\w*|aggregat\w*|distinct|unique|per|sum|count|ungrouped)\b",
|
|
re.IGNORECASE,
|
|
),
|
|
"how rows are grouped or aggregated, or that nothing is",
|
|
),
|
|
}
|
|
|
|
|
|
def _items(line: re.Match[str] | None) -> set[str]:
|
|
return set() if line is None else {p.strip() for p in line.group(1).split(",")}
|
|
|
|
|
|
def is_sql(statement: str) -> bool:
|
|
"""A rendered statement offers a SQL dialect or carries the Database tag."""
|
|
langs = _items(LANGUAGES_RE.search(statement))
|
|
return bool(langs & SQL_LANGS) or "Database" in _items(TAGS_RE.search(statement))
|
|
|
|
|
|
def missing_facets(text: str) -> tuple[str, ...]:
|
|
return tuple(
|
|
name for name, facet in FACETS.items() if not facet.pattern.search(text)
|
|
)
|
|
|
|
|
|
def check(text: str) -> req_check.Check:
|
|
missing = missing_facets(text)
|
|
return req_check.Check("incomplete" if missing else "pass", missing, VIA)
|
|
|
|
|
|
def hints(missing: tuple[str, ...] | list[str]) -> list[str]:
|
|
return [f"{name}: {FACETS[name].hint}" for name in missing if name in FACETS]
|