Coverage for agentos/marketplace/skills/spreadsheet/spreadsheet.py: 10%
58 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 23:17 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 23:17 +0800
1"""
2spreadsheet — CSV-based spreadsheet operations.
4Actions: read, filter, sort, aggregate, columns
5Lightweight CSV manipulation without pandas.
6"""
8import csv
9import os
10from typing import Any
13def run(
14 action: str = "read",
15 file_path: str = "",
16 column: str = "",
17 condition: str = "",
18 sort_by: str = "",
19 aggregate: str = "",
20 delimiter: str = ",",
21 **kwargs: Any,
22) -> str:
23 if not file_path or not os.path.exists(file_path):
24 return f"[spreadsheet] File not found: {file_path}"
26 try:
27 with open(file_path, newline="", encoding="utf-8") as f:
28 reader = csv.DictReader(f, delimiter=delimiter)
29 rows = list(reader)
30 except Exception as e:
31 return f"[spreadsheet] Read error: {e}"
33 headers = reader.fieldnames or []
35 if action == "columns":
36 return f"Columns ({len(headers)}): {', '.join(headers)}"
38 if action == "read":
39 return _format(rows[:20], headers)
41 if action == "filter":
42 if not column or column not in headers:
43 return f"[spreadsheet] Column '{column}' not found. Available: {headers}"
44 filtered = [r for r in rows if condition.lower() in r.get(column, "").lower()]
45 return _format(filtered[:30], headers)
47 if action == "sort":
48 if not sort_by or sort_by not in headers:
49 return f"[spreadsheet] Column '{sort_by}' not found."
50 descending = kwargs.get("desc", False)
51 sorted_rows = sorted(rows, key=lambda r: r.get(sort_by, ""), reverse=descending)
52 return _format(sorted_rows[:30], headers)
54 if action == "aggregate":
55 if column not in headers:
56 return f"[spreadsheet] Column '{column}' not found."
57 values = []
58 for r in rows:
59 try:
60 values.append(float(r.get(column, 0)))
61 except (ValueError, TypeError):
62 pass
63 if not values:
64 return f"[spreadsheet] No numeric values in '{column}'."
65 if aggregate == "sum":
66 return f"Sum of {column}: {sum(values)}"
67 if aggregate == "avg":
68 return f"Average of {column}: {sum(values)/len(values):.2f}"
69 if aggregate == "min":
70 return f"Min of {column}: {min(values)}"
71 if aggregate == "max":
72 return f"Max of {column}: {max(values)}"
73 if aggregate == "count":
74 return f"Count of {column}: {len(values)}"
75 return f"Sum: {sum(values)}, Avg: {sum(values)/len(values):.2f}, Min: {min(values)}, Max: {max(values)}, Count: {len(values)}" # noqa: E501
77 return (
78 f"[spreadsheet] Unknown action: {action}. Available: read, filter, sort, aggregate, columns"
79 )
82def _format(rows, headers):
83 if not rows:
84 return "[spreadsheet] No matching rows."
85 result = "\t".join(headers) + "\n"
86 result += "\n".join("\t".join(str(r.get(h, "")) for h in headers) for r in rows)
87 return result
90__all__ = ["run"]