Spaces:
Sleeping
Sleeping
| # 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() | |