datawrapper-mcp 0.0.1__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.
- datawrapper_mcp-0.0.1/.clinerules +188 -0
- datawrapper_mcp-0.0.1/.github/dependabot.yml +16 -0
- datawrapper_mcp-0.0.1/.github/workflows/continuous-deployment.yaml +143 -0
- datawrapper_mcp-0.0.1/.github/workflows/docs.yaml +76 -0
- datawrapper_mcp-0.0.1/.gitignore +110 -0
- datawrapper_mcp-0.0.1/.pre-commit-config.yaml +47 -0
- datawrapper_mcp-0.0.1/.vscode/extensions.json +6 -0
- datawrapper_mcp-0.0.1/.vscode/launch.json +24 -0
- datawrapper_mcp-0.0.1/.vscode/settings.json +15 -0
- datawrapper_mcp-0.0.1/CODE_OF_CONDUCT.md +76 -0
- datawrapper_mcp-0.0.1/CONTRIBUTING.md +122 -0
- datawrapper_mcp-0.0.1/LICENSE +21 -0
- datawrapper_mcp-0.0.1/MANIFEST.in +5 -0
- datawrapper_mcp-0.0.1/PKG-INFO +295 -0
- datawrapper_mcp-0.0.1/README.md +238 -0
- datawrapper_mcp-0.0.1/SECURITY.md +5 -0
- datawrapper_mcp-0.0.1/claude_desktop_config.example.json +11 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/__init__.py +1 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/config.py +24 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/__init__.py +19 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/create.py +52 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/delete.py +25 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/export.py +48 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/publish.py +26 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/retrieve.py +27 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/schema.py +31 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/handlers/update.py +61 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/server.py +101 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/tools.py +209 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp/utils.py +55 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp.egg-info/PKG-INFO +295 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp.egg-info/SOURCES.txt +44 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp.egg-info/dependency_links.txt +1 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp.egg-info/entry_points.txt +2 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp.egg-info/requires.txt +39 -0
- datawrapper_mcp-0.0.1/datawrapper_mcp.egg-info/top_level.txt +1 -0
- datawrapper_mcp-0.0.1/pyproject.toml +96 -0
- datawrapper_mcp-0.0.1/setup.cfg +4 -0
- datawrapper_mcp-0.0.1/tests/__init__.py +1 -0
- datawrapper_mcp-0.0.1/tests/conftest.py +102 -0
- datawrapper_mcp-0.0.1/tests/test_schema.py +103 -0
- datawrapper_mcp-0.0.1/tests/test_schema_handler.py +170 -0
- datawrapper_mcp-0.0.1/tests/test_update.py +236 -0
- datawrapper_mcp-0.0.1/tests/test_update_aliases.py +147 -0
- datawrapper_mcp-0.0.1/tests/test_update_chart_type.py +92 -0
- datawrapper_mcp-0.0.1/uv.lock +3514 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# datawrapper-mcp Project Rules
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
This is a Model Context Protocol (MCP) server that provides tools for creating Datawrapper charts through AI assistants. The server leverages Pydantic models from the datawrapper library for validation and schema generation.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
### Two-Tier Tool Design
|
|
10
|
+
|
|
11
|
+
1. **Chart Creation** (`create_chart`): Full control using Pydantic models
|
|
12
|
+
- Takes data, chart_type, and complete chart_config dict
|
|
13
|
+
- Validates config using Pydantic's model_validate()
|
|
14
|
+
- Allows specification of all chart properties (colors, axes, labels, etc.)
|
|
15
|
+
- Uses Pydantic class instance method: `chart.create(access_token)`
|
|
16
|
+
|
|
17
|
+
2. **Schema Discovery** (`get_chart_schema`): Explore available options
|
|
18
|
+
- Returns Pydantic JSON schema for a chart type
|
|
19
|
+
- Shows all properties, types, defaults, and descriptions
|
|
20
|
+
- Enables AI to discover chart capabilities dynamically
|
|
21
|
+
|
|
22
|
+
### Key Design Decisions
|
|
23
|
+
|
|
24
|
+
- **Pydantic-Only**: Uses ONLY Pydantic class instance methods, never base Datawrapper client
|
|
25
|
+
- **No Deprecated Methods**: Avoids dw.create_chart(), dw.publish_chart(), etc. which will be deprecated
|
|
26
|
+
- **Instance Methods**: Uses chart.create(), chart.publish(), chart.update(), chart.delete()
|
|
27
|
+
- **Factory Pattern**: Uses get_chart(chart_id, access_token) from datawrapper.chart_factory
|
|
28
|
+
- **Type-Safe**: All configurations validated through Pydantic
|
|
29
|
+
- **Flexible Data**: Supports JSON strings, list of dicts, or dict of arrays
|
|
30
|
+
|
|
31
|
+
## File Structure
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
datawrapper_mcp/
|
|
35
|
+
├── __init__.py # Package initialization
|
|
36
|
+
└── server.py # Main MCP server implementation
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Dependencies
|
|
40
|
+
|
|
41
|
+
- `datawrapper>=2.0.7`: Python wrapper for Datawrapper API with Pydantic models
|
|
42
|
+
- `mcp[cli]>=1.20.0`: Model Context Protocol SDK
|
|
43
|
+
- `pandas>=2.0.0`: Data manipulation for chart data
|
|
44
|
+
|
|
45
|
+
## Environment Variables
|
|
46
|
+
|
|
47
|
+
- `DATAWRAPPER_API_TOKEN`: Required. Get from https://app.datawrapper.de/account/api-tokens
|
|
48
|
+
|
|
49
|
+
## Supported Chart Types
|
|
50
|
+
|
|
51
|
+
The server supports 8 chart types from the datawrapper library:
|
|
52
|
+
- `bar`: BarChart
|
|
53
|
+
- `line`: LineChart
|
|
54
|
+
- `area`: AreaChart
|
|
55
|
+
- `arrow`: ArrowChart
|
|
56
|
+
- `column`: ColumnChart
|
|
57
|
+
- `multiple_column`: MultipleColumnChart
|
|
58
|
+
- `scatter`: ScatterPlot
|
|
59
|
+
- `stacked_bar`: StackedBarChart
|
|
60
|
+
|
|
61
|
+
## Available Tools
|
|
62
|
+
|
|
63
|
+
1. **create_chart**: Chart creation with full Pydantic config
|
|
64
|
+
2. **get_chart_schema**: Get JSON schema for a chart type
|
|
65
|
+
3. **publish_chart**: Publish chart to make it public
|
|
66
|
+
4. **get_chart**: Retrieve chart information
|
|
67
|
+
5. **update_chart**: Update chart data or configuration (Pydantic-validated)
|
|
68
|
+
6. **delete_chart**: Delete a chart
|
|
69
|
+
7. **export_chart_png**: Export chart as PNG image
|
|
70
|
+
|
|
71
|
+
## MCP Resources
|
|
72
|
+
|
|
73
|
+
- `datawrapper://chart-types`: Returns schemas for all chart types
|
|
74
|
+
|
|
75
|
+
## Data Format
|
|
76
|
+
|
|
77
|
+
The server accepts three data formats:
|
|
78
|
+
|
|
79
|
+
1. **List of records** (recommended):
|
|
80
|
+
```json
|
|
81
|
+
[{"year": 2020, "value": 100}, {"year": 2021, "value": 150}]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
2. **Dict of arrays**:
|
|
85
|
+
```json
|
|
86
|
+
{"year": [2020, 2021], "value": [100, 150]}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
3. **JSON string**: String representation of either format above
|
|
90
|
+
|
|
91
|
+
All formats are converted to pandas DataFrame internally.
|
|
92
|
+
|
|
93
|
+
## Implementation Notes
|
|
94
|
+
|
|
95
|
+
### Critical Implementation Rules
|
|
96
|
+
|
|
97
|
+
**NEVER use these deprecated methods:**
|
|
98
|
+
- `dw.create_chart()` - Use `chart.create(access_token)` instead
|
|
99
|
+
- `dw.publish_chart()` - Use `chart.publish(access_token)` instead
|
|
100
|
+
- `dw.add_data()` - Use `chart.data = df` then `chart.update(access_token)` instead
|
|
101
|
+
- `dw.update_metadata()` - Use `setattr(chart, key, value)` then `chart.update(access_token)` instead
|
|
102
|
+
- `dw.delete_chart()` - Use `chart.delete(access_token)` instead
|
|
103
|
+
- `dw.get_chart()` - Use `get_chart(chart_id, access_token)` from datawrapper.chart_factory instead
|
|
104
|
+
|
|
105
|
+
**Always use Pydantic class instance methods:**
|
|
106
|
+
- Create: `chart.create(access_token=api_token)`
|
|
107
|
+
- Publish: `chart.publish(access_token=api_token)`
|
|
108
|
+
- Update: `chart.update(access_token=api_token)`
|
|
109
|
+
- Delete: `chart.delete(access_token=api_token)`
|
|
110
|
+
- Retrieve: `get_chart(chart_id, access_token=api_token)` (factory function)
|
|
111
|
+
|
|
112
|
+
### Error Handling
|
|
113
|
+
|
|
114
|
+
- Missing API token raises ValueError with helpful message
|
|
115
|
+
- Invalid chart configs return TextContent with validation errors
|
|
116
|
+
- All tool calls wrapped in try/except to return error messages
|
|
117
|
+
- Chart type mapping errors provide clear feedback on supported types
|
|
118
|
+
|
|
119
|
+
### Update Chart Implementation
|
|
120
|
+
|
|
121
|
+
The `update_chart` tool strictly enforces Pydantic validation:
|
|
122
|
+
|
|
123
|
+
1. **Retrieves existing chart** using `get_chart()` factory function (returns correct Pydantic class instance)
|
|
124
|
+
2. **Gets chart class directly** from instance using `type(chart)` - no manual type mapping needed
|
|
125
|
+
3. **Gets current config** using `chart.model_dump()`
|
|
126
|
+
4. **Merges new config** with existing config
|
|
127
|
+
5. **Validates through Pydantic** using `model_validate()` on the chart's class
|
|
128
|
+
6. **Updates chart attributes** from validated model
|
|
129
|
+
7. **Rejects low-level structures** like 'metadata' or 'visualize' - only accepts high-level Pydantic fields
|
|
130
|
+
|
|
131
|
+
This simplified approach:
|
|
132
|
+
- Eliminates manual type mapping dictionary (no exposure of low-level API types like "d3-bars-stacked")
|
|
133
|
+
- Uses the chart class directly from the `get_chart()` factory function
|
|
134
|
+
- Ensures chatbots cannot bypass the Pydantic abstraction layer and must use the intended API surface (title, intro, byline, source_name, etc.)
|
|
135
|
+
|
|
136
|
+
### Chart Creation Flow
|
|
137
|
+
|
|
138
|
+
1. Get API token from environment
|
|
139
|
+
2. Convert JSON data to pandas DataFrame
|
|
140
|
+
3. Get appropriate Pydantic chart class
|
|
141
|
+
4. Validate configuration using model_validate()
|
|
142
|
+
5. Set data on chart instance: `chart.data = df`
|
|
143
|
+
6. Create chart using instance method: `chart.create(access_token)`
|
|
144
|
+
7. Return chart ID and URLs using `chart.get_editor_url()`
|
|
145
|
+
|
|
146
|
+
### Pydantic Integration
|
|
147
|
+
|
|
148
|
+
- Uses `model_validate()` for config validation
|
|
149
|
+
- Uses `model_json_schema()` for schema generation
|
|
150
|
+
- Uses instance methods: `.create()`, `.publish()`, `.update()`, `.delete()`
|
|
151
|
+
- Uses factory function: `get_chart(chart_id, access_token)` for retrieval
|
|
152
|
+
- Uses helper methods: `.get_editor_url()`, `.get_public_url()`
|
|
153
|
+
- Provides helpful validation errors from Pydantic
|
|
154
|
+
|
|
155
|
+
## Testing Considerations
|
|
156
|
+
|
|
157
|
+
To test the server:
|
|
158
|
+
1. Set DATAWRAPPER_API_TOKEN environment variable
|
|
159
|
+
2. Install package: `pip install -e .`
|
|
160
|
+
3. Run server: `datawrapper-mcp`
|
|
161
|
+
4. Configure MCP client to connect to server
|
|
162
|
+
5. Test with AI assistant or MCP inspector
|
|
163
|
+
|
|
164
|
+
## Future Enhancements
|
|
165
|
+
|
|
166
|
+
Potential improvements:
|
|
167
|
+
- Add more chart types as datawrapper library adds them
|
|
168
|
+
- Support for map visualizations
|
|
169
|
+
- Batch chart operations
|
|
170
|
+
- Chart templates/presets
|
|
171
|
+
- Data validation helpers
|
|
172
|
+
- Export options (PNG, SVG, PDF)
|
|
173
|
+
|
|
174
|
+
## Common Issues
|
|
175
|
+
|
|
176
|
+
1. **Missing API token**: Set DATAWRAPPER_API_TOKEN environment variable
|
|
177
|
+
2. **Invalid chart config**: Use get_chart_schema to see valid options
|
|
178
|
+
3. **Data format errors**: Ensure data is JSON string, list of dicts, or dict of arrays
|
|
179
|
+
4. **Type errors**: Check Pydantic validation messages for required fields
|
|
180
|
+
|
|
181
|
+
## Development Workflow
|
|
182
|
+
|
|
183
|
+
1. Make changes to src/datawrapper_mcp/server.py
|
|
184
|
+
2. Test locally with `python -m datawrapper_mcp.server`
|
|
185
|
+
3. Update README.md if adding features
|
|
186
|
+
4. Update this .clinerules file with new information
|
|
187
|
+
5. Run pre-commit hooks before committing
|
|
188
|
+
6. Update version in src/datawrapper_mcp/__init__.py if needed
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "pip"
|
|
9
|
+
directory: "/"
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "monthly"
|
|
12
|
+
|
|
13
|
+
- package-ecosystem: "github-actions"
|
|
14
|
+
directory: "/.github/workflows"
|
|
15
|
+
schedule:
|
|
16
|
+
interval: "monthly"
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
name: Continuous deployment
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
|
|
7
|
+
concurrency:
|
|
8
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
9
|
+
cancel-in-progress: true
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint-python:
|
|
13
|
+
name: Lint Python code
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- id: checkout
|
|
17
|
+
name: Checkout
|
|
18
|
+
uses: actions/checkout@v5
|
|
19
|
+
|
|
20
|
+
- id: check-ruff
|
|
21
|
+
name: Check with Ruff
|
|
22
|
+
uses: astral-sh/ruff-action@v3
|
|
23
|
+
with:
|
|
24
|
+
args: 'check --exit-zero --verbose'
|
|
25
|
+
|
|
26
|
+
- id: format-ruff
|
|
27
|
+
name: Format with Ruff
|
|
28
|
+
uses: astral-sh/ruff-action@v3
|
|
29
|
+
with:
|
|
30
|
+
args: 'format --check --verbose'
|
|
31
|
+
|
|
32
|
+
mypy-python:
|
|
33
|
+
name: Check Python static typing
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- id: checkout
|
|
37
|
+
name: Checkout
|
|
38
|
+
uses: actions/checkout@v5
|
|
39
|
+
|
|
40
|
+
- id: install-uv
|
|
41
|
+
name: Install uv
|
|
42
|
+
uses: astral-sh/setup-uv@v7
|
|
43
|
+
with:
|
|
44
|
+
version: "latest"
|
|
45
|
+
enable-cache: true
|
|
46
|
+
cache-dependency-glob: '**/pyproject.toml'
|
|
47
|
+
|
|
48
|
+
- id: install-python
|
|
49
|
+
name: Install Python
|
|
50
|
+
run: uv python install 3.13
|
|
51
|
+
|
|
52
|
+
- id: install-python-dependencies
|
|
53
|
+
name: Install Python dependencies
|
|
54
|
+
run: uv sync --extra mypy
|
|
55
|
+
|
|
56
|
+
- id: mypy
|
|
57
|
+
name: Run mypy
|
|
58
|
+
run: uv run mypy ./ --ignore-missing-imports
|
|
59
|
+
|
|
60
|
+
test-python:
|
|
61
|
+
name: Test Python code
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
strategy:
|
|
64
|
+
matrix:
|
|
65
|
+
python: ['3.10', '3.11', '3.12', '3.13',]
|
|
66
|
+
steps:
|
|
67
|
+
- name: Checkout
|
|
68
|
+
uses: actions/checkout@v5
|
|
69
|
+
|
|
70
|
+
- id: install-uv
|
|
71
|
+
name: Install uv
|
|
72
|
+
uses: astral-sh/setup-uv@v7
|
|
73
|
+
with:
|
|
74
|
+
version: "latest"
|
|
75
|
+
enable-cache: true
|
|
76
|
+
cache-dependency-glob: '**/pyproject.toml'
|
|
77
|
+
|
|
78
|
+
- id: install-python
|
|
79
|
+
name: Install Python
|
|
80
|
+
run: uv python install ${{ matrix.python }}
|
|
81
|
+
|
|
82
|
+
- id: install-python-dependencies
|
|
83
|
+
name: Install Python dependencies
|
|
84
|
+
run: uv sync --extra test --python ${{ matrix.python }}
|
|
85
|
+
|
|
86
|
+
- id: tests
|
|
87
|
+
name: Run tests
|
|
88
|
+
run: uv run pytest --cov -sv
|
|
89
|
+
|
|
90
|
+
build:
|
|
91
|
+
name: Build release candidate
|
|
92
|
+
runs-on: ubuntu-latest
|
|
93
|
+
needs: [test-python]
|
|
94
|
+
steps:
|
|
95
|
+
- name: Checkout
|
|
96
|
+
uses: actions/checkout@v5
|
|
97
|
+
|
|
98
|
+
- id: install-uv
|
|
99
|
+
name: Install uv
|
|
100
|
+
uses: astral-sh/setup-uv@v7
|
|
101
|
+
with:
|
|
102
|
+
version: "latest"
|
|
103
|
+
enable-cache: true
|
|
104
|
+
cache-dependency-glob: '**/pyproject.toml'
|
|
105
|
+
|
|
106
|
+
- id: install-python
|
|
107
|
+
name: Install Python
|
|
108
|
+
run: uv python install 3.13
|
|
109
|
+
|
|
110
|
+
- id: build
|
|
111
|
+
name: Build releases
|
|
112
|
+
run: uv build --sdist --wheel
|
|
113
|
+
|
|
114
|
+
- id: save
|
|
115
|
+
name: Save artifact
|
|
116
|
+
uses: actions/upload-artifact@v5
|
|
117
|
+
with:
|
|
118
|
+
name: release-candidate
|
|
119
|
+
path: ./dist
|
|
120
|
+
if-no-files-found: error
|
|
121
|
+
|
|
122
|
+
release:
|
|
123
|
+
name: PyPI release
|
|
124
|
+
runs-on: ubuntu-latest
|
|
125
|
+
needs: [build]
|
|
126
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
|
127
|
+
permissions:
|
|
128
|
+
id-token: write
|
|
129
|
+
contents: read
|
|
130
|
+
steps:
|
|
131
|
+
- id: fetch
|
|
132
|
+
name: Fetch artifact
|
|
133
|
+
uses: actions/download-artifact@v6
|
|
134
|
+
with:
|
|
135
|
+
name: release-candidate
|
|
136
|
+
path: ./dist
|
|
137
|
+
|
|
138
|
+
- id: publish
|
|
139
|
+
name: Publish release
|
|
140
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
141
|
+
with:
|
|
142
|
+
verbose: true
|
|
143
|
+
verify_metadata: false
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
|
|
7
|
+
concurrency:
|
|
8
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
9
|
+
cancel-in-progress: true
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- id: checkout
|
|
17
|
+
name: Checkout
|
|
18
|
+
uses: actions/checkout@v5
|
|
19
|
+
|
|
20
|
+
- id: install-uv
|
|
21
|
+
name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v7
|
|
23
|
+
with:
|
|
24
|
+
version: "latest"
|
|
25
|
+
enable-cache: true
|
|
26
|
+
cache-dependency-glob: '**/pyproject.toml'
|
|
27
|
+
|
|
28
|
+
- id: install-python
|
|
29
|
+
name: Install Python
|
|
30
|
+
run: uv python install 3.13
|
|
31
|
+
|
|
32
|
+
- id: install-python-dependencies
|
|
33
|
+
name: Install Python dependencies
|
|
34
|
+
run: uv sync --extra docs
|
|
35
|
+
|
|
36
|
+
# - id: build-sphinx-documentation
|
|
37
|
+
# name: Build Sphinx documentation
|
|
38
|
+
# run: uv run sphinx-build -M html ./docs ./_build/
|
|
39
|
+
|
|
40
|
+
# - id: upload-release-candidate
|
|
41
|
+
# name: Upload release candidate
|
|
42
|
+
# uses: actions/upload-artifact@v5
|
|
43
|
+
# with:
|
|
44
|
+
# name: release-candidate
|
|
45
|
+
# path: ./_build/html/
|
|
46
|
+
|
|
47
|
+
# deploy:
|
|
48
|
+
# name: Deploy
|
|
49
|
+
# runs-on: ubuntu-latest
|
|
50
|
+
# needs: build
|
|
51
|
+
# if: ${{ github.ref_name == 'main' }}
|
|
52
|
+
# steps:
|
|
53
|
+
# - name: Download release candidate
|
|
54
|
+
# uses: actions/download-artifact@v4
|
|
55
|
+
# with:
|
|
56
|
+
# name: release-candidate
|
|
57
|
+
# path: ./docs/
|
|
58
|
+
|
|
59
|
+
# - id: configure-aws
|
|
60
|
+
# name: Configure AWS Credentials
|
|
61
|
+
# uses: aws-actions/configure-aws-credentials@v4
|
|
62
|
+
# with:
|
|
63
|
+
# aws-access-key-id: ${{ secrets.DOCS_AWS_ACCESS_KEY_ID }}
|
|
64
|
+
# aws-secret-access-key: ${{ secrets.DOCS_AWS_SECRET_ACCESS_KEY }}
|
|
65
|
+
# aws-region: ${{ secrets.DOCS_AWS_REGION }}
|
|
66
|
+
|
|
67
|
+
# - id: upload-to-s3
|
|
68
|
+
# name: Upload documentation to Amazon S3
|
|
69
|
+
# uses: datadesk/delivery-deploy-action@v1
|
|
70
|
+
# with:
|
|
71
|
+
# bucket: ${{ secrets.DOCS_AWS_BUCKET }}
|
|
72
|
+
# base-path: ${{ secrets.DOCS_AWS_BASE_PATH }}
|
|
73
|
+
# dir: ./docs/
|
|
74
|
+
# should-cache: false
|
|
75
|
+
# use-accelerate-endpoint: false
|
|
76
|
+
# public: true
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
MANIFEST
|
|
27
|
+
|
|
28
|
+
# PyInstaller
|
|
29
|
+
# Usually these files are written by a python script from a template
|
|
30
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
|
|
38
|
+
# Unit test / coverage reports
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
.hypothesis/
|
|
48
|
+
.pytest_cache/
|
|
49
|
+
|
|
50
|
+
# Translations
|
|
51
|
+
*.mo
|
|
52
|
+
*.pot
|
|
53
|
+
|
|
54
|
+
# Django stuff:
|
|
55
|
+
*.log
|
|
56
|
+
local_settings.py
|
|
57
|
+
db.sqlite3
|
|
58
|
+
|
|
59
|
+
# Flask stuff:
|
|
60
|
+
instance/
|
|
61
|
+
.webassets-cache
|
|
62
|
+
|
|
63
|
+
# Scrapy stuff:
|
|
64
|
+
.scrapy
|
|
65
|
+
|
|
66
|
+
# Sphinx documentation
|
|
67
|
+
docs/_build/
|
|
68
|
+
|
|
69
|
+
# PyBuilder
|
|
70
|
+
target/
|
|
71
|
+
|
|
72
|
+
# Jupyter Notebook
|
|
73
|
+
.ipynb_checkpoints
|
|
74
|
+
|
|
75
|
+
# pyenv
|
|
76
|
+
.python-version
|
|
77
|
+
|
|
78
|
+
# celery beat schedule file
|
|
79
|
+
celerybeat-schedule
|
|
80
|
+
|
|
81
|
+
# SageMath parsed files
|
|
82
|
+
*.sage.py
|
|
83
|
+
|
|
84
|
+
# Environments
|
|
85
|
+
.env
|
|
86
|
+
.venv
|
|
87
|
+
env/
|
|
88
|
+
venv/
|
|
89
|
+
ENV/
|
|
90
|
+
env.bak/
|
|
91
|
+
venv.bak/
|
|
92
|
+
|
|
93
|
+
# Spyder project settings
|
|
94
|
+
.spyderproject
|
|
95
|
+
.spyproject
|
|
96
|
+
|
|
97
|
+
# Rope project settings
|
|
98
|
+
.ropeproject
|
|
99
|
+
|
|
100
|
+
# mkdocs documentation
|
|
101
|
+
/site
|
|
102
|
+
|
|
103
|
+
# mypy
|
|
104
|
+
.mypy_cache/
|
|
105
|
+
|
|
106
|
+
# ruff
|
|
107
|
+
.ruff_cache/
|
|
108
|
+
|
|
109
|
+
# pip and uv
|
|
110
|
+
./build/
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# See https://pre-commit.com for more information
|
|
2
|
+
# See https://pre-commit.com/hooks.html for more hooks
|
|
3
|
+
repos:
|
|
4
|
+
- repo: 'https://github.com/pre-commit/pre-commit-hooks'
|
|
5
|
+
rev: v5.0.0
|
|
6
|
+
hooks:
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- id: end-of-file-fixer
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
args:
|
|
12
|
+
- '--maxkb=100000'
|
|
13
|
+
- id: fix-byte-order-marker
|
|
14
|
+
- id: check-case-conflict
|
|
15
|
+
- id: check-json
|
|
16
|
+
- id: mixed-line-ending
|
|
17
|
+
- id: check-ast
|
|
18
|
+
- id: check-merge-conflict
|
|
19
|
+
|
|
20
|
+
- repo: 'https://github.com/astral-sh/ruff-pre-commit'
|
|
21
|
+
rev: v0.11.2
|
|
22
|
+
hooks:
|
|
23
|
+
- id: ruff
|
|
24
|
+
args:
|
|
25
|
+
- '--fix'
|
|
26
|
+
- id: ruff-format
|
|
27
|
+
|
|
28
|
+
- repo: 'https://github.com/asottile/blacken-docs'
|
|
29
|
+
rev: 1.19.1
|
|
30
|
+
hooks:
|
|
31
|
+
- id: blacken-docs
|
|
32
|
+
additional_dependencies:
|
|
33
|
+
- black
|
|
34
|
+
|
|
35
|
+
- repo: 'https://github.com/asottile/pyupgrade'
|
|
36
|
+
rev: v3.19.1
|
|
37
|
+
hooks:
|
|
38
|
+
- id: pyupgrade
|
|
39
|
+
args:
|
|
40
|
+
- '--py37-plus'
|
|
41
|
+
|
|
42
|
+
- repo: 'https://github.com/pre-commit/mirrors-mypy'
|
|
43
|
+
rev: v1.15.0
|
|
44
|
+
hooks:
|
|
45
|
+
- id: mypy
|
|
46
|
+
# additional_dependencies:
|
|
47
|
+
# - types-requests
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"configurations": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Python: Current File",
|
|
6
|
+
"type": "python",
|
|
7
|
+
"request": "launch",
|
|
8
|
+
"program": "${file}",
|
|
9
|
+
"console": "integratedTerminal",
|
|
10
|
+
"justMyCode": true,
|
|
11
|
+
"python": "${workspaceFolder}/.venv/bin/python"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "Python: Debug Notebook",
|
|
15
|
+
"type": "python",
|
|
16
|
+
"request": "launch",
|
|
17
|
+
"program": "${file}",
|
|
18
|
+
"console": "integratedTerminal",
|
|
19
|
+
"justMyCode": true,
|
|
20
|
+
"python": "${workspaceFolder}/.venv/bin/python",
|
|
21
|
+
"purpose": ["debug-in-terminal"]
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
|
|
3
|
+
"python.terminal.activateEnvironment": true,
|
|
4
|
+
"jupyter.notebookFileRoot": "${workspaceFolder}",
|
|
5
|
+
"jupyter.interactiveWindow.cellMarker.codeRegex": "^(#\\s*%%|#\\s*\\<codecell\\>|#\\s*In\\[\\d*\\]:|#\\s*In\\[ \\]:|#\\s*cell)",
|
|
6
|
+
"jupyter.interactiveWindow.cellMarker.default": "# %%",
|
|
7
|
+
"python.analysis.extraPaths": [
|
|
8
|
+
"${workspaceFolder}"
|
|
9
|
+
],
|
|
10
|
+
"python.testing.pytestArgs": [
|
|
11
|
+
"tests"
|
|
12
|
+
],
|
|
13
|
+
"python.testing.unittestEnabled": false,
|
|
14
|
+
"python.testing.pytestEnabled": true
|
|
15
|
+
}
|