Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -26,43 +26,44 @@ def page1_upload_and_merge(group_attendance_file, customer_attendance_file, leav
|
|
| 26 |
except Exception as e:
|
| 27 |
return f"β Error loading files: {str(e)}", None, None
|
| 28 |
|
| 29 |
-
# Clean
|
| 30 |
df_group = clean_columns(df_group)
|
| 31 |
df_segments = clean_columns(df_segments)
|
| 32 |
df_customer = clean_columns(df_customer)
|
| 33 |
df_visits = clean_columns(df_visits)
|
| 34 |
df_leaves = clean_columns(df_leaves)
|
| 35 |
|
|
|
|
| 36 |
try:
|
| 37 |
df_group['employee_code'] = pd.to_numeric(df_group['employee_code'], errors='coerce').astype('Int64')
|
| 38 |
df_segments['employee_code'] = pd.to_numeric(df_segments['employee_code'], errors='coerce').astype('Int64')
|
| 39 |
df_customer['erp_number'] = pd.to_numeric(df_customer['erp_number'], errors='coerce').astype('Int64')
|
| 40 |
df_visits['erp_no'] = pd.to_numeric(df_visits['erp_no'], errors='coerce').astype('Int64')
|
| 41 |
df_leaves['erp_number'] = pd.to_numeric(df_leaves['erp_number'], errors='coerce').astype('Int64')
|
| 42 |
-
|
| 43 |
-
print("Qr Attendance",df_customer)
|
| 44 |
-
|
| 45 |
-
|
| 46 |
except Exception as e:
|
| 47 |
return f"β Error normalizing IDs: {str(e)}", None, None
|
| 48 |
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
-
# Store
|
| 59 |
state['df_group'] = df_group
|
| 60 |
state['df_customer'] = df_customer
|
| 61 |
|
| 62 |
-
return "β
Page 1:
|
| 63 |
|
| 64 |
|
| 65 |
-
def
|
| 66 |
df_group = state.get('df_group')
|
| 67 |
df_customer = state.get('df_customer')
|
| 68 |
|
|
@@ -79,42 +80,48 @@ def page2_qr_based_attendance():
|
|
| 79 |
df_group = convert_dates(df_group)
|
| 80 |
df_customer = convert_dates(df_customer)
|
| 81 |
|
| 82 |
-
# Match
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
df_group['qr_based_attendance'] = df_group['employee_code'].apply(
|
| 85 |
-
lambda x: "Present" if x in
|
| 86 |
)
|
| 87 |
|
| 88 |
-
#
|
| 89 |
seg_group = df_group.groupby('segment')['employee_code'].nunique().reset_index()
|
| 90 |
seg_group.columns = ['segment', 'employee_count']
|
| 91 |
|
| 92 |
-
seg_qr =
|
| 93 |
seg_qr.columns = ['segment', 'qr_based_attendance']
|
| 94 |
|
| 95 |
summary = seg_group.merge(seg_qr, on='segment', how='left')
|
| 96 |
summary['qr_based_attendance'] = summary['qr_based_attendance'].fillna(0).astype(int)
|
| 97 |
summary.columns = ['Segment', 'Employee Count', 'QR-based Attendance']
|
| 98 |
-
print(summary['QR-based Attendance'])
|
| 99 |
-
|
| 100 |
-
# Debug log
|
| 101 |
-
debug_log = f"""π§ͺ **Debug Info**
|
| 102 |
-
- `employee_code` in Group Attendance (unique): {df_group['employee_code'].nunique()}
|
| 103 |
-
- `erp_number` in Customer Attendance (unique): {df_customer['erp_number'].nunique()}
|
| 104 |
-
- Matching Employee Codes: {df_group['employee_code'].isin(present_ids).sum()} / {len(df_group)}
|
| 105 |
-
"""
|
| 106 |
|
| 107 |
-
#
|
| 108 |
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
|
| 109 |
with pd.ExcelWriter(temp_file.name, engine='xlsxwriter') as writer:
|
| 110 |
df_group.to_excel(writer, index=False, sheet_name='Group_Attendance')
|
| 111 |
summary.to_excel(writer, index=False, sheet_name='Segment_Summary')
|
| 112 |
|
| 113 |
-
return "β
Page 2: QR attendance
|
| 114 |
|
| 115 |
|
|
|
|
| 116 |
with gr.Blocks() as demo:
|
| 117 |
-
gr.Markdown("## π AI Attendance App
|
| 118 |
|
| 119 |
with gr.Tab("π₯ Page 1: Upload & Normalize"):
|
| 120 |
group_file = gr.File(label="1. Group Attendance (.xlsx)")
|
|
@@ -123,7 +130,7 @@ with gr.Blocks() as demo:
|
|
| 123 |
visits_file = gr.File(label="4. Official Visits (.xlsx)")
|
| 124 |
segment_file = gr.File(label="5. Management Segregation (.xlsx)")
|
| 125 |
|
| 126 |
-
upload_btn = gr.Button("β
Process & Merge
|
| 127 |
page1_status = gr.Textbox(label="Status")
|
| 128 |
df1 = gr.Dataframe(label="Group Attendance")
|
| 129 |
df2 = gr.Dataframe(label="Customer Attendance")
|
|
@@ -134,16 +141,16 @@ with gr.Blocks() as demo:
|
|
| 134 |
outputs=[page1_status, df1, df2]
|
| 135 |
)
|
| 136 |
|
| 137 |
-
with gr.Tab("π² Page 2: QR
|
| 138 |
qr_btn = gr.Button("π Generate QR Summary")
|
| 139 |
qr_status = gr.Textbox(label="Status")
|
| 140 |
qr_df = gr.Dataframe(label="Updated Group Attendance")
|
| 141 |
summary_df = gr.Dataframe(label="Segment Summary")
|
| 142 |
download = gr.File(label="π₯ Download Excel Report")
|
| 143 |
-
debug = gr.Textbox(label="π Debug Info", lines=
|
| 144 |
|
| 145 |
qr_btn.click(
|
| 146 |
-
fn=
|
| 147 |
inputs=[],
|
| 148 |
outputs=[qr_status, qr_df, summary_df, download, debug]
|
| 149 |
)
|
|
|
|
| 26 |
except Exception as e:
|
| 27 |
return f"β Error loading files: {str(e)}", None, None
|
| 28 |
|
| 29 |
+
# Clean column names
|
| 30 |
df_group = clean_columns(df_group)
|
| 31 |
df_segments = clean_columns(df_segments)
|
| 32 |
df_customer = clean_columns(df_customer)
|
| 33 |
df_visits = clean_columns(df_visits)
|
| 34 |
df_leaves = clean_columns(df_leaves)
|
| 35 |
|
| 36 |
+
# Normalize ID fields
|
| 37 |
try:
|
| 38 |
df_group['employee_code'] = pd.to_numeric(df_group['employee_code'], errors='coerce').astype('Int64')
|
| 39 |
df_segments['employee_code'] = pd.to_numeric(df_segments['employee_code'], errors='coerce').astype('Int64')
|
| 40 |
df_customer['erp_number'] = pd.to_numeric(df_customer['erp_number'], errors='coerce').astype('Int64')
|
| 41 |
df_visits['erp_no'] = pd.to_numeric(df_visits['erp_no'], errors='coerce').astype('Int64')
|
| 42 |
df_leaves['erp_number'] = pd.to_numeric(df_leaves['erp_number'], errors='coerce').astype('Int64')
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
except Exception as e:
|
| 44 |
return f"β Error normalizing IDs: {str(e)}", None, None
|
| 45 |
|
| 46 |
+
# Merge segments into group attendance
|
| 47 |
+
try:
|
| 48 |
+
df_group = df_group.merge(
|
| 49 |
+
df_segments[['employee_code', 'segment']],
|
| 50 |
+
on='employee_code',
|
| 51 |
+
how='left',
|
| 52 |
+
suffixes=('', '_seg')
|
| 53 |
+
)
|
| 54 |
+
df_group['segment'] = df_group['segment_seg'].combine_first(df_group['segment'])
|
| 55 |
+
df_group.drop(columns=['segment_seg'], inplace=True)
|
| 56 |
+
except Exception as e:
|
| 57 |
+
return f"β Error merging segment info: {str(e)}", None, None
|
| 58 |
|
| 59 |
+
# Store for next step
|
| 60 |
state['df_group'] = df_group
|
| 61 |
state['df_customer'] = df_customer
|
| 62 |
|
| 63 |
+
return "β
Page 1: Upload complete and segment merged", df_group, df_customer
|
| 64 |
|
| 65 |
|
| 66 |
+
def page2_qr_summary():
|
| 67 |
df_group = state.get('df_group')
|
| 68 |
df_customer = state.get('df_customer')
|
| 69 |
|
|
|
|
| 80 |
df_group = convert_dates(df_group)
|
| 81 |
df_customer = convert_dates(df_customer)
|
| 82 |
|
| 83 |
+
# Match employee IDs
|
| 84 |
+
group_ids = set(df_group['employee_code'].dropna().astype('Int64'))
|
| 85 |
+
qr_ids = set(df_customer['erp_number'].dropna().astype('Int64'))
|
| 86 |
+
matched_ids = group_ids & qr_ids
|
| 87 |
+
|
| 88 |
+
# Debug samples
|
| 89 |
+
debug_text = f"""
|
| 90 |
+
π₯ Group Attendance Unique IDs: {len(group_ids)}
|
| 91 |
+
π± Customer Attendance Unique IDs (ERP): {len(qr_ids)}
|
| 92 |
+
β
Matched IDs: {len(matched_ids)}
|
| 93 |
+
π Sample Group IDs: {list(group_ids)[:10]}
|
| 94 |
+
π Sample ERP IDs: {list(qr_ids)[:10]}
|
| 95 |
+
"""
|
| 96 |
+
|
| 97 |
+
# Mark QR-based attendance
|
| 98 |
df_group['qr_based_attendance'] = df_group['employee_code'].apply(
|
| 99 |
+
lambda x: "Present" if x in matched_ids else "Absent"
|
| 100 |
)
|
| 101 |
|
| 102 |
+
# Generate summary
|
| 103 |
seg_group = df_group.groupby('segment')['employee_code'].nunique().reset_index()
|
| 104 |
seg_group.columns = ['segment', 'employee_count']
|
| 105 |
|
| 106 |
+
seg_qr = df_group[df_group['qr_based_attendance'] == 'Present'].groupby('segment')['employee_code'].nunique().reset_index()
|
| 107 |
seg_qr.columns = ['segment', 'qr_based_attendance']
|
| 108 |
|
| 109 |
summary = seg_group.merge(seg_qr, on='segment', how='left')
|
| 110 |
summary['qr_based_attendance'] = summary['qr_based_attendance'].fillna(0).astype(int)
|
| 111 |
summary.columns = ['Segment', 'Employee Count', 'QR-based Attendance']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
+
# Export to Excel
|
| 114 |
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
|
| 115 |
with pd.ExcelWriter(temp_file.name, engine='xlsxwriter') as writer:
|
| 116 |
df_group.to_excel(writer, index=False, sheet_name='Group_Attendance')
|
| 117 |
summary.to_excel(writer, index=False, sheet_name='Segment_Summary')
|
| 118 |
|
| 119 |
+
return "β
Page 2: QR attendance summary generated.", df_group, summary, temp_file.name, debug_text
|
| 120 |
|
| 121 |
|
| 122 |
+
# Gradio App
|
| 123 |
with gr.Blocks() as demo:
|
| 124 |
+
gr.Markdown("## π AI Attendance App (Step 1: QR Matching with Summary)")
|
| 125 |
|
| 126 |
with gr.Tab("π₯ Page 1: Upload & Normalize"):
|
| 127 |
group_file = gr.File(label="1. Group Attendance (.xlsx)")
|
|
|
|
| 130 |
visits_file = gr.File(label="4. Official Visits (.xlsx)")
|
| 131 |
segment_file = gr.File(label="5. Management Segregation (.xlsx)")
|
| 132 |
|
| 133 |
+
upload_btn = gr.Button("β
Process & Merge")
|
| 134 |
page1_status = gr.Textbox(label="Status")
|
| 135 |
df1 = gr.Dataframe(label="Group Attendance")
|
| 136 |
df2 = gr.Dataframe(label="Customer Attendance")
|
|
|
|
| 141 |
outputs=[page1_status, df1, df2]
|
| 142 |
)
|
| 143 |
|
| 144 |
+
with gr.Tab("π² Page 2: QR Summary"):
|
| 145 |
qr_btn = gr.Button("π Generate QR Summary")
|
| 146 |
qr_status = gr.Textbox(label="Status")
|
| 147 |
qr_df = gr.Dataframe(label="Updated Group Attendance")
|
| 148 |
summary_df = gr.Dataframe(label="Segment Summary")
|
| 149 |
download = gr.File(label="π₯ Download Excel Report")
|
| 150 |
+
debug = gr.Textbox(label="π Debug Info", lines=8)
|
| 151 |
|
| 152 |
qr_btn.click(
|
| 153 |
+
fn=page2_qr_summary,
|
| 154 |
inputs=[],
|
| 155 |
outputs=[qr_status, qr_df, summary_df, download, debug]
|
| 156 |
)
|