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.
- smartspread-1.0.0/PKG-INFO +61 -0
- smartspread-1.0.0/README.md +47 -0
- smartspread-1.0.0/pyproject.toml +30 -0
- smartspread-1.0.0/setup.cfg +4 -0
- smartspread-1.0.0/smart_spread/__init__.py +1 -0
- smartspread-1.0.0/smart_spread/smart_spread.py +229 -0
- smartspread-1.0.0/smartspread.egg-info/PKG-INFO +61 -0
- smartspread-1.0.0/smartspread.egg-info/SOURCES.txt +9 -0
- smartspread-1.0.0/smartspread.egg-info/dependency_links.txt +1 -0
- smartspread-1.0.0/smartspread.egg-info/requires.txt +4 -0
- smartspread-1.0.0/smartspread.egg-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
smart_spread
|