Parse an uploaded file and return data as JSON without saving to workspace. Used for client-side preview of formats that the browser cannot parse natively (e.g. legacy .xls).
()
| 838 | |
| 839 | @tables_bp.route('/parse-file', methods=['POST']) |
| 840 | def parse_file(): |
| 841 | """Parse an uploaded file and return data as JSON without saving to workspace. |
| 842 | |
| 843 | Used for client-side preview of formats that the browser cannot parse |
| 844 | natively (e.g. legacy .xls). |
| 845 | """ |
| 846 | try: |
| 847 | if 'file' not in request.files: |
| 848 | raise AppError(ErrorCode.INVALID_REQUEST, "No file provided") |
| 849 | |
| 850 | file = request.files['file'] |
| 851 | filename = file.filename or '' |
| 852 | if not filename or not is_supported_file(filename): |
| 853 | raise AppError(ErrorCode.INVALID_REQUEST, "Unsupported file format") |
| 854 | |
| 855 | ext = os.path.splitext(filename)[1].lower() |
| 856 | |
| 857 | if ext in ('.xls', '.xlsx'): |
| 858 | engine = 'xlrd' if ext == '.xls' else 'openpyxl' |
| 859 | xls = pd.ExcelFile(file.stream, engine=engine) |
| 860 | sheets = [] |
| 861 | for sheet_name in xls.sheet_names: |
| 862 | df = xls.parse(sheet_name) |
| 863 | df = df.where(df.notna(), None) |
| 864 | records = df_to_safe_records(df) |
| 865 | sheets.append({ |
| 866 | "sheet_name": sheet_name, |
| 867 | "columns": list(df.columns), |
| 868 | "row_count": len(records), |
| 869 | "data": records, |
| 870 | }) |
| 871 | return json_ok({"sheets": sheets}) |
| 872 | elif ext == '.csv': |
| 873 | raw = normalize_text_encoding(file.stream.read(), 'csv') |
| 874 | df = pd.read_csv(io.BytesIO(raw)) |
| 875 | df = df.where(df.notna(), None) |
| 876 | records = df_to_safe_records(df) |
| 877 | return json_ok({ |
| 878 | "sheets": [{ |
| 879 | "sheet_name": "Sheet1", |
| 880 | "columns": list(df.columns), |
| 881 | "row_count": len(records), |
| 882 | "data": records, |
| 883 | }], |
| 884 | }) |
| 885 | else: |
| 886 | raise AppError(ErrorCode.INVALID_REQUEST, f"Server-side parsing not supported for {ext}") |
| 887 | |
| 888 | except AppError: |
| 889 | raise |
| 890 | except Exception as e: |
| 891 | logger.error("Error parsing file", exc_info=True) |
| 892 | raise AppError(ErrorCode.FILE_PARSE_ERROR, "Failed to parse the uploaded file") |
| 893 | |
| 894 | |
| 895 | @tables_bp.route('/sync-table-data', methods=['POST']) |
nothing calls this directly
no test coverage detected