smartspread 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.1
2
+ Name: smartspread
3
+ Version: 1.0.0
4
+ Summary: A Python library that extends gspread for enhanced spreadsheet manipulation.
5
+ Author-email: Arved Klöhn <arved.kloehn@gmail.com>
6
+ Project-URL: Homepage, https://github.com/Redundando/smart_spread
7
+ Project-URL: Repository, https://github.com/Redundando/smart_spread
8
+ Requires-Python: >=3.7
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: gspread>=5.0.0
11
+ Requires-Dist: pandas>=1.3.0
12
+ Requires-Dist: cacherator>=0.1.0
13
+ Requires-Dist: logorator>=0.1.0
14
+
15
+ # SmartSpread
16
+
17
+ SmartSpread is a Python library that extends the functionality of `gspread` for enhanced spreadsheet manipulation, enabling easier integration with Google Sheets.
18
+
19
+ ## Features
20
+ - Simplified spreadsheet creation and access.
21
+ - Easy tab (worksheet) management.
22
+ - Supports data handling with Pandas DataFrames, lists, and dictionaries.
23
+ - Batch updates and row-level modifications.
24
+ - Cached properties for optimized performance.
25
+
26
+ ## Installation
27
+ ```bash
28
+ pip install smartspread
29
+ ```
30
+
31
+ ## Requirements
32
+ - Python 3.7+
33
+ - `gspread`
34
+ - `pandas`
35
+ - `cacherator`
36
+ - `logorator`
37
+
38
+ ## Usage
39
+ ```python
40
+ from smartspread import SmartSpread
41
+
42
+ # Initialize
43
+ spread = SmartSpread(sheet_identifier="MySheet", key_file="path/to/google_service_account_keyfile.json")
44
+
45
+ # Write data
46
+ data = [["Name", "Age"], ["Alice", 30], ["Bob", 25]]
47
+ spread.write_to_tab(data, tab_name="Sheet1", overwrite_tab=True)
48
+
49
+ # Read data
50
+ df = spread.tab_to_df("Sheet1")
51
+ print(df)
52
+ ```
53
+
54
+ ## License
55
+ This project is licensed under the MIT License.
56
+
57
+ ## Contributing
58
+ Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
59
+
60
+ ## Author
61
+ Arved Klöhn - [Redundando](https://github.com/Redundando/)
@@ -0,0 +1,47 @@
1
+ # SmartSpread
2
+
3
+ SmartSpread is a Python library that extends the functionality of `gspread` for enhanced spreadsheet manipulation, enabling easier integration with Google Sheets.
4
+
5
+ ## Features
6
+ - Simplified spreadsheet creation and access.
7
+ - Easy tab (worksheet) management.
8
+ - Supports data handling with Pandas DataFrames, lists, and dictionaries.
9
+ - Batch updates and row-level modifications.
10
+ - Cached properties for optimized performance.
11
+
12
+ ## Installation
13
+ ```bash
14
+ pip install smartspread
15
+ ```
16
+
17
+ ## Requirements
18
+ - Python 3.7+
19
+ - `gspread`
20
+ - `pandas`
21
+ - `cacherator`
22
+ - `logorator`
23
+
24
+ ## Usage
25
+ ```python
26
+ from smartspread import SmartSpread
27
+
28
+ # Initialize
29
+ spread = SmartSpread(sheet_identifier="MySheet", key_file="path/to/google_service_account_keyfile.json")
30
+
31
+ # Write data
32
+ data = [["Name", "Age"], ["Alice", 30], ["Bob", 25]]
33
+ spread.write_to_tab(data, tab_name="Sheet1", overwrite_tab=True)
34
+
35
+ # Read data
36
+ df = spread.tab_to_df("Sheet1")
37
+ print(df)
38
+ ```
39
+
40
+ ## License
41
+ This project is licensed under the MIT License.
42
+
43
+ ## Contributing
44
+ Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
45
+
46
+ ## Author
47
+ Arved Klöhn - [Redundando](https://github.com/Redundando/)
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=61.0",
4
+ "wheel"
5
+ ]
6
+ build-backend = "setuptools.build_meta"
7
+
8
+ [project]
9
+ name = "smartspread"
10
+ version = "1.0.0"
11
+ readme = "README.md"
12
+ description = "A Python library that extends gspread for enhanced spreadsheet manipulation."
13
+ authors = [
14
+ { name = "Arved Klöhn", email = "arved.kloehn@gmail.com" }
15
+ ]
16
+
17
+ requires-python = ">=3.7"
18
+ dependencies = [
19
+ "gspread>=5.0.0",
20
+ "pandas>=1.3.0",
21
+ "cacherator>=0.1.0",
22
+ "logorator>=0.1.0"
23
+ ]
24
+
25
+ [project.urls]
26
+ "Homepage" = "https://github.com/Redundando/smart_spread"
27
+ "Repository" = "https://github.com/Redundando/smart_spread"
28
+
29
+ [tool.setuptools]
30
+ packages = ["smart_spread"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ from .smart_spread import SmartSpread
@@ -0,0 +1,229 @@
1
+ import gspread
2
+ from cacherator import JSONCache, Cached
3
+ from logorator import Logger
4
+ import pandas as pd
5
+
6
+ class SmartSpread(JSONCache):
7
+
8
+ def __init__(self,
9
+ sheet_identifier="",
10
+ directory = "data/smart_spread",
11
+ user_email = None,
12
+ key_file = "",
13
+ clear_cache = False):
14
+ super().__init__(directory=directory, data_id=f"{sheet_identifier}", clear_cache=clear_cache)
15
+ self.user_email = user_email
16
+ self.key_file = key_file
17
+ self.sheet_identifier = sheet_identifier
18
+ self.gc = gspread.service_account(filename=key_file)
19
+
20
+
21
+
22
+ @property
23
+ @Cached()
24
+ def sheet(self):
25
+ try:
26
+ try:
27
+ # Attempt to open by ID
28
+ sheet = self.gc.open_by_key(self.sheet_identifier)
29
+ Logger.note(f"Spreadsheet '{sheet.title}' successfully opened by ID.")
30
+ except gspread.exceptions.SpreadsheetNotFound:
31
+ # If not found by ID, try to open by name
32
+ sheet = self.gc.open(self.sheet_identifier)
33
+ Logger.note(f"Spreadsheet '{sheet.title}' successfully opened by name.")
34
+ return sheet
35
+ except gspread.exceptions.SpreadsheetNotFound:
36
+ Logger.note(f"Spreadsheet '{self.sheet_identifier}' not found.")
37
+ return self.create_sheet()
38
+
39
+ @Logger(mode="short")
40
+ def create_sheet(self):
41
+ Logger.note(f"Creating a new spreadsheet ('{self.sheet_identifier}').", mode="short")
42
+ try:
43
+ # Create a new spreadsheet if it does not exist
44
+ new_sheet = self.gc.create(self.sheet_identifier)
45
+ if self.user_email:
46
+ new_sheet.share(email_address=self.user_email,perm_type="user", role="writer")
47
+ Logger.note(f"Access granted to {self.user_email}.", mode="short")
48
+ return new_sheet
49
+ except Exception as e:
50
+ Logger.note(f"Error creating spreadsheet: {e}", mode="short")
51
+ raise
52
+
53
+
54
+
55
+ @Logger(mode="short")
56
+ def grant_access(self, email:str=None, role:str="owner"):
57
+ if not self.sheet:
58
+ raise ValueError("No spreadsheet is currently opened. Please open or create a sheet first.")
59
+ try:
60
+ self.sheet.share(email, perm_type="user", role=role)
61
+ Logger.note(f"Access granted to '{email}' with role '{role}' for sheet '{self.sheet.title}'.", mode="short")
62
+ except Exception as e:
63
+ Logger.note(f"Error granting access to '{email}': {e}", mode="short")
64
+ raise
65
+
66
+ @property
67
+ @Cached()
68
+ def url(self):
69
+ return self.sheet.url
70
+
71
+ @property
72
+ @Cached()
73
+ def tab_names(self):
74
+ if not self.sheet:
75
+ raise ValueError("No spreadsheet is currently opened. Please open a sheet first.")
76
+
77
+ try:
78
+ tab_names = [worksheet.title for worksheet in self.sheet.worksheets()]
79
+ return tab_names
80
+ except Exception as e:
81
+ Logger.note(f"Error fetching tab names: {e}", mode="short")
82
+ raise
83
+
84
+ @Cached()
85
+ @Logger(mode="short")
86
+ def get_tab_values(self, tab_name:str=None):
87
+ if not self.sheet:
88
+ Logger.note("No spreadsheet is currently opened. Please open or create a sheet first.", mode="short")
89
+ raise ValueError("No spreadsheet is currently opened. Please open or create a sheet first.")
90
+
91
+ try:
92
+ if tab_name is None:
93
+ tab_name = self.tab_names[0]
94
+ worksheet = self.sheet.worksheet(tab_name)
95
+ data = worksheet.get_all_values()
96
+ return data
97
+ except gspread.exceptions.WorksheetNotFound:
98
+ raise ValueError(f"Worksheet '{tab_name}' not found in spreadsheet '{self.sheet.title}'.")
99
+ except Exception as e:
100
+ Logger.note(f"Error retrieving data from worksheet '{tab_name}': {e}", mode="short")
101
+ raise
102
+
103
+ def tab_to_list(self, tab_name: str = None):
104
+ return self.get_tab_values(tab_name=tab_name)
105
+
106
+ @Cached()
107
+ def tab_to_flat_list(self, tab_name: str = None):
108
+ try:
109
+ data = self.get_tab_values(tab_name)
110
+ return [item for row in data for item in row]
111
+ except Exception as e:
112
+ Logger.note(f"Error converting tab values to list: {e}", mode="short")
113
+ raise
114
+
115
+ @Cached()
116
+ @Logger(mode="short")
117
+ def tab_to_dict(self, tab_name: str = None):
118
+ try:
119
+ data = self.get_tab_values(tab_name)
120
+ if len(data) < 2:
121
+ raise ValueError("Insufficient data to create a dictionary. Need at least headers and one row.")
122
+ headers = data[0]
123
+ rows = data[1:]
124
+ return [dict(zip(headers, row)) for row in rows]
125
+ except Exception as e:
126
+ Logger.note(f"Error converting tab values to dict: {e}", mode="short")
127
+ raise
128
+
129
+ @Cached()
130
+ @Logger(mode="short")
131
+ def tab_to_df(self, tab_name: str = None):
132
+ try:
133
+ data = self.get_tab_values(tab_name)
134
+ if len(data) < 2:
135
+ raise ValueError("Insufficient data to create a DataFrame. Need at least headers and one row.")
136
+ df = pd.DataFrame(data[1:], columns=data[0])
137
+ return df
138
+ except Exception as e:
139
+ Logger.note(f"Error converting tab values to DataFrame: {e}", mode="short")
140
+ raise
141
+
142
+ @Logger(mode="short")
143
+ def filter_rows_by_column(self, tab_name: str, column_name: str, pattern: str):
144
+ try:
145
+ df = self.tab_to_df(tab_name)
146
+ if column_name not in df.columns:
147
+ raise ValueError(f"Column '{column_name}' not found in the data.")
148
+ matching_rows = df[df[column_name].str.contains(pattern, na=False)]
149
+ return matching_rows
150
+ except Exception as e:
151
+ Logger.note(f"Error filtering rows by column '{column_name}': {e}", mode="short")
152
+ raise
153
+
154
+ @Logger(mode="short")
155
+ def update_row_by_column_pattern(self, tab_name: str, column_name: str, pattern: str, updates: dict):
156
+ try:
157
+ worksheet = self.sheet.worksheet(tab_name)
158
+ df = self.tab_to_df(tab_name)
159
+ if column_name not in df.columns:
160
+ raise ValueError(f"Column '{column_name}' not found in the data.")
161
+
162
+ # Find the first matching row index
163
+ matching_row_index = df[df[column_name].str.contains(pattern, na=False)].index[0]
164
+
165
+ # Add missing columns and apply updates
166
+ headers = df.columns.tolist()
167
+ row = worksheet.row_values(matching_row_index + 2) # Adjust for 1-based index and header row
168
+ while len(row) < len(headers):
169
+ row.append("")
170
+
171
+ # Update only the modified columns
172
+ for col, value in updates.items():
173
+ if col not in headers:
174
+ headers.append(col)
175
+ worksheet.update_cell(1, len(headers), col)
176
+ col_idx = headers.index(col) + 1
177
+ worksheet.update_cell(matching_row_index + 2, col_idx, value)
178
+
179
+ Logger.note(f"Row updated successfully in tab '{tab_name}'.", mode="short")
180
+
181
+ except Exception as e:
182
+ Logger.note(f"Error updating row by column pattern: {e}", mode="short")
183
+ raise
184
+
185
+
186
+ def tab_exists(self, tab_name: str) -> bool:
187
+ try:
188
+ # Attempt to get the worksheet by name
189
+ self.sheet.worksheet(tab_name)
190
+ return True
191
+ except gspread.exceptions.WorksheetNotFound:
192
+ return False
193
+
194
+
195
+ @Logger(mode="short")
196
+ def write_to_tab(self, data, tab_name: str, overwrite_tab: bool = False):
197
+ try:
198
+ if self.tab_exists(tab_name):
199
+ worksheet = self.sheet.worksheet(tab_name)
200
+ else:
201
+ Logger.note(f"Tab '{tab_name}' not found. Creating new tab.")
202
+ worksheet = self.sheet.add_worksheet(title=tab_name, rows=1000, cols=26)
203
+
204
+ # Prepare data
205
+ if isinstance(data, pd.DataFrame):
206
+ values = [data.columns.tolist()] + data.values.tolist()
207
+ elif isinstance(data, list) and all(isinstance(row, dict) for row in data):
208
+ keys = list(data[0].keys())
209
+ values = [keys] + [[row.get(k, "") for k in keys] for row in data]
210
+ elif isinstance(data, list) and all(isinstance(row, list) for row in data):
211
+ values = data
212
+ else:
213
+ raise ValueError("Unsupported data format. Provide a DataFrame, List of Lists, or List of Dicts.")
214
+
215
+ # Overwrite behavior
216
+ if overwrite_tab:
217
+ worksheet.clear()
218
+ worksheet.update(values)
219
+ else:
220
+ # Prepare range for the batch update
221
+ start_cell = 'A1'
222
+ end_cell = f'{chr(65 + len(values[0]) - 1)}{len(values)}' # Calculates range based on data size
223
+ worksheet.update(f'{start_cell}:{end_cell}', values)
224
+
225
+ Logger.note(f"Data written successfully to '{tab_name}'.",)
226
+
227
+ except Exception as e:
228
+ Logger.note(f"Error writing data to tab '{tab_name}': {e}")
229
+ raise
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.1
2
+ Name: smartspread
3
+ Version: 1.0.0
4
+ Summary: A Python library that extends gspread for enhanced spreadsheet manipulation.
5
+ Author-email: Arved Klöhn <arved.kloehn@gmail.com>
6
+ Project-URL: Homepage, https://github.com/Redundando/smart_spread
7
+ Project-URL: Repository, https://github.com/Redundando/smart_spread
8
+ Requires-Python: >=3.7
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: gspread>=5.0.0
11
+ Requires-Dist: pandas>=1.3.0
12
+ Requires-Dist: cacherator>=0.1.0
13
+ Requires-Dist: logorator>=0.1.0
14
+
15
+ # SmartSpread
16
+
17
+ SmartSpread is a Python library that extends the functionality of `gspread` for enhanced spreadsheet manipulation, enabling easier integration with Google Sheets.
18
+
19
+ ## Features
20
+ - Simplified spreadsheet creation and access.
21
+ - Easy tab (worksheet) management.
22
+ - Supports data handling with Pandas DataFrames, lists, and dictionaries.
23
+ - Batch updates and row-level modifications.
24
+ - Cached properties for optimized performance.
25
+
26
+ ## Installation
27
+ ```bash
28
+ pip install smartspread
29
+ ```
30
+
31
+ ## Requirements
32
+ - Python 3.7+
33
+ - `gspread`
34
+ - `pandas`
35
+ - `cacherator`
36
+ - `logorator`
37
+
38
+ ## Usage
39
+ ```python
40
+ from smartspread import SmartSpread
41
+
42
+ # Initialize
43
+ spread = SmartSpread(sheet_identifier="MySheet", key_file="path/to/google_service_account_keyfile.json")
44
+
45
+ # Write data
46
+ data = [["Name", "Age"], ["Alice", 30], ["Bob", 25]]
47
+ spread.write_to_tab(data, tab_name="Sheet1", overwrite_tab=True)
48
+
49
+ # Read data
50
+ df = spread.tab_to_df("Sheet1")
51
+ print(df)
52
+ ```
53
+
54
+ ## License
55
+ This project is licensed under the MIT License.
56
+
57
+ ## Contributing
58
+ Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
59
+
60
+ ## Author
61
+ Arved Klöhn - [Redundando](https://github.com/Redundando/)
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ smart_spread/__init__.py
4
+ smart_spread/smart_spread.py
5
+ smartspread.egg-info/PKG-INFO
6
+ smartspread.egg-info/SOURCES.txt
7
+ smartspread.egg-info/dependency_links.txt
8
+ smartspread.egg-info/requires.txt
9
+ smartspread.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ gspread>=5.0.0
2
+ pandas>=1.3.0
3
+ cacherator>=0.1.0
4
+ logorator>=0.1.0
@@ -0,0 +1 @@
1
+ smart_spread