-
Notifications
You must be signed in to change notification settings - Fork 0
Performance optimization #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
c98d8b5
3d17371
b901f12
c0be70f
9663fba
e5a4186
0a4442f
6695ed7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| "@platforma-open/milaboratories.immune-assay-data.coverage-mode-calc": minor | ||
| "@platforma-open/milaboratories.immune-assay-data.xlsx-to-csv": minor | ||
| "@platforma-open/milaboratories.immune-assay-data.workflow": minor | ||
| "@platforma-open/milaboratories.immune-assay-data.model": minor | ||
| "@platforma-open/milaboratories.immune-assay-data.ui": minor | ||
| --- | ||
|
|
||
| - Introduce fast mode for sequence match | ||
| - Support XLSX file as assay data input |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "name": "@platforma-open/milaboratories.immune-assay-data.xlsx-to-csv", | ||
| "version": "1.0.0", | ||
| "scripts": { | ||
| "build": "pl-pkg build", | ||
| "prepublishOnly": "pl-pkg prepublish", | ||
| "do-pack": "rm -f *.tgz && pl-pkg build && pnpm pack && mv platforma-open*.tgz package.tgz", | ||
| "changeset": "changeset", | ||
| "version-packages": "changeset version" | ||
| }, | ||
| "files": [ | ||
| "./dist/**/*" | ||
| ], | ||
| "dependencies": {}, | ||
| "devDependencies": { | ||
| "@platforma-sdk/package-builder": "catalog:", | ||
| "@platforma-open/milaboratories.runenv-python-3": "catalog:" | ||
| }, | ||
| "block-software": { | ||
| "entrypoints": { | ||
| "main": { | ||
| "binary": { | ||
| "artifact": { | ||
| "type": "python", | ||
| "registry": "platforma-open", | ||
| "environment": "@platforma-open/milaboratories.runenv-python-3:3.12.10", | ||
| "dependencies": { | ||
| "toolset": "pip", | ||
| "requirements": "requirements.txt" | ||
| }, | ||
| "root": "./src" | ||
| }, | ||
| "cmd": [ | ||
| "python", | ||
| "{pkg}/main.py" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #!/usr/bin/env python3 | ||
| """Convert XLSX files to CSV format.""" | ||
|
|
||
| import argparse | ||
| import csv | ||
| import sys | ||
|
|
||
| from openpyxl import load_workbook | ||
|
|
||
|
|
||
| def find_header_row(rows: list) -> int: | ||
| """Find the header row index by looking for the first row where most cells are non-empty.""" | ||
| for i, row in enumerate(rows): | ||
| non_empty = sum(1 for cell in row if cell is not None and str(cell).strip()) | ||
| if non_empty > 1: | ||
| return i | ||
| return 0 | ||
|
|
||
|
|
||
| def xlsx_to_csv(input_file: str, output_file: str) -> None: | ||
| """Read the first worksheet of an XLSX file and write it as CSV.""" | ||
| wb = load_workbook(input_file, read_only=True, data_only=True) | ||
| ws = wb[wb.sheetnames[0]] | ||
|
|
||
| rows = list(ws.iter_rows(values_only=True)) | ||
| header_idx = find_header_row(rows) | ||
|
|
||
| with open(output_file, 'w', newline='') as f: | ||
| writer = csv.writer(f) | ||
| for row in rows[header_idx:]: | ||
| writer.writerow( | ||
| ['' if cell is None else cell for cell in row] | ||
| ) | ||
|
|
||
| wb.close() | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description="Convert XLSX to CSV.") | ||
| parser.add_argument("-i", "--input", required=True, help="Input XLSX file path.") | ||
| parser.add_argument("-o", "--output", required=True, help="Output CSV file path.") | ||
| args = parser.parse_args() | ||
|
|
||
| try: | ||
| xlsx_to_csv(args.input, args.output) | ||
| print(f"Successfully converted '{args.input}' to '{args.output}'") | ||
| except Exception as e: | ||
| print(f"Error: {e}", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| openpyxl |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -147,6 +147,7 @@ export async function importFile(file: LocalImportFileHandle) { | |||||
| const fileName = getFileNameFromHandle(file); | ||||||
| const extension = fileName.split('.').pop()?.toLowerCase(); | ||||||
| app.model.args.fileExtension = extension; | ||||||
| app.model.args.detectedXsvType = undefined; | ||||||
|
|
||||||
| let rawData: TableData; | ||||||
|
|
||||||
|
|
@@ -165,6 +166,7 @@ export async function importFile(file: LocalImportFileHandle) { | |||||
| } else { | ||||||
| // Handle Excel/CSV files as before | ||||||
| const data = await getRawPlatformaInstance().lsDriver.getLocalFileContent(file); | ||||||
|
|
||||||
| const wb = XLSX.read(data); | ||||||
|
|
||||||
| // @TODO: allow user to select worksheet | ||||||
|
|
@@ -175,6 +177,14 @@ export async function importFile(file: LocalImportFileHandle) { | |||||
| raw: true, | ||||||
| blankrows: false, | ||||||
| }) as TableData; | ||||||
|
|
||||||
| // Detect actual delimiter (extension may not match content). | ||||||
| // XLSX auto-detects internally via guess_sep but doesn't expose the result, | ||||||
| // so we check the first line of the already-in-memory data buffer. | ||||||
| if (extension === 'csv' || extension === 'tsv') { | ||||||
| const firstLine = new TextDecoder().decode(new Uint8Array(data).slice(0, 4096)).split('\n')[0] ?? ''; | ||||||
| app.model.args.detectedXsvType = firstLine.includes('\t') ? 'tsv' : 'csv'; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current delimiter detection logic
Suggested change
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| const header = rawData[0]; | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loading all rows into memory with
list(ws.iter_rows(values_only=True))can be inefficient for large XLSX files, potentially causing high memory consumption. This is likely why the workflow allocates 16GiB for this step. To improve performance and reduce memory usage, consider processing the file as a stream. You can iterate overws.iter_rows()directly and write to the CSV row-by-row, after finding the header by inspecting the first few hundred rows.