joschetan commited on
Commit
1aea4b2
·
verified ·
1 Parent(s): 8b97dd5

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +330 -0
app.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import requests
4
+ import json
5
+ import base64
6
+ from io import BytesIO
7
+ import openpyxl
8
+
9
+ # 📌 1. Mukhya Page Configuration
10
+ st.set_page_config(
11
+ page_title="CK Export Invoice Processor Pro",
12
+ page_icon="🚢",
13
+ layout="wide",
14
+ initial_sidebar_state="expanded"
15
+ )
16
+
17
+ WEB_APP_URL = "https://script.google.com/macros/s/AKfycbwEsmWdnkVW3H7_fD99vPMrqhvmY6iJHP1ZooKuwDlj2VE4cht_FBgFyem9xDRFlbjuNw/exec"
18
+ SPREADSHEET_ID = "182qRuH7R0jZqWVKHCg_oAG1SK5CUSkQpxVPxH2O8QUQ"
19
+
20
+ def fetch_all_from_sheet():
21
+ """गूगल शीट से JSON डेटा, टेम्पलेट्स, API Key और Exchange Rates एक साथ फेच करता है (Session State Caching के साथ)"""
22
+ if "gsheet_master_cache" in st.session_state and st.session_state["gsheet_master_cache"] is not None:
23
+ return st.session_state["gsheet_master_cache"]
24
+
25
+ try:
26
+ response = requests.get(f"{WEB_APP_URL}?action=get_data", timeout=20)
27
+ if response.status_code == 200:
28
+ res_text = response.text.strip()
29
+ if res_text.startswith("<"):
30
+ return None
31
+ data = response.json()
32
+ st.session_state["gsheet_master_cache"] = data
33
+ return data
34
+ except Exception:
35
+ pass
36
+ return None
37
+
38
+ def clear_sheet_cache():
39
+ if "gsheet_master_cache" in st.session_state:
40
+ st.session_state["gsheet_master_cache"] = None
41
+
42
+ def save_exchange_rates_to_sheet(ex_data):
43
+ try:
44
+ payload = {
45
+ "action": "save_exchange_rates",
46
+ "exchange_data": ex_data
47
+ }
48
+ res = requests.post(WEB_APP_URL, data=json.dumps(payload), timeout=30)
49
+ if res.status_code == 200:
50
+ clear_sheet_cache()
51
+ return True
52
+ return False
53
+ except Exception:
54
+ return False
55
+
56
+ # 📌 2. Session State Initialization (Global App Lock ke liye)
57
+ if "app_authenticated" not in st.session_state:
58
+ st.session_state["app_authenticated"] = False
59
+
60
+ # 🔒 Agar app unlock nahi hai, toh pehle password screen dikhayein
61
+ if not st.session_state["app_authenticated"]:
62
+ col1, col2, col3 = st.columns([1, 1.5, 1])
63
+ with col2:
64
+ st.markdown("<br><br>", unsafe_allow_html=True)
65
+ st.markdown("<h2 style='text-align: center;'>🚢 CK Export Invoice Prjocessor Pro</h2>", unsafe_allow_html=True)
66
+ st.markdown("<p style='text-align: center; color: gray;'>Kripya aage badhne ke liye app ka password darj karein.</p>", unsafe_allow_html=True)
67
+
68
+ with st.form(key="login_form"):
69
+ pass_input = st.text_input("Password darj karein:", type="password", key="global_lock_pwd")
70
+ submit_button = st.form_submit_button("Unlock App", use_container_width=True, type="primary")
71
+
72
+ if submit_button:
73
+ if pass_input == "CKJ":
74
+ st.session_state["app_authenticated"] = True
75
+ st.rerun()
76
+ else:
77
+ st.error("❌ Galat password! Kripya sahi password darj karein.")
78
+ st.stop()
79
+
80
+ # ==========================================
81
+ # 🚀 Password sahi hone ke baad dikhne wala main app code
82
+ # ==========================================
83
+
84
+ st.markdown("""
85
+ <style>
86
+ [data-testid="stSidebarCollapseButton"] { display: none !important; }
87
+ [data-testid="stSidebarCollapsedControl"] { display: none !important; }
88
+ .creator-card {
89
+ background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
90
+ padding: 15px;
91
+ border-radius: 12px;
92
+ color: white;
93
+ text-align: center;
94
+ box-shadow: 0 4px 10px rgba(0,0,0,0.2);
95
+ margin-bottom: 12px;
96
+ margin-top: 10px;
97
+ }
98
+ .creator-name {
99
+ font-size: 18px;
100
+ font-weight: 700;
101
+ margin-top: 8px;
102
+ margin-bottom: 2px;
103
+ }
104
+ .creator-title {
105
+ font-size: 12px;
106
+ color: #d1d8e0;
107
+ letter-spacing: 1px;
108
+ text-transform: uppercase;
109
+ font-weight: 600;
110
+ }
111
+ [data-testid="stMetricValue"] {
112
+ font-size: 18px !important;
113
+ }
114
+ [data-testid="stMetricLabel"] {
115
+ font-size: 12px !important;
116
+ }
117
+ </style>
118
+ """, unsafe_allow_html=True)
119
+
120
+ with st.sidebar:
121
+ st.markdown("<br>", unsafe_allow_html=True)
122
+
123
+ # 1. Sabse upar: Photo
124
+ try:
125
+ st.image("ck_photo.jpg", use_container_width=True)
126
+ except:
127
+ st.info("Photo loading...")
128
+
129
+ # 2. Uske niche: Chetan Joshi wala blue profile card
130
+ st.markdown("""
131
+ <div class="creator-card">
132
+ <div class="creator-name">Chetan Joshi</div>
133
+ <div class="creator-title">📞 +91 98253 06898</div>
134
+ <hr style="border-color: rgba(255,255,255,0.2); margin: 8px 0;">
135
+ <p style='font-size: 11px; color: #f1f2f6; margin: 0;'>
136
+ <b>CK Export Invoice Pro v2.0</b><br>
137
+ Enterprise Automation & Precision.
138
+ </p>
139
+ </div>
140
+ """, unsafe_allow_html=True)
141
+
142
+ # Session State & Google Sheet Fetch for Exchange Rates
143
+ if "exchange_rates" not in st.session_state or not isinstance(st.session_state["exchange_rates"], dict):
144
+ sheet_full_data = fetch_all_from_sheet()
145
+ sheet_ex = sheet_full_data.get("exchange_rates", None) if isinstance(sheet_full_data, dict) else None
146
+
147
+ if sheet_ex and isinstance(sheet_ex, dict):
148
+ st.session_state["exchange_rates"] = sheet_ex
149
+ else:
150
+ st.session_state["exchange_rates"] = {
151
+ "date": "21-08-2026",
152
+ "rates": {"EUR": "109.8", "GBP": "128.15", "USD": "94.8"},
153
+ "all_rates": {}
154
+ }
155
+
156
+ ex_data = st.session_state["exchange_rates"]
157
+ if "date" not in ex_data: ex_data["date"] = "21-08-2026"
158
+ if "rates" not in ex_data: ex_data["rates"] = {"EUR": "109.8", "GBP": "128.15", "USD": "94.8"}
159
+ if "all_rates" not in ex_data: ex_data["all_rates"] = {}
160
+
161
+ # 3. Uske niche: Teen currencies (EUR, GBP, USD) chote font mein
162
+ r = ex_data["rates"]
163
+ col_e, col_g, col_u = st.columns(3)
164
+ with col_e: st.metric(label="EUR", value=r.get("EUR", "109.8"))
165
+ with col_g: st.metric(label="GBP", value=r.get("GBP", "128.15"))
166
+ with col_u: st.metric(label="USD", value=r.get("USD", "94.8"))
167
+
168
+ # 4. Uske niche: Bohat Badi Size mein Effective Date (w.e.f)
169
+ st.markdown(f"<div style='text-align: center; margin-top: 10px; margin-bottom: 12px;'><span style='font-size: 20px; font-weight: 800; color: #00cec9; background: rgba(0, 206, 201, 0.1); padding: 4px 10px; border-radius: 6px; display: inline-block;'>📅 w.e.f: {ex_data.get('date', 'N/A')}</span></div>", unsafe_allow_html=True)
170
+
171
+ # 5. Uske niche: Customs Exchange Rates heading aur PDF uploader
172
+ st.markdown("##### 💱 Customs Exchange Rates")
173
+
174
+ if "processed_ex_file_id" not in st.session_state:
175
+ st.session_state["processed_ex_file_id"] = None
176
+
177
+ ex_pdf = st.file_uploader("➡️ Upload Rate PDF", type=["pdf"], key="ex_pdf_sidebar")
178
+ if ex_pdf is not None:
179
+ file_signature = f"{ex_pdf.name}_{ex_pdf.size}"
180
+ if st.session_state["processed_ex_file_id"] != file_signature:
181
+ import pdfplumber
182
+ import re
183
+ try:
184
+ with pdfplumber.open(ex_pdf) as pdf:
185
+ text = ""
186
+ for page in pdf.pages:
187
+ t = page.extract_text()
188
+ if t: text += t + "\n"
189
+
190
+ date_match = re.search(r"w\.e\.f[\s\.:]*([\d]{2}[\-\/][\d]{2}[\-\/][\d]{4})", text, re.IGNORECASE)
191
+ if not date_match:
192
+ date_match = re.search(r"w\.e\.f[\s\.:]*([0-9A-Za-z\-]+)", text, re.IGNORECASE)
193
+
194
+ if date_match:
195
+ ex_data["date"] = date_match.group(1).strip()
196
+
197
+ lines = text.split("\n")
198
+ all_parsed = {}
199
+ for line in lines:
200
+ parts = line.split()
201
+ if len(parts) >= 5 and parts[0].isupper() and len(parts[0]) == 3:
202
+ curr_code = parts[0]
203
+ export_val = parts[-1]
204
+ try:
205
+ float(export_val)
206
+ all_parsed[curr_code] = export_val
207
+ except:
208
+ pass
209
+
210
+ if all_parsed:
211
+ ex_data["all_rates"] = all_parsed
212
+ for c in ["EUR", "GBP", "USD"]:
213
+ if c in all_parsed:
214
+ ex_data["rates"][c] = all_parsed[c]
215
+
216
+ save_exchange_rates_to_sheet(ex_data)
217
+ st.session_state["processed_ex_file_id"] = file_signature
218
+
219
+ st.success(f"🎉 Rates updated & saved to Sheet (w.e.f {ex_data['date']})!")
220
+ st.rerun()
221
+ else:
222
+ st.warning("⚠️ PDF se data nahi padha ja saka!")
223
+ except Exception as e:
224
+ st.error(f"Error: {str(e)}")
225
+
226
+ # Expander to view all other currency rates
227
+ if ex_data["all_rates"]:
228
+ with st.expander("📉 View All Currencies"):
229
+ for c_code, c_val in ex_data["all_rates"].items():
230
+ st.markdown(f"**{c_code}**: `{c_val}`")
231
+
232
+ st.markdown("---")
233
+
234
+ if st.button("🔒 Lock App", use_container_width=True):
235
+ st.session_state["app_authenticated"] = False
236
+ st.rerun()
237
+
238
+ st.markdown("---")
239
+
240
+ @st.cache_data(show_spinner=False)
241
+ def load_data_from_gsheet():
242
+ """Sidhe 'Shipper_JSON_Database' sheet se sara data load karta hai"""
243
+ shipper_db = {}
244
+ master_rules_template = {}
245
+
246
+ try:
247
+ json_db_url = f"https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/gviz/tq?tqx=out:csv&sheet=Shipper_JSON_Database"
248
+ df_json = pd.read_csv(json_db_url)
249
+ if not df_json.empty:
250
+ df_json.columns = df_json.columns.str.strip()
251
+ for _, row in df_json.iterrows():
252
+ if "ShipperName" in df_json.columns and pd.notna(row["ShipperName"]):
253
+ s_name = str(row["ShipperName"]).strip()
254
+ if s_name and s_name.lower() != "nan":
255
+ shipper_json_str = row["ShipperJSON"] if "ShipperJSON" in df_json.columns and pd.notna(row["ShipperJSON"]) else "{}"
256
+ try:
257
+ shipper_db[s_name] = json.loads(shipper_json_str)
258
+ except:
259
+ shipper_db[s_name] = {
260
+ "allowed_uploads": ["Full Job Excel Format File"],
261
+ "uploaded_files": {},
262
+ "mapping_rules": {},
263
+ "item_table_rules": {},
264
+ "item_table_rule_name": "parser_welspun",
265
+ "igst_config": {"lut_keywords": "", "paid_keywords": ""}
266
+ }
267
+ except Exception as e:
268
+ st.error(f"Error loading Shipper_JSON_Database: {e}")
269
+
270
+ return shipper_db, master_rules_template
271
+
272
+ # 🔄 Data load engine
273
+ if "shipper_database" not in st.session_state or "master_rules_template" not in st.session_state:
274
+ db, m_template = load_data_from_gsheet()
275
+ st.session_state["shipper_database"] = db
276
+ st.session_state["master_rules_template"] = m_template
277
+ st.session_state["master_types"] = ["Full Job Excel Format File"]
278
+
279
+ if "admin_authenticated" not in st.session_state: st.session_state["admin_authenticated"] = False
280
+ if "processed_file_ready" not in st.session_state: st.session_state["processed_file_ready"] = None
281
+
282
+ # ==========================================
283
+ # 🖥️ MAIN PAGE ROUTING DISPLAY
284
+ # ==========================================
285
+ if st.session_state["admin_authenticated"]:
286
+ top_col1, top_col2 = st.columns([8, 2])
287
+ with top_col1:
288
+ st.title("🚢 CK Export Processor - Admin Mode")
289
+ with top_col2:
290
+ if st.button("🚪 Log Out Admin", type="primary", use_container_width=True):
291
+ st.session_state["admin_authenticated"] = False
292
+ st.rerun()
293
+
294
+ st.write("---")
295
+
296
+ sub_menu = st.radio(
297
+ "📋 Admin settings chunein:",
298
+ ["i. 🏢 Add Shipper Name & Setup", "iii. 🌍 Global Masters & Common Dictionaries"],
299
+ horizontal=True
300
+ )
301
+ st.write("---")
302
+
303
+ from shipper_data import render_shipper_data
304
+ from global_masters import render_global_masters
305
+
306
+ if sub_menu == "i. 🏢 Add Shipper Name & Setup":
307
+ render_shipper_data()
308
+ elif sub_menu == "iii. 🌍 Global Masters & Common Dictionaries":
309
+ render_global_masters()
310
+
311
+ else:
312
+ from processor import render_processor
313
+
314
+ col_l, col_c, col_r = st.columns([1, 5, 1])
315
+ with col_c:
316
+ st.title("🚢 CK Export Invoice Processor")
317
+ st.write("---")
318
+ render_processor()
319
+
320
+ st.write("---")
321
+ st.write("---")
322
+
323
+ with st.expander("🛠️ Admin Settings Access"):
324
+ pwd = st.text_input("Admin password darj karein:", type="password", key="main_admin_pwd")
325
+ if st.button("Login Karein"):
326
+ if pwd == "TEST":
327
+ st.session_state["admin_authenticated"] = True
328
+ st.rerun()
329
+ else:
330
+ st.error("Galat password!")