File size: 5,454 Bytes
06a1d47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# app.py

# ─── monkey-patch Gradio to swallow API-info errors ───
import gradio as _gr

_orig_get_api_info = _gr.Blocks.get_api_info

def _safe_get_api_info(self):
    try:
        return _orig_get_api_info(self)
    except Exception:
        # return empty spec if parsing fails
        return {}

_gr.Blocks.get_api_info = _safe_get_api_info
# ───────────────────────────────────────────────────────

# ─── optional: patch client_utils.get_type to handle boolean schemas ───
import gradio_client.utils as _client_utils

_orig_get_type = _client_utils.get_type

def safe_get_type(schema, *args, **kwargs):
    if isinstance(schema, bool):
        return "dict"
    return _orig_get_type(schema, *args, **kwargs)

_client_utils.get_type = safe_get_type
# ─────────────────────────────────────────────────────────────────────

import gradio as gr
import pandas as pd
from fuzzywuzzy import process, fuzz
import tempfile
import os
from openpyxl import load_workbook
from openpyxl.styles import Alignment


def auto_correct_names(series, threshold=90):
    """Auto-correct typos in chatter names using fuzzy matching."""
    unique_names = series.dropna().unique()
    name_mapping = {}
    
    for name in unique_names:
        matches = process.extractBests(
            name, unique_names, 
            scorer=fuzz.token_sort_ratio, 
            score_cutoff=threshold
        )
        if matches:
            best_match = max(matches, key=lambda x: (x[1], list(series).count(x[0])))
            name_mapping[name] = best_match[0]
    
    return series.replace(name_mapping)


def adjust_excel_formatting(file_path):
    """Adjust column widths and enable text wrapping in Excel file."""
    wb = load_workbook(file_path)
    ws = wb.active

    for col in ws.columns:
        max_length = 0
        col_letter = col[0].column_letter
        for cell in col:
            if cell.value:
                max_length = max(max_length, len(str(cell.value)))
            cell.alignment = Alignment(wrap_text=True)
        ws.column_dimensions[col_letter].width = max_length + 2

    wb.save(file_path)


def process_file(input_file):
    """Process uploaded Excel file and return output"""
    try:
        input_df = pd.read_excel(input_file, header=1)
        date_columns = input_df.columns[1:].tolist()
        df_long = input_df.melt(
            id_vars=[input_df.columns[0]],
            var_name='DATE',
            value_name='CHATTER'
        )
        df_long['DATE'] = pd.Categorical(
            df_long['DATE'],
            categories=date_columns,
            ordered=True
        )
        df_long['CHATTER'] = auto_correct_names(df_long['CHATTER'])
        grouped = df_long.groupby(['CHATTER', 'DATE'], observed=True)[input_df.columns[0]] \
            .apply(lambda x: ', '.join(sorted(x))).reset_index()
        pivoted = grouped.pivot(index='CHATTER', columns='DATE', values=input_df.columns[0])
        chatter_order = grouped['CHATTER'].value_counts().index.tolist()
        final_df = pivoted.reindex(chatter_order)[date_columns].fillna("OFF")
        final_df = final_df.reset_index()
        original_filename = os.path.basename(input_file)
        name_part, ext_part = os.path.splitext(original_filename)
        processed_filename = f"{name_part}_processed{ext_part}"
        temp_file_path = os.path.join(tempfile.gettempdir(), processed_filename)
        final_df.to_excel(temp_file_path, index=False, sheet_name='Schedule')
        adjust_excel_formatting(temp_file_path)
        return final_df, temp_file_path
    except Exception as e:
        error_df = pd.DataFrame({"Error": [f"⚠️ {str(e)}"]})
        return error_df, None


def download_file(out_path):
    """Return the processed file path for download"""
    return out_path

with gr.Blocks(title="Schedule Processor") as demo:
    gr.Markdown("# πŸ“… Schedule Processor")
    gr.Markdown("Upload your schedule Excel file and download the formatted version")
    with gr.Row():
        input_file = gr.File(label="Upload Schedule File", type="filepath")
    with gr.Row():
        process_btn = gr.Button("Process File", variant="primary")
        reset_btn = gr.Button("Upload New File")
    output_table = gr.Dataframe(label="Preview", wrap=True)
    download_button = gr.Button("Download Processed File", visible=False)
    temp_file_path = gr.State(value=None)

    def reset_components():
        return [
            None,
            pd.DataFrame(),
            None,
            gr.update(visible=False)
        ]

    def process_and_show(file):
        df, out_path = process_file(file)
        if out_path:
            return df, out_path, gr.update(visible=True)
        return df, None, gr.update(visible=False)

    process_btn.click(
        process_and_show,
        inputs=input_file,
        outputs=[output_table, temp_file_path, download_button]
    )

    reset_btn.click(
        reset_components,
        outputs=[input_file, output_table, temp_file_path, download_button]
    )

    download_button.click(
        download_file,
        inputs=temp_file_path,
        outputs=gr.File(label="Processed Schedule")
    )

if __name__ == "__main__":
    demo.launch()