modular 0.1.0__tar.gz → 0.2.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.
- {modular-0.1.0 → modular-0.2.0}/LICENSE +1 -1
- modular-0.2.0/PKG-INFO +216 -0
- modular-0.2.0/README.md +198 -0
- modular-0.2.0/modular/__init__.py +5 -0
- modular-0.2.0/modular/tables/__init__.py +9 -0
- modular-0.2.0/modular/tables/converters.py +82 -0
- modular-0.2.0/modular/tables/normalize.py +104 -0
- modular-0.2.0/modular/tables/table.py +37 -0
- modular-0.2.0/modular.egg-info/PKG-INFO +216 -0
- {modular-0.1.0 → modular-0.2.0}/modular.egg-info/SOURCES.txt +4 -1
- modular-0.2.0/modular.egg-info/requires.txt +4 -0
- {modular-0.1.0 → modular-0.2.0}/pyproject.toml +9 -3
- modular-0.1.0/PKG-INFO +0 -92
- modular-0.1.0/README.md +0 -77
- modular-0.1.0/modular/__init__.py +0 -5
- modular-0.1.0/modular/tables/__init__.py +0 -7
- modular-0.1.0/modular/tables/converters.py +0 -102
- modular-0.1.0/modular.egg-info/PKG-INFO +0 -92
- {modular-0.1.0 → modular-0.2.0}/modular.egg-info/dependency_links.txt +0 -0
- {modular-0.1.0 → modular-0.2.0}/modular.egg-info/top_level.txt +0 -0
- {modular-0.1.0 → modular-0.2.0}/setup.cfg +0 -0
modular-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modular
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A modular package for all sorts of things where you want choices
|
|
5
|
+
Author-email: Jeffrey Spies <code@jeffspies.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/221b-io/modular
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/221b-io/modular/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
16
|
+
Requires-Dist: pytest-cov>=4.0; extra == "test"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# Modular
|
|
20
|
+
|
|
21
|
+
The code you will see here will be a set of two sorts:
|
|
22
|
+
- utilities to abstract core functionality (e.g., document/object storage) in such a way that said functionality can be referred to in a standardized, serializable way, while handling the implementation-specific stuff (e.g., MongoDB, postgres)--including versioning--as purely config that exists far-removed from the code
|
|
23
|
+
- some handy tools crafted in a standardized, serializable way, like the `tables` module below, that will also serve as examples of the approach
|
|
24
|
+
|
|
25
|
+
Why modular?
|
|
26
|
+
- It helps devs write clean, readable, configurable code.
|
|
27
|
+
- Actions are easily loggable--something taken for granted in highly asynchronous codebases.
|
|
28
|
+
- Because code is inherently configurable, environment management is much simpler.
|
|
29
|
+
- You can help avoid lock-in, either because you want to choose the best performing tool for the job and easily switch as needed or because e.g., pricing, license, priorities, or principles of a tool changes.
|
|
30
|
+
|
|
31
|
+
Use this a dependency injection (DI) framework? Yes and no. But yes.
|
|
32
|
+
|
|
33
|
+
## History
|
|
34
|
+
|
|
35
|
+
I brought two of these into my dissertation project, which then became the flagship product of the non-profit I co-founded: the Open Science Framework. These were [`modular-odm`](https://github.com/cos-archives/modular-odm) and [`modular-file renderer`](https://github.com/CenterForOpenScience/modular-file-renderer). The latter is still used today; the former, while used heavily at COS, never reached its full potential. That may now change. The goal was to use abstraction to maximize choice and minimize lock-in.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install modular
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
### Tables Module
|
|
46
|
+
|
|
47
|
+
The `tables` module provides a flexible system for normalizing and formatting tabular data. It consists of three main components:
|
|
48
|
+
|
|
49
|
+
1. **Data Normalization** (`normalize_table`): Converts various input formats into a standardized table structure
|
|
50
|
+
2. **Format Converters**: Built-in formatters for common output formats
|
|
51
|
+
3. **High-level Table Function**: Combines normalization and formatting in one step
|
|
52
|
+
|
|
53
|
+
#### Supported Formats:
|
|
54
|
+
- Delimited (CSV, TSV, etc.)
|
|
55
|
+
- Markdown
|
|
56
|
+
- reStructuredText
|
|
57
|
+
- Custom formats through formatter functions
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
### Basic Usage
|
|
62
|
+
|
|
63
|
+
The simplest way to create formatted tables is using the `table` function:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from modular.tables import table, markdown, delimited, rst
|
|
67
|
+
|
|
68
|
+
data = [
|
|
69
|
+
{"name": "Alice", "age": 30, "salary": 50000.123},
|
|
70
|
+
{"name": "Bob", "age": 25, "salary": 60000.456}
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# Convert to Markdown
|
|
74
|
+
md_table = table(data, formatter=markdown)
|
|
75
|
+
|
|
76
|
+
# Convert to CSV
|
|
77
|
+
csv_table = table(data, formatter=delimited)
|
|
78
|
+
|
|
79
|
+
# Convert to TSV
|
|
80
|
+
tsv_table = table(data, formatter=lambda t: delimited(t, delimiter='\t'))
|
|
81
|
+
|
|
82
|
+
# Convert to reStructuredText
|
|
83
|
+
rst_table = table(data, formatter=rst)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Advanced Features
|
|
87
|
+
|
|
88
|
+
#### Column Ordering
|
|
89
|
+
|
|
90
|
+
Control the order of columns in the output:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
md_table = table(
|
|
94
|
+
data,
|
|
95
|
+
formatter=markdown,
|
|
96
|
+
order=['name', 'city', 'age']
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Value Formatting
|
|
101
|
+
|
|
102
|
+
Apply format strings to specific columns or set a default format for all numeric values:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Format specific columns
|
|
106
|
+
md_table = table(
|
|
107
|
+
data,
|
|
108
|
+
formatter=markdown,
|
|
109
|
+
cell_formats=['{:s}', '{:d}', '${:.2f}'] # string, integer, currency format
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Set default number format for all numeric values
|
|
113
|
+
md_table = table(
|
|
114
|
+
data,
|
|
115
|
+
formatter=markdown,
|
|
116
|
+
default_number_format='{:.3f}' # 3 decimal places for all numbers
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Combine both
|
|
120
|
+
md_table = table(
|
|
121
|
+
data,
|
|
122
|
+
formatter=markdown,
|
|
123
|
+
cell_formats=['{:s}', None, '${:.2f}'], # Use default_number_format where None
|
|
124
|
+
default_number_format='{:.3f}'
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Custom Headers
|
|
129
|
+
|
|
130
|
+
Specify custom column headers:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
data = [
|
|
134
|
+
['Alice', 30, 50000.123],
|
|
135
|
+
['Bob', 25, 60000.456]
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
md_table = table(
|
|
139
|
+
data,
|
|
140
|
+
formatter=markdown,
|
|
141
|
+
header=['Name', 'Age', 'Salary']
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Custom Formatters
|
|
146
|
+
|
|
147
|
+
Create your own formatters for custom output formats:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
def html_formatter(table):
|
|
151
|
+
if not table['data']:
|
|
152
|
+
return ""
|
|
153
|
+
|
|
154
|
+
html = "<table>\n"
|
|
155
|
+
# Header row
|
|
156
|
+
html += " <tr>\n"
|
|
157
|
+
for header in table['header']:
|
|
158
|
+
html += f" <th>{header}</th>\n"
|
|
159
|
+
html += " </tr>\n"
|
|
160
|
+
# Data rows
|
|
161
|
+
for row in table['data']:
|
|
162
|
+
html += " <tr>\n"
|
|
163
|
+
for cell in row:
|
|
164
|
+
html += f" <td>{cell}</td>\n"
|
|
165
|
+
html += " </tr>\n"
|
|
166
|
+
html += "</table>"
|
|
167
|
+
return html
|
|
168
|
+
|
|
169
|
+
html_table = table(data, formatter=html_formatter)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Low-level API
|
|
173
|
+
|
|
174
|
+
If you need more control, you can use the lower-level functions directly:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from modular.tables import normalize_table, markdown
|
|
178
|
+
|
|
179
|
+
# First normalize the data
|
|
180
|
+
normalized = normalize_table(
|
|
181
|
+
data,
|
|
182
|
+
header=['Name', 'Age', 'Salary'],
|
|
183
|
+
order=['Name', 'Salary', 'Age'],
|
|
184
|
+
cell_formats=['{:s}', '{:d}', '{:.2f}']
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Then convert to desired format
|
|
188
|
+
md_table = markdown(normalized)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
To install the package in development mode:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
git clone https://github.com/yourusername/modular.git
|
|
197
|
+
cd modular
|
|
198
|
+
pip install -e .
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Running Tests
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Run all tests
|
|
205
|
+
pytest tests/tables/
|
|
206
|
+
|
|
207
|
+
# Run specific test file
|
|
208
|
+
pytest tests/tables/test_table.py
|
|
209
|
+
|
|
210
|
+
# Run specific test class
|
|
211
|
+
pytest tests/tables/test_table.py::TestBuiltinFormatters
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
modular-0.2.0/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Modular
|
|
2
|
+
|
|
3
|
+
The code you will see here will be a set of two sorts:
|
|
4
|
+
- utilities to abstract core functionality (e.g., document/object storage) in such a way that said functionality can be referred to in a standardized, serializable way, while handling the implementation-specific stuff (e.g., MongoDB, postgres)--including versioning--as purely config that exists far-removed from the code
|
|
5
|
+
- some handy tools crafted in a standardized, serializable way, like the `tables` module below, that will also serve as examples of the approach
|
|
6
|
+
|
|
7
|
+
Why modular?
|
|
8
|
+
- It helps devs write clean, readable, configurable code.
|
|
9
|
+
- Actions are easily loggable--something taken for granted in highly asynchronous codebases.
|
|
10
|
+
- Because code is inherently configurable, environment management is much simpler.
|
|
11
|
+
- You can help avoid lock-in, either because you want to choose the best performing tool for the job and easily switch as needed or because e.g., pricing, license, priorities, or principles of a tool changes.
|
|
12
|
+
|
|
13
|
+
Use this a dependency injection (DI) framework? Yes and no. But yes.
|
|
14
|
+
|
|
15
|
+
## History
|
|
16
|
+
|
|
17
|
+
I brought two of these into my dissertation project, which then became the flagship product of the non-profit I co-founded: the Open Science Framework. These were [`modular-odm`](https://github.com/cos-archives/modular-odm) and [`modular-file renderer`](https://github.com/CenterForOpenScience/modular-file-renderer). The latter is still used today; the former, while used heavily at COS, never reached its full potential. That may now change. The goal was to use abstraction to maximize choice and minimize lock-in.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install modular
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
### Tables Module
|
|
28
|
+
|
|
29
|
+
The `tables` module provides a flexible system for normalizing and formatting tabular data. It consists of three main components:
|
|
30
|
+
|
|
31
|
+
1. **Data Normalization** (`normalize_table`): Converts various input formats into a standardized table structure
|
|
32
|
+
2. **Format Converters**: Built-in formatters for common output formats
|
|
33
|
+
3. **High-level Table Function**: Combines normalization and formatting in one step
|
|
34
|
+
|
|
35
|
+
#### Supported Formats:
|
|
36
|
+
- Delimited (CSV, TSV, etc.)
|
|
37
|
+
- Markdown
|
|
38
|
+
- reStructuredText
|
|
39
|
+
- Custom formats through formatter functions
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
The simplest way to create formatted tables is using the `table` function:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from modular.tables import table, markdown, delimited, rst
|
|
49
|
+
|
|
50
|
+
data = [
|
|
51
|
+
{"name": "Alice", "age": 30, "salary": 50000.123},
|
|
52
|
+
{"name": "Bob", "age": 25, "salary": 60000.456}
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
# Convert to Markdown
|
|
56
|
+
md_table = table(data, formatter=markdown)
|
|
57
|
+
|
|
58
|
+
# Convert to CSV
|
|
59
|
+
csv_table = table(data, formatter=delimited)
|
|
60
|
+
|
|
61
|
+
# Convert to TSV
|
|
62
|
+
tsv_table = table(data, formatter=lambda t: delimited(t, delimiter='\t'))
|
|
63
|
+
|
|
64
|
+
# Convert to reStructuredText
|
|
65
|
+
rst_table = table(data, formatter=rst)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Advanced Features
|
|
69
|
+
|
|
70
|
+
#### Column Ordering
|
|
71
|
+
|
|
72
|
+
Control the order of columns in the output:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
md_table = table(
|
|
76
|
+
data,
|
|
77
|
+
formatter=markdown,
|
|
78
|
+
order=['name', 'city', 'age']
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Value Formatting
|
|
83
|
+
|
|
84
|
+
Apply format strings to specific columns or set a default format for all numeric values:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# Format specific columns
|
|
88
|
+
md_table = table(
|
|
89
|
+
data,
|
|
90
|
+
formatter=markdown,
|
|
91
|
+
cell_formats=['{:s}', '{:d}', '${:.2f}'] # string, integer, currency format
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Set default number format for all numeric values
|
|
95
|
+
md_table = table(
|
|
96
|
+
data,
|
|
97
|
+
formatter=markdown,
|
|
98
|
+
default_number_format='{:.3f}' # 3 decimal places for all numbers
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Combine both
|
|
102
|
+
md_table = table(
|
|
103
|
+
data,
|
|
104
|
+
formatter=markdown,
|
|
105
|
+
cell_formats=['{:s}', None, '${:.2f}'], # Use default_number_format where None
|
|
106
|
+
default_number_format='{:.3f}'
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Custom Headers
|
|
111
|
+
|
|
112
|
+
Specify custom column headers:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
data = [
|
|
116
|
+
['Alice', 30, 50000.123],
|
|
117
|
+
['Bob', 25, 60000.456]
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
md_table = table(
|
|
121
|
+
data,
|
|
122
|
+
formatter=markdown,
|
|
123
|
+
header=['Name', 'Age', 'Salary']
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Custom Formatters
|
|
128
|
+
|
|
129
|
+
Create your own formatters for custom output formats:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
def html_formatter(table):
|
|
133
|
+
if not table['data']:
|
|
134
|
+
return ""
|
|
135
|
+
|
|
136
|
+
html = "<table>\n"
|
|
137
|
+
# Header row
|
|
138
|
+
html += " <tr>\n"
|
|
139
|
+
for header in table['header']:
|
|
140
|
+
html += f" <th>{header}</th>\n"
|
|
141
|
+
html += " </tr>\n"
|
|
142
|
+
# Data rows
|
|
143
|
+
for row in table['data']:
|
|
144
|
+
html += " <tr>\n"
|
|
145
|
+
for cell in row:
|
|
146
|
+
html += f" <td>{cell}</td>\n"
|
|
147
|
+
html += " </tr>\n"
|
|
148
|
+
html += "</table>"
|
|
149
|
+
return html
|
|
150
|
+
|
|
151
|
+
html_table = table(data, formatter=html_formatter)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Low-level API
|
|
155
|
+
|
|
156
|
+
If you need more control, you can use the lower-level functions directly:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from modular.tables import normalize_table, markdown
|
|
160
|
+
|
|
161
|
+
# First normalize the data
|
|
162
|
+
normalized = normalize_table(
|
|
163
|
+
data,
|
|
164
|
+
header=['Name', 'Age', 'Salary'],
|
|
165
|
+
order=['Name', 'Salary', 'Age'],
|
|
166
|
+
cell_formats=['{:s}', '{:d}', '{:.2f}']
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Then convert to desired format
|
|
170
|
+
md_table = markdown(normalized)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Development
|
|
174
|
+
|
|
175
|
+
To install the package in development mode:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
git clone https://github.com/yourusername/modular.git
|
|
179
|
+
cd modular
|
|
180
|
+
pip install -e .
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Running Tests
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Run all tests
|
|
187
|
+
pytest tests/tables/
|
|
188
|
+
|
|
189
|
+
# Run specific test file
|
|
190
|
+
pytest tests/tables/test_table.py
|
|
191
|
+
|
|
192
|
+
# Run specific test class
|
|
193
|
+
pytest tests/tables/test_table.py::TestBuiltinFormatters
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Table conversion functions for various formats.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List, Any
|
|
6
|
+
import csv
|
|
7
|
+
from io import StringIO
|
|
8
|
+
|
|
9
|
+
def delimited(table: Dict[str, List], delimiter: str = ',') -> str:
|
|
10
|
+
"""
|
|
11
|
+
Convert normalized table data to a delimited string format.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
table: Dictionary with 'header' and 'data' keys from normalize_table
|
|
15
|
+
delimiter: Character to use as delimiter (default: ',')
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
String containing the delimited data
|
|
19
|
+
"""
|
|
20
|
+
if not table['data']:
|
|
21
|
+
return ""
|
|
22
|
+
|
|
23
|
+
output = StringIO(newline='') # Set newline='' to control line endings
|
|
24
|
+
writer = csv.writer(output, delimiter=delimiter, lineterminator='\n') # Explicitly set line endings
|
|
25
|
+
writer.writerow(table['header'])
|
|
26
|
+
writer.writerows(table['data'])
|
|
27
|
+
return output.getvalue()
|
|
28
|
+
|
|
29
|
+
def markdown(table: Dict[str, List]) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Convert normalized table data to Markdown table format.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
table: Dictionary with 'header' and 'data' keys from normalize_table
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
String containing the Markdown table
|
|
38
|
+
"""
|
|
39
|
+
if not table['data']:
|
|
40
|
+
return ""
|
|
41
|
+
|
|
42
|
+
# Create header row
|
|
43
|
+
markdown = "| " + " | ".join(str(h) for h in table['header']) + " |\n"
|
|
44
|
+
# Create separator row
|
|
45
|
+
markdown += "| " + " | ".join(["---"] * len(table['header'])) + " |\n"
|
|
46
|
+
# Add data rows
|
|
47
|
+
for row in table['data']:
|
|
48
|
+
markdown += "| " + " | ".join(str(cell) for cell in row) + " |\n"
|
|
49
|
+
|
|
50
|
+
return markdown
|
|
51
|
+
|
|
52
|
+
def rst(table: Dict[str, List]) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Convert normalized table data to reStructuredText table format.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
table: Dictionary with 'header' and 'data' keys from normalize_table
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
String containing the reStructuredText table
|
|
61
|
+
"""
|
|
62
|
+
if not table['data']:
|
|
63
|
+
return ""
|
|
64
|
+
|
|
65
|
+
# Calculate column widths
|
|
66
|
+
headers = [str(h) for h in table['header']]
|
|
67
|
+
data = [[str(cell) for cell in row] for row in table['data']]
|
|
68
|
+
|
|
69
|
+
col_widths = [max(len(str(cell)) for cell in col)
|
|
70
|
+
for col in zip(headers, *data)]
|
|
71
|
+
|
|
72
|
+
# Create header row
|
|
73
|
+
rst = "+" + "+".join("-" * (width + 2) for width in col_widths) + "+\n"
|
|
74
|
+
rst += "|" + "|".join(f" {header:<{width}} " for header, width in zip(headers, col_widths)) + "|\n"
|
|
75
|
+
rst += "+" + "+".join("=" * (width + 2) for width in col_widths) + "+\n"
|
|
76
|
+
|
|
77
|
+
# Add data rows
|
|
78
|
+
for row in data:
|
|
79
|
+
rst += "|" + "|".join(f" {cell:<{width}} " for cell, width in zip(row, col_widths)) + "|\n"
|
|
80
|
+
rst += "+" + "+".join("-" * (width + 2) for width in col_widths) + "+\n"
|
|
81
|
+
|
|
82
|
+
return rst
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from typing import List, Dict, Union, Optional, Any
|
|
2
|
+
import numbers
|
|
3
|
+
|
|
4
|
+
def normalize_table(
|
|
5
|
+
data: Union[List[Dict[str, Any]], List[List[Any]]],
|
|
6
|
+
header: Optional[List[str]] = None,
|
|
7
|
+
order: Optional[List[str]] = None,
|
|
8
|
+
cell_formats: Optional[List[str]] = None,
|
|
9
|
+
default_number_format: str = "{:.2f}"
|
|
10
|
+
) -> Dict[str, List]:
|
|
11
|
+
"""
|
|
12
|
+
Normalize different data formats into a consistent table structure with formatting options.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
data: List of dictionaries or list of lists containing the table data
|
|
16
|
+
header: Optional list of column names. If None and data is list of dicts,
|
|
17
|
+
will use dict keys as header
|
|
18
|
+
order: Optional list of column names/keys to specify column order
|
|
19
|
+
cell_formats: Optional list of format strings to apply to each column
|
|
20
|
+
default_number_format: Format string to apply to numeric values when no
|
|
21
|
+
cell_formats specified
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Dictionary with keys:
|
|
25
|
+
'data': List of lists containing the normalized table data
|
|
26
|
+
'header': List of column names
|
|
27
|
+
"""
|
|
28
|
+
if not data:
|
|
29
|
+
return {'data': [], 'header': []}
|
|
30
|
+
|
|
31
|
+
# Handle list of dictionaries
|
|
32
|
+
if isinstance(data[0], dict):
|
|
33
|
+
if header is None:
|
|
34
|
+
# Use keys from first dictionary to establish initial order
|
|
35
|
+
header = list(data[0].keys())
|
|
36
|
+
# Add any additional keys from other dictionaries
|
|
37
|
+
for d in data[1:]:
|
|
38
|
+
for key in d.keys():
|
|
39
|
+
if key not in header:
|
|
40
|
+
header.append(key)
|
|
41
|
+
|
|
42
|
+
# Convert dicts to lists based on header order
|
|
43
|
+
table_data = [
|
|
44
|
+
[row.get(col, '') for col in header]
|
|
45
|
+
for row in data
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
# Handle list of lists
|
|
49
|
+
else:
|
|
50
|
+
if header is None:
|
|
51
|
+
# Generate numeric headers
|
|
52
|
+
header = [str(i) for i in range(len(data[0]))]
|
|
53
|
+
table_data = list(data) # Make a copy to avoid modifying input
|
|
54
|
+
|
|
55
|
+
# Apply ordering if specified
|
|
56
|
+
if order:
|
|
57
|
+
# Create mapping of current positions
|
|
58
|
+
current_positions = {h: i for i, h in enumerate(header)}
|
|
59
|
+
|
|
60
|
+
# Create new header order
|
|
61
|
+
new_header = []
|
|
62
|
+
# First add specified columns in order
|
|
63
|
+
for h in order:
|
|
64
|
+
if h in header:
|
|
65
|
+
new_header.append(h)
|
|
66
|
+
# Then add any remaining columns
|
|
67
|
+
new_header.extend(h for h in header if h not in new_header)
|
|
68
|
+
|
|
69
|
+
# Create mapping for reordering data
|
|
70
|
+
reorder_indices = [current_positions[h] for h in new_header]
|
|
71
|
+
|
|
72
|
+
# Reorder the data
|
|
73
|
+
table_data = [
|
|
74
|
+
[row[i] for i in reorder_indices]
|
|
75
|
+
for row in table_data
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
header = new_header
|
|
79
|
+
|
|
80
|
+
# Apply formatting
|
|
81
|
+
if cell_formats or default_number_format != "{:.2f}": # Apply if cell_formats provided or custom default_number_format
|
|
82
|
+
# Use cell_formats if provided, otherwise use default_number_format for all columns
|
|
83
|
+
formats = cell_formats if cell_formats else [default_number_format] * len(header)
|
|
84
|
+
# Ensure formats matches number of columns
|
|
85
|
+
formats = (formats + [default_number_format] * len(header))[:len(header)]
|
|
86
|
+
|
|
87
|
+
formatted_data = []
|
|
88
|
+
for row in table_data:
|
|
89
|
+
formatted_row = []
|
|
90
|
+
for value, fmt in zip(row, formats):
|
|
91
|
+
if isinstance(value, numbers.Number):
|
|
92
|
+
try:
|
|
93
|
+
formatted_row.append(fmt.format(value))
|
|
94
|
+
except ValueError:
|
|
95
|
+
formatted_row.append(default_number_format.format(value))
|
|
96
|
+
else:
|
|
97
|
+
formatted_row.append(str(value))
|
|
98
|
+
formatted_data.append(formatted_row)
|
|
99
|
+
table_data = formatted_data
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
'data': table_data,
|
|
103
|
+
'header': header
|
|
104
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import List, Dict, Union, Optional, Any, Callable
|
|
2
|
+
import numbers
|
|
3
|
+
from .normalize import normalize_table
|
|
4
|
+
|
|
5
|
+
def table(
|
|
6
|
+
data: Union[List[Dict[str, Any]], List[List[Any]]],
|
|
7
|
+
formatter: Callable[[Dict[str, List]], str],
|
|
8
|
+
header: Optional[List[str]] = None,
|
|
9
|
+
order: Optional[List[str]] = None,
|
|
10
|
+
cell_formats: Optional[List[str]] = None,
|
|
11
|
+
default_number_format: str = "{:.2f}"
|
|
12
|
+
) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Create a formatted table from input data.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
data: List of dictionaries or list of lists containing the table data
|
|
18
|
+
formatter: Function that takes a normalized table dict and returns formatted string
|
|
19
|
+
header: Optional list of column names. If None and data is list of dicts,
|
|
20
|
+
will use dict keys as header
|
|
21
|
+
order: Optional list of column names/keys to specify column order
|
|
22
|
+
cell_formats: Optional list of format strings to apply to each column
|
|
23
|
+
default_number_format: Format string to apply to numeric values when no
|
|
24
|
+
cell_formats specified
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
String containing the formatted table
|
|
28
|
+
"""
|
|
29
|
+
normalized = normalize_table(
|
|
30
|
+
data=data,
|
|
31
|
+
header=header,
|
|
32
|
+
order=order,
|
|
33
|
+
cell_formats=cell_formats,
|
|
34
|
+
default_number_format=default_number_format
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return formatter(normalized)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modular
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A modular package for all sorts of things where you want choices
|
|
5
|
+
Author-email: Jeffrey Spies <code@jeffspies.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/221b-io/modular
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/221b-io/modular/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
16
|
+
Requires-Dist: pytest-cov>=4.0; extra == "test"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# Modular
|
|
20
|
+
|
|
21
|
+
The code you will see here will be a set of two sorts:
|
|
22
|
+
- utilities to abstract core functionality (e.g., document/object storage) in such a way that said functionality can be referred to in a standardized, serializable way, while handling the implementation-specific stuff (e.g., MongoDB, postgres)--including versioning--as purely config that exists far-removed from the code
|
|
23
|
+
- some handy tools crafted in a standardized, serializable way, like the `tables` module below, that will also serve as examples of the approach
|
|
24
|
+
|
|
25
|
+
Why modular?
|
|
26
|
+
- It helps devs write clean, readable, configurable code.
|
|
27
|
+
- Actions are easily loggable--something taken for granted in highly asynchronous codebases.
|
|
28
|
+
- Because code is inherently configurable, environment management is much simpler.
|
|
29
|
+
- You can help avoid lock-in, either because you want to choose the best performing tool for the job and easily switch as needed or because e.g., pricing, license, priorities, or principles of a tool changes.
|
|
30
|
+
|
|
31
|
+
Use this a dependency injection (DI) framework? Yes and no. But yes.
|
|
32
|
+
|
|
33
|
+
## History
|
|
34
|
+
|
|
35
|
+
I brought two of these into my dissertation project, which then became the flagship product of the non-profit I co-founded: the Open Science Framework. These were [`modular-odm`](https://github.com/cos-archives/modular-odm) and [`modular-file renderer`](https://github.com/CenterForOpenScience/modular-file-renderer). The latter is still used today; the former, while used heavily at COS, never reached its full potential. That may now change. The goal was to use abstraction to maximize choice and minimize lock-in.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install modular
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
### Tables Module
|
|
46
|
+
|
|
47
|
+
The `tables` module provides a flexible system for normalizing and formatting tabular data. It consists of three main components:
|
|
48
|
+
|
|
49
|
+
1. **Data Normalization** (`normalize_table`): Converts various input formats into a standardized table structure
|
|
50
|
+
2. **Format Converters**: Built-in formatters for common output formats
|
|
51
|
+
3. **High-level Table Function**: Combines normalization and formatting in one step
|
|
52
|
+
|
|
53
|
+
#### Supported Formats:
|
|
54
|
+
- Delimited (CSV, TSV, etc.)
|
|
55
|
+
- Markdown
|
|
56
|
+
- reStructuredText
|
|
57
|
+
- Custom formats through formatter functions
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
### Basic Usage
|
|
62
|
+
|
|
63
|
+
The simplest way to create formatted tables is using the `table` function:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from modular.tables import table, markdown, delimited, rst
|
|
67
|
+
|
|
68
|
+
data = [
|
|
69
|
+
{"name": "Alice", "age": 30, "salary": 50000.123},
|
|
70
|
+
{"name": "Bob", "age": 25, "salary": 60000.456}
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# Convert to Markdown
|
|
74
|
+
md_table = table(data, formatter=markdown)
|
|
75
|
+
|
|
76
|
+
# Convert to CSV
|
|
77
|
+
csv_table = table(data, formatter=delimited)
|
|
78
|
+
|
|
79
|
+
# Convert to TSV
|
|
80
|
+
tsv_table = table(data, formatter=lambda t: delimited(t, delimiter='\t'))
|
|
81
|
+
|
|
82
|
+
# Convert to reStructuredText
|
|
83
|
+
rst_table = table(data, formatter=rst)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Advanced Features
|
|
87
|
+
|
|
88
|
+
#### Column Ordering
|
|
89
|
+
|
|
90
|
+
Control the order of columns in the output:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
md_table = table(
|
|
94
|
+
data,
|
|
95
|
+
formatter=markdown,
|
|
96
|
+
order=['name', 'city', 'age']
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Value Formatting
|
|
101
|
+
|
|
102
|
+
Apply format strings to specific columns or set a default format for all numeric values:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Format specific columns
|
|
106
|
+
md_table = table(
|
|
107
|
+
data,
|
|
108
|
+
formatter=markdown,
|
|
109
|
+
cell_formats=['{:s}', '{:d}', '${:.2f}'] # string, integer, currency format
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Set default number format for all numeric values
|
|
113
|
+
md_table = table(
|
|
114
|
+
data,
|
|
115
|
+
formatter=markdown,
|
|
116
|
+
default_number_format='{:.3f}' # 3 decimal places for all numbers
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Combine both
|
|
120
|
+
md_table = table(
|
|
121
|
+
data,
|
|
122
|
+
formatter=markdown,
|
|
123
|
+
cell_formats=['{:s}', None, '${:.2f}'], # Use default_number_format where None
|
|
124
|
+
default_number_format='{:.3f}'
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Custom Headers
|
|
129
|
+
|
|
130
|
+
Specify custom column headers:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
data = [
|
|
134
|
+
['Alice', 30, 50000.123],
|
|
135
|
+
['Bob', 25, 60000.456]
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
md_table = table(
|
|
139
|
+
data,
|
|
140
|
+
formatter=markdown,
|
|
141
|
+
header=['Name', 'Age', 'Salary']
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Custom Formatters
|
|
146
|
+
|
|
147
|
+
Create your own formatters for custom output formats:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
def html_formatter(table):
|
|
151
|
+
if not table['data']:
|
|
152
|
+
return ""
|
|
153
|
+
|
|
154
|
+
html = "<table>\n"
|
|
155
|
+
# Header row
|
|
156
|
+
html += " <tr>\n"
|
|
157
|
+
for header in table['header']:
|
|
158
|
+
html += f" <th>{header}</th>\n"
|
|
159
|
+
html += " </tr>\n"
|
|
160
|
+
# Data rows
|
|
161
|
+
for row in table['data']:
|
|
162
|
+
html += " <tr>\n"
|
|
163
|
+
for cell in row:
|
|
164
|
+
html += f" <td>{cell}</td>\n"
|
|
165
|
+
html += " </tr>\n"
|
|
166
|
+
html += "</table>"
|
|
167
|
+
return html
|
|
168
|
+
|
|
169
|
+
html_table = table(data, formatter=html_formatter)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Low-level API
|
|
173
|
+
|
|
174
|
+
If you need more control, you can use the lower-level functions directly:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from modular.tables import normalize_table, markdown
|
|
178
|
+
|
|
179
|
+
# First normalize the data
|
|
180
|
+
normalized = normalize_table(
|
|
181
|
+
data,
|
|
182
|
+
header=['Name', 'Age', 'Salary'],
|
|
183
|
+
order=['Name', 'Salary', 'Age'],
|
|
184
|
+
cell_formats=['{:s}', '{:d}', '{:.2f}']
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Then convert to desired format
|
|
188
|
+
md_table = markdown(normalized)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
To install the package in development mode:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
git clone https://github.com/yourusername/modular.git
|
|
197
|
+
cd modular
|
|
198
|
+
pip install -e .
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Running Tests
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Run all tests
|
|
205
|
+
pytest tests/tables/
|
|
206
|
+
|
|
207
|
+
# Run specific test file
|
|
208
|
+
pytest tests/tables/test_table.py
|
|
209
|
+
|
|
210
|
+
# Run specific test class
|
|
211
|
+
pytest tests/tables/test_table.py::TestBuiltinFormatters
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -5,6 +5,9 @@ modular/__init__.py
|
|
|
5
5
|
modular.egg-info/PKG-INFO
|
|
6
6
|
modular.egg-info/SOURCES.txt
|
|
7
7
|
modular.egg-info/dependency_links.txt
|
|
8
|
+
modular.egg-info/requires.txt
|
|
8
9
|
modular.egg-info/top_level.txt
|
|
9
10
|
modular/tables/__init__.py
|
|
10
|
-
modular/tables/converters.py
|
|
11
|
+
modular/tables/converters.py
|
|
12
|
+
modular/tables/normalize.py
|
|
13
|
+
modular/tables/table.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "modular"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "Jeffrey Spies", email = "code@jeffspies.com" },
|
|
10
10
|
]
|
|
@@ -18,5 +18,11 @@ classifiers = [
|
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
[project.urls]
|
|
21
|
-
"Homepage" = "https://github.com/
|
|
22
|
-
"Bug Tracker" = "https://github.com/
|
|
21
|
+
"Homepage" = "https://github.com/221b-io/modular"
|
|
22
|
+
"Bug Tracker" = "https://github.com/221b-io/modular/issues"
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
test = [
|
|
26
|
+
"pytest>=7.0",
|
|
27
|
+
"pytest-cov>=4.0",
|
|
28
|
+
]
|
modular-0.1.0/PKG-INFO
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: modular
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: A modular package for all sorts of things where you want choices
|
|
5
|
-
Author-email: Jeffrey Spies <code@jeffspies.com>
|
|
6
|
-
Project-URL: Homepage, https://github.com/yourusername/modular
|
|
7
|
-
Project-URL: Bug Tracker, https://github.com/yourusername/modular/issues
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.7
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Dynamic: license-file
|
|
15
|
-
|
|
16
|
-
# Modular
|
|
17
|
-
|
|
18
|
-
A Python package for various data processing utilities with a focus on modularity and choice.
|
|
19
|
-
|
|
20
|
-
## Installation
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
pip install modular
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Features
|
|
27
|
-
|
|
28
|
-
### Tables Module
|
|
29
|
-
|
|
30
|
-
The `tables` module provides functions to convert data into various table formats.
|
|
31
|
-
|
|
32
|
-
#### Supported Formats:
|
|
33
|
-
- Delimited (CSV, TSV, etc.)
|
|
34
|
-
- Markdown
|
|
35
|
-
- reStructuredText
|
|
36
|
-
|
|
37
|
-
## Usage
|
|
38
|
-
|
|
39
|
-
### Converting Data to Different Formats
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
from modular.tables import to_delimited, to_markdown, to_rst
|
|
43
|
-
|
|
44
|
-
# Example data as a list of dictionaries
|
|
45
|
-
data_dict = [
|
|
46
|
-
{"name": "Alice", "age": 30, "city": "New York"},
|
|
47
|
-
{"name": "Bob", "age": 25, "city": "Los Angeles"},
|
|
48
|
-
{"name": "Charlie", "age": 35, "city": "Chicago"}
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
# Convert to CSV
|
|
52
|
-
csv_output = to_delimited(data_dict)
|
|
53
|
-
print(csv_output)
|
|
54
|
-
|
|
55
|
-
# Convert to TSV
|
|
56
|
-
tsv_output = to_delimited(data_dict, delimiter='\t')
|
|
57
|
-
print(tsv_output)
|
|
58
|
-
|
|
59
|
-
# Convert to Markdown
|
|
60
|
-
md_output = to_markdown(data_dict)
|
|
61
|
-
print(md_output)
|
|
62
|
-
|
|
63
|
-
# Convert to reStructuredText
|
|
64
|
-
rst_output = to_rst(data_dict)
|
|
65
|
-
print(rst_output)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
You can also use lists of lists:
|
|
69
|
-
|
|
70
|
-
```python
|
|
71
|
-
data_list = [
|
|
72
|
-
["Alice", 30, "New York"],
|
|
73
|
-
["Bob", 25, "Los Angeles"],
|
|
74
|
-
["Charlie", 35, "Chicago"]
|
|
75
|
-
]
|
|
76
|
-
|
|
77
|
-
# Convert to any format as shown above
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Development
|
|
81
|
-
|
|
82
|
-
To install the package in development mode:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
git clone https://github.com/yourusername/modular.git
|
|
86
|
-
cd modular
|
|
87
|
-
pip install -e .
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## License
|
|
91
|
-
|
|
92
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
modular-0.1.0/README.md
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# Modular
|
|
2
|
-
|
|
3
|
-
A Python package for various data processing utilities with a focus on modularity and choice.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pip install modular
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Features
|
|
12
|
-
|
|
13
|
-
### Tables Module
|
|
14
|
-
|
|
15
|
-
The `tables` module provides functions to convert data into various table formats.
|
|
16
|
-
|
|
17
|
-
#### Supported Formats:
|
|
18
|
-
- Delimited (CSV, TSV, etc.)
|
|
19
|
-
- Markdown
|
|
20
|
-
- reStructuredText
|
|
21
|
-
|
|
22
|
-
## Usage
|
|
23
|
-
|
|
24
|
-
### Converting Data to Different Formats
|
|
25
|
-
|
|
26
|
-
```python
|
|
27
|
-
from modular.tables import to_delimited, to_markdown, to_rst
|
|
28
|
-
|
|
29
|
-
# Example data as a list of dictionaries
|
|
30
|
-
data_dict = [
|
|
31
|
-
{"name": "Alice", "age": 30, "city": "New York"},
|
|
32
|
-
{"name": "Bob", "age": 25, "city": "Los Angeles"},
|
|
33
|
-
{"name": "Charlie", "age": 35, "city": "Chicago"}
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
# Convert to CSV
|
|
37
|
-
csv_output = to_delimited(data_dict)
|
|
38
|
-
print(csv_output)
|
|
39
|
-
|
|
40
|
-
# Convert to TSV
|
|
41
|
-
tsv_output = to_delimited(data_dict, delimiter='\t')
|
|
42
|
-
print(tsv_output)
|
|
43
|
-
|
|
44
|
-
# Convert to Markdown
|
|
45
|
-
md_output = to_markdown(data_dict)
|
|
46
|
-
print(md_output)
|
|
47
|
-
|
|
48
|
-
# Convert to reStructuredText
|
|
49
|
-
rst_output = to_rst(data_dict)
|
|
50
|
-
print(rst_output)
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
You can also use lists of lists:
|
|
54
|
-
|
|
55
|
-
```python
|
|
56
|
-
data_list = [
|
|
57
|
-
["Alice", 30, "New York"],
|
|
58
|
-
["Bob", 25, "Los Angeles"],
|
|
59
|
-
["Charlie", 35, "Chicago"]
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
# Convert to any format as shown above
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Development
|
|
66
|
-
|
|
67
|
-
To install the package in development mode:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
git clone https://github.com/yourusername/modular.git
|
|
71
|
-
cd modular
|
|
72
|
-
pip install -e .
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## License
|
|
76
|
-
|
|
77
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Table conversion functions for various formats.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import Union, List, Dict, Any
|
|
6
|
-
import csv
|
|
7
|
-
from io import StringIO
|
|
8
|
-
|
|
9
|
-
def _normalize_data(data: Union[List[List[Any]], List[Dict[str, Any]]]) -> tuple[List[List[str]], List[str]]:
|
|
10
|
-
"""
|
|
11
|
-
Normalize input data to a consistent format.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
data: List of lists or list of dictionaries
|
|
15
|
-
|
|
16
|
-
Returns:
|
|
17
|
-
Tuple of (rows, headers)
|
|
18
|
-
"""
|
|
19
|
-
if not data:
|
|
20
|
-
return [], []
|
|
21
|
-
|
|
22
|
-
if isinstance(data[0], dict):
|
|
23
|
-
headers = list(data[0].keys())
|
|
24
|
-
rows = [[str(row.get(header, '')) for header in headers] for row in data]
|
|
25
|
-
else:
|
|
26
|
-
rows = [[str(cell) for cell in row] for row in data]
|
|
27
|
-
headers = [f"Column {i+1}" for i in range(len(rows[0]))] if rows else []
|
|
28
|
-
|
|
29
|
-
return rows, headers
|
|
30
|
-
|
|
31
|
-
def to_delimited(data: Union[List[List[Any]], List[Dict[str, Any]]],
|
|
32
|
-
delimiter: str = ',') -> str:
|
|
33
|
-
"""
|
|
34
|
-
Convert data to a delimited string format.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
data: List of lists or list of dictionaries
|
|
38
|
-
delimiter: Character to use as delimiter (default: ',')
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
String containing the delimited data
|
|
42
|
-
"""
|
|
43
|
-
rows, headers = _normalize_data(data)
|
|
44
|
-
output = StringIO()
|
|
45
|
-
writer = csv.writer(output, delimiter=delimiter)
|
|
46
|
-
writer.writerow(headers)
|
|
47
|
-
writer.writerows(rows)
|
|
48
|
-
return output.getvalue()
|
|
49
|
-
|
|
50
|
-
def to_markdown(data: Union[List[List[Any]], List[Dict[str, Any]]]) -> str:
|
|
51
|
-
"""
|
|
52
|
-
Convert data to Markdown table format.
|
|
53
|
-
|
|
54
|
-
Args:
|
|
55
|
-
data: List of lists or list of dictionaries
|
|
56
|
-
|
|
57
|
-
Returns:
|
|
58
|
-
String containing the Markdown table
|
|
59
|
-
"""
|
|
60
|
-
rows, headers = _normalize_data(data)
|
|
61
|
-
if not rows:
|
|
62
|
-
return ""
|
|
63
|
-
|
|
64
|
-
# Create header row
|
|
65
|
-
markdown = "| " + " | ".join(headers) + " |\n"
|
|
66
|
-
# Create separator row
|
|
67
|
-
markdown += "| " + " | ".join(["---"] * len(headers)) + " |\n"
|
|
68
|
-
# Add data rows
|
|
69
|
-
for row in rows:
|
|
70
|
-
markdown += "| " + " | ".join(row) + " |\n"
|
|
71
|
-
|
|
72
|
-
return markdown
|
|
73
|
-
|
|
74
|
-
def to_rst(data: Union[List[List[Any]], List[Dict[str, Any]]]) -> str:
|
|
75
|
-
"""
|
|
76
|
-
Convert data to reStructuredText table format.
|
|
77
|
-
|
|
78
|
-
Args:
|
|
79
|
-
data: List of lists or list of dictionaries
|
|
80
|
-
|
|
81
|
-
Returns:
|
|
82
|
-
String containing the reStructuredText table
|
|
83
|
-
"""
|
|
84
|
-
rows, headers = _normalize_data(data)
|
|
85
|
-
if not rows:
|
|
86
|
-
return ""
|
|
87
|
-
|
|
88
|
-
# Calculate column widths
|
|
89
|
-
col_widths = [max(len(str(cell)) for cell in col)
|
|
90
|
-
for col in zip(headers, *rows)]
|
|
91
|
-
|
|
92
|
-
# Create header row
|
|
93
|
-
rst = "+" + "+".join("-" * (width + 2) for width in col_widths) + "+\n"
|
|
94
|
-
rst += "|" + "|".join(f" {header:<{width}} " for header, width in zip(headers, col_widths)) + "|\n"
|
|
95
|
-
rst += "+" + "+".join("=" * (width + 2) for width in col_widths) + "+\n"
|
|
96
|
-
|
|
97
|
-
# Add data rows
|
|
98
|
-
for row in rows:
|
|
99
|
-
rst += "|" + "|".join(f" {cell:<{width}} " for cell, width in zip(row, col_widths)) + "|\n"
|
|
100
|
-
rst += "+" + "+".join("-" * (width + 2) for width in col_widths) + "+\n"
|
|
101
|
-
|
|
102
|
-
return rst
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: modular
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: A modular package for all sorts of things where you want choices
|
|
5
|
-
Author-email: Jeffrey Spies <code@jeffspies.com>
|
|
6
|
-
Project-URL: Homepage, https://github.com/yourusername/modular
|
|
7
|
-
Project-URL: Bug Tracker, https://github.com/yourusername/modular/issues
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.7
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Dynamic: license-file
|
|
15
|
-
|
|
16
|
-
# Modular
|
|
17
|
-
|
|
18
|
-
A Python package for various data processing utilities with a focus on modularity and choice.
|
|
19
|
-
|
|
20
|
-
## Installation
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
pip install modular
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Features
|
|
27
|
-
|
|
28
|
-
### Tables Module
|
|
29
|
-
|
|
30
|
-
The `tables` module provides functions to convert data into various table formats.
|
|
31
|
-
|
|
32
|
-
#### Supported Formats:
|
|
33
|
-
- Delimited (CSV, TSV, etc.)
|
|
34
|
-
- Markdown
|
|
35
|
-
- reStructuredText
|
|
36
|
-
|
|
37
|
-
## Usage
|
|
38
|
-
|
|
39
|
-
### Converting Data to Different Formats
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
from modular.tables import to_delimited, to_markdown, to_rst
|
|
43
|
-
|
|
44
|
-
# Example data as a list of dictionaries
|
|
45
|
-
data_dict = [
|
|
46
|
-
{"name": "Alice", "age": 30, "city": "New York"},
|
|
47
|
-
{"name": "Bob", "age": 25, "city": "Los Angeles"},
|
|
48
|
-
{"name": "Charlie", "age": 35, "city": "Chicago"}
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
# Convert to CSV
|
|
52
|
-
csv_output = to_delimited(data_dict)
|
|
53
|
-
print(csv_output)
|
|
54
|
-
|
|
55
|
-
# Convert to TSV
|
|
56
|
-
tsv_output = to_delimited(data_dict, delimiter='\t')
|
|
57
|
-
print(tsv_output)
|
|
58
|
-
|
|
59
|
-
# Convert to Markdown
|
|
60
|
-
md_output = to_markdown(data_dict)
|
|
61
|
-
print(md_output)
|
|
62
|
-
|
|
63
|
-
# Convert to reStructuredText
|
|
64
|
-
rst_output = to_rst(data_dict)
|
|
65
|
-
print(rst_output)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
You can also use lists of lists:
|
|
69
|
-
|
|
70
|
-
```python
|
|
71
|
-
data_list = [
|
|
72
|
-
["Alice", 30, "New York"],
|
|
73
|
-
["Bob", 25, "Los Angeles"],
|
|
74
|
-
["Charlie", 35, "Chicago"]
|
|
75
|
-
]
|
|
76
|
-
|
|
77
|
-
# Convert to any format as shown above
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Development
|
|
81
|
-
|
|
82
|
-
To install the package in development mode:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
git clone https://github.com/yourusername/modular.git
|
|
86
|
-
cd modular
|
|
87
|
-
pip install -e .
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## License
|
|
91
|
-
|
|
92
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|