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.
Files changed (143) hide show
  1. ixmp4-0.1.0/LICENSE +21 -0
  2. ixmp4-0.1.0/PKG-INFO +121 -0
  3. ixmp4-0.1.0/README.md +86 -0
  4. ixmp4-0.1.0/ixmp4/__init__.py +22 -0
  5. ixmp4-0.1.0/ixmp4/__main__.py +4 -0
  6. ixmp4-0.1.0/ixmp4/cli/__init__.py +102 -0
  7. ixmp4-0.1.0/ixmp4/cli/platforms.py +181 -0
  8. ixmp4-0.1.0/ixmp4/cli/server.py +46 -0
  9. ixmp4-0.1.0/ixmp4/cli/utils.py +10 -0
  10. ixmp4-0.1.0/ixmp4/conf/__init__.py +83 -0
  11. ixmp4-0.1.0/ixmp4/conf/auth.py +152 -0
  12. ixmp4-0.1.0/ixmp4/conf/base.py +15 -0
  13. ixmp4-0.1.0/ixmp4/conf/credentials.py +28 -0
  14. ixmp4-0.1.0/ixmp4/conf/manager.py +179 -0
  15. ixmp4-0.1.0/ixmp4/conf/toml.py +64 -0
  16. ixmp4-0.1.0/ixmp4/conf/user.py +20 -0
  17. ixmp4-0.1.0/ixmp4/core/__init__.py +8 -0
  18. ixmp4-0.1.0/ixmp4/core/base.py +24 -0
  19. ixmp4-0.1.0/ixmp4/core/decorators.py +18 -0
  20. ixmp4-0.1.0/ixmp4/core/exceptions.py +167 -0
  21. ixmp4-0.1.0/ixmp4/core/iamc/__init__.py +4 -0
  22. ixmp4-0.1.0/ixmp4/core/iamc/data.py +106 -0
  23. ixmp4-0.1.0/ixmp4/core/iamc/repository.py +8 -0
  24. ixmp4-0.1.0/ixmp4/core/iamc/variable.py +116 -0
  25. ixmp4-0.1.0/ixmp4/core/meta.py +16 -0
  26. ixmp4-0.1.0/ixmp4/core/model.py +114 -0
  27. ixmp4-0.1.0/ixmp4/core/platform.py +91 -0
  28. ixmp4-0.1.0/ixmp4/core/region.py +151 -0
  29. ixmp4-0.1.0/ixmp4/core/run.py +130 -0
  30. ixmp4-0.1.0/ixmp4/core/scenario.py +114 -0
  31. ixmp4-0.1.0/ixmp4/core/unit.py +142 -0
  32. ixmp4-0.1.0/ixmp4/core/utils.py +14 -0
  33. ixmp4-0.1.0/ixmp4/data/__init__.py +0 -0
  34. ixmp4-0.1.0/ixmp4/data/abstract/__init__.py +39 -0
  35. ixmp4-0.1.0/ixmp4/data/abstract/base.py +82 -0
  36. ixmp4-0.1.0/ixmp4/data/abstract/docs.py +101 -0
  37. ixmp4-0.1.0/ixmp4/data/abstract/iamc/__init__.py +11 -0
  38. ixmp4-0.1.0/ixmp4/data/abstract/iamc/datapoint.py +137 -0
  39. ixmp4-0.1.0/ixmp4/data/abstract/iamc/measurand.py +53 -0
  40. ixmp4-0.1.0/ixmp4/data/abstract/iamc/timeseries.py +189 -0
  41. ixmp4-0.1.0/ixmp4/data/abstract/iamc/variable.py +106 -0
  42. ixmp4-0.1.0/ixmp4/data/abstract/meta.py +204 -0
  43. ixmp4-0.1.0/ixmp4/data/abstract/model.py +115 -0
  44. ixmp4-0.1.0/ixmp4/data/abstract/region.py +164 -0
  45. ixmp4-0.1.0/ixmp4/data/abstract/run.py +229 -0
  46. ixmp4-0.1.0/ixmp4/data/abstract/scenario.py +108 -0
  47. ixmp4-0.1.0/ixmp4/data/abstract/unit.py +141 -0
  48. ixmp4-0.1.0/ixmp4/data/api/__init__.py +25 -0
  49. ixmp4-0.1.0/ixmp4/data/api/base.py +232 -0
  50. ixmp4-0.1.0/ixmp4/data/api/docs.py +43 -0
  51. ixmp4-0.1.0/ixmp4/data/api/iamc/__init__.py +11 -0
  52. ixmp4-0.1.0/ixmp4/data/api/iamc/datapoint.py +48 -0
  53. ixmp4-0.1.0/ixmp4/data/api/iamc/timeseries.py +49 -0
  54. ixmp4-0.1.0/ixmp4/data/api/iamc/variable.py +61 -0
  55. ixmp4-0.1.0/ixmp4/data/api/meta.py +60 -0
  56. ixmp4-0.1.0/ixmp4/data/api/model.py +58 -0
  57. ixmp4-0.1.0/ixmp4/data/api/region.py +69 -0
  58. ixmp4-0.1.0/ixmp4/data/api/run.py +80 -0
  59. ixmp4-0.1.0/ixmp4/data/api/scenario.py +57 -0
  60. ixmp4-0.1.0/ixmp4/data/api/unit.py +60 -0
  61. ixmp4-0.1.0/ixmp4/data/auth/context.py +84 -0
  62. ixmp4-0.1.0/ixmp4/data/auth/decorators.py +40 -0
  63. ixmp4-0.1.0/ixmp4/data/backend/__init__.py +5 -0
  64. ixmp4-0.1.0/ixmp4/data/backend/api.py +67 -0
  65. ixmp4-0.1.0/ixmp4/data/backend/base.py +33 -0
  66. ixmp4-0.1.0/ixmp4/data/backend/db.py +122 -0
  67. ixmp4-0.1.0/ixmp4/data/db/__init__.py +28 -0
  68. ixmp4-0.1.0/ixmp4/data/db/base.py +455 -0
  69. ixmp4-0.1.0/ixmp4/data/db/docs.py +109 -0
  70. ixmp4-0.1.0/ixmp4/data/db/filters.py +88 -0
  71. ixmp4-0.1.0/ixmp4/data/db/iamc/__init__.py +13 -0
  72. ixmp4-0.1.0/ixmp4/data/db/iamc/base.py +18 -0
  73. ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/__init__.py +3 -0
  74. ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/filter.py +176 -0
  75. ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/model.py +63 -0
  76. ixmp4-0.1.0/ixmp4/data/db/iamc/datapoint/repository.py +195 -0
  77. ixmp4-0.1.0/ixmp4/data/db/iamc/measurand.py +97 -0
  78. ixmp4-0.1.0/ixmp4/data/db/iamc/timeseries.py +178 -0
  79. ixmp4-0.1.0/ixmp4/data/db/iamc/variable/__init__.py +3 -0
  80. ixmp4-0.1.0/ixmp4/data/db/iamc/variable/docs.py +8 -0
  81. ixmp4-0.1.0/ixmp4/data/db/iamc/variable/filter.py +30 -0
  82. ixmp4-0.1.0/ixmp4/data/db/iamc/variable/model.py +18 -0
  83. ixmp4-0.1.0/ixmp4/data/db/iamc/variable/repository.py +53 -0
  84. ixmp4-0.1.0/ixmp4/data/db/meta.py +252 -0
  85. ixmp4-0.1.0/ixmp4/data/db/model/__init__.py +4 -0
  86. ixmp4-0.1.0/ixmp4/data/db/model/docs.py +17 -0
  87. ixmp4-0.1.0/ixmp4/data/db/model/filter.py +62 -0
  88. ixmp4-0.1.0/ixmp4/data/db/model/model.py +17 -0
  89. ixmp4-0.1.0/ixmp4/data/db/model/repository.py +54 -0
  90. ixmp4-0.1.0/ixmp4/data/db/region/__init__.py +4 -0
  91. ixmp4-0.1.0/ixmp4/data/db/region/docs.py +19 -0
  92. ixmp4-0.1.0/ixmp4/data/db/region/filter.py +56 -0
  93. ixmp4-0.1.0/ixmp4/data/db/region/model.py +18 -0
  94. ixmp4-0.1.0/ixmp4/data/db/region/repository.py +59 -0
  95. ixmp4-0.1.0/ixmp4/data/db/run/__init__.py +2 -0
  96. ixmp4-0.1.0/ixmp4/data/db/run/filter.py +7 -0
  97. ixmp4-0.1.0/ixmp4/data/db/run/model.py +41 -0
  98. ixmp4-0.1.0/ixmp4/data/db/run/repository.py +179 -0
  99. ixmp4-0.1.0/ixmp4/data/db/scenario/__init__.py +4 -0
  100. ixmp4-0.1.0/ixmp4/data/db/scenario/docs.py +7 -0
  101. ixmp4-0.1.0/ixmp4/data/db/scenario/filter.py +64 -0
  102. ixmp4-0.1.0/ixmp4/data/db/scenario/model.py +17 -0
  103. ixmp4-0.1.0/ixmp4/data/db/scenario/repository.py +64 -0
  104. ixmp4-0.1.0/ixmp4/data/db/timeseries.py +137 -0
  105. ixmp4-0.1.0/ixmp4/data/db/unit/__init__.py +5 -0
  106. ixmp4-0.1.0/ixmp4/data/db/unit/docs.py +22 -0
  107. ixmp4-0.1.0/ixmp4/data/db/unit/filter.py +62 -0
  108. ixmp4-0.1.0/ixmp4/data/db/unit/model.py +17 -0
  109. ixmp4-0.1.0/ixmp4/data/db/unit/repository.py +66 -0
  110. ixmp4-0.1.0/ixmp4/data/db/utils.py +15 -0
  111. ixmp4-0.1.0/ixmp4/data/types.py +12 -0
  112. ixmp4-0.1.0/ixmp4/db/__init__.py +63 -0
  113. ixmp4-0.1.0/ixmp4/db/filters.py +213 -0
  114. ixmp4-0.1.0/ixmp4/db/migrations/env.py +89 -0
  115. ixmp4-0.1.0/ixmp4/db/migrations/script.py.mako +25 -0
  116. ixmp4-0.1.0/ixmp4/db/migrations/versions/c71efc396d2b_initial_migration.py +506 -0
  117. ixmp4-0.1.0/ixmp4/db/utils/__init__.py +15 -0
  118. ixmp4-0.1.0/ixmp4/db/utils/alembic.py +44 -0
  119. ixmp4-0.1.0/ixmp4/db/utils/sqlite.py +37 -0
  120. ixmp4-0.1.0/ixmp4/py.typed +0 -0
  121. ixmp4-0.1.0/ixmp4/server/__init__.py +19 -0
  122. ixmp4-0.1.0/ixmp4/server/rest/__init__.py +58 -0
  123. ixmp4-0.1.0/ixmp4/server/rest/base.py +7 -0
  124. ixmp4-0.1.0/ixmp4/server/rest/decorators.py +6 -0
  125. ixmp4-0.1.0/ixmp4/server/rest/deps.py +79 -0
  126. ixmp4-0.1.0/ixmp4/server/rest/docs.py +135 -0
  127. ixmp4-0.1.0/ixmp4/server/rest/iamc/__init__.py +1 -0
  128. ixmp4-0.1.0/ixmp4/server/rest/iamc/datapoint.py +92 -0
  129. ixmp4-0.1.0/ixmp4/server/rest/iamc/model.py +35 -0
  130. ixmp4-0.1.0/ixmp4/server/rest/iamc/region.py +35 -0
  131. ixmp4-0.1.0/ixmp4/server/rest/iamc/scenario.py +35 -0
  132. ixmp4-0.1.0/ixmp4/server/rest/iamc/timeseries.py +66 -0
  133. ixmp4-0.1.0/ixmp4/server/rest/iamc/unit.py +35 -0
  134. ixmp4-0.1.0/ixmp4/server/rest/iamc/variable.py +52 -0
  135. ixmp4-0.1.0/ixmp4/server/rest/meta.py +68 -0
  136. ixmp4-0.1.0/ixmp4/server/rest/model.py +53 -0
  137. ixmp4-0.1.0/ixmp4/server/rest/region.py +60 -0
  138. ixmp4-0.1.0/ixmp4/server/rest/run.py +65 -0
  139. ixmp4-0.1.0/ixmp4/server/rest/scenario.py +52 -0
  140. ixmp4-0.1.0/ixmp4/server/rest/unit.py +59 -0
  141. ixmp4-0.1.0/ixmp4/server/workers.py +8 -0
  142. ixmp4-0.1.0/pyproject.toml +112 -0
  143. 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
+ [![license: MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/iiasa/ixmp4/blob/main/LICENSE)
40
+ [![python](https://img.shields.io/badge/python-3.10-blue?logo=python&logoColor=white)](https://github.com/iiasa/ixmp4)
41
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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
+ [![license: MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/iiasa/ixmp4/blob/main/LICENSE)
6
+ [![python](https://img.shields.io/badge/python-3.10-blue?logo=python&logoColor=white)](https://github.com/iiasa/ixmp4)
7
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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,4 @@
1
+ from ixmp4.cli import app
2
+
3
+ if __name__ == "__main__":
4
+ app()
@@ -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