File size: 5,454 Bytes
e387e2b
 
d2ed2b1
 
 
 
 
 
 
 
 
 
 
 
 
 
e387e2b
d2ed2b1
 
 
 
e387e2b
 
 
 
 
 
d2ed2b1
 
e387e2b
d2ed2b1
06b9c84
 
 
950501b
b175dc7
 
06b9c84
d2ed2b1
06b9c84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d2ed2b1
b175dc7
 
 
 
 
 
 
 
 
 
 
a341f2b
 
b175dc7
 
 
d2ed2b1
06b9c84
 
 
facc6a0
06b9c84
 
d2ed2b1
 
06b9c84
 
 
d2ed2b1
06b9c84
 
 
 
 
 
 
 
 
950501b
22d5ae9
 
 
 
5190e9a
b175dc7
5190e9a
06b9c84
facc6a0
 
06b9c84
d2ed2b1
e634182
 
 
 
06b9c84
 
 
 
ba190fc
42ad299
 
 
950501b
92f884b
156d664
42ad299
 
 
d2ed2b1
 
 
 
42ad299
 
06b9c84
ba190fc
06b9c84
e634182
 
92f884b
42ad299
06b9c84
 
e634182
92f884b
 
42ad299
 
e634182
42ad299
 
92f884b
e634182
156d664
e634182
06b9c84
 
 
e387e2b
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()