[{"data":1,"prerenderedAt":2624},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file":3,"surround:\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file":2594,"ancestors:\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file":2603,"children:\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file":2608,"related:\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file":2609},{"id":4,"title":5,"body":6,"dateModified":2563,"datePublished":2563,"description":2564,"extension":2565,"faq":2566,"keyword":2575,"kind":2576,"meta":2577,"navigation":94,"path":2585,"seo":2586,"slug":2588,"stem":2589,"topics":2590,"type":2592,"__hash__":2593},"docs\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file\u002Findex.md","Build a Gradio App That Cleans an Uploaded Excel File",{"type":7,"value":8,"toc":2552},"minimark",[9,19,24,56,59,63,66,591,603,607,614,1508,1527,1704,1708,1718,2001,2020,2034,2275,2279,2314,2418,2422,2465,2469,2487,2491,2497,2503,2509,2515,2519,2548],[10,11,12,13,18],"p",{},"Many cleaning scripts end up being run by their author on other people's behalf: someone emails a messy export, the script runs, the clean file goes back. Turning the script into a small web app lets people do it themselves, and Gradio is one of the quickest ways to get there — a few components and one function become a working page with file upload, options, a preview and a download, no HTML or JavaScript required. This page wraps a pandas cleaning function in a Gradio app: the user uploads a workbook, picks a sheet from a list filled in as soon as the file arrives, ticks the cleaning options, sees a preview and a summary of what changed, and downloads the result. Problems with the file appear as readable messages rather than tracebacks, uploads are size-limited, temporary files are cleaned up, and the whole app is tested through Gradio's Python client. It belongs to ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002F","Serving Excel Files from Python Web Apps",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install gradio pandas openpyxl\n","bash","",[32,33,34],"code",{"__ignoreMap":30},[35,36,39,43,47,50,53],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," gradio",[35,51,52],{"class":45}," pandas",[35,54,55],{"class":45}," openpyxl\n",[10,57,58],{},"The examples were run with Gradio 6. The same structure works in Gradio 4 and 5; only some component options have moved between versions. The sample upload is a supplier export with an info sheet in front of the data, padded text, inconsistent headers, a blank row and duplicates — the usual state of files people want cleaned.",[20,60,62],{"id":61},"step-1-a-cleaning-function-that-knows-nothing-about-the-web","Step 1 — A cleaning function that knows nothing about the web",[10,64,65],{},"Keep the cleaning logic in a plain function that takes a DataFrame and options and returns the cleaned DataFrame plus a list of what it did. It can then be tested on its own and reused outside the app:",[25,67,71],{"className":68,"code":69,"language":70,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\ndef clean_frame(df, trim=True, drop_blank=True, drop_dupes=True):\n    notes = []\n    renamed = {c: str(c).strip().lower().replace(\" \", \"_\") for c in df.columns}\n    if any(k != v for k, v in renamed.items()):\n        df = df.rename(columns=renamed)\n        notes.append(\"normalised column names\")\n    if trim:\n        text = df.select_dtypes(include=[\"object\", \"string\"]).columns\n        changed = sum((df[c].notna() & (df[c].str.strip() != df[c])).sum() for c in text)\n        for c in text:\n            df[c] = df[c].str.strip()\n        notes.append(f\"trimmed spaces in {changed} cells\")\n    if drop_blank:\n        before = len(df)\n        df = df.dropna(how=\"all\")\n        notes.append(f\"removed {before - len(df)} blank rows\")\n    if drop_dupes:\n        before = len(df)\n        df = df.drop_duplicates()\n        notes.append(f\"removed {before - len(df)} duplicate rows\")\n    return df.reset_index(drop=True), notes\n\nraw = pd.read_excel(\"supplier_export.xlsx\", sheet_name=\"Invoices\")\ncleaned, notes = clean_frame(raw)\nprint(len(raw), \"->\", len(cleaned), \"rows;\", \"; \".join(notes))\n","python",[32,72,73,89,96,133,144,185,213,233,245,253,283,317,330,341,367,375,389,409,439,447,458,468,494,513,518,544,555],{"__ignoreMap":30},[35,74,75,79,83,86],{"class":37,"line":38},[35,76,78],{"class":77},"s-kum","import",[35,80,82],{"class":81},"skGVy"," pandas ",[35,84,85],{"class":77},"as",[35,87,88],{"class":81}," pd\n",[35,90,92],{"class":37,"line":91},2,[35,93,95],{"emptyLinePlaceholder":94},true,"\n",[35,97,99,102,106,109,112,116,119,121,123,126,128,130],{"class":37,"line":98},3,[35,100,101],{"class":77},"def",[35,103,105],{"class":104},"s_Opv"," clean_frame",[35,107,108],{"class":81},"(df, trim",[35,110,111],{"class":77},"=",[35,113,115],{"class":114},"sP0c6","True",[35,117,118],{"class":81},", drop_blank",[35,120,111],{"class":77},[35,122,115],{"class":114},[35,124,125],{"class":81},", drop_dupes",[35,127,111],{"class":77},[35,129,115],{"class":114},[35,131,132],{"class":81},"):\n",[35,134,136,139,141],{"class":37,"line":135},4,[35,137,138],{"class":81},"    notes ",[35,140,111],{"class":77},[35,142,143],{"class":81}," []\n",[35,145,147,150,152,155,158,161,164,167,170,173,176,179,182],{"class":37,"line":146},5,[35,148,149],{"class":81},"    renamed ",[35,151,111],{"class":77},[35,153,154],{"class":81}," {c: ",[35,156,157],{"class":114},"str",[35,159,160],{"class":81},"(c).strip().lower().replace(",[35,162,163],{"class":45},"\" \"",[35,165,166],{"class":81},", ",[35,168,169],{"class":45},"\"_\"",[35,171,172],{"class":81},") ",[35,174,175],{"class":77},"for",[35,177,178],{"class":81}," c ",[35,180,181],{"class":77},"in",[35,183,184],{"class":81}," df.columns}\n",[35,186,188,191,194,197,200,203,205,208,210],{"class":37,"line":187},6,[35,189,190],{"class":77},"    if",[35,192,193],{"class":114}," any",[35,195,196],{"class":81},"(k ",[35,198,199],{"class":77},"!=",[35,201,202],{"class":81}," v ",[35,204,175],{"class":77},[35,206,207],{"class":81}," k, v ",[35,209,181],{"class":77},[35,211,212],{"class":81}," renamed.items()):\n",[35,214,216,219,221,224,228,230],{"class":37,"line":215},7,[35,217,218],{"class":81},"        df ",[35,220,111],{"class":77},[35,222,223],{"class":81}," df.rename(",[35,225,227],{"class":226},"sa561","columns",[35,229,111],{"class":77},[35,231,232],{"class":81},"renamed)\n",[35,234,236,239,242],{"class":37,"line":235},8,[35,237,238],{"class":81},"        notes.append(",[35,240,241],{"class":45},"\"normalised column names\"",[35,243,244],{"class":81},")\n",[35,246,248,250],{"class":37,"line":247},9,[35,249,190],{"class":77},[35,251,252],{"class":81}," trim:\n",[35,254,256,259,261,264,267,269,272,275,277,280],{"class":37,"line":255},10,[35,257,258],{"class":81},"        text ",[35,260,111],{"class":77},[35,262,263],{"class":81}," df.select_dtypes(",[35,265,266],{"class":226},"include",[35,268,111],{"class":77},[35,270,271],{"class":81},"[",[35,273,274],{"class":45},"\"object\"",[35,276,166],{"class":81},[35,278,279],{"class":45},"\"string\"",[35,281,282],{"class":81},"]).columns\n",[35,284,286,289,291,294,297,300,303,305,308,310,312,314],{"class":37,"line":285},11,[35,287,288],{"class":81},"        changed ",[35,290,111],{"class":77},[35,292,293],{"class":114}," sum",[35,295,296],{"class":81},"((df[c].notna() ",[35,298,299],{"class":77},"&",[35,301,302],{"class":81}," (df[c].str.strip() ",[35,304,199],{"class":77},[35,306,307],{"class":81}," df[c])).sum() ",[35,309,175],{"class":77},[35,311,178],{"class":81},[35,313,181],{"class":77},[35,315,316],{"class":81}," text)\n",[35,318,320,323,325,327],{"class":37,"line":319},12,[35,321,322],{"class":77},"        for",[35,324,178],{"class":81},[35,326,181],{"class":77},[35,328,329],{"class":81}," text:\n",[35,331,333,336,338],{"class":37,"line":332},13,[35,334,335],{"class":81},"            df[c] ",[35,337,111],{"class":77},[35,339,340],{"class":81}," df[c].str.strip()\n",[35,342,344,346,349,352,356,359,362,365],{"class":37,"line":343},14,[35,345,238],{"class":81},[35,347,348],{"class":77},"f",[35,350,351],{"class":45},"\"trimmed spaces in ",[35,353,355],{"class":354},"sSjpA","{",[35,357,358],{"class":81},"changed",[35,360,361],{"class":354},"}",[35,363,364],{"class":45}," cells\"",[35,366,244],{"class":81},[35,368,370,372],{"class":37,"line":369},15,[35,371,190],{"class":77},[35,373,374],{"class":81}," drop_blank:\n",[35,376,378,381,383,386],{"class":37,"line":377},16,[35,379,380],{"class":81},"        before ",[35,382,111],{"class":77},[35,384,385],{"class":114}," len",[35,387,388],{"class":81},"(df)\n",[35,390,392,394,396,399,402,404,407],{"class":37,"line":391},17,[35,393,218],{"class":81},[35,395,111],{"class":77},[35,397,398],{"class":81}," df.dropna(",[35,400,401],{"class":226},"how",[35,403,111],{"class":77},[35,405,406],{"class":45},"\"all\"",[35,408,244],{"class":81},[35,410,412,414,416,419,421,424,427,429,432,434,437],{"class":37,"line":411},18,[35,413,238],{"class":81},[35,415,348],{"class":77},[35,417,418],{"class":45},"\"removed ",[35,420,355],{"class":354},[35,422,423],{"class":81},"before ",[35,425,426],{"class":77},"-",[35,428,385],{"class":114},[35,430,431],{"class":81},"(df)",[35,433,361],{"class":354},[35,435,436],{"class":45}," blank rows\"",[35,438,244],{"class":81},[35,440,442,444],{"class":37,"line":441},19,[35,443,190],{"class":77},[35,445,446],{"class":81}," drop_dupes:\n",[35,448,450,452,454,456],{"class":37,"line":449},20,[35,451,380],{"class":81},[35,453,111],{"class":77},[35,455,385],{"class":114},[35,457,388],{"class":81},[35,459,461,463,465],{"class":37,"line":460},21,[35,462,218],{"class":81},[35,464,111],{"class":77},[35,466,467],{"class":81}," df.drop_duplicates()\n",[35,469,471,473,475,477,479,481,483,485,487,489,492],{"class":37,"line":470},22,[35,472,238],{"class":81},[35,474,348],{"class":77},[35,476,418],{"class":45},[35,478,355],{"class":354},[35,480,423],{"class":81},[35,482,426],{"class":77},[35,484,385],{"class":114},[35,486,431],{"class":81},[35,488,361],{"class":354},[35,490,491],{"class":45}," duplicate rows\"",[35,493,244],{"class":81},[35,495,497,500,503,506,508,510],{"class":37,"line":496},23,[35,498,499],{"class":77},"    return",[35,501,502],{"class":81}," df.reset_index(",[35,504,505],{"class":226},"drop",[35,507,111],{"class":77},[35,509,115],{"class":114},[35,511,512],{"class":81},"), notes\n",[35,514,516],{"class":37,"line":515},24,[35,517,95],{"emptyLinePlaceholder":94},[35,519,521,524,526,529,532,534,537,539,542],{"class":37,"line":520},25,[35,522,523],{"class":81},"raw ",[35,525,111],{"class":77},[35,527,528],{"class":81}," pd.read_excel(",[35,530,531],{"class":45},"\"supplier_export.xlsx\"",[35,533,166],{"class":81},[35,535,536],{"class":226},"sheet_name",[35,538,111],{"class":77},[35,540,541],{"class":45},"\"Invoices\"",[35,543,244],{"class":81},[35,545,547,550,552],{"class":37,"line":546},26,[35,548,549],{"class":81},"cleaned, notes ",[35,551,111],{"class":77},[35,553,554],{"class":81}," clean_frame(raw)\n",[35,556,558,561,564,567,570,573,575,577,580,583,585,588],{"class":37,"line":557},27,[35,559,560],{"class":114},"print",[35,562,563],{"class":81},"(",[35,565,566],{"class":114},"len",[35,568,569],{"class":81},"(raw), ",[35,571,572],{"class":45},"\"->\"",[35,574,166],{"class":81},[35,576,566],{"class":114},[35,578,579],{"class":81},"(cleaned), ",[35,581,582],{"class":45},"\"rows;\"",[35,584,166],{"class":81},[35,586,587],{"class":45},"\"; \"",[35,589,590],{"class":81},".join(notes))\n",[10,592,593,594,597,598,602],{},"Six rows become three. Three cells had stray spaces — the ",[32,595,596],{},"notna()"," test keeps empty cells from being counted, since a missing value never equals itself. Trimming makes \" Acme Ltd\" and \"Acme Ltd\" equal, which turns two near-duplicates into exact ones, and the blank row goes. The order of the steps matters for exactly that reason — trim before deduplicating. Cleaning techniques themselves are covered in depth in ",[14,599,601],{"href":600},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002F","Cleaning Excel Data with pandas","; the app is only a front end to them.",[20,604,606],{"id":605},"step-2-the-app","Step 2 — The app",[10,608,609,610,613],{},"The interface is a ",[32,611,612],{},"gr.Blocks"," layout. Two event handlers do the work: one fills the sheet list when a file is uploaded, the other cleans and returns a preview, a summary and a file to download:",[25,615,617],{"className":68,"code":616,"language":70,"meta":30,"style":30},"import tempfile\nfrom pathlib import Path\nimport gradio as gr\n\nREQUIRED = {\"supplier_name\", \"invoice_no\", \"amount\"}\n\ndef list_sheets(path):\n    if not path:\n        return gr.Dropdown(choices=[], value=None)\n    names = pd.ExcelFile(path).sheet_names\n    best = next((n for n in names if n.lower() not in {\"info\", \"notes\", \"readme\"}), names[0])\n    return gr.Dropdown(choices=names, value=best)\n\ndef run_clean(path, sheet, options):\n    if not path:\n        raise gr.Error(\"Upload a workbook first.\")\n    try:\n        df = pd.read_excel(path, sheet_name=sheet)\n    except Exception as e:\n        raise gr.Error(f\"Could not read sheet '{sheet}': {e}\")\n    df, notes = clean_frame(df, trim=\"Trim spaces\" in options, drop_blank=\"Remove blank rows\" in options,\n                            drop_dupes=\"Remove duplicates\" in options)\n    missing = REQUIRED - set(df.columns)\n    if missing:\n        raise gr.Error(f\"Sheet '{sheet}' is missing column(s): {', '.join(sorted(missing))}.\")\n    out = Path(tempfile.mkdtemp()) \u002F f\"{Path(path).stem}_clean.xlsx\"\n    with pd.ExcelWriter(out) as xw:\n        df.to_excel(xw, sheet_name=\"Clean\", index=False)\n        pd.DataFrame({\"change\": notes}).to_excel(xw, sheet_name=\"Changes\", index=False)\n    summary = f\"**{len(df)} rows** written. \" + \"; \".join(notes) + \".\"\n    return df.head(20), summary, str(out)\n\nwith gr.Blocks(title=\"Excel cleaner\", delete_cache=(3600, 3600)) as demo:\n    gr.Markdown(\"## Clean an Excel file\\nUpload a workbook, choose the sheet and options, then download the cleaned file.\")\n    with gr.Row():\n        upload = gr.File(label=\"Workbook (.xlsx)\", file_types=[\".xlsx\"], type=\"filepath\")\n        with gr.Column():\n            sheet = gr.Dropdown(label=\"Sheet\", choices=[])\n            options = gr.CheckboxGroup([\"Trim spaces\", \"Remove blank rows\", \"Remove duplicates\"],\n                                       value=[\"Trim spaces\", \"Remove blank rows\", \"Remove duplicates\"], label=\"Cleaning\")\n            go = gr.Button(\"Clean\", variant=\"primary\")\n    summary = gr.Markdown()\n    preview = gr.Dataframe(label=\"Preview (first 20 rows)\")\n    result = gr.File(label=\"Cleaned workbook\")\n    upload.upload(list_sheets, upload, sheet, api_name=\"sheets\")\n    go.click(run_clean, [upload, sheet, options], [preview, summary, result], api_name=\"clean\")\n",[32,618,619,626,639,651,655,682,686,696,706,732,742,801,821,825,835,843,856,864,880,894,927,963,978,997,1004,1045,1073,1086,1111,1140,1179,1198,1203,1244,1261,1269,1313,1322,1348,1372,1403,1428,1438,1458,1477,1493],{"__ignoreMap":30},[35,620,621,623],{"class":37,"line":38},[35,622,78],{"class":77},[35,624,625],{"class":81}," tempfile\n",[35,627,628,631,634,636],{"class":37,"line":91},[35,629,630],{"class":77},"from",[35,632,633],{"class":81}," pathlib ",[35,635,78],{"class":77},[35,637,638],{"class":81}," Path\n",[35,640,641,643,646,648],{"class":37,"line":98},[35,642,78],{"class":77},[35,644,645],{"class":81}," gradio ",[35,647,85],{"class":77},[35,649,650],{"class":81}," gr\n",[35,652,653],{"class":37,"line":135},[35,654,95],{"emptyLinePlaceholder":94},[35,656,657,660,663,666,669,671,674,676,679],{"class":37,"line":146},[35,658,659],{"class":114},"REQUIRED",[35,661,662],{"class":77}," =",[35,664,665],{"class":81}," {",[35,667,668],{"class":45},"\"supplier_name\"",[35,670,166],{"class":81},[35,672,673],{"class":45},"\"invoice_no\"",[35,675,166],{"class":81},[35,677,678],{"class":45},"\"amount\"",[35,680,681],{"class":81},"}\n",[35,683,684],{"class":37,"line":187},[35,685,95],{"emptyLinePlaceholder":94},[35,687,688,690,693],{"class":37,"line":215},[35,689,101],{"class":77},[35,691,692],{"class":104}," list_sheets",[35,694,695],{"class":81},"(path):\n",[35,697,698,700,703],{"class":37,"line":235},[35,699,190],{"class":77},[35,701,702],{"class":77}," not",[35,704,705],{"class":81}," path:\n",[35,707,708,711,714,717,719,722,725,727,730],{"class":37,"line":247},[35,709,710],{"class":77},"        return",[35,712,713],{"class":81}," gr.Dropdown(",[35,715,716],{"class":226},"choices",[35,718,111],{"class":77},[35,720,721],{"class":81},"[], ",[35,723,724],{"class":226},"value",[35,726,111],{"class":77},[35,728,729],{"class":114},"None",[35,731,244],{"class":81},[35,733,734,737,739],{"class":37,"line":255},[35,735,736],{"class":81},"    names ",[35,738,111],{"class":77},[35,740,741],{"class":81}," pd.ExcelFile(path).sheet_names\n",[35,743,744,747,749,752,755,757,760,762,765,768,771,774,777,779,782,784,787,789,792,795,798],{"class":37,"line":285},[35,745,746],{"class":81},"    best ",[35,748,111],{"class":77},[35,750,751],{"class":114}," next",[35,753,754],{"class":81},"((n ",[35,756,175],{"class":77},[35,758,759],{"class":81}," n ",[35,761,181],{"class":77},[35,763,764],{"class":81}," names ",[35,766,767],{"class":77},"if",[35,769,770],{"class":81}," n.lower() ",[35,772,773],{"class":77},"not",[35,775,776],{"class":77}," in",[35,778,665],{"class":81},[35,780,781],{"class":45},"\"info\"",[35,783,166],{"class":81},[35,785,786],{"class":45},"\"notes\"",[35,788,166],{"class":81},[35,790,791],{"class":45},"\"readme\"",[35,793,794],{"class":81},"}), names[",[35,796,797],{"class":114},"0",[35,799,800],{"class":81},"])\n",[35,802,803,805,807,809,811,814,816,818],{"class":37,"line":319},[35,804,499],{"class":77},[35,806,713],{"class":81},[35,808,716],{"class":226},[35,810,111],{"class":77},[35,812,813],{"class":81},"names, ",[35,815,724],{"class":226},[35,817,111],{"class":77},[35,819,820],{"class":81},"best)\n",[35,822,823],{"class":37,"line":332},[35,824,95],{"emptyLinePlaceholder":94},[35,826,827,829,832],{"class":37,"line":343},[35,828,101],{"class":77},[35,830,831],{"class":104}," run_clean",[35,833,834],{"class":81},"(path, sheet, options):\n",[35,836,837,839,841],{"class":37,"line":369},[35,838,190],{"class":77},[35,840,702],{"class":77},[35,842,705],{"class":81},[35,844,845,848,851,854],{"class":37,"line":377},[35,846,847],{"class":77},"        raise",[35,849,850],{"class":81}," gr.Error(",[35,852,853],{"class":45},"\"Upload a workbook first.\"",[35,855,244],{"class":81},[35,857,858,861],{"class":37,"line":391},[35,859,860],{"class":77},"    try",[35,862,863],{"class":81},":\n",[35,865,866,868,870,873,875,877],{"class":37,"line":411},[35,867,218],{"class":81},[35,869,111],{"class":77},[35,871,872],{"class":81}," pd.read_excel(path, ",[35,874,536],{"class":226},[35,876,111],{"class":77},[35,878,879],{"class":81},"sheet)\n",[35,881,882,885,888,891],{"class":37,"line":441},[35,883,884],{"class":77},"    except",[35,886,887],{"class":114}," Exception",[35,889,890],{"class":77}," as",[35,892,893],{"class":81}," e:\n",[35,895,896,898,900,902,905,907,910,912,915,917,920,922,925],{"class":37,"line":449},[35,897,847],{"class":77},[35,899,850],{"class":81},[35,901,348],{"class":77},[35,903,904],{"class":45},"\"Could not read sheet '",[35,906,355],{"class":354},[35,908,909],{"class":81},"sheet",[35,911,361],{"class":354},[35,913,914],{"class":45},"': ",[35,916,355],{"class":354},[35,918,919],{"class":81},"e",[35,921,361],{"class":354},[35,923,924],{"class":45},"\"",[35,926,244],{"class":81},[35,928,929,932,934,937,940,942,945,947,950,953,955,958,960],{"class":37,"line":460},[35,930,931],{"class":81},"    df, notes ",[35,933,111],{"class":77},[35,935,936],{"class":81}," clean_frame(df, ",[35,938,939],{"class":226},"trim",[35,941,111],{"class":77},[35,943,944],{"class":45},"\"Trim spaces\"",[35,946,776],{"class":77},[35,948,949],{"class":81}," options, ",[35,951,952],{"class":226},"drop_blank",[35,954,111],{"class":77},[35,956,957],{"class":45},"\"Remove blank rows\"",[35,959,776],{"class":77},[35,961,962],{"class":81}," options,\n",[35,964,965,968,970,973,975],{"class":37,"line":470},[35,966,967],{"class":226},"                            drop_dupes",[35,969,111],{"class":77},[35,971,972],{"class":45},"\"Remove duplicates\"",[35,974,776],{"class":77},[35,976,977],{"class":81}," options)\n",[35,979,980,983,985,988,991,994],{"class":37,"line":496},[35,981,982],{"class":81},"    missing ",[35,984,111],{"class":77},[35,986,987],{"class":114}," REQUIRED",[35,989,990],{"class":77}," -",[35,992,993],{"class":114}," set",[35,995,996],{"class":81},"(df.columns)\n",[35,998,999,1001],{"class":37,"line":515},[35,1000,190],{"class":77},[35,1002,1003],{"class":81}," missing:\n",[35,1005,1006,1008,1010,1012,1015,1017,1019,1021,1024,1026,1029,1032,1035,1038,1040,1043],{"class":37,"line":520},[35,1007,847],{"class":77},[35,1009,850],{"class":81},[35,1011,348],{"class":77},[35,1013,1014],{"class":45},"\"Sheet '",[35,1016,355],{"class":354},[35,1018,909],{"class":81},[35,1020,361],{"class":354},[35,1022,1023],{"class":45},"' is missing column(s): ",[35,1025,355],{"class":354},[35,1027,1028],{"class":45},"', '",[35,1030,1031],{"class":81},".join(",[35,1033,1034],{"class":114},"sorted",[35,1036,1037],{"class":81},"(missing))",[35,1039,361],{"class":354},[35,1041,1042],{"class":45},".\"",[35,1044,244],{"class":81},[35,1046,1047,1050,1052,1055,1058,1061,1063,1065,1068,1070],{"class":37,"line":546},[35,1048,1049],{"class":81},"    out ",[35,1051,111],{"class":77},[35,1053,1054],{"class":81}," Path(tempfile.mkdtemp()) ",[35,1056,1057],{"class":77},"\u002F",[35,1059,1060],{"class":77}," f",[35,1062,924],{"class":45},[35,1064,355],{"class":354},[35,1066,1067],{"class":81},"Path(path).stem",[35,1069,361],{"class":354},[35,1071,1072],{"class":45},"_clean.xlsx\"\n",[35,1074,1075,1078,1081,1083],{"class":37,"line":557},[35,1076,1077],{"class":77},"    with",[35,1079,1080],{"class":81}," pd.ExcelWriter(out) ",[35,1082,85],{"class":77},[35,1084,1085],{"class":81}," xw:\n",[35,1087,1089,1092,1094,1096,1099,1101,1104,1106,1109],{"class":37,"line":1088},28,[35,1090,1091],{"class":81},"        df.to_excel(xw, ",[35,1093,536],{"class":226},[35,1095,111],{"class":77},[35,1097,1098],{"class":45},"\"Clean\"",[35,1100,166],{"class":81},[35,1102,1103],{"class":226},"index",[35,1105,111],{"class":77},[35,1107,1108],{"class":114},"False",[35,1110,244],{"class":81},[35,1112,1114,1117,1120,1123,1125,1127,1130,1132,1134,1136,1138],{"class":37,"line":1113},29,[35,1115,1116],{"class":81},"        pd.DataFrame({",[35,1118,1119],{"class":45},"\"change\"",[35,1121,1122],{"class":81},": notes}).to_excel(xw, ",[35,1124,536],{"class":226},[35,1126,111],{"class":77},[35,1128,1129],{"class":45},"\"Changes\"",[35,1131,166],{"class":81},[35,1133,1103],{"class":226},[35,1135,111],{"class":77},[35,1137,1108],{"class":114},[35,1139,244],{"class":81},[35,1141,1143,1146,1148,1150,1153,1155,1157,1159,1161,1164,1167,1170,1173,1176],{"class":37,"line":1142},30,[35,1144,1145],{"class":81},"    summary ",[35,1147,111],{"class":77},[35,1149,1060],{"class":77},[35,1151,1152],{"class":45},"\"**",[35,1154,355],{"class":354},[35,1156,566],{"class":114},[35,1158,431],{"class":81},[35,1160,361],{"class":354},[35,1162,1163],{"class":45}," rows** written. \"",[35,1165,1166],{"class":77}," +",[35,1168,1169],{"class":45}," \"; \"",[35,1171,1172],{"class":81},".join(notes) ",[35,1174,1175],{"class":77},"+",[35,1177,1178],{"class":45}," \".\"\n",[35,1180,1182,1184,1187,1190,1193,1195],{"class":37,"line":1181},31,[35,1183,499],{"class":77},[35,1185,1186],{"class":81}," df.head(",[35,1188,1189],{"class":114},"20",[35,1191,1192],{"class":81},"), summary, ",[35,1194,157],{"class":114},[35,1196,1197],{"class":81},"(out)\n",[35,1199,1201],{"class":37,"line":1200},32,[35,1202,95],{"emptyLinePlaceholder":94},[35,1204,1206,1209,1212,1215,1217,1220,1222,1225,1227,1229,1232,1234,1236,1239,1241],{"class":37,"line":1205},33,[35,1207,1208],{"class":77},"with",[35,1210,1211],{"class":81}," gr.Blocks(",[35,1213,1214],{"class":226},"title",[35,1216,111],{"class":77},[35,1218,1219],{"class":45},"\"Excel cleaner\"",[35,1221,166],{"class":81},[35,1223,1224],{"class":226},"delete_cache",[35,1226,111],{"class":77},[35,1228,563],{"class":81},[35,1230,1231],{"class":114},"3600",[35,1233,166],{"class":81},[35,1235,1231],{"class":114},[35,1237,1238],{"class":81},")) ",[35,1240,85],{"class":77},[35,1242,1243],{"class":81}," demo:\n",[35,1245,1247,1250,1253,1256,1259],{"class":37,"line":1246},34,[35,1248,1249],{"class":81},"    gr.Markdown(",[35,1251,1252],{"class":45},"\"## Clean an Excel file",[35,1254,1255],{"class":354},"\\n",[35,1257,1258],{"class":45},"Upload a workbook, choose the sheet and options, then download the cleaned file.\"",[35,1260,244],{"class":81},[35,1262,1264,1266],{"class":37,"line":1263},35,[35,1265,1077],{"class":77},[35,1267,1268],{"class":81}," gr.Row():\n",[35,1270,1272,1275,1277,1280,1283,1285,1288,1290,1293,1295,1297,1300,1303,1306,1308,1311],{"class":37,"line":1271},36,[35,1273,1274],{"class":81},"        upload ",[35,1276,111],{"class":77},[35,1278,1279],{"class":81}," gr.File(",[35,1281,1282],{"class":226},"label",[35,1284,111],{"class":77},[35,1286,1287],{"class":45},"\"Workbook (.xlsx)\"",[35,1289,166],{"class":81},[35,1291,1292],{"class":226},"file_types",[35,1294,111],{"class":77},[35,1296,271],{"class":81},[35,1298,1299],{"class":45},"\".xlsx\"",[35,1301,1302],{"class":81},"], ",[35,1304,1305],{"class":226},"type",[35,1307,111],{"class":77},[35,1309,1310],{"class":45},"\"filepath\"",[35,1312,244],{"class":81},[35,1314,1316,1319],{"class":37,"line":1315},37,[35,1317,1318],{"class":77},"        with",[35,1320,1321],{"class":81}," gr.Column():\n",[35,1323,1325,1328,1330,1332,1334,1336,1339,1341,1343,1345],{"class":37,"line":1324},38,[35,1326,1327],{"class":81},"            sheet ",[35,1329,111],{"class":77},[35,1331,713],{"class":81},[35,1333,1282],{"class":226},[35,1335,111],{"class":77},[35,1337,1338],{"class":45},"\"Sheet\"",[35,1340,166],{"class":81},[35,1342,716],{"class":226},[35,1344,111],{"class":77},[35,1346,1347],{"class":81},"[])\n",[35,1349,1351,1354,1356,1359,1361,1363,1365,1367,1369],{"class":37,"line":1350},39,[35,1352,1353],{"class":81},"            options ",[35,1355,111],{"class":77},[35,1357,1358],{"class":81}," gr.CheckboxGroup([",[35,1360,944],{"class":45},[35,1362,166],{"class":81},[35,1364,957],{"class":45},[35,1366,166],{"class":81},[35,1368,972],{"class":45},[35,1370,1371],{"class":81},"],\n",[35,1373,1375,1378,1380,1382,1384,1386,1388,1390,1392,1394,1396,1398,1401],{"class":37,"line":1374},40,[35,1376,1377],{"class":226},"                                       value",[35,1379,111],{"class":77},[35,1381,271],{"class":81},[35,1383,944],{"class":45},[35,1385,166],{"class":81},[35,1387,957],{"class":45},[35,1389,166],{"class":81},[35,1391,972],{"class":45},[35,1393,1302],{"class":81},[35,1395,1282],{"class":226},[35,1397,111],{"class":77},[35,1399,1400],{"class":45},"\"Cleaning\"",[35,1402,244],{"class":81},[35,1404,1406,1409,1411,1414,1416,1418,1421,1423,1426],{"class":37,"line":1405},41,[35,1407,1408],{"class":81},"            go ",[35,1410,111],{"class":77},[35,1412,1413],{"class":81}," gr.Button(",[35,1415,1098],{"class":45},[35,1417,166],{"class":81},[35,1419,1420],{"class":226},"variant",[35,1422,111],{"class":77},[35,1424,1425],{"class":45},"\"primary\"",[35,1427,244],{"class":81},[35,1429,1431,1433,1435],{"class":37,"line":1430},42,[35,1432,1145],{"class":81},[35,1434,111],{"class":77},[35,1436,1437],{"class":81}," gr.Markdown()\n",[35,1439,1441,1444,1446,1449,1451,1453,1456],{"class":37,"line":1440},43,[35,1442,1443],{"class":81},"    preview ",[35,1445,111],{"class":77},[35,1447,1448],{"class":81}," gr.Dataframe(",[35,1450,1282],{"class":226},[35,1452,111],{"class":77},[35,1454,1455],{"class":45},"\"Preview (first 20 rows)\"",[35,1457,244],{"class":81},[35,1459,1461,1464,1466,1468,1470,1472,1475],{"class":37,"line":1460},44,[35,1462,1463],{"class":81},"    result ",[35,1465,111],{"class":77},[35,1467,1279],{"class":81},[35,1469,1282],{"class":226},[35,1471,111],{"class":77},[35,1473,1474],{"class":45},"\"Cleaned workbook\"",[35,1476,244],{"class":81},[35,1478,1480,1483,1486,1488,1491],{"class":37,"line":1479},45,[35,1481,1482],{"class":81},"    upload.upload(list_sheets, upload, sheet, ",[35,1484,1485],{"class":226},"api_name",[35,1487,111],{"class":77},[35,1489,1490],{"class":45},"\"sheets\"",[35,1492,244],{"class":81},[35,1494,1496,1499,1501,1503,1506],{"class":37,"line":1495},46,[35,1497,1498],{"class":81},"    go.click(run_clean, [upload, sheet, options], [preview, summary, result], ",[35,1500,1485],{"class":226},[35,1502,111],{"class":77},[35,1504,1505],{"class":45},"\"clean\"",[35,1507,244],{"class":81},[10,1509,1510,1513,1514,1517,1518,1521,1522,1526],{},[32,1511,1512],{},"type=\"filepath\""," hands the function the path of the uploaded file in Gradio's temporary directory. Returning a ",[32,1515,1516],{},"gr.Dropdown(...)"," from the upload handler updates the existing dropdown's choices and value, so the list of sheets appears as soon as the file lands; the handler pre-selects the first sheet that is not obviously an info or notes sheet. ",[32,1519,1520],{},"gr.Error"," shows its message to the user in the page, which is the right place for \"this sheet has no Amount column\" — a raw traceback helps nobody. The download includes a ",[1523,1524,1525],"em",{},"Changes"," sheet listing what was done, so the person receiving the file can see why it has fewer rows than they sent.",[1528,1529,1537,1538,1537,1541,1537,1545,1537,1552,1537,1562,1537,1569,1537,1574,1537,1579,1537,1582,1537,1586,1537,1591,1537,1593,1537,1595,1537,1600,1537,1605,1537,1613,1537,1619,1537,1624,1537,1627,1537,1632,1537,1637,1537,1642,1537,1645,1537,1652,1537,1657,1537,1660,1537,1663,1537,1667,1537,1671,1537,1674,1537,1677,1537,1682,1537,1686,1537,1689,1537,1692,1537,1699],"svg",{"viewBox":1530,"role":1531,"ariaLabelledBy":1532,"xmlns":1535,"style":1536},"4 0 752 388","img",[1533,1534],"gr-events-t","gr-events-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[1214,1539,1540],{"id":1533},"What happens when someone uses the app",[1542,1543,1544],"desc",{"id":1534},"The user uploads a workbook; the upload event lists its sheets and fills the dropdown. The user picks options and clicks Clean; the clean handler reads the chosen sheet, cleans it, writes a new workbook and returns a preview, a summary and the file for download.",[1546,1547],"rect",{"x":1548,"y":797,"width":1549,"height":1550,"fill":1551},"4","752","388","#ffffff",[1546,1553],{"x":1554,"y":1555,"width":1556,"height":1557,"rx":1558,"fill":1559,"stroke":1560,"style":1561},"20.0","16.0","180.0","48.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[1563,1564,1568],"text",{"x":1565,"y":1566,"style":1567},"110.0","44.5","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Browser",[1546,1570],{"x":1571,"y":1555,"width":1556,"height":1557,"rx":1558,"fill":1572,"stroke":1573,"style":1561},"290.0","#e7ebef","var(--line,#cdd5e6)",[1563,1575,1578],{"x":1576,"y":1566,"style":1577},"380.0","font-size:13px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Gradio app",[1546,1580],{"x":1581,"y":1555,"width":1556,"height":1557,"rx":1558,"fill":1572,"stroke":1573,"style":1561},"560.0",[1563,1583,1585],{"x":1584,"y":1566,"style":1577},"650.0","pandas",[37,1587],{"x1":1565,"y1":1588,"x2":1565,"y2":1589,"stroke":1573,"style":1590},"64.0","372.0","stroke-width:1.5px;stroke-dasharray:5 4",[37,1592],{"x1":1576,"y1":1588,"x2":1576,"y2":1589,"stroke":1573,"style":1590},[37,1594],{"x1":1584,"y1":1588,"x2":1584,"y2":1589,"stroke":1573,"style":1590},[37,1596],{"x1":1597,"y1":1598,"x2":1599,"y2":1598,"stroke":1560,"style":1561},"115.0","104.0","371.0",[1601,1602],"polygon",{"points":1603,"fill":1604},"375.0,104.0 366.0,109.0 366.0,99.0","#5b5cf0",[1546,1606],{"x":1607,"y":1608,"width":1609,"height":1610,"rx":1611,"fill":1551,"stroke":1573,"style":1612},"194.3","75.0","101.5","21.0","10","stroke-width:1.2px",[1563,1614,1618],{"x":1615,"y":1616,"style":1617},"245.0","89.5","font-size:11.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","1. upload .xlsx",[37,1620],{"x1":1621,"y1":1622,"x2":1623,"y2":1622,"stroke":1560,"style":1561},"385.0","152.0","641.0",[1601,1625],{"points":1626,"fill":1604},"645.0,152.0 636.0,157.0 636.0,147.0",[1546,1628],{"x":1629,"y":1630,"width":1631,"height":1610,"rx":1611,"fill":1551,"stroke":1573,"style":1612},"431.2","123.0","167.6",[1563,1633,1636],{"x":1634,"y":1635,"style":1617},"515.0","137.5","2. ExcelFile.sheet_names",[37,1638],{"x1":1639,"y1":1640,"x2":1641,"y2":1640,"stroke":1560,"style":1561},"375.0","200.0","119.0",[1601,1643],{"points":1644,"fill":1604},"115.0,200.0 124.0,195.0 124.0,205.0",[1546,1646],{"x":1647,"y":1648,"width":1649,"height":1610,"rx":1611,"fill":1650,"stroke":1651,"style":1612},"155.1","171.0","179.8","#fdefd8","var(--gold,#b4740a)",[1563,1653,1656],{"x":1615,"y":1654,"style":1655},"185.5","font-size:11.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","3. dropdown: Info, Invoices",[37,1658],{"x1":1597,"y1":1659,"x2":1599,"y2":1659,"stroke":1560,"style":1561},"248.0",[1601,1661],{"points":1662,"fill":1604},"375.0,248.0 366.0,253.0 366.0,243.0",[1546,1664],{"x":1665,"y":1666,"width":1665,"height":1610,"rx":1611,"fill":1551,"stroke":1573,"style":1612},"163.3","219.0",[1563,1668,1670],{"x":1615,"y":1669,"style":1617},"233.5","4. Clean (sheet, options)",[37,1672],{"x1":1621,"y1":1673,"x2":1623,"y2":1673,"stroke":1560,"style":1561},"296.0",[1601,1675],{"points":1676,"fill":1604},"645.0,296.0 636.0,301.0 636.0,291.0",[1546,1678],{"x":1679,"y":1680,"width":1681,"height":1610,"rx":1611,"fill":1551,"stroke":1573,"style":1612},"447.4","267.0","135.1",[1563,1683,1685],{"x":1634,"y":1684,"style":1617},"281.5","5. read, clean, write",[37,1687],{"x1":1639,"y1":1688,"x2":1641,"y2":1688,"stroke":1560,"style":1561},"344.0",[1601,1690],{"points":1691,"fill":1604},"115.0,344.0 124.0,339.0 124.0,349.0",[1546,1693],{"x":1694,"y":1695,"width":1696,"height":1610,"rx":1611,"fill":1697,"stroke":1698,"style":1612},"160.9","315.0","168.3","#d9f4f1","var(--teal,#0f9488)",[1563,1700,1703],{"x":1615,"y":1701,"style":1702},"329.5","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","6. preview, summary, file",[20,1705,1707],{"id":1706},"step-3-run-it-and-test-it-through-the-client","Step 3 — Run it and test it through the client",[10,1709,1710,1711,1714,1715,1717],{},"Launch the app and drive it exactly as a browser would, with ",[32,1712,1713],{},"gradio_client",". Each handler with an ",[32,1716,1485],{}," is callable:",[25,1719,1721],{"className":68,"code":1720,"language":70,"meta":30,"style":30},"from gradio_client import Client, handle_file\n\ndemo.launch(prevent_thread_lock=True, server_port=7861, quiet=True, max_file_size=\"20mb\", show_error=True)\nclient = Client(\"http:\u002F\u002F127.0.0.1:7861\u002F\", verbose=False)\n\nprint(\"sheets:\", client.predict(handle_file(\"supplier_export.xlsx\"), api_name=\"\u002Fsheets\")[\"value\"])\ntable, text, path = client.predict(handle_file(\"supplier_export.xlsx\"), \"Invoices\",\n                                   [\"Trim spaces\", \"Remove blank rows\", \"Remove duplicates\"], api_name=\"\u002Fclean\")\nprint(text)\nprint(pd.read_excel(path, sheet_name=\"Clean\").to_string(index=False))\ntry:\n    client.predict(handle_file(\"supplier_export.xlsx\"), \"Info\", [\"Trim spaces\"], api_name=\"\u002Fclean\")\nexcept Exception as e:\n    print(type(e).__name__ + \":\", e)\ndemo.close()\n",[32,1722,1723,1735,1739,1791,1815,1819,1851,1870,1896,1903,1928,1935,1962,1973,1996],{"__ignoreMap":30},[35,1724,1725,1727,1730,1732],{"class":37,"line":38},[35,1726,630],{"class":77},[35,1728,1729],{"class":81}," gradio_client ",[35,1731,78],{"class":77},[35,1733,1734],{"class":81}," Client, handle_file\n",[35,1736,1737],{"class":37,"line":91},[35,1738,95],{"emptyLinePlaceholder":94},[35,1740,1741,1744,1747,1749,1751,1753,1756,1758,1761,1763,1766,1768,1770,1772,1775,1777,1780,1782,1785,1787,1789],{"class":37,"line":98},[35,1742,1743],{"class":81},"demo.launch(",[35,1745,1746],{"class":226},"prevent_thread_lock",[35,1748,111],{"class":77},[35,1750,115],{"class":114},[35,1752,166],{"class":81},[35,1754,1755],{"class":226},"server_port",[35,1757,111],{"class":77},[35,1759,1760],{"class":114},"7861",[35,1762,166],{"class":81},[35,1764,1765],{"class":226},"quiet",[35,1767,111],{"class":77},[35,1769,115],{"class":114},[35,1771,166],{"class":81},[35,1773,1774],{"class":226},"max_file_size",[35,1776,111],{"class":77},[35,1778,1779],{"class":45},"\"20mb\"",[35,1781,166],{"class":81},[35,1783,1784],{"class":226},"show_error",[35,1786,111],{"class":77},[35,1788,115],{"class":114},[35,1790,244],{"class":81},[35,1792,1793,1796,1798,1801,1804,1806,1809,1811,1813],{"class":37,"line":135},[35,1794,1795],{"class":81},"client ",[35,1797,111],{"class":77},[35,1799,1800],{"class":81}," Client(",[35,1802,1803],{"class":45},"\"http:\u002F\u002F127.0.0.1:7861\u002F\"",[35,1805,166],{"class":81},[35,1807,1808],{"class":226},"verbose",[35,1810,111],{"class":77},[35,1812,1108],{"class":114},[35,1814,244],{"class":81},[35,1816,1817],{"class":37,"line":146},[35,1818,95],{"emptyLinePlaceholder":94},[35,1820,1821,1823,1825,1828,1831,1833,1836,1838,1840,1843,1846,1849],{"class":37,"line":187},[35,1822,560],{"class":114},[35,1824,563],{"class":81},[35,1826,1827],{"class":45},"\"sheets:\"",[35,1829,1830],{"class":81},", client.predict(handle_file(",[35,1832,531],{"class":45},[35,1834,1835],{"class":81},"), ",[35,1837,1485],{"class":226},[35,1839,111],{"class":77},[35,1841,1842],{"class":45},"\"\u002Fsheets\"",[35,1844,1845],{"class":81},")[",[35,1847,1848],{"class":45},"\"value\"",[35,1850,800],{"class":81},[35,1852,1853,1856,1858,1861,1863,1865,1867],{"class":37,"line":215},[35,1854,1855],{"class":81},"table, text, path ",[35,1857,111],{"class":77},[35,1859,1860],{"class":81}," client.predict(handle_file(",[35,1862,531],{"class":45},[35,1864,1835],{"class":81},[35,1866,541],{"class":45},[35,1868,1869],{"class":81},",\n",[35,1871,1872,1875,1877,1879,1881,1883,1885,1887,1889,1891,1894],{"class":37,"line":235},[35,1873,1874],{"class":81},"                                   [",[35,1876,944],{"class":45},[35,1878,166],{"class":81},[35,1880,957],{"class":45},[35,1882,166],{"class":81},[35,1884,972],{"class":45},[35,1886,1302],{"class":81},[35,1888,1485],{"class":226},[35,1890,111],{"class":77},[35,1892,1893],{"class":45},"\"\u002Fclean\"",[35,1895,244],{"class":81},[35,1897,1898,1900],{"class":37,"line":247},[35,1899,560],{"class":114},[35,1901,1902],{"class":81},"(text)\n",[35,1904,1905,1907,1910,1912,1914,1916,1919,1921,1923,1925],{"class":37,"line":255},[35,1906,560],{"class":114},[35,1908,1909],{"class":81},"(pd.read_excel(path, ",[35,1911,536],{"class":226},[35,1913,111],{"class":77},[35,1915,1098],{"class":45},[35,1917,1918],{"class":81},").to_string(",[35,1920,1103],{"class":226},[35,1922,111],{"class":77},[35,1924,1108],{"class":114},[35,1926,1927],{"class":81},"))\n",[35,1929,1930,1933],{"class":37,"line":285},[35,1931,1932],{"class":77},"try",[35,1934,863],{"class":81},[35,1936,1937,1940,1942,1944,1947,1950,1952,1954,1956,1958,1960],{"class":37,"line":319},[35,1938,1939],{"class":81},"    client.predict(handle_file(",[35,1941,531],{"class":45},[35,1943,1835],{"class":81},[35,1945,1946],{"class":45},"\"Info\"",[35,1948,1949],{"class":81},", [",[35,1951,944],{"class":45},[35,1953,1302],{"class":81},[35,1955,1485],{"class":226},[35,1957,111],{"class":77},[35,1959,1893],{"class":45},[35,1961,244],{"class":81},[35,1963,1964,1967,1969,1971],{"class":37,"line":332},[35,1965,1966],{"class":77},"except",[35,1968,887],{"class":114},[35,1970,890],{"class":77},[35,1972,893],{"class":81},[35,1974,1975,1978,1980,1982,1985,1988,1990,1993],{"class":37,"line":343},[35,1976,1977],{"class":114},"    print",[35,1979,563],{"class":81},[35,1981,1305],{"class":114},[35,1983,1984],{"class":81},"(e).",[35,1986,1987],{"class":114},"__name__",[35,1989,1166],{"class":77},[35,1991,1992],{"class":45}," \":\"",[35,1994,1995],{"class":81},", e)\n",[35,1997,1998],{"class":37,"line":369},[35,1999,2000],{"class":81},"demo.close()\n",[10,2002,2003,2004,2007,2008,2011,2012,2015,2016,2019],{},"The upload handler offers both sheets and pre-selects ",[1523,2005,2006],{},"Invoices",". Cleaning returns the summary, and the downloaded workbook holds the three clean rows with tidy headers. Choosing the ",[1523,2009,2010],{},"Info"," sheet produces an ",[32,2013,2014],{},"AppError"," carrying the same friendly message the browser would show. Tests like these — written with pytest and a fixture that launches the app — check the whole round trip, including file handling, which testing ",[32,2017,2018],{},"clean_frame"," alone does not.",[10,2021,2022,2023,2025,2026,2029,2030,2033],{},"The client is also how other programs can use the tool. Because every event with an ",[32,2024,1485],{}," is exposed as an endpoint, a scheduled job, a notebook or another team's script can send a workbook to the same app and get the cleaned file back, with exactly the same rules the people using the page get. ",[32,2027,2028],{},"client.view_api()"," prints the available endpoints and their parameters, and Gradio adds a \"Use via API\" link to the page footer that shows the same information to anyone curious. If the app should not be callable this way, set ",[32,2031,2032],{},"api_name=False"," on an event, or turn off the API page altogether when launching.",[1528,2035,1537,2041,1537,2044,1537,2047,1537,2052,1537,2059,1537,2063,1537,2069,1537,2073,1537,2077,1537,2080,1537,2085,1537,2087,1537,2091,1537,2093,1537,2096,1537,2099,1537,2103,1537,2105,1537,2109,1537,2111,1537,2114,1537,2117,1537,2121,1537,2123,1537,2126,1537,2128,1537,2131,1537,2134,1537,2137,1537,2139,1537,2142,1537,2144,1537,2146,1537,2149,1537,2153,1537,2155,1537,2157,1537,2160,1537,2164,1537,2166,1537,2168,1537,2170,1537,2172,1537,2175,1537,2179,1537,2182,1537,2186,1537,2189,1537,2191,1537,2194,1537,2197,1537,2201,1537,2203,1537,2206,1537,2208,1537,2210,1537,2212,1537,2215,1537,2217,1537,2219,1537,2221,1537,2223,1537,2225,1537,2227,1537,2229,1537,2231,1537,2233,1537,2235,1537,2237,1537,2240,1537,2242,1537,2245,1537,2251,1537,2255,1537,2260,1537,2263,1537,2269],{"viewBox":2036,"role":1531,"ariaLabelledBy":2037,"xmlns":1535,"style":2040},"66 0 627 248",[2038,2039],"gr-clean-t","gr-clean-d","width:100%;max-width:627px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[1214,2042,2043],{"id":2038},"The Invoices sheet before and after cleaning",[1542,2045,2046],{"id":2039},"Six rows with padded names, inconsistent headers, a blank row and duplicates become three rows with normalised headers and trimmed text after trimming, removing the blank row and removing duplicates.",[1546,2048],{"x":2049,"y":797,"width":2050,"height":2051,"fill":1551},"66","627","248",[1546,2053],{"x":2054,"y":2055,"width":2056,"height":2057,"rx":797,"fill":1572,"stroke":1573,"style":2058},"82.7","40.0","32.0","24.0","stroke-width:1px",[1546,2060],{"x":2061,"y":2055,"width":2062,"height":2057,"rx":797,"fill":1572,"stroke":1573,"style":2058},"114.7","121.7",[1563,2064,2068],{"x":2065,"y":2066,"style":2067},"175.5","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[1546,2070],{"x":2071,"y":2055,"width":2072,"height":2057,"rx":797,"fill":1572,"stroke":1573,"style":2058},"236.4","89.3",[1563,2074,2076],{"x":2075,"y":2066,"style":2067},"281.1","B",[1546,2078],{"x":2054,"y":1588,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},"28.0",[1563,2081,2084],{"x":2082,"y":2083,"style":2067},"98.7","83.0","1",[1546,2086],{"x":2061,"y":1588,"width":2062,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2088,2090],{"x":2065,"y":2083,"style":2089},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle"," Supplier Name ",[1546,2092],{"x":2071,"y":1588,"width":2072,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2094,2095],{"x":2075,"y":2083,"style":2089},"Invoice No",[1546,2097],{"x":2054,"y":2098,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},"92.0",[1563,2100,2102],{"x":2082,"y":2101,"style":2067},"111.0","2",[1546,2104],{"x":2061,"y":2098,"width":2062,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2106,2108],{"x":2065,"y":2101,"style":2107},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","  Acme Ltd",[1546,2110],{"x":2071,"y":2098,"width":2072,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2112,2113],{"x":2075,"y":2101,"style":2107},"INV-1",[1546,2115],{"x":2054,"y":2116,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},"120.0",[1563,2118,2120],{"x":2082,"y":2119,"style":2067},"139.0","3",[1546,2122],{"x":2061,"y":2116,"width":2062,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2124,2125],{"x":2065,"y":2119,"style":2107},"Brightside & Co ",[1546,2127],{"x":2071,"y":2116,"width":2072,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2129,2130],{"x":2075,"y":2119,"style":2107},"INV-2",[1546,2132],{"x":2054,"y":2133,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},"148.0",[1563,2135,1548],{"x":2082,"y":2136,"style":2067},"167.0",[1546,2138],{"x":2061,"y":2133,"width":2062,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2140,2141],{"x":2065,"y":2136,"style":2107},"Brightside & Co",[1546,2143],{"x":2071,"y":2133,"width":2072,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2145,2130],{"x":2075,"y":2136,"style":2107},[1546,2147],{"x":2054,"y":2148,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},"176.0",[1563,2150,2152],{"x":2082,"y":2151,"style":2067},"195.0","5",[1546,2154],{"x":2061,"y":2148,"width":2062,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1546,2156],{"x":2071,"y":2148,"width":2072,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1546,2158],{"x":2054,"y":2159,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},"204.0",[1563,2161,2163],{"x":2082,"y":2162,"style":2067},"223.0","6",[1546,2165],{"x":2061,"y":2159,"width":2062,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2167,2108],{"x":2065,"y":2162,"style":2107},[1546,2169],{"x":2071,"y":2159,"width":2072,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2171,2113],{"x":2075,"y":2162,"style":2107},[1546,2173],{"x":2174,"y":2055,"width":2056,"height":2057,"rx":797,"fill":1572,"stroke":1573,"style":2058},"436.1",[1546,2176],{"x":2177,"y":2055,"width":2178,"height":2057,"rx":797,"fill":1572,"stroke":1573,"style":2058},"468.1","118.7",[1563,2180,2068],{"x":2181,"y":2066,"style":2067},"527.5",[1546,2183],{"x":2184,"y":2055,"width":2185,"height":2057,"rx":797,"fill":1572,"stroke":1573,"style":2058},"586.9","90.5",[1563,2187,2076],{"x":2188,"y":2066,"style":2067},"632.1",[1546,2190],{"x":2174,"y":1588,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},[1563,2192,2084],{"x":2193,"y":2083,"style":2067},"452.1",[1546,2195],{"x":2177,"y":1588,"width":2178,"height":2079,"rx":797,"fill":1697,"stroke":1698,"style":2196},"stroke-width:1.5px",[1563,2198,2200],{"x":2181,"y":2083,"style":2199},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","supplier_name",[1546,2202],{"x":2184,"y":1588,"width":2185,"height":2079,"rx":797,"fill":1697,"stroke":1698,"style":2196},[1563,2204,2205],{"x":2188,"y":2083,"style":2199},"invoice_no",[1546,2207],{"x":2174,"y":2098,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},[1563,2209,2102],{"x":2193,"y":2101,"style":2067},[1546,2211],{"x":2177,"y":2098,"width":2178,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2213,2214],{"x":2181,"y":2101,"style":2107},"Acme Ltd",[1546,2216],{"x":2184,"y":2098,"width":2185,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2218,2113],{"x":2188,"y":2101,"style":2107},[1546,2220],{"x":2174,"y":2116,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},[1563,2222,2120],{"x":2193,"y":2119,"style":2067},[1546,2224],{"x":2177,"y":2116,"width":2178,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2226,2141],{"x":2181,"y":2119,"style":2107},[1546,2228],{"x":2184,"y":2116,"width":2185,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2230,2130],{"x":2188,"y":2119,"style":2107},[1546,2232],{"x":2174,"y":2133,"width":2056,"height":2079,"rx":797,"fill":1572,"stroke":1573,"style":2058},[1563,2234,1548],{"x":2193,"y":2136,"style":2067},[1546,2236],{"x":2177,"y":2133,"width":2178,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2238,2239],{"x":2181,"y":2136,"style":2107},"Corex GmbH",[1546,2241],{"x":2184,"y":2133,"width":2185,"height":2079,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2243,2244],{"x":2188,"y":2136,"style":2107},"INV-3",[1563,2246,2250],{"x":2247,"y":2248,"style":2249},"204.2","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[1563,2252,2254],{"x":2253,"y":2248,"style":2249},"556.7","after",[37,2256],{"x1":2257,"y1":2258,"x2":2259,"y2":2258,"stroke":1560,"style":1561},"335.7","108.0","422.1",[1601,2261],{"points":2262,"fill":1604},"426.1,108.0 417.1,113.0 417.1,103.0",[1546,2264],{"x":2265,"y":1608,"width":2266,"height":2267,"rx":2268,"fill":1551,"stroke":1573,"style":1612},"352.8","56.3","22.0","11",[1563,2270,2274],{"x":2271,"y":2272,"style":2273},"380.9","90.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","clean",[20,2276,2278],{"id":2277},"step-4-before-other-people-use-it","Step 4 — Before other people use it",[10,2280,2281,2282,2285,2286,2289,2290,2293,2294,2297,2298,2301,2302,2305,2306,2309,2310,18],{},"A tool on a laptop and a tool used by a team differ in a few settings. ",[32,2283,2284],{},"max_file_size=\"20mb\""," in ",[32,2287,2288],{},"launch()"," rejects oversized uploads before your code sees them. ",[32,2291,2292],{},"delete_cache=(3600, 3600)"," on the ",[32,2295,2296],{},"Blocks"," makes Gradio delete uploaded and generated files older than an hour, checking every hour — uploaded spreadsheets often contain data that should not sit on a server indefinitely. ",[32,2299,2300],{},"server_name=\"0.0.0.0\""," makes the app reachable from other machines, and ",[32,2303,2304],{},"auth="," adds a simple login; for anything beyond a small team, put it behind your organisation's proxy and single sign-on instead. Long-running cleaning jobs are queued by Gradio automatically; ",[32,2307,2308],{},"demo.queue(default_concurrency_limit=2)"," limits how many run at once so a few large files cannot exhaust memory. Validating the file more strictly before cleaning — size, sheet structure, macro-enabled formats — is covered in ",[14,2311,2313],{"href":2312},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fvalidate-an-uploaded-excel-file-in-a-web-app\u002F","Validate an Uploaded Excel File in a Web App",[1528,2315,1537,2320,1537,2323,1537,2326,1537,2329,1537,2332,1537,2336,1537,2341,1537,2344,1537,2348,1537,2352,1537,2357,1537,2359,1537,2363,1537,2365,1537,2368,1537,2370,1537,2374,1537,2376,1537,2378,1537,2380,1537,2383,1537,2386,1537,2390,1537,2392,1537,2396,1537,2398,1537,2401,1537,2404,1537,2407,1537,2409,1537,2413,1537,2415],{"viewBox":2316,"role":1531,"ariaLabelledBy":2317,"xmlns":1535,"style":1536},"4 0 752 208",[2318,2319],"gr-settings-t","gr-settings-d",[1214,2321,2322],{"id":2318},"Settings worth changing before sharing the app",[1542,2324,2325],{"id":2319},"max_file_size rejects large uploads, delete_cache removes old temporary files, server_name and auth control who can reach the app, and the queue's concurrency limit stops several large files exhausting memory.",[1546,2327],{"x":1548,"y":797,"width":1549,"height":2328,"fill":1551},"208",[1546,2330],{"x":1554,"y":1555,"width":2331,"height":2055,"rx":797,"fill":1551,"stroke":1573,"style":2058},"101.3",[1546,2333],{"x":2334,"y":1555,"width":2335,"height":2055,"rx":797,"fill":1559,"stroke":1560,"style":1612},"121.3","309.3",[1563,2337,2340],{"x":2338,"y":2339,"style":1567},"276.0","41.0","setting",[1546,2342],{"x":2343,"y":1555,"width":2335,"height":2055,"rx":797,"fill":1559,"stroke":1560,"style":1612},"430.7",[1563,2345,2347],{"x":2346,"y":2339,"style":1567},"585.3","protects against",[1546,2349],{"x":1554,"y":2350,"width":2331,"height":2351,"rx":797,"fill":1572,"stroke":1573,"style":2058},"56.0","34.0",[1563,2353,2356],{"x":2056,"y":2354,"style":2355},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","upload size",[1546,2358],{"x":2334,"y":2350,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2360,2284],{"x":2338,"y":2361,"style":2362},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[1546,2364],{"x":2343,"y":2350,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2366,2367],{"x":2346,"y":2361,"style":2362},"huge uploads",[1546,2369],{"x":1554,"y":2272,"width":2331,"height":2351,"rx":797,"fill":1572,"stroke":1573,"style":2058},[1563,2371,2373],{"x":2056,"y":2372,"style":2355},"112.0","temp files",[1546,2375],{"x":2334,"y":2272,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2377,2292],{"x":2338,"y":2101,"style":2362},[1546,2379],{"x":2343,"y":2272,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2381,2382],{"x":2346,"y":2101,"style":2362},"data left on disk",[1546,2384],{"x":1554,"y":2385,"width":2331,"height":2351,"rx":797,"fill":1572,"stroke":1573,"style":2058},"124.0",[1563,2387,2389],{"x":2056,"y":2388,"style":2355},"146.0","access",[1546,2391],{"x":2334,"y":2385,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2393,2395],{"x":2338,"y":2394,"style":2362},"145.0","server_name, auth",[1546,2397],{"x":2343,"y":2385,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2399,2400],{"x":2346,"y":2394,"style":2362},"anyone on the network",[1546,2402],{"x":1554,"y":2403,"width":2331,"height":2351,"rx":797,"fill":1572,"stroke":1573,"style":2058},"158.0",[1563,2405,2406],{"x":2056,"y":1556,"style":2355},"load",[1546,2408],{"x":2334,"y":2403,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2410,2412],{"x":2338,"y":2411,"style":2362},"179.0","queue concurrency",[1546,2414],{"x":2343,"y":2403,"width":2335,"height":2351,"rx":797,"fill":1551,"stroke":1573,"style":2058},[1563,2416,2417],{"x":2346,"y":2411,"style":2362},"memory exhaustion",[20,2419,2421],{"id":2420},"common-pitfalls","Common pitfalls",[2423,2424,2425,2433,2442,2448,2457],"ul",{},[2426,2427,2428,2432],"li",{},[2429,2430,2431],"strong",{},"Cleaning logic inside the handler."," Keep it in a plain function you can test and reuse.",[2426,2434,2435,2438,2439,2441],{},[2429,2436,2437],{},"Tracebacks shown to users."," Raise ",[32,2440,1520],{}," with a readable message.",[2426,2443,2444,2447],{},[2429,2445,2446],{},"Deduplicating before trimming."," Near-duplicates survive; trim first.",[2426,2449,2450,2453,2454,2456],{},[2429,2451,2452],{},"Uploaded files kept forever."," Set ",[32,2455,1224],{}," or clean up yourself.",[2426,2458,2459,2453,2462,2464],{},[2429,2460,2461],{},"No limits.",[32,2463,1774],{}," and a concurrency limit before sharing.",[20,2466,2468],{"id":2467},"conclusion","Conclusion",[10,2470,2471,2472,2474,2475,2478,2479,2481,2482,2484,2485,18],{},"To let people clean their own Excel files, put the cleaning in a plain pandas function and wrap it in a Gradio ",[32,2473,2296],{}," app: a file upload whose ",[32,2476,2477],{},"upload"," event fills a sheet dropdown, options as a checkbox group, and a button whose handler returns a preview, a summary and a cleaned workbook with a sheet listing the changes. Use ",[32,2480,1520],{}," for problems, limit upload size and concurrency, let ",[32,2483,1224],{}," remove old files, and test the whole round trip with ",[32,2486,1713],{},[20,2488,2490],{"id":2489},"frequently-asked-questions","Frequently asked questions",[10,2492,2493,2496],{},[2429,2494,2495],{},"How do I upload an Excel file in Gradio?","\nUse gr.File with file_types set to .xlsx and type set to filepath; the handler receives the path of the uploaded file and can read it with pandas.",[10,2498,2499,2502],{},[2429,2500,2501],{},"How do I let users download the cleaned file?","\nWrite the result to a temporary path and return that path to a gr.File output component; Gradio serves it for download.",[10,2504,2505,2508],{},[2429,2506,2507],{},"How do I show an error message in a Gradio app?","\nRaise gr.Error with the message; Gradio displays it in the page instead of a traceback.",[10,2510,2511,2514],{},[2429,2512,2513],{},"How do I test a Gradio app from Python?","\nLaunch it with prevent_thread_lock, then call its handlers through gradio_client's Client.predict with the api_name of each event.",[20,2516,2518],{"id":2517},"related","Related",[2423,2520,2521,2527,2534,2541],{},[2426,2522,2523,2524,2526],{},"Up one level: ",[14,2525,17],{"href":16}," — Excel in web apps.",[2426,2528,2529,2533],{},[14,2530,2532],{"href":2531},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-streamlit-app-to-upload-and-download-excel\u002F","Build a Streamlit App to Upload and Download Excel"," — the same idea with Streamlit.",[2426,2535,2536,2540],{},[14,2537,2539],{"href":2538},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fhandle-an-uploaded-excel-file-in-flask-and-fastapi\u002F","Handle an Uploaded Excel File in Flask and FastAPI"," — a lower-level approach.",[2426,2542,2543,2547],{},[14,2544,2546],{"href":2545},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstrip-whitespace-and-normalise-text-columns-with-pandas\u002F","Strip Whitespace and Normalise Text Columns with pandas"," — the trimming step in detail.",[2549,2550,2551],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":30,"searchDepth":91,"depth":91,"links":2553},[2554,2555,2556,2557,2558,2559,2560,2561,2562],{"id":22,"depth":91,"text":23},{"id":61,"depth":91,"text":62},{"id":605,"depth":91,"text":606},{"id":1706,"depth":91,"text":1707},{"id":2277,"depth":91,"text":2278},{"id":2420,"depth":91,"text":2421},{"id":2467,"depth":91,"text":2468},{"id":2489,"depth":91,"text":2490},{"id":2517,"depth":91,"text":2518},"2026-10-02","Give colleagues a browser tool for cleaning their own Excel files with Gradio: upload a workbook, pick the sheet from a list filled in on upload, choose cleaning options, see a preview and a summary of what changed, and download the cleaned file, with friendly error messages, upload limits, temporary-file clean-up and tests through the Gradio client.","md",[2567,2569,2571,2573],{"q":2495,"a":2568},"Use gr.File with file_types set to .xlsx and type set to filepath; the handler receives the path of the uploaded file and can read it with pandas.",{"q":2501,"a":2570},"Write the result to a temporary path and return that path to a gr.File output component; Gradio serves it for download.",{"q":2507,"a":2572},"Raise gr.Error with the message; Gradio displays it in the page instead of a traceback.",{"q":2513,"a":2574},"Launch it with prevent_thread_lock, then call its handlers through gradio_client's Client.predict with the api_name of each event.","gradio app that cleans an uploaded excel file",null,{"breadcrumb":2578},[2579,2581,2584],{"name":2580,"item":1057},"Home",{"name":2582,"item":2583},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":17,"item":16},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file",{"title":5,"description":2587},"A Gradio web app for Excel cleaning in Python: file upload, sheet dropdown filled on upload, cleaning options, preview, change summary and download, gr.Error messages, max_file_size, delete_cache and gradio_client tests.","build-a-gradio-app-that-cleans-an-uploaded-excel-file","automating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-gradio-app-that-cleans-an-uploaded-excel-file\u002Findex",[2591],"web-apps","how-to","vD24NceVkgXaSIEpoiwnUxzDnFK8vNeWpBQ97-OI_2I",[2595,2599],{"title":2596,"path":2597,"stem":2598,"children":-1},"Add an Export to Excel Action in Django Admin","\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fadd-an-export-to-excel-action-in-django-admin","automating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fadd-an-export-to-excel-action-in-django-admin\u002Findex",{"title":2600,"path":2601,"stem":2602,"children":-1},"Build a Streamlit App to Upload and Download Excel Files","\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-streamlit-app-to-upload-and-download-excel","automating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-a-streamlit-app-to-upload-and-download-excel\u002Findex",[2604,2606],{"path":2605,"title":2582},"\u002Fautomating-reporting-workflows",{"path":2607,"title":17},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps",[],[2610,2613,2616,2617,2618,2621],{"path":2611,"title":2612,"kind":2592},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fschedule-excel-reports-with-celery-beat","Schedule Excel Reports with Celery Beat",{"path":2614,"title":2615,"kind":2592},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Ftrigger-an-excel-report-from-a-webhook","Trigger an Excel Report from a Webhook",{"path":2597,"title":2596,"kind":2592},{"path":2601,"title":2600,"kind":2592},{"path":2619,"title":2620,"kind":2592},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fbuild-an-excel-workbook-in-memory-with-bytesio","Build an Excel Workbook in Memory with BytesIO",{"path":2622,"title":2623,"kind":2592},"\u002Fautomating-reporting-workflows\u002Fserving-excel-files-from-python-web-apps\u002Fcache-generated-excel-files-in-a-web-app","Cache Generated Excel Files in a Web App",1790972140296]