Coverage for agentos/marketplace/skills/spreadsheet/spreadsheet.py: 10%

58 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-06 19:15 +0800

1""" 

2spreadsheet — CSV-based spreadsheet operations. 

3 

4Actions: read, filter, sort, aggregate, columns 

5Lightweight CSV manipulation without pandas. 

6""" 

7 

8import csv 

9import os 

10from typing import Any 

11 

12 

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}" 

25 

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}" 

32 

33 headers = reader.fieldnames or [] 

34 

35 if action == "columns": 

36 return f"Columns ({len(headers)}): {', '.join(headers)}" 

37 

38 if action == "read": 

39 return _format(rows[:20], headers) 

40 

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) 

46 

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) 

53 

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 

76 

77 return ( 

78 f"[spreadsheet] Unknown action: {action}. Available: read, filter, sort, aggregate, columns" 

79 ) 

80 

81 

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 

88 

89 

90__all__ = ["run"]