ixmp4 0.1.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.
- ixmp4-0.1.0/LICENSE +21 -0
- ixmp4-0.1.0/PKG-INFO +121 -0
- ixmp4-0.1.0/README.md +86 -0
- ixmp4-0.1.0/ixmp4/__init__.py +22 -0
- ixmp4-0.1.0/ixmp4/__main__.py +4 -0
- ixmp4-0.1.0/ixmp4/cli/__init__.py +102 -0
- ixmp4-0.1.0/ixmp4/cli/platforms.py +181 -0
- ixmp4-0.1.0/ixmp4/cli/server.py +46 -0
- ixmp4-0.1.0/ixmp4/cli/utils.py +10 -0
- ixmp4-0.1.0/ixmp4/conf/__init__.py +83 -0
- ixmp4-0.1.0/ixmp4/conf/auth.py +152 -0
- ixmp4-0.1.0/ixmp4/conf/base.py +15 -0
- ixmp4-0.1.0/ixmp4/conf/credentials.py +28 -0
- ixmp4-0.1.0/ixmp4/conf/manager.py +179 -0
- ixmp4-0.1.0/ixmp4/conf/toml.py +64 -0
- ixmp4-0.1.0/ixmp4/conf/user.py +20 -0
- ixmp4-0.1.0/ixmp4/core/__init__.py +8 -0
- ixmp4-0.1.0/ixmp4/core/base.py +24 -0
- ixmp4-0.1.0/ixmp4/core/decorators.py +18 -0
- ixmp4-0.1.0/ixmp4/core/exceptions.py +167 -0
- ixmp4-0.1.0/ixmp4/core/iamc/__init__.py +4 -0
- ixmp4-0.1.0/ixmp4/core/iamc/data.py +106 -0
- ixmp4-0.1.0/ixmp4/core/iamc/repository.py +8 -0
- ixmp4-0.1.0/ixmp4/core/iamc/variable.py +116 -0
- ixmp4-0.1.0/ixmp4/core/meta.py +16 -0
- ixmp4-0.1.0/ixmp4/core/model.py +114 -0
- ixmp4-0.1.0/ixmp4/core/platform.py +91 -0
- ixmp4-0.1.0/ixmp4/core/region.py +151 -0
- ixmp4-0.1.0/ixmp4/core/run.py +130 -0
- ixmp4-0.1.0/ixmp4/core/scenario.py +114 -0
- ixmp4-0.1.0/ixmp4/core/unit.py +142 -0
- ixmp4-0.1.0/ixmp4/core/utils.py +14 -0
- ixmp4-0.1.0/ixmp4/data/__init__.py +0 -0
- ixmp4-0.1.0/ixmp4/data/abstract/__init__.py +39 -0
- ixmp4-0.1.0/ixmp4/data/abstract/base.py +82 -0
- ixmp4-0.1.0/ixmp4/data/abstract/docs.py +101 -0
- ixmp4-0.1.0/ixmp4/data/abstract/iamc/__init__.py +11 -0
- ixmp4-0.1.0/ixmp4/data/abstract/iamc/datapoint.py +137 -0
- ixmp4-0.1.0/ixmp4/data/abstract/iamc/measurand.py +53 -0
- ixmp4-0.1.0/ixmp4/data/abstract/iamc/timeseries.py +189 -0
- ixmp4-0.1.0/ixmp4/data/abstract/iamc/variable.py +106 -0
- ixmp4-0.1.0/ixmp4/data/abstract/meta.py +204 -0
- ixmp4-0.1.0/ixmp4/data/abstract/model.py +115 -0
- ixmp4-0.1.0/ixmp4/data/abstract/region.py +164 -0
- ixmp4-0.1.0/ixmp4/data/abstract/run.py +229 -0
- ixmp4-0.1.0/ixmp4/data/abstract/scenario.py +108 -0
- ixmp4-0.1.0/ixmp4/data/abstract/unit.py +141 -0
- ixmp4-0.1.0/ixmp4/data/api/__init__.py +25 -0
- ixmp4-0.1.0/ixmp4/data/api/base.py +232 -0
- ixmp4-0.1.0/ixmp4/data/api/docs.py +43 -0
- ixmp4-0.1.0/ixmp4/data/api/iamc/__init__.py +11 -0
- ixmp4-0.1.0/ixmp4/data/api/iamc/datapoint.py +48 -0
- ixmp4-0.1.0/ixmp4/data/api/iamc/timeseries.py +49 -0
- ixmp4-0.1.0/ixmp4/data/api/iamc/variable.py +61 -0
- ixmp4-0.1.0/ixmp4/data/api/meta.py +60 -0
- ixmp4-0.1.0/ixmp4/data/api/model.py +58 -0
- ixmp4-0.1.0/ixmp4/data/api/region.py +69 -0
- ixmp4-0.1.0/ixmp4/data/api/run.py +80 -0
- ixmp4-0.1.0/ixmp4/data/api/scenario.py +57 -0
- ixmp4-0.1.0/ixmp4/data/api/unit.py +60 -0
- ixmp4-0.1.0/ixmp4/data/auth/context.py +84 -0
- ixmp4-0.1.0/ixmp4/data/auth/decorators.py +40 -0
- ixmp4-0.1.0/ixmp4/data/backend/__init__.py +5 -0
- ixmp4-0.1.0/ixmp4/data/backend/api.py +67 -0
- ixmp4-0.1.0/ixmp4/data/backend/base.py +33 -0
- ixmp4-0.1.0/ixmp4/data/backend/db.py +122 -0
- ixmp4-0.1.0/ixmp4/data/db/__init__.py +28 -0
- ixmp4-0.1.0/ixmp4/data/db/base.py +455 -0
- ixmp4-0.1.0/ixmp4/data/db/docs.py +109 -0
- ixmp4-0.1.0/ixmp4/data/db/filters.py +88 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/__init__.py +13 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/base.py +18 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/__init__.py +3 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/filter.py +176 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/model.py +63 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/repository.py +195 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/measurand.py +97 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/timeseries.py +178 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/variable/__init__.py +3 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/variable/docs.py +8 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/variable/filter.py +30 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/variable/model.py +18 -0
- ixmp4-0.1.0/ixmp4/data/db/iamc/variable/repository.py +53 -0
- ixmp4-0.1.0/ixmp4/data/db/meta.py +252 -0
- ixmp4-0.1.0/ixmp4/data/db/model/__init__.py +4 -0
- ixmp4-0.1.0/ixmp4/data/db/model/docs.py +17 -0
- ixmp4-0.1.0/ixmp4/data/db/model/filter.py +62 -0
- ixmp4-0.1.0/ixmp4/data/db/model/model.py +17 -0
- ixmp4-0.1.0/ixmp4/data/db/model/repository.py +54 -0
- ixmp4-0.1.0/ixmp4/data/db/region/__init__.py +4 -0
- ixmp4-0.1.0/ixmp4/data/db/region/docs.py +19 -0
- ixmp4-0.1.0/ixmp4/data/db/region/filter.py +56 -0
- ixmp4-0.1.0/ixmp4/data/db/region/model.py +18 -0
- ixmp4-0.1.0/ixmp4/data/db/region/repository.py +59 -0
- ixmp4-0.1.0/ixmp4/data/db/run/__init__.py +2 -0
- ixmp4-0.1.0/ixmp4/data/db/run/filter.py +7 -0
- ixmp4-0.1.0/ixmp4/data/db/run/model.py +41 -0
- ixmp4-0.1.0/ixmp4/data/db/run/repository.py +179 -0
- ixmp4-0.1.0/ixmp4/data/db/scenario/__init__.py +4 -0
- ixmp4-0.1.0/ixmp4/data/db/scenario/docs.py +7 -0
- ixmp4-0.1.0/ixmp4/data/db/scenario/filter.py +64 -0
- ixmp4-0.1.0/ixmp4/data/db/scenario/model.py +17 -0
- ixmp4-0.1.0/ixmp4/data/db/scenario/repository.py +64 -0
- ixmp4-0.1.0/ixmp4/data/db/timeseries.py +137 -0
- ixmp4-0.1.0/ixmp4/data/db/unit/__init__.py +5 -0
- ixmp4-0.1.0/ixmp4/data/db/unit/docs.py +22 -0
- ixmp4-0.1.0/ixmp4/data/db/unit/filter.py +62 -0
- ixmp4-0.1.0/ixmp4/data/db/unit/model.py +17 -0
- ixmp4-0.1.0/ixmp4/data/db/unit/repository.py +66 -0
- ixmp4-0.1.0/ixmp4/data/db/utils.py +15 -0
- ixmp4-0.1.0/ixmp4/data/types.py +12 -0
- ixmp4-0.1.0/ixmp4/db/__init__.py +63 -0
- ixmp4-0.1.0/ixmp4/db/filters.py +213 -0
- ixmp4-0.1.0/ixmp4/db/migrations/env.py +89 -0
- ixmp4-0.1.0/ixmp4/db/migrations/script.py.mako +25 -0
- ixmp4-0.1.0/ixmp4/db/migrations/versions/c71efc396d2b_initial_migration.py +506 -0
- ixmp4-0.1.0/ixmp4/db/utils/__init__.py +15 -0
- ixmp4-0.1.0/ixmp4/db/utils/alembic.py +44 -0
- ixmp4-0.1.0/ixmp4/db/utils/sqlite.py +37 -0
- ixmp4-0.1.0/ixmp4/py.typed +0 -0
- ixmp4-0.1.0/ixmp4/server/__init__.py +19 -0
- ixmp4-0.1.0/ixmp4/server/rest/__init__.py +58 -0
- ixmp4-0.1.0/ixmp4/server/rest/base.py +7 -0
- ixmp4-0.1.0/ixmp4/server/rest/decorators.py +6 -0
- ixmp4-0.1.0/ixmp4/server/rest/deps.py +79 -0
- ixmp4-0.1.0/ixmp4/server/rest/docs.py +135 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/__init__.py +1 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/datapoint.py +92 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/model.py +35 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/region.py +35 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/scenario.py +35 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/timeseries.py +66 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/unit.py +35 -0
- ixmp4-0.1.0/ixmp4/server/rest/iamc/variable.py +52 -0
- ixmp4-0.1.0/ixmp4/server/rest/meta.py +68 -0
- ixmp4-0.1.0/ixmp4/server/rest/model.py +53 -0
- ixmp4-0.1.0/ixmp4/server/rest/region.py +60 -0
- ixmp4-0.1.0/ixmp4/server/rest/run.py +65 -0
- ixmp4-0.1.0/ixmp4/server/rest/scenario.py +52 -0
- ixmp4-0.1.0/ixmp4/server/rest/unit.py +59 -0
- ixmp4-0.1.0/ixmp4/server/workers.py +8 -0
- ixmp4-0.1.0/pyproject.toml +112 -0
- ixmp4-0.1.0/setup.py +80 -0
ixmp4-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 IIASA
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ixmp4-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ixmp4
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: a data warehouse for scenario analysis
|
|
5
|
+
Home-page: https://software.ece.iiasa.ac.at
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Max Wolschlager
|
|
8
|
+
Author-email: wolschlager@iiasa.ac.at
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Requires-Dist: PyJWT (>=2.4.0,<3.0.0)
|
|
15
|
+
Requires-Dist: SQLAlchemy-Utils (>=0.40.0,<0.41.0)
|
|
16
|
+
Requires-Dist: SQLAlchemy[mypy] (>=2.0.5,<3.0.0)
|
|
17
|
+
Requires-Dist: alembic (>=1.10.2,<2.0.0)
|
|
18
|
+
Requires-Dist: dask (>=2023.4.0,<2024.0.0)
|
|
19
|
+
Requires-Dist: fastapi (>=0.94.0,<0.95.0)
|
|
20
|
+
Requires-Dist: httpx[http2] (>=0.23.3,<0.24.0)
|
|
21
|
+
Requires-Dist: openpyxl (>=3.0.9,<4.0.0)
|
|
22
|
+
Requires-Dist: oracledb (>=1.2.2,<2.0.0)
|
|
23
|
+
Requires-Dist: pandas (>=2.0.0,<3.0.0)
|
|
24
|
+
Requires-Dist: pandera (>=0.13.4,<0.14.0)
|
|
25
|
+
Requires-Dist: psycopg2 (>=2.9.3,<3.0.0)
|
|
26
|
+
Requires-Dist: pydantic (>=1.10.5,<2.0.0)
|
|
27
|
+
Requires-Dist: python-dotenv (>=0.19.0,<0.20.0)
|
|
28
|
+
Requires-Dist: requests (>=2.27.1,<3.0.0)
|
|
29
|
+
Requires-Dist: rtoml (>=0.8.0,<0.9.0)
|
|
30
|
+
Requires-Dist: typer (>=0.4.0,<0.5.0)
|
|
31
|
+
Project-URL: Documentation, https://docs.ece.iiasa.ac.at/projects/ixmp4
|
|
32
|
+
Project-URL: Repository, https://github.com/iiasa/ixmp4
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# The ixmp4 package for scenario data management
|
|
36
|
+
|
|
37
|
+
Copyright (c) 2023 IIASA - Energy, Climate, and Environment Program (ECE)
|
|
38
|
+
|
|
39
|
+
[](https://github.com/iiasa/ixmp4/blob/main/LICENSE)
|
|
40
|
+
[](https://github.com/iiasa/ixmp4)
|
|
41
|
+
[](https://github.com/psf/black)
|
|
42
|
+
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
The **ixmp4** package is a data warehouse for high-powered scenario analysis
|
|
46
|
+
in the domain of integrated assessment of climate change and energy systems modeling.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
The **ixmp4** package is released under the [MIT license](https://github.com/iiasa/ixmp4/blob/main/LICENSE).
|
|
51
|
+
|
|
52
|
+
## Requirements
|
|
53
|
+
|
|
54
|
+
This project requires Python 3.10 and poetry (>= 1.2).
|
|
55
|
+
|
|
56
|
+
## Setup
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Install Poetry, minimum version >=1.2 required
|
|
60
|
+
curl -sSL https://install.python-poetry.org | python -
|
|
61
|
+
|
|
62
|
+
# You may have to reinitialize your shell at this point.
|
|
63
|
+
source ~/.bashrc
|
|
64
|
+
|
|
65
|
+
# Activate in-project virtualenvs
|
|
66
|
+
poetry config virtualenvs.in-project true
|
|
67
|
+
|
|
68
|
+
# Install dependencies
|
|
69
|
+
# (using "--with dev,docs,server" if dev and docs dependencies should be installed as well)
|
|
70
|
+
poetry install --with dev,docs,server
|
|
71
|
+
|
|
72
|
+
# Activate virtual environment
|
|
73
|
+
poetry shell
|
|
74
|
+
|
|
75
|
+
# Copy the template environment configuration
|
|
76
|
+
cp template.env .env
|
|
77
|
+
|
|
78
|
+
# Add a test platform
|
|
79
|
+
ixmp4 platforms add test
|
|
80
|
+
|
|
81
|
+
# Start the asgi server
|
|
82
|
+
ixmp4 server start
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## CLI
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
ixmp4 --help
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Docs
|
|
92
|
+
|
|
93
|
+
Check [doc/README.md](doc/README.md) on how to build and serve dev documentation locally.
|
|
94
|
+
|
|
95
|
+
## Docker Image
|
|
96
|
+
|
|
97
|
+
Check [docker/README.md](docker/README.md) on how to build and publish docker images.
|
|
98
|
+
|
|
99
|
+
## Developing
|
|
100
|
+
|
|
101
|
+
See [DEVELOPING.md](DEVELOPING.md) for guidance. When contributing to this project via
|
|
102
|
+
a Pull Request, add your name to the "authors" section in the `pyproject.toml` file.
|
|
103
|
+
|
|
104
|
+
## Funding ackownledgement
|
|
105
|
+
|
|
106
|
+
<img src="./doc/source/_static/ECEMF-logo.png" width="264" height="100"
|
|
107
|
+
alt="ECEMF logo" />
|
|
108
|
+
<img src="./doc/source/_static/openENTRANCE-logo.png" width="187" height="120"
|
|
109
|
+
alt="openENTRANCE logo" />
|
|
110
|
+
<img src="./doc/source/_static/ariadne-bmbf-logo.png" width="353" height="100"
|
|
111
|
+
alt="Kopernikus project ARIADNE logo" />
|
|
112
|
+
|
|
113
|
+
The development of the **ixmp4** package was funded from the EU Horizon 2020 projects
|
|
114
|
+
[openENTRANCE](https://openentrance.eu) and [ECEMF](https://ecemf.eu)
|
|
115
|
+
as well as the BMBF Kopernikus project [ARIADNE](https://ariadneprojekt.de)
|
|
116
|
+
(FKZ 03SFK5A by the German Federal Ministry of Education and Research).
|
|
117
|
+
|
|
118
|
+
<img src="./doc/source/_static/EU-logo-300x201.jpg" width="80" height="54" align="left"
|
|
119
|
+
alt="EU logo" /> This project has received funding from the European Union’s Horizon
|
|
120
|
+
2020 research and innovation programme under grant agreement No. 835896 and 101022622.
|
|
121
|
+
|
ixmp4-0.1.0/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# The ixmp4 package for scenario data management
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 IIASA - Energy, Climate, and Environment Program (ECE)
|
|
4
|
+
|
|
5
|
+
[](https://github.com/iiasa/ixmp4/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/iiasa/ixmp4)
|
|
7
|
+
[](https://github.com/psf/black)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The **ixmp4** package is a data warehouse for high-powered scenario analysis
|
|
12
|
+
in the domain of integrated assessment of climate change and energy systems modeling.
|
|
13
|
+
|
|
14
|
+
## License
|
|
15
|
+
|
|
16
|
+
The **ixmp4** package is released under the [MIT license](https://github.com/iiasa/ixmp4/blob/main/LICENSE).
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
This project requires Python 3.10 and poetry (>= 1.2).
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Install Poetry, minimum version >=1.2 required
|
|
26
|
+
curl -sSL https://install.python-poetry.org | python -
|
|
27
|
+
|
|
28
|
+
# You may have to reinitialize your shell at this point.
|
|
29
|
+
source ~/.bashrc
|
|
30
|
+
|
|
31
|
+
# Activate in-project virtualenvs
|
|
32
|
+
poetry config virtualenvs.in-project true
|
|
33
|
+
|
|
34
|
+
# Install dependencies
|
|
35
|
+
# (using "--with dev,docs,server" if dev and docs dependencies should be installed as well)
|
|
36
|
+
poetry install --with dev,docs,server
|
|
37
|
+
|
|
38
|
+
# Activate virtual environment
|
|
39
|
+
poetry shell
|
|
40
|
+
|
|
41
|
+
# Copy the template environment configuration
|
|
42
|
+
cp template.env .env
|
|
43
|
+
|
|
44
|
+
# Add a test platform
|
|
45
|
+
ixmp4 platforms add test
|
|
46
|
+
|
|
47
|
+
# Start the asgi server
|
|
48
|
+
ixmp4 server start
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## CLI
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
ixmp4 --help
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Docs
|
|
58
|
+
|
|
59
|
+
Check [doc/README.md](doc/README.md) on how to build and serve dev documentation locally.
|
|
60
|
+
|
|
61
|
+
## Docker Image
|
|
62
|
+
|
|
63
|
+
Check [docker/README.md](docker/README.md) on how to build and publish docker images.
|
|
64
|
+
|
|
65
|
+
## Developing
|
|
66
|
+
|
|
67
|
+
See [DEVELOPING.md](DEVELOPING.md) for guidance. When contributing to this project via
|
|
68
|
+
a Pull Request, add your name to the "authors" section in the `pyproject.toml` file.
|
|
69
|
+
|
|
70
|
+
## Funding ackownledgement
|
|
71
|
+
|
|
72
|
+
<img src="./doc/source/_static/ECEMF-logo.png" width="264" height="100"
|
|
73
|
+
alt="ECEMF logo" />
|
|
74
|
+
<img src="./doc/source/_static/openENTRANCE-logo.png" width="187" height="120"
|
|
75
|
+
alt="openENTRANCE logo" />
|
|
76
|
+
<img src="./doc/source/_static/ariadne-bmbf-logo.png" width="353" height="100"
|
|
77
|
+
alt="Kopernikus project ARIADNE logo" />
|
|
78
|
+
|
|
79
|
+
The development of the **ixmp4** package was funded from the EU Horizon 2020 projects
|
|
80
|
+
[openENTRANCE](https://openentrance.eu) and [ECEMF](https://ecemf.eu)
|
|
81
|
+
as well as the BMBF Kopernikus project [ARIADNE](https://ariadneprojekt.de)
|
|
82
|
+
(FKZ 03SFK5A by the German Federal Ministry of Education and Research).
|
|
83
|
+
|
|
84
|
+
<img src="./doc/source/_static/EU-logo-300x201.jpg" width="80" height="54" align="left"
|
|
85
|
+
alt="EU logo" /> This project has received funding from the European Union’s Horizon
|
|
86
|
+
2020 research and innovation programme under grant agreement No. 835896 and 101022622.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
|
|
3
|
+
from ixmp4.core import (
|
|
4
|
+
Platform as Platform,
|
|
5
|
+
Region as Region,
|
|
6
|
+
Unit as Unit,
|
|
7
|
+
Run as Run,
|
|
8
|
+
Variable as Variable,
|
|
9
|
+
Model as Model,
|
|
10
|
+
Scenario as Scenario,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from ixmp4.core.exceptions import (
|
|
14
|
+
NotFound as NotFound,
|
|
15
|
+
NotUnique as NotUnique,
|
|
16
|
+
IxmpError as IxmpError,
|
|
17
|
+
InconsistentIamcType as InconsistentIamcType,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from ixmp4.data.abstract import DataPoint as DataPoint
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check the CLI help command on how to use it:
|
|
3
|
+
|
|
4
|
+
.. code:: bash
|
|
5
|
+
|
|
6
|
+
ixmp4 --help
|
|
7
|
+
ixmp4 platforms --help
|
|
8
|
+
ixmp4 test --help
|
|
9
|
+
ixmp4 server --help
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional
|
|
14
|
+
import typer
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from ixmp4.core.exceptions import InvalidCredentials
|
|
18
|
+
from ixmp4.conf.auth import ManagerAuth
|
|
19
|
+
from ixmp4.conf import settings
|
|
20
|
+
from ixmp4.cli import platforms
|
|
21
|
+
from . import utils
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
app = typer.Typer()
|
|
25
|
+
app.add_typer(platforms.app, name="platforms")
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
from . import server
|
|
29
|
+
|
|
30
|
+
app.add_typer(server.app, name="server")
|
|
31
|
+
except ImportError:
|
|
32
|
+
# No server installed
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@app.command()
|
|
37
|
+
def login(
|
|
38
|
+
username: str = typer.Argument(..., help="Your username."),
|
|
39
|
+
password: str = typer.Option(
|
|
40
|
+
...,
|
|
41
|
+
help="Your password. Will be saved in plain-text.",
|
|
42
|
+
prompt=True,
|
|
43
|
+
hide_input=True,
|
|
44
|
+
),
|
|
45
|
+
):
|
|
46
|
+
try:
|
|
47
|
+
auth = ManagerAuth(username, password, settings.manager_url)
|
|
48
|
+
user = auth.get_user()
|
|
49
|
+
utils.good(f"Successfully authenticated as user '{user.username}'.")
|
|
50
|
+
if typer.confirm(
|
|
51
|
+
"Are you sure you want to save your credentials in plain-text for future use?"
|
|
52
|
+
):
|
|
53
|
+
settings.credentials.set("default", username, password)
|
|
54
|
+
|
|
55
|
+
except InvalidCredentials:
|
|
56
|
+
raise typer.BadParameter(
|
|
57
|
+
"The credentials you provided are not valid. (Wrong username or password?)"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
import pytest
|
|
63
|
+
|
|
64
|
+
@app.command(
|
|
65
|
+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
|
|
66
|
+
)
|
|
67
|
+
def test(
|
|
68
|
+
ctx: typer.Context,
|
|
69
|
+
with_backend: Optional[bool] = False,
|
|
70
|
+
with_benchmarks: Optional[bool] = False,
|
|
71
|
+
dry: Optional[bool] = False,
|
|
72
|
+
):
|
|
73
|
+
opts = [
|
|
74
|
+
"--cov-report",
|
|
75
|
+
"xml:.coverage.xml",
|
|
76
|
+
"--cov-report",
|
|
77
|
+
"term",
|
|
78
|
+
"--cov=ixmp4",
|
|
79
|
+
"-rsx",
|
|
80
|
+
] + ctx.args
|
|
81
|
+
|
|
82
|
+
if not with_backend:
|
|
83
|
+
opts += ["--ignore=tests/data"]
|
|
84
|
+
|
|
85
|
+
if not with_benchmarks:
|
|
86
|
+
opts += ["--benchmark-skip"]
|
|
87
|
+
else:
|
|
88
|
+
opts += ["--benchmark-group-by=func", "--benchmark-columns=min"]
|
|
89
|
+
|
|
90
|
+
if dry:
|
|
91
|
+
utils.echo("pytest " + " ".join(opts))
|
|
92
|
+
raise typer.Exit(0)
|
|
93
|
+
exit_code = pytest.main(opts)
|
|
94
|
+
raise typer.Exit(
|
|
95
|
+
code=exit_code.value
|
|
96
|
+
if isinstance(exit_code, pytest.ExitCode)
|
|
97
|
+
else exit_code
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
except ImportError:
|
|
101
|
+
# No pytest installed
|
|
102
|
+
pass
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from ixmp4.conf import settings
|
|
8
|
+
from ixmp4.conf.manager import ManagerPlatformInfo
|
|
9
|
+
from ixmp4.conf.toml import TomlPlatformInfo
|
|
10
|
+
from ixmp4.core.exceptions import PlatformNotFound
|
|
11
|
+
from ixmp4.db.utils import alembic, sqlite
|
|
12
|
+
|
|
13
|
+
from . import utils
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
app = typer.Typer()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_name(name: str):
|
|
20
|
+
match = re.match(r"^[\w\-_]*$", name)
|
|
21
|
+
if match is None:
|
|
22
|
+
raise typer.BadParameter("Platform name must be slug-like.")
|
|
23
|
+
else:
|
|
24
|
+
return name
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def validate_dsn(dsn: str | None):
|
|
28
|
+
if dsn is None:
|
|
29
|
+
return None
|
|
30
|
+
match = re.match(r"^(sqlite|postgresql|oracle|https|http)(\:\/\/)", dsn)
|
|
31
|
+
if match is None:
|
|
32
|
+
raise typer.BadParameter(
|
|
33
|
+
"Platform dsn must be a valid URl or database connection string."
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
return dsn
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def prompt_sqlite_dsn(name: str):
|
|
40
|
+
path = sqlite.get_database_path(name)
|
|
41
|
+
dsn = sqlite.get_dsn(path)
|
|
42
|
+
if path.exists():
|
|
43
|
+
if typer.confirm(
|
|
44
|
+
f"A file at the standard filesystem location for name '{name}' already exists. "
|
|
45
|
+
"Do you want to add the existing file to the platform registry?"
|
|
46
|
+
):
|
|
47
|
+
return dsn
|
|
48
|
+
else:
|
|
49
|
+
raise typer.Exit()
|
|
50
|
+
else:
|
|
51
|
+
if typer.confirm(
|
|
52
|
+
f"No file at the standard filesystem location for name '{name}' exists. "
|
|
53
|
+
"Do you want to create a new database?"
|
|
54
|
+
):
|
|
55
|
+
alembic.upgrade_database(dsn, "head")
|
|
56
|
+
return dsn
|
|
57
|
+
else:
|
|
58
|
+
raise typer.Exit()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@app.command(help="Adds a new platform to ixmp4's toml registry.")
|
|
62
|
+
def add(
|
|
63
|
+
name: str = typer.Argument(
|
|
64
|
+
...,
|
|
65
|
+
help="The string identifier of the platform to add. Must be slug-like.",
|
|
66
|
+
callback=validate_name,
|
|
67
|
+
),
|
|
68
|
+
dsn: Optional[str] = typer.Option(
|
|
69
|
+
None,
|
|
70
|
+
help="A data source name. Can be a http(s) URl or a database connection string.",
|
|
71
|
+
callback=validate_dsn,
|
|
72
|
+
),
|
|
73
|
+
):
|
|
74
|
+
try:
|
|
75
|
+
settings.toml.get_platform(name)
|
|
76
|
+
raise typer.BadParameter(
|
|
77
|
+
f"Platform with name '{name}' already exists. "
|
|
78
|
+
"Choose another name or remove the existing platform."
|
|
79
|
+
)
|
|
80
|
+
except PlatformNotFound:
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
if dsn is None:
|
|
84
|
+
utils.echo(
|
|
85
|
+
"No DSN supplied, assuming you want to add a local sqlite database..."
|
|
86
|
+
)
|
|
87
|
+
dsn = prompt_sqlite_dsn(name)
|
|
88
|
+
|
|
89
|
+
settings.toml.add_platform(name, dsn)
|
|
90
|
+
utils.good("\nPlatform added successfully.")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def prompt_sqlite_removal(dsn: str):
|
|
94
|
+
path = Path(dsn.replace("sqlite://", ""))
|
|
95
|
+
path_str = typer.style(path, fg=typer.colors.CYAN)
|
|
96
|
+
if typer.confirm(
|
|
97
|
+
"Do you want to remove the associated database file at "
|
|
98
|
+
f"{path_str} aswell?" # type: ignore
|
|
99
|
+
):
|
|
100
|
+
path.unlink()
|
|
101
|
+
utils.echo("\nDatabase file deleted.")
|
|
102
|
+
else:
|
|
103
|
+
utils.echo("\nDatabase file left intact.")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@app.command(help="Removes a platform from ixmp4's toml registry.")
|
|
107
|
+
def remove(
|
|
108
|
+
name: str = typer.Argument(
|
|
109
|
+
..., help="The string identifier of the platform to remove."
|
|
110
|
+
)
|
|
111
|
+
):
|
|
112
|
+
try:
|
|
113
|
+
platform = settings.toml.get_platform(name)
|
|
114
|
+
except PlatformNotFound:
|
|
115
|
+
raise typer.BadParameter(f"Platform '{name}' does not exist.")
|
|
116
|
+
|
|
117
|
+
if typer.confirm(
|
|
118
|
+
f"Are you sure you want to remove the platform '{platform.name}' with dsn '{platform.dsn}'?"
|
|
119
|
+
):
|
|
120
|
+
if platform.dsn.startswith("sqlite://"):
|
|
121
|
+
prompt_sqlite_removal(platform.dsn)
|
|
122
|
+
settings.toml.remove_platform(name)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def tabulate_platforms(
|
|
126
|
+
platforms: list[TomlPlatformInfo] | list[ManagerPlatformInfo],
|
|
127
|
+
):
|
|
128
|
+
utils.echo("Platform".ljust(15) + "DSN\n".ljust(15))
|
|
129
|
+
total = 0
|
|
130
|
+
for p in platforms:
|
|
131
|
+
name = p.name
|
|
132
|
+
if len(name) > 12:
|
|
133
|
+
name = name[:9] + "..."
|
|
134
|
+
name = name.ljust(15)
|
|
135
|
+
|
|
136
|
+
utils.important(name, nl=False)
|
|
137
|
+
utils.echo(p.dsn.ljust(10))
|
|
138
|
+
total += 1
|
|
139
|
+
|
|
140
|
+
utils.info("\n" + str(total), nl=False)
|
|
141
|
+
utils.echo(" total \n")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@app.command("list", help="Lists all registered platforms.")
|
|
145
|
+
def list_():
|
|
146
|
+
toml_path_str = typer.style(settings.toml.path, fg=typer.colors.CYAN)
|
|
147
|
+
toml_platforms = settings.toml.list_platforms()
|
|
148
|
+
utils.echo(f"Platforms in '{toml_path_str}':")
|
|
149
|
+
tabulate_platforms(toml_platforms)
|
|
150
|
+
|
|
151
|
+
if settings.manager is not None:
|
|
152
|
+
manager_url_str = typer.style(settings.manager.url, fg=typer.colors.CYAN)
|
|
153
|
+
manager_platforms = settings.manager.list_platforms()
|
|
154
|
+
utils.echo(f"Platforms accessible via '{manager_url_str}':")
|
|
155
|
+
tabulate_platforms(manager_platforms)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@app.command(
|
|
159
|
+
help="Migrates all database platforms from your local toml file to the newest revision."
|
|
160
|
+
)
|
|
161
|
+
def upgrade():
|
|
162
|
+
for c in settings.toml.list_platforms():
|
|
163
|
+
if c.dsn.startswith("http"):
|
|
164
|
+
utils.echo(f"Skipping '{c.name}' because it is a REST platform.")
|
|
165
|
+
else:
|
|
166
|
+
utils.echo(f"Upgrading platform '{c.name}' with dsn '{c.dsn}'...")
|
|
167
|
+
alembic.upgrade_database(c.dsn, "head")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@app.command(
|
|
171
|
+
help="Stamps all database platforms from your local toml file with the given revision."
|
|
172
|
+
)
|
|
173
|
+
def stamp(revision: str) -> None:
|
|
174
|
+
for c in settings.toml.list_platforms():
|
|
175
|
+
if c.dsn.startswith("http"):
|
|
176
|
+
utils.echo(f"Skipping '{c.name}' because it is a REST platform.")
|
|
177
|
+
else:
|
|
178
|
+
utils.echo(
|
|
179
|
+
f"Stamping platform '{c.name}' with dsn '{c.dsn}' to '{revision}'..."
|
|
180
|
+
)
|
|
181
|
+
alembic.stamp_database(c.dsn, revision)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
import uvicorn # type: ignore[import]
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from fastapi.openapi.utils import get_openapi
|
|
8
|
+
from ixmp4.server import v1
|
|
9
|
+
|
|
10
|
+
from . import utils
|
|
11
|
+
|
|
12
|
+
app = typer.Typer()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.command()
|
|
16
|
+
def start(
|
|
17
|
+
host: Optional[str] = typer.Option(
|
|
18
|
+
"127.0.0.1",
|
|
19
|
+
help="The hostname to bind to.",
|
|
20
|
+
),
|
|
21
|
+
port: Optional[int] = typer.Option(
|
|
22
|
+
9000,
|
|
23
|
+
help="Requested server port.",
|
|
24
|
+
),
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Starts the ixmp4 web api."""
|
|
27
|
+
|
|
28
|
+
uvicorn.run("ixmp4.server:app", host=host, port=port, reload=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@app.command()
|
|
32
|
+
def dump_schema(output_file: Optional[typer.FileTextWrite] = typer.Option(None, "-o")):
|
|
33
|
+
schema = get_openapi(
|
|
34
|
+
title=v1.title,
|
|
35
|
+
version=v1.version,
|
|
36
|
+
openapi_version=v1.openapi_version,
|
|
37
|
+
description=v1.description,
|
|
38
|
+
routes=v1.routes,
|
|
39
|
+
)
|
|
40
|
+
if output_file is None:
|
|
41
|
+
utils.echo(json.dumps(schema))
|
|
42
|
+
else:
|
|
43
|
+
json.dump(
|
|
44
|
+
schema,
|
|
45
|
+
output_file,
|
|
46
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
import typer
|
|
3
|
+
|
|
4
|
+
echo = typer.echo
|
|
5
|
+
good = partial(typer.secho, fg=typer.colors.GREEN)
|
|
6
|
+
bad = partial(typer.secho, fg=typer.colors.RED)
|
|
7
|
+
error = partial(typer.secho, fg=typer.colors.RED, err=True)
|
|
8
|
+
info = partial(typer.secho, fg=typer.colors.CYAN)
|
|
9
|
+
important = partial(typer.secho, fg=typer.colors.MAGENTA)
|
|
10
|
+
secondary = partial(typer.secho, fg=typer.colors.BRIGHT_BLACK)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from dotenv import load_dotenv
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseSettings, Field, validator, HttpUrl, Extra
|
|
5
|
+
|
|
6
|
+
from ixmp4.core.exceptions import InvalidCredentials
|
|
7
|
+
from .credentials import Credentials
|
|
8
|
+
from .toml import TomlConfig
|
|
9
|
+
from .manager import ManagerConfig
|
|
10
|
+
from .auth import ManagerAuth
|
|
11
|
+
from .user import local_user
|
|
12
|
+
from .base import PlatformInfo as PlatformInfo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Settings(BaseSettings):
|
|
16
|
+
mode: str = "production"
|
|
17
|
+
storage_directory: Path = Field("~/.local/share/ixmp4/", env="ixmp4_dir")
|
|
18
|
+
secret_hs256: str = "default_secret_hs256"
|
|
19
|
+
migration_db_uri: str = "sqlite:///./run/db.sqlite"
|
|
20
|
+
manager_url: HttpUrl = Field("https://api.manager.ece.iiasa.ac.at/v1")
|
|
21
|
+
|
|
22
|
+
class Config:
|
|
23
|
+
env_prefix = "ixmp4_"
|
|
24
|
+
extra = Extra.allow
|
|
25
|
+
|
|
26
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
27
|
+
super().__init__(*args, **kwargs)
|
|
28
|
+
|
|
29
|
+
self.storage_directory.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
|
|
31
|
+
database_dir = self.storage_directory / "databases"
|
|
32
|
+
database_dir.mkdir(exist_ok=True)
|
|
33
|
+
self.load_credentials()
|
|
34
|
+
self.load_manager_config()
|
|
35
|
+
self.load_toml_config()
|
|
36
|
+
|
|
37
|
+
def load_credentials(self):
|
|
38
|
+
credentials_config = self.storage_directory / "credentials.toml"
|
|
39
|
+
credentials_config.touch()
|
|
40
|
+
self.credentials = Credentials(credentials_config)
|
|
41
|
+
|
|
42
|
+
self.default_credentials = None
|
|
43
|
+
self.default_auth = None
|
|
44
|
+
try:
|
|
45
|
+
self.default_credentials = self.credentials.get("default")
|
|
46
|
+
except KeyError:
|
|
47
|
+
# TODO: WARNING: No default credentials provided.
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
if self.default_credentials is not None:
|
|
51
|
+
try:
|
|
52
|
+
username, password = self.default_credentials
|
|
53
|
+
self.default_auth = ManagerAuth(username, password, self.manager_url)
|
|
54
|
+
except InvalidCredentials:
|
|
55
|
+
# TODO: WARNING: Default credentials invalid.
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
def load_manager_config(self):
|
|
59
|
+
self.manager = None
|
|
60
|
+
if self.default_auth is not None:
|
|
61
|
+
self.manager = ManagerConfig(
|
|
62
|
+
self.manager_url, self.default_auth, remote=True
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def load_toml_config(self):
|
|
66
|
+
if self.default_auth is not None:
|
|
67
|
+
toml_user = self.default_auth.get_user()
|
|
68
|
+
else:
|
|
69
|
+
toml_user = local_user
|
|
70
|
+
toml_config = self.storage_directory / "platforms.toml"
|
|
71
|
+
toml_config.touch()
|
|
72
|
+
self.toml = TomlConfig(toml_config, toml_user)
|
|
73
|
+
|
|
74
|
+
@validator("storage_directory")
|
|
75
|
+
def expand_user(cls, v):
|
|
76
|
+
# translate ~/asdf into /home/user/asdf
|
|
77
|
+
return Path.expanduser(v)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
load_dotenv()
|
|
81
|
+
# strict typechecking fails due to a bug
|
|
82
|
+
# https://docs.pydantic.dev/visual_studio_code/#adding-a-default-with-field
|
|
83
|
+
settings = Settings() # type: ignore
|