File size: 3,071 Bytes
b336134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""
Pre-execution validator.

Checks that a resolved intent is compatible with the dataframe schema
before any data gets touched. This is the safety net that stops
"increase the name column" from ever reaching Polars.
"""
from __future__ import annotations

from typing import Optional
import polars as pl

from config import DATA_DIR
from core.column_registry import column_registry


# Operations that REQUIRE numeric dtype on the target column
NUMERIC_ONLY_OPS = {"increase", "decrease", "sum", "average", "min", "max"}


def get_schema(session_id: str) -> Optional[pl.Schema]:
    """Read just the Parquet metadata to get column dtypes β€” no data loaded."""
    import os
    from services.session_manager import session_manager
    pq_path = session_manager.get_filepath(session_id)
    if not os.path.exists(pq_path):
        return None
    try:
        schema = pl.scan_parquet(pq_path).collect_schema()
        return schema
    except Exception:
        return None


def validate_intent(session_id: str, intent: dict) -> Optional[str]:
    """Return an error string if the intent is invalid, else ``None``."""
    schema = get_schema(session_id)
    if schema is None:
        return "Session ka data nahi mila β€” dobara upload karo"

    col = intent.get("column")
    op = intent.get("operation", "")

    # ── Column existence (skip for operations that don't target a column) ──
    no_column_ops = {"remove_duplicates"}
    if op not in no_column_ops and col:
        if col not in schema:
            return f"'{col}' column nahi hai file mein"

    # ── Numeric dtype check ────────────────────────────────────────────
    if op in NUMERIC_ONLY_OPS and col:
        dtype = schema[col]
        if dtype not in (pl.Float64, pl.Int64, pl.Int32, pl.Float32, pl.UInt64, pl.UInt32):
            return f"'{col}' numeric column nahi hai ({dtype}), ye operation apply nahi ho sakta"

    # ── Filter: need condition + value ──────────────────────────────────
    if op == "filter":
        if intent.get("condition") is None or intent.get("filter_value") is None:
            return "Filter ke liye condition aur value dono chahiye, jaise: 'salary > 50000 dikhao'"

    # ── Find & Replace: need old + new ──────────────────────────────────
    if op == "find_replace":
        if not intent.get("old_value") or not intent.get("new_value"):
            return "Find & Replace ke liye purana aur naya value dono chahiye, jaise: 'Active ko Inactive se badlo'"

    # ── Rename: need new_name ───────────────────────────────────────────
    if op == "rename_column":
        if not intent.get("new_name"):
            return "Rename ke liye naya naam chahiye, jaise: 'name ko full_name rename karo'"

    return None  # All good