gkdivya commited on
Commit
c78ab8d
Β·
verified Β·
1 Parent(s): 93c996d

Create admin_patterns.py

Browse files
Files changed (1) hide show
  1. admin_patterns.py +191 -0
admin_patterns.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # admin_patterns.py
2
+ import os
3
+ import json
4
+ import re
5
+ import shutil
6
+ import pandas as pd
7
+ from datetime import datetime
8
+
9
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
10
+ PATTERN_FILE = os.path.join(BASE_DIR, "patterns.json")
11
+ pattern_config = None
12
+ DEFAULT_PATTERN_CONFIG = {"global": [], "states": {}}
13
+
14
+
15
+ def _make_backup_of_patterns():
16
+ """
17
+ If PATTERN_FILE exists, copy it to patterns_backup_{timestamp}.json
18
+ Returns backup path or None on failure/if not exists.
19
+ """
20
+ try:
21
+ if os.path.exists(PATTERN_FILE):
22
+ ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
23
+ backup_name = f"patterns_backup_{ts}.json"
24
+ backup_path = os.path.join(BASE_DIR, backup_name)
25
+ shutil.copyfile(PATTERN_FILE, backup_path)
26
+ return backup_path
27
+ except Exception as e:
28
+ print("ERROR creating patterns.json backup:", e)
29
+ return None
30
+
31
+
32
+ def load_pattern_config(debug: bool = False):
33
+ """
34
+ Load patterns.json. If missing, create an empty skeleton.
35
+ """
36
+ global pattern_config
37
+ if os.path.exists(PATTERN_FILE):
38
+ try:
39
+ with open(PATTERN_FILE, "r", encoding="utf-8") as f:
40
+ cfg = json.load(f)
41
+ pattern_config = cfg
42
+ if debug:
43
+ print("DEBUG: Successfully loaded patterns.json")
44
+ print("DEBUG: global count:", len(cfg.get("global", [])))
45
+ print("DEBUG: state keys:", list(cfg.get("states", {}).keys()))
46
+ return cfg
47
+ except Exception as e:
48
+ print("ERROR: Failed to parse patterns.json:", e)
49
+ pattern_config = {"global": [], "states": {}}
50
+ return pattern_config
51
+ else:
52
+ # create empty file
53
+ try:
54
+ with open(PATTERN_FILE, "w", encoding="utf-8") as f:
55
+ json.dump(DEFAULT_PATTERN_CONFIG, f, indent=2, ensure_ascii=False)
56
+ print("DEBUG: Created empty patterns.json at", PATTERN_FILE)
57
+ except Exception as e:
58
+ print("ERROR: Could not create patterns.json:", e)
59
+ pattern_config = DEFAULT_PATTERN_CONFIG.copy()
60
+ return pattern_config
61
+
62
+
63
+ def save_pattern_config(cfg: dict):
64
+ """
65
+ Save the given config to PATTERN_FILE with a timestamped backup of the previous file.
66
+ """
67
+ global pattern_config
68
+ # create backup first
69
+ backup_path = _make_backup_of_patterns()
70
+ if backup_path:
71
+ print(f"INFO: patterns.json backed up to {backup_path}")
72
+ # write new file
73
+ with open(PATTERN_FILE, "w", encoding="utf-8") as f:
74
+ json.dump(cfg, f, indent=2, ensure_ascii=False)
75
+ pattern_config = cfg.copy()
76
+ return True
77
+
78
+
79
+ def build_patterns_from_config(cfg: dict, state_key: str | None):
80
+ global_list = [(p["pattern"], p["replacement"]) for p in cfg.get("global", [])]
81
+ state_list = []
82
+ if state_key:
83
+ state_key_up = state_key.upper().strip()
84
+ state_patterns = cfg.get("states", {}).get(state_key_up, [])
85
+ state_list = [(p["pattern"], p["replacement"]) for p in state_patterns]
86
+ return global_list, state_list
87
+
88
+
89
+ def normalize_with_patterns_dynamic(s: str, state_key: str | None):
90
+ global pattern_config
91
+ if not isinstance(s, str):
92
+ return ""
93
+ s = s.upper()
94
+ if pattern_config is None:
95
+ load_pattern_config(debug=False)
96
+ cfg = pattern_config or DEFAULT_PATTERN_CONFIG
97
+ global_patterns, state_patterns = build_patterns_from_config(cfg, state_key)
98
+ for pat, repl in global_patterns:
99
+ try:
100
+ s = re.sub(pat, repl, s)
101
+ except re.error:
102
+ continue
103
+ for pat, repl in state_patterns:
104
+ try:
105
+ s = re.sub(pat, repl, s)
106
+ except re.error:
107
+ continue
108
+ s = re.sub(r"[^A-Z0-9]+", " ", s)
109
+ s = re.sub(r"\s+", " ", s).strip()
110
+ return s
111
+
112
+
113
+ # Admin helpers for the UI
114
+ def load_global_patterns_for_editor():
115
+ cfg = load_pattern_config(debug=False)
116
+ return pd.DataFrame(cfg.get("global", []))
117
+
118
+
119
+ def load_state_patterns_for_editor(selected_state: str | None, new_state_name: str | None):
120
+ cfg = load_pattern_config(debug=False)
121
+ key = None
122
+ if new_state_name and new_state_name.strip():
123
+ key = new_state_name.strip().upper()
124
+ elif selected_state:
125
+ key = selected_state.strip().upper()
126
+ if not key:
127
+ return pd.DataFrame(columns=["pattern", "replacement"])
128
+ state_patterns = cfg.get("states", {}).get(key, [])
129
+ return pd.DataFrame(state_patterns)
130
+
131
+
132
+ def save_global_patterns_from_editor(df: pd.DataFrame, admin_password: str, expected_password: str):
133
+ if expected_password is None:
134
+ return "❌ ADMIN password not configured in environment."
135
+ if admin_password != expected_password:
136
+ return "❌ Invalid admin password. Global patterns NOT saved."
137
+ cfg = load_pattern_config(debug=False)
138
+ cfg["global"] = df.fillna("").to_dict(orient="records")
139
+ save_pattern_config(cfg)
140
+ gcount = len(cfg.get("global", []))
141
+ skeys = sorted(list(cfg.get("states", {}).keys()))
142
+ return f"βœ… Global patterns saved β€” global={gcount}, state_keys={skeys}"
143
+
144
+
145
+ def save_state_patterns_from_editor(selected_state: str | None, new_state_name: str | None, df: pd.DataFrame, admin_password: str, expected_password: str):
146
+ if expected_password is None:
147
+ return "❌ ADMIN password not configured in environment."
148
+ if admin_password != expected_password:
149
+ return "❌ Invalid admin password. State patterns NOT saved."
150
+ key = None
151
+ if new_state_name and new_state_name.strip():
152
+ key = new_state_name.strip().upper()
153
+ elif selected_state:
154
+ key = selected_state.strip().upper()
155
+ if not key:
156
+ return "⚠ Please select a state or type a new state key."
157
+ cfg = load_pattern_config(debug=False)
158
+ cfg.setdefault("states", {})[key] = df.fillna("").to_dict(orient="records")
159
+ save_pattern_config(cfg)
160
+ gcount = len(cfg.get("global", []))
161
+ skeys = sorted(list(cfg.get("states", {}).keys()))
162
+ return f"βœ… Patterns for {key} saved β€” global={gcount}, state_keys={skeys}"
163
+
164
+
165
+ def refresh_pattern_config():
166
+ cfg = load_pattern_config(debug=True)
167
+ gcount = len(cfg.get("global", []))
168
+ skeys = sorted(list(cfg.get("states", {}).keys()))
169
+ return f"Refreshed patterns.json β€” global={gcount}, state_keys={skeys}"
170
+
171
+
172
+ def show_patterns_file_info():
173
+ info_lines = []
174
+ info_lines.append(f"PATTERN_FILE: {PATTERN_FILE}")
175
+ info_lines.append(f"Exists: {os.path.exists(PATTERN_FILE)}")
176
+ if os.path.exists(PATTERN_FILE):
177
+ try:
178
+ size = os.path.getsize(PATTERN_FILE)
179
+ info_lines.append(f"Size (bytes): {size}")
180
+ with open(PATTERN_FILE, "r", encoding="utf-8") as f:
181
+ txt = f.read(1000)
182
+ info_lines.append("Preview (first 1000 chars):")
183
+ info_lines.append("```json\n" + txt + ("\n... (truncated)" if len(txt) >= 1000 else "") + "\n```")
184
+ except Exception as e:
185
+ info_lines.append("ERROR reading file: " + str(e))
186
+ try:
187
+ listing = os.listdir(BASE_DIR)
188
+ info_lines.append("Files in BASE_DIR: " + ", ".join(listing))
189
+ except Exception as e:
190
+ info_lines.append("ERROR listing BASE_DIR: " + str(e))
191
+ return "\n\n".join(info_lines)