Upload 6 files
Browse files- app.py +7 -2
- queries.py +344 -0
- recon.py +285 -0
- requirements.txt +2 -0
app.py
CHANGED
@@ -57,10 +57,11 @@ st.markdown(html_title, unsafe_allow_html=True)
|
|
57 |
|
58 |
menu_options = [
|
59 |
{"label": "Internal users", "icon": "π", "description": "Upload a document and schedule it for email"},
|
60 |
-
{"label": "External users", "icon": "π", "description": "Schedule an email with BigQuery data"}
|
|
|
61 |
]
|
62 |
|
63 |
-
|
64 |
selected_option = option_menu(
|
65 |
menu_title="Select Integration", # Title of the menu
|
66 |
options=[option["label"] for option in menu_options], # Displayed options
|
@@ -379,3 +380,7 @@ if selected_option == "External users":
|
|
379 |
elif selected_option == "Internal users":
|
380 |
with open('ap.py') as file:
|
381 |
exec(file.read())
|
|
|
|
|
|
|
|
|
|
57 |
|
58 |
menu_options = [
|
59 |
{"label": "Internal users", "icon": "π", "description": "Upload a document and schedule it for email"},
|
60 |
+
{"label": "External users", "icon": "π", "description": "Schedule an email with BigQuery data"},
|
61 |
+
{"label": "Recon checking", "icon": "π", "description": "Schedule an email with BigQuery data"}
|
62 |
]
|
63 |
|
64 |
+
# Create the custom option menu
|
65 |
selected_option = option_menu(
|
66 |
menu_title="Select Integration", # Title of the menu
|
67 |
options=[option["label"] for option in menu_options], # Displayed options
|
|
|
380 |
elif selected_option == "Internal users":
|
381 |
with open('ap.py') as file:
|
382 |
exec(file.read())
|
383 |
+
|
384 |
+
elif selected_option == "Recon checking":
|
385 |
+
with open('recon.py') as file:
|
386 |
+
exec(file.read())
|
queries.py
ADDED
@@ -0,0 +1,344 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
queries = {
|
3 |
+
# Query 1
|
4 |
+
'1. fynd-db.finance_recon_tool_asia.01_finance_avis_data_final': '''
|
5 |
+
select
|
6 |
+
bag_id,
|
7 |
+
concat(bag_id,Recon_Status,Settlement_type,Transaction_Type) as Merged,
|
8 |
+
count(concat(bag_id,Recon_Status,Settlement_type,Transaction_Type))
|
9 |
+
from `fynd-db.finance_recon_tool_asia.01_finance_avis_data_final`
|
10 |
+
group by
|
11 |
+
1,2
|
12 |
+
having count(concat(bag_id,Recon_Status,Settlement_type,Transaction_Type)) not in (1)
|
13 |
+
''',
|
14 |
+
|
15 |
+
# Query 2
|
16 |
+
'2. Seller fees date validation': '''
|
17 |
+
select
|
18 |
+
*
|
19 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
20 |
+
where
|
21 |
+
invoice_generation_date is null
|
22 |
+
''',
|
23 |
+
|
24 |
+
# Query 3
|
25 |
+
'3. Transaction components validation': '''
|
26 |
+
select
|
27 |
+
bag_id,
|
28 |
+
transaction_type,
|
29 |
+
inserted_date,
|
30 |
+
round(transaction_fee+packaging_fee+logistics_charges+refund_support_fees+sla_charges-net_charges,0) as diff
|
31 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
32 |
+
where
|
33 |
+
round(transaction_fee+packaging_fee+logistics_charges+refund_support_fees+sla_charges-net_charges,0) not in (0,-1,-1)
|
34 |
+
and inserted_date > '2023-09-30'
|
35 |
+
group by 1,2,3, transaction_fee,packaging_fee,refund_support_fees,sla_charges,net_charges,logistics_charges
|
36 |
+
''',
|
37 |
+
|
38 |
+
# Query 4
|
39 |
+
'4. Transactions in net collection validation': '''
|
40 |
+
select
|
41 |
+
concat(bag_id,Settlement_type) as Merged,
|
42 |
+
count(concat(bag_id,Settlement_type))
|
43 |
+
from `fynd-db.finance_recon_tool_asia.09_seller_net_collection_daily`
|
44 |
+
group by 1
|
45 |
+
having
|
46 |
+
count(concat(bag_id,Settlement_type)) not in (1)
|
47 |
+
''',
|
48 |
+
|
49 |
+
# Query 5
|
50 |
+
'5. Transactions in seller fees validation': '''
|
51 |
+
select
|
52 |
+
bag_id,
|
53 |
+
concat(bag_id,transaction_type) as Merged,
|
54 |
+
count(concat(bag_id,transaction_type))
|
55 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
56 |
+
group by 1,2
|
57 |
+
having
|
58 |
+
count(concat(bag_id,transaction_type)) not in (1)
|
59 |
+
''',
|
60 |
+
|
61 |
+
|
62 |
+
# Query 6
|
63 |
+
'6. Transactions in seller claims validation': '''
|
64 |
+
select
|
65 |
+
current_shipment_id,
|
66 |
+
concat(current_shipment_id,transaction_type) as Merged,
|
67 |
+
count(concat(current_shipment_id,transaction_type))
|
68 |
+
from `fynd-db.finance_recon_tool_asia.12_seller_claims_daily`
|
69 |
+
group by 1,2
|
70 |
+
having
|
71 |
+
count(concat(current_shipment_id,transaction_type)) not in (1)
|
72 |
+
''',
|
73 |
+
|
74 |
+
|
75 |
+
# Query 7
|
76 |
+
'7. Aggregate liability validation': '''
|
77 |
+
select
|
78 |
+
*
|
79 |
+
from `fynd-db.finance_recon_tool_asia.12_seller_claims_daily`
|
80 |
+
where
|
81 |
+
aggregate_liability is null
|
82 |
+
''',
|
83 |
+
|
84 |
+
|
85 |
+
# Query 8
|
86 |
+
'8. Lost claim validation': '''
|
87 |
+
select
|
88 |
+
*
|
89 |
+
from `fynd-db.finance_recon_tool_asia.01_finance_avis_data_final` as A
|
90 |
+
left join
|
91 |
+
`fynd-db.finance_recon_tool_asia.12_seller_claims_daily` as B
|
92 |
+
on
|
93 |
+
A.current_shipment_id = B.current_shipment_id
|
94 |
+
where
|
95 |
+
A.claim_settle_date is not null
|
96 |
+
and A.transaction_type = 'Claim'
|
97 |
+
and A.dp_partner = 'fynd'
|
98 |
+
and B.current_shipment_id is null
|
99 |
+
''',
|
100 |
+
|
101 |
+
|
102 |
+
|
103 |
+
# Query 9
|
104 |
+
'9. Gstin validation': '''
|
105 |
+
select
|
106 |
+
company_id,
|
107 |
+
company_name,
|
108 |
+
ordering_channel
|
109 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
110 |
+
where
|
111 |
+
company_gstn is null
|
112 |
+
group by
|
113 |
+
1,2,3
|
114 |
+
''',
|
115 |
+
|
116 |
+
|
117 |
+
|
118 |
+
# Query 10
|
119 |
+
'10. Positive transaction components validation': '''
|
120 |
+
select
|
121 |
+
*
|
122 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
123 |
+
where
|
124 |
+
transaction_type in ('Sale','SLA','Claim')
|
125 |
+
and net_charges > 0
|
126 |
+
''',
|
127 |
+
|
128 |
+
|
129 |
+
# Query 11
|
130 |
+
'11. Negative transaction components validation': '''
|
131 |
+
select
|
132 |
+
*
|
133 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
134 |
+
where
|
135 |
+
transaction_type = 'Return'
|
136 |
+
and net_charges < 0
|
137 |
+
''',
|
138 |
+
|
139 |
+
|
140 |
+
# Query 12
|
141 |
+
'12. GST Tag validation': '''
|
142 |
+
select
|
143 |
+
company_id,
|
144 |
+
company_name,
|
145 |
+
ordering_channel,
|
146 |
+
company_gstn,
|
147 |
+
gst_tag,
|
148 |
+
case WHEN LENGTH(company_gstn) = 10 then "SGST"
|
149 |
+
when SUBSTRING(company_gstn,1,2) = '27' then "SGST" else "IGST" end as Tag,
|
150 |
+
case when gst_tag = (case WHEN LENGTH(company_gstn) = 10 then "SGST"
|
151 |
+
when SUBSTRING(company_gstn,1,2) = '27' then "SGST" else "IGST" end) then "Match" else "Not_Match" end as CC
|
152 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
153 |
+
where
|
154 |
+
(case when gst_tag = (case WHEN LENGTH(company_gstn) = 10 then "SGST"
|
155 |
+
when SUBSTRING(company_gstn,1,2) = '27' then "SGST" else "IGST" end) then "Match" else "Not_Match" end) = "Not_Match"
|
156 |
+
group by 1,2,3,4,5,6
|
157 |
+
''',
|
158 |
+
|
159 |
+
# Query 13
|
160 |
+
'13. 09_Net collection all data validation': '''
|
161 |
+
select
|
162 |
+
A.company_id,
|
163 |
+
A.company_name,
|
164 |
+
A.ordering_channel,
|
165 |
+
A.bag_id,
|
166 |
+
A.Settlement_type,
|
167 |
+
A.recon_status,
|
168 |
+
A.recon_date,
|
169 |
+
A.inserted_date,
|
170 |
+
B.bag_id,
|
171 |
+
B.Settlement_type
|
172 |
+
from `fynd-db.finance_recon_tool_asia.01_finance_avis_data_final` as A
|
173 |
+
left join
|
174 |
+
`fynd-db.finance_recon_tool_asia.09_seller_net_collection_daily` as B
|
175 |
+
on
|
176 |
+
A.bag_id = B.bag_id
|
177 |
+
and A.Settlement_type = B.Settlement_type
|
178 |
+
where
|
179 |
+
A.Settlement_type not in ('NA','SLA')
|
180 |
+
and A.recon_status in ('delivery_done','return_bag_delivered','return_bag_picked')
|
181 |
+
and B.bag_id is null
|
182 |
+
and (case when A.Settlement_type = 'collection' and A.collection_partner = 'fynd' then 'yes'
|
183 |
+
when A.Settlement_type = 'refund' and A.refund_partner = 'fynd' then 'yes' else 'no' end) = 'yes'
|
184 |
+
group by
|
185 |
+
1,2,3,4,5,6,7,8,9,10
|
186 |
+
''',
|
187 |
+
|
188 |
+
|
189 |
+
# Query 14
|
190 |
+
'14. Net collection collection & refund validation': '''
|
191 |
+
select
|
192 |
+
bag_id,
|
193 |
+
settlement_type,
|
194 |
+
collection_partner,
|
195 |
+
refund_partner,
|
196 |
+
case when settlement_type in ('collection','Claim',"dispute","rectify","rectify_R") and collection_partner = 'fynd' then 'Yes' when settlement_type = 'refund' and refund_partner = 'fynd' then 'Yes' else 'No' end as Comment
|
197 |
+
from `fynd-db.finance_recon_tool_asia.09_seller_net_collection_daily`
|
198 |
+
where
|
199 |
+
(case when settlement_type in ('collection','Claim',"dispute","rectify","rectify_R") and collection_partner = 'fynd' then 'Yes' when settlement_type = 'refund' and refund_partner = 'fynd' then 'Yes' else 'No' end) = 'No'
|
200 |
+
''',
|
201 |
+
|
202 |
+
|
203 |
+
# Query 15
|
204 |
+
'15. 09_Net collection collection & refund validation': '''
|
205 |
+
select
|
206 |
+
A.bag_id,
|
207 |
+
B.bag_id,
|
208 |
+
A.settlement_type,
|
209 |
+
B.settlement_type,
|
210 |
+
A.transaction_type,
|
211 |
+
A.collection_partner,
|
212 |
+
A.refund_partner,
|
213 |
+
case when A.settlement_type = 'collection' and A.collection_partner = 'fynd' then 'Yes' when A.settlement_type = 'refund' and A.refund_partner = 'fynd' then 'Yes' else 'No' end as Comment
|
214 |
+
from `fynd-db.finance_recon_tool_asia.01_finance_avis_data_final` as A
|
215 |
+
left join
|
216 |
+
`fynd-db.finance_recon_tool_asia.09_seller_net_collection_daily` as B
|
217 |
+
on
|
218 |
+
A.bag_id = B.bag_id
|
219 |
+
and A.settlement_type = B.settlement_type
|
220 |
+
where
|
221 |
+
A.settlement_type <> 'NA'
|
222 |
+
and A.recon_status not in ("bag_lost","return_bag_lost")
|
223 |
+
and (case when A.settlement_type = 'collection' and A.collection_partner = 'fynd' then 'yes' when A.settlement_type = 'refund' and A.refund_partner = 'fynd' then 'yes' else 'No' end) = 'yes'
|
224 |
+
and (case when A.transaction_type = 'Sale' and A.collection_partner = 'fynd' then 'Yes' when A.transaction_type = 'Return' and A.refund_partner = 'fynd' then 'Yes' when A.transaction_type = 'Claim' and A.collection_partner = 'fynd' then 'Yes' else 'No' end) = 'Yes'
|
225 |
+
and B.bag_id is null
|
226 |
+
''',
|
227 |
+
|
228 |
+
|
229 |
+
# Query 16
|
230 |
+
'16. NA settlement validation': '''
|
231 |
+
select
|
232 |
+
*
|
233 |
+
from `fynd-db.finance_recon_tool_asia.09_seller_net_collection_daily`
|
234 |
+
where
|
235 |
+
-- settlement_type = 'NA'
|
236 |
+
(case when settlement_type in ('collection','Claim','rectify','rectify_R') and collection_partner = 'fynd' then 'Yes' when settlement_type = 'refund' and refund_partner = 'fynd' then 'Yes' else 'No'end ) = 'No'
|
237 |
+
''',
|
238 |
+
|
239 |
+
|
240 |
+
# Query 17
|
241 |
+
'17. Fynd collection placed bags validation': '''
|
242 |
+
select
|
243 |
+
A.bag_id,
|
244 |
+
B.bag_id,
|
245 |
+
A.order_type,
|
246 |
+
A.transaction_type,
|
247 |
+
case when B.settlement_type = 'collection' then 'Sale' else 'Return' end as transaction_type,
|
248 |
+
from `fynd-db.finance_recon_tool_asia.11_seller_fees_daily` as A
|
249 |
+
left join
|
250 |
+
`fynd-db.finance_recon_tool_asia.09_seller_net_collection_daily` as B
|
251 |
+
on
|
252 |
+
A.bag_id = B.bag_id
|
253 |
+
and A.transaction_type = (case when B.settlement_type = 'collection' then 'Sale' when B.settlement_type = 'refund' then 'Return' else 'NA' end )
|
254 |
+
where
|
255 |
+
transaction_type <> 'SLA'
|
256 |
+
and (case when A.transaction_type = 'Sale' and A.collection_partner = 'fynd' then 'yes' when A.transaction_type = 'Return' and A.refund_partner = 'fynd' then 'yes' else 'no'end) = 'yes'
|
257 |
+
and B.bag_id is null
|
258 |
+
''',
|
259 |
+
|
260 |
+
|
261 |
+
# # Query 18
|
262 |
+
# '18. Disbursement table validation': '''
|
263 |
+
# select
|
264 |
+
# A.sett_id,
|
265 |
+
# B.sett_id,
|
266 |
+
# NP,
|
267 |
+
# net_amount,
|
268 |
+
# case when A.sett_id = B.sett_id and round(NP - net_amount,0) in (0,1) then 'Match' else "Not_Match" end as Diff
|
269 |
+
# from
|
270 |
+
# (SELECT
|
271 |
+
# sett_id,
|
272 |
+
# SUM(seller_net_collection) AS NP
|
273 |
+
# FROM
|
274 |
+
# `fynd-db.finance_recon_tool_asia.09_seller_net_collection_daily`
|
275 |
+
# GROUP BY
|
276 |
+
# 1
|
277 |
+
# UNION ALL
|
278 |
+
# SELECT
|
279 |
+
# sett_id,
|
280 |
+
# SUM(total_charges) AS NP
|
281 |
+
# FROM
|
282 |
+
# `fynd-db.finance_recon_tool_asia.11_seller_fees_daily`
|
283 |
+
# GROUP BY
|
284 |
+
# 1
|
285 |
+
# UNION ALL
|
286 |
+
# SELECT
|
287 |
+
# sett_id,
|
288 |
+
# SUM(claimable_amt) AS NP
|
289 |
+
# FROM
|
290 |
+
# `fynd-db.finance_recon_tool_asia.12_seller_claims_daily`
|
291 |
+
# GROUP BY
|
292 |
+
# 1
|
293 |
+
# UNION ALL
|
294 |
+
# SELECT
|
295 |
+
# sett_id,
|
296 |
+
# SUM(dispute_amount) AS NP
|
297 |
+
# FROM
|
298 |
+
# `fynd-db.finance_recon_tool_asia.17_seller_manual_Dispute`
|
299 |
+
# GROUP BY
|
300 |
+
# 1) as A
|
301 |
+
# --left join
|
302 |
+
# right join
|
303 |
+
# (select
|
304 |
+
# sett_id,
|
305 |
+
# sum(net_amount) as net_amount
|
306 |
+
# from
|
307 |
+
# `fynd-db.finance_recon_tool_asia.disbursement_summary`
|
308 |
+
# group by
|
309 |
+
# 1) as B
|
310 |
+
# on
|
311 |
+
# A.sett_id = B.sett_id
|
312 |
+
# where
|
313 |
+
# (case when A.sett_id = B.sett_id and round(NP - net_amount,0) in (0,1) then 'Match' else "Not_Match" end) = 'Not_Match'
|
314 |
+
# ''',
|
315 |
+
|
316 |
+
|
317 |
+
# Query 19
|
318 |
+
'18. PPD validation': '''
|
319 |
+
select
|
320 |
+
*
|
321 |
+
from
|
322 |
+
(SELECT
|
323 |
+
*
|
324 |
+
FROM
|
325 |
+
`fynd-db.Outstanding.09_Payable_File`
|
326 |
+
where
|
327 |
+
expected_payout_date <= current_date()
|
328 |
+
and order_type = "PPD"
|
329 |
+
and lower(collection_partner) = "seller") as A
|
330 |
+
left join
|
331 |
+
(select
|
332 |
+
bag_id,
|
333 |
+
txn_id,
|
334 |
+
collected_amount
|
335 |
+
from
|
336 |
+
`fynd-db.finance_recon_tool_asia.05_partner_collection`) as B
|
337 |
+
on
|
338 |
+
A.bag_id = B.bag_id
|
339 |
+
where
|
340 |
+
B.bag_id is not null
|
341 |
+
''',
|
342 |
+
|
343 |
+
# Add more queries as needed
|
344 |
+
}
|
recon.py
ADDED
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from google.cloud import bigquery
|
2 |
+
import functions_framework
|
3 |
+
from queries import queries
|
4 |
+
from google.oauth2 import service_account
|
5 |
+
import json
|
6 |
+
import requests
|
7 |
+
import streamlit as st
|
8 |
+
import pyperclip
|
9 |
+
from ap import send_message_via_webhook, Webhook_urls
|
10 |
+
|
11 |
+
# Dropdown for channels/members
|
12 |
+
webhook_url = list(Webhook_urls.keys())
|
13 |
+
html_subject = """
|
14 |
+
<html>
|
15 |
+
<head>
|
16 |
+
<style>
|
17 |
+
.button {
|
18 |
+
display: inline-block;
|
19 |
+
padding: 10px 20px;
|
20 |
+
border-radius: 12px;
|
21 |
+
background: linear-gradient(to bottom, #f8f9fa, #e0e0e0);
|
22 |
+
box-shadow:
|
23 |
+
0 6px 12px rgba(0, 0, 0, 0.3),
|
24 |
+
0 8px 16px rgba(0, 0, 0, 0.2),
|
25 |
+
inset 0 -2px 4px rgba(255, 255, 255, 0.6);
|
26 |
+
text-align: center;
|
27 |
+
position: relative;
|
28 |
+
transform: translateY(4px);
|
29 |
+
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
30 |
+
cursor: pointer;
|
31 |
+
user-select: none;
|
32 |
+
}
|
33 |
+
.button:hover {
|
34 |
+
box-shadow:
|
35 |
+
0 8px 16px rgba(0, 0, 0, 0.3),
|
36 |
+
0 12px 24px rgba(0, 0, 0, 0.2);
|
37 |
+
transform: translateY(2px);
|
38 |
+
}
|
39 |
+
.button:active {
|
40 |
+
box-shadow:
|
41 |
+
0 4px 8px rgba(0, 0, 0, 0.3),
|
42 |
+
0 6px 12px rgba(0, 0, 0, 0.2);
|
43 |
+
transform: translateY(0);
|
44 |
+
}
|
45 |
+
</style>
|
46 |
+
</head>
|
47 |
+
<body>
|
48 |
+
<div class="button">
|
49 |
+
<h3 style="
|
50 |
+
font-size: 20px;
|
51 |
+
color: #ffffff;
|
52 |
+
background-image: linear-gradient(to right, #800000, #ff0000, #ffdab9);
|
53 |
+
background-clip: text;
|
54 |
+
-webkit-background-clip: text;
|
55 |
+
text-fill-color: transparent;
|
56 |
+
-webkit-text-fill-color: transparent;
|
57 |
+
margin: 0;
|
58 |
+
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.4);
|
59 |
+
">Select channels/members</h3>
|
60 |
+
</div>
|
61 |
+
</body>
|
62 |
+
</html>
|
63 |
+
"""
|
64 |
+
|
65 |
+
|
66 |
+
st.markdown(html_subject, unsafe_allow_html=True)
|
67 |
+
selection = st.multiselect("", webhook_url)
|
68 |
+
# SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/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"
|
69 |
+
|
70 |
+
|
71 |
+
|
72 |
+
def check_duplicates(credentials_file):
|
73 |
+
"""Check for duplicates using BigQuery with the provided credentials file."""
|
74 |
+
results = {}
|
75 |
+
credentials = service_account.Credentials.from_service_account_info(json.loads(credentials_file))
|
76 |
+
client = bigquery.Client(credentials=credentials, project=credentials.project_id)
|
77 |
+
|
78 |
+
for i, (query_name, query) in enumerate(queries.items()):
|
79 |
+
|
80 |
+
query_job = client.query(query)
|
81 |
+
df = query_job.result().to_dataframe()
|
82 |
+
# For debugging, write the DataFrame to the Streamlit app
|
83 |
+
st.write(f"{query_name}:", df)
|
84 |
+
|
85 |
+
button_styles = """
|
86 |
+
<style>
|
87 |
+
div.stButton > button {
|
88 |
+
color: #ffffff; /* Text color */
|
89 |
+
font-size: 30px;
|
90 |
+
background-image: linear-gradient(to right, #800000, #ff0000); /* Maroon to light red gradient */
|
91 |
+
border: none;
|
92 |
+
padding: 10px 20px;
|
93 |
+
cursor: pointer;
|
94 |
+
border-radius: 15px;
|
95 |
+
display: inline-block;
|
96 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 8px 15px rgba(0, 0, 0, 0.1); /* Box shadow */
|
97 |
+
transition: all 0.3s ease; /* Smooth transition on hover */
|
98 |
+
}
|
99 |
+
div.stButton > button:hover {
|
100 |
+
background-color: #00ff00; /* Hover background color */
|
101 |
+
color: #ff0000; /* Hover text color */
|
102 |
+
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2), 0 12px 20px rgba(0, 0, 0, 0.2); /* Box shadow on hover */
|
103 |
+
}
|
104 |
+
</style>
|
105 |
+
"""
|
106 |
+
st.markdown(button_styles, unsafe_allow_html=True)
|
107 |
+
if st.button(f"Copy Query", key=f"copy_query_{i}"):
|
108 |
+
pyperclip.copy(query)
|
109 |
+
st.success('Query copied to clipboard!')
|
110 |
+
|
111 |
+
if not df.empty:
|
112 |
+
duplicate_count = len(df)
|
113 |
+
results[query_name] = duplicate_count
|
114 |
+
|
115 |
+
return results
|
116 |
+
|
117 |
+
# Streamlit UI
|
118 |
+
html_subject = """
|
119 |
+
<html>
|
120 |
+
<head>
|
121 |
+
<style>
|
122 |
+
.button {
|
123 |
+
display: inline-block;
|
124 |
+
padding: 10px 20px;
|
125 |
+
border-radius: 12px;
|
126 |
+
background: linear-gradient(to bottom, #f8f9fa, #e0e0e0);
|
127 |
+
box-shadow:
|
128 |
+
0 6px 12px rgba(0, 0, 0, 0.3),
|
129 |
+
0 8px 16px rgba(0, 0, 0, 0.2),
|
130 |
+
inset 0 -2px 4px rgba(255, 255, 255, 0.6);
|
131 |
+
text-align: center;
|
132 |
+
position: relative;
|
133 |
+
transform: translateY(4px);
|
134 |
+
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
135 |
+
cursor: pointer;
|
136 |
+
user-select: none;
|
137 |
+
}
|
138 |
+
.button:hover {
|
139 |
+
box-shadow:
|
140 |
+
0 8px 16px rgba(0, 0, 0, 0.3),
|
141 |
+
0 12px 24px rgba(0, 0, 0, 0.2);
|
142 |
+
transform: translateY(2px);
|
143 |
+
}
|
144 |
+
.button:active {
|
145 |
+
box-shadow:
|
146 |
+
0 4px 8px rgba(0, 0, 0, 0.3),
|
147 |
+
0 6px 12px rgba(0, 0, 0, 0.2);
|
148 |
+
transform: translateY(0);
|
149 |
+
}
|
150 |
+
</style>
|
151 |
+
</head>
|
152 |
+
<body>
|
153 |
+
<div class="button">
|
154 |
+
<h3 style="
|
155 |
+
font-size: 20px;
|
156 |
+
color: #ffffff;
|
157 |
+
background-image: linear-gradient(to right, #800000, #ff0000, #ffdab9);
|
158 |
+
background-clip: text;
|
159 |
+
-webkit-background-clip: text;
|
160 |
+
text-fill-color: transparent;
|
161 |
+
-webkit-text-fill-color: transparent;
|
162 |
+
margin: 0;
|
163 |
+
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.4);
|
164 |
+
">Upload service a/c credentials</h3>
|
165 |
+
</div>
|
166 |
+
</body>
|
167 |
+
</html>
|
168 |
+
"""
|
169 |
+
|
170 |
+
st.markdown(html_subject, unsafe_allow_html=True)
|
171 |
+
|
172 |
+
# Upload credentials file
|
173 |
+
credentials_file = st.file_uploader("", type="json")
|
174 |
+
|
175 |
+
if credentials_file is not None:
|
176 |
+
# Read the credentials file
|
177 |
+
credentials_data = credentials_file.read().decode("utf-8")
|
178 |
+
|
179 |
+
# Check for duplicates
|
180 |
+
results = check_duplicates(credentials_data)
|
181 |
+
st.write("")
|
182 |
+
st.write("")
|
183 |
+
|
184 |
+
if results:
|
185 |
+
# Define the HTML message with gradient text and extra spacing
|
186 |
+
html_subject = """
|
187 |
+
<html>
|
188 |
+
<head>
|
189 |
+
<style>
|
190 |
+
.button {
|
191 |
+
display: inline-block;
|
192 |
+
padding: 10px 20px;
|
193 |
+
border-radius: 12px;
|
194 |
+
background: linear-gradient(to bottom, #f8f9fa, #e0e0e0);
|
195 |
+
box-shadow:
|
196 |
+
0 6px 12px rgba(0, 0, 0, 0.3),
|
197 |
+
0 8px 16px rgba(0, 0, 0, 0.2),
|
198 |
+
inset 0 -2px 4px rgba(255, 255, 255, 0.6);
|
199 |
+
text-align: center;
|
200 |
+
position: relative;
|
201 |
+
transform: translateY(4px);
|
202 |
+
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
203 |
+
cursor: pointer;
|
204 |
+
user-select: none;
|
205 |
+
}
|
206 |
+
.button:hover {
|
207 |
+
box-shadow:
|
208 |
+
0 8px 16px rgba(0, 0, 0, 0.3),
|
209 |
+
0 12px 24px rgba(0, 0, 0, 0.2);
|
210 |
+
transform: translateY(2px);
|
211 |
+
}
|
212 |
+
.button:active {
|
213 |
+
box-shadow:
|
214 |
+
0 4px 8px rgba(0, 0, 0, 0.3),
|
215 |
+
0 6px 12px rgba(0, 0, 0, 0.2);
|
216 |
+
transform: translateY(0);
|
217 |
+
}
|
218 |
+
.spacing {
|
219 |
+
margin: 20px 0; /* Adjust the value as needed */
|
220 |
+
}
|
221 |
+
.result-box {
|
222 |
+
border: 1px solid #ddd;
|
223 |
+
border-radius: 8px;
|
224 |
+
background: #f9f9f9;
|
225 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
226 |
+
padding: 10px;
|
227 |
+
margin-bottom: 10px;
|
228 |
+
font-size: 16px;
|
229 |
+
}
|
230 |
+
.result-title {
|
231 |
+
font-weight: bold;
|
232 |
+
color: #333;
|
233 |
+
}
|
234 |
+
</style>
|
235 |
+
</head>
|
236 |
+
<body>
|
237 |
+
<div class="button">
|
238 |
+
<h3 style="
|
239 |
+
font-size: 20px;
|
240 |
+
color: #ffffff;
|
241 |
+
background-image: linear-gradient(to right, #800000, #ff0000, #ffdab9);
|
242 |
+
background-clip: text;
|
243 |
+
-webkit-background-clip: text;
|
244 |
+
text-fill-color: transparent;
|
245 |
+
-webkit-text-fill-color: transparent;
|
246 |
+
margin: 0;
|
247 |
+
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.4);
|
248 |
+
">Duplicate Counts for Queries:</h3>
|
249 |
+
</div>
|
250 |
+
<div class="spacing"></div> <!-- Adds spacing between the heading and the content -->
|
251 |
+
</body>
|
252 |
+
</html>
|
253 |
+
"""
|
254 |
+
|
255 |
+
# Display HTML message with spacing
|
256 |
+
st.markdown(html_subject, unsafe_allow_html=True)
|
257 |
+
|
258 |
+
# Prepare the plain text message with styled boxes
|
259 |
+
message_html = ""
|
260 |
+
for query_name, count in results.items():
|
261 |
+
message_html += f"""
|
262 |
+
<div class="result-box">
|
263 |
+
<div class="result-title">{query_name}</div>
|
264 |
+
<div>{count} duplicate rows</div>
|
265 |
+
</div>
|
266 |
+
"""
|
267 |
+
|
268 |
+
# Display the styled message in Streamlit
|
269 |
+
st.markdown(message_html, unsafe_allow_html=True)
|
270 |
+
|
271 |
+
# Prepare plain text for Slack messages
|
272 |
+
message_text = ""
|
273 |
+
for query_name, count in results.items():
|
274 |
+
message_text += f"*{query_name}*\n{count} duplicate rows\n\n"
|
275 |
+
|
276 |
+
if not results:
|
277 |
+
message_text = "No duplicates found in the queries."
|
278 |
+
|
279 |
+
# Send the message to selected channels
|
280 |
+
for channel in selection:
|
281 |
+
webhook_url = Webhook_urls.get(channel)
|
282 |
+
if webhook_url:
|
283 |
+
send_message_via_webhook(message_text, webhook_url)
|
284 |
+
else:
|
285 |
+
st.error(f"Webhook URL not found for channel: {channel}")
|
requirements.txt
CHANGED
@@ -124,6 +124,7 @@ pyngrok==7.2.0
|
|
124 |
pyOpenSSL==24.2.1
|
125 |
pyparsing==3.1.2
|
126 |
PyPDF2==3.0.1
|
|
|
127 |
PySocks==1.7.1
|
128 |
pytesseract==0.3.10
|
129 |
python-dateutil==2.9.0.post0
|
@@ -184,6 +185,7 @@ websocket-client==1.8.0
|
|
184 |
Werkzeug==3.0.3
|
185 |
wrapt==1.16.0
|
186 |
wsproto==1.2.0
|
|
|
187 |
XlsxWriter==3.2.0
|
188 |
xxhash==3.4.1
|
189 |
yarl==1.9.4
|
|
|
124 |
pyOpenSSL==24.2.1
|
125 |
pyparsing==3.1.2
|
126 |
PyPDF2==3.0.1
|
127 |
+
pyperclip==1.9.0
|
128 |
PySocks==1.7.1
|
129 |
pytesseract==0.3.10
|
130 |
python-dateutil==2.9.0.post0
|
|
|
185 |
Werkzeug==3.0.3
|
186 |
wrapt==1.16.0
|
187 |
wsproto==1.2.0
|
188 |
+
xlrd==2.0.1
|
189 |
XlsxWriter==3.2.0
|
190 |
xxhash==3.4.1
|
191 |
yarl==1.9.4
|