tyba-client 0.2.3__tar.gz → 0.2.4__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.
Potentially problematic release.
This version of tyba-client might be problematic. Click here for more details.
- tyba_client-0.2.4/PKG-INFO +56 -0
- tyba_client-0.2.4/README.md +32 -0
- {tyba_client-0.2.3 → tyba_client-0.2.4}/pyproject.toml +7 -3
- {tyba_client-0.2.3 → tyba_client-0.2.4}/tyba_client/client.py +10 -1
- tyba_client-0.2.4/tyba_client/io.py +166 -0
- {tyba_client-0.2.3 → tyba_client-0.2.4}/tyba_client/models.py +2 -26
- tyba_client-0.2.4/tyba_client/utils.py +29 -0
- tyba_client-0.2.3/PKG-INFO +0 -24
- tyba_client-0.2.3/README.md +0 -1
- tyba_client-0.2.3/setup.py +0 -36
- tyba_client-0.2.3/tyba_client/utils.py +0 -61
- {tyba_client-0.2.3 → tyba_client-0.2.4}/LICENSE +0 -0
- {tyba_client-0.2.3 → tyba_client-0.2.4}/tyba_client/__init__.py +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: tyba-client
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Summary: A Python API client for the Tyba Public API
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Tyler Nisonoff
|
|
7
|
+
Author-email: tyler@tybaenergy.com
|
|
8
|
+
Requires-Python: >=3.8,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Requires-Dist: dataclasses-json (>=0.5.4,<0.6.0)
|
|
16
|
+
Requires-Dist: generation-models (>=0.3.0,<0.4.0)
|
|
17
|
+
Requires-Dist: marshmallow (>=3.12.1,<4.0.0)
|
|
18
|
+
Requires-Dist: pandas (>=1.3.2,<2.0.0)
|
|
19
|
+
Requires-Dist: requests (>=2.25.1,<3.0.0)
|
|
20
|
+
Requires-Dist: structlog (>=23.1.0,<24.0.0)
|
|
21
|
+
Requires-Dist: tyba-financial-model (>=0.1.0,<0.2.0)
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Tyba API Client
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
For examples see [https://github.com/Tyba-Energy/tyba-client-notebooks](https://github.com/Tyba-Energy/tyba-client-notebooks).
|
|
28
|
+
The script examples in tyba-python-client/examples will be deprecated eventually.
|
|
29
|
+
|
|
30
|
+
## Development
|
|
31
|
+
### Docs
|
|
32
|
+
We use [`sphinx`](https://github.com/sphinx-doc/sphinx) and
|
|
33
|
+
[`autodoc-pydantic`](https://github.com/mansenfranzen/autodoc_pydantic) to manage the documentation for the client.
|
|
34
|
+
Source .rst files can be found in docs/source.
|
|
35
|
+
|
|
36
|
+
To generate/update documentation for the Tyba client, first make sure
|
|
37
|
+
your poetry environment includes the latest versions of all the dependency packages included in the docs. For example,
|
|
38
|
+
if `generation-models` was recently updated and pushed to pypi, you should run `poetry update` (or if you are concerned
|
|
39
|
+
about changing other packages, just `poetry add generation-models==x.x.x` where `x.x.x` is the latest version).
|
|
40
|
+
Then, `cd` into the docs directory and run the makefile that generates the HTML documentation
|
|
41
|
+
```bash
|
|
42
|
+
# Assuming you are already in the tyba-python-client directory
|
|
43
|
+
$ cd docs
|
|
44
|
+
$ poetry run make html
|
|
45
|
+
```
|
|
46
|
+
The HTML documentation can be found in docs/build/html.
|
|
47
|
+
|
|
48
|
+
This HTML documentation now needs to be uploaded to s3, so it
|
|
49
|
+
can be served at [https://docs.tybaenergy.com/api/](https://docs.tybaenergy.com/api/). We have a python script to do this
|
|
50
|
+
```bash
|
|
51
|
+
poetry run python docs/upload_to_s3.py
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Tyba API Client
|
|
2
|
+
|
|
3
|
+
## Examples
|
|
4
|
+
For examples see [https://github.com/Tyba-Energy/tyba-client-notebooks](https://github.com/Tyba-Energy/tyba-client-notebooks).
|
|
5
|
+
The script examples in tyba-python-client/examples will be deprecated eventually.
|
|
6
|
+
|
|
7
|
+
## Development
|
|
8
|
+
### Docs
|
|
9
|
+
We use [`sphinx`](https://github.com/sphinx-doc/sphinx) and
|
|
10
|
+
[`autodoc-pydantic`](https://github.com/mansenfranzen/autodoc_pydantic) to manage the documentation for the client.
|
|
11
|
+
Source .rst files can be found in docs/source.
|
|
12
|
+
|
|
13
|
+
To generate/update documentation for the Tyba client, first make sure
|
|
14
|
+
your poetry environment includes the latest versions of all the dependency packages included in the docs. For example,
|
|
15
|
+
if `generation-models` was recently updated and pushed to pypi, you should run `poetry update` (or if you are concerned
|
|
16
|
+
about changing other packages, just `poetry add generation-models==x.x.x` where `x.x.x` is the latest version).
|
|
17
|
+
Then, `cd` into the docs directory and run the makefile that generates the HTML documentation
|
|
18
|
+
```bash
|
|
19
|
+
# Assuming you are already in the tyba-python-client directory
|
|
20
|
+
$ cd docs
|
|
21
|
+
$ poetry run make html
|
|
22
|
+
```
|
|
23
|
+
The HTML documentation can be found in docs/build/html.
|
|
24
|
+
|
|
25
|
+
This HTML documentation now needs to be uploaded to s3, so it
|
|
26
|
+
can be served at [https://docs.tybaenergy.com/api/](https://docs.tybaenergy.com/api/). We have a python script to do this
|
|
27
|
+
```bash
|
|
28
|
+
poetry run python docs/upload_to_s3.py
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tyba-client"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
4
4
|
description = "A Python API client for the Tyba Public API"
|
|
5
5
|
authors = ["Tyler Nisonoff <tyler@tybaenergy.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -16,13 +16,17 @@ dataclasses-json = "^0.5.4"
|
|
|
16
16
|
marshmallow = "^3.12.1"
|
|
17
17
|
pandas = "^1.3.2"
|
|
18
18
|
tyba-financial-model = "^0.1.0"
|
|
19
|
-
generation-models = "^0.
|
|
19
|
+
generation-models = "^0.3.0"
|
|
20
20
|
structlog = "^23.1.0"
|
|
21
21
|
|
|
22
|
-
[tool.poetry.dev
|
|
22
|
+
[tool.poetry.group.dev.dependencies]
|
|
23
23
|
pytest = "^5.2"
|
|
24
24
|
ipython = "^7.29.0"
|
|
25
25
|
ipdb = "^0.13.9"
|
|
26
|
+
sphinx = "^7.0.1"
|
|
27
|
+
autodoc-pydantic = "^1.8.0"
|
|
28
|
+
sphinx-material = {git = "https://github.com/bashtage/sphinx-material.git"}
|
|
29
|
+
boto3 = "^1.28.22"
|
|
26
30
|
|
|
27
31
|
[build-system]
|
|
28
32
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -13,6 +13,9 @@ logger = get_logger()
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class Ancillary(object):
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
"""
|
|
16
19
|
def __init__(self, prices):
|
|
17
20
|
self.prices = prices
|
|
18
21
|
|
|
@@ -34,6 +37,9 @@ class Ancillary(object):
|
|
|
34
37
|
|
|
35
38
|
|
|
36
39
|
class LMP(object):
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
"""
|
|
37
43
|
def __init__(self, prices):
|
|
38
44
|
self.prices = prices
|
|
39
45
|
|
|
@@ -51,6 +57,9 @@ class LMP(object):
|
|
|
51
57
|
|
|
52
58
|
|
|
53
59
|
class Services(object):
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
"""
|
|
54
63
|
def __init__(self, client):
|
|
55
64
|
self.client = client
|
|
56
65
|
self.ancillary = Ancillary(self)
|
|
@@ -64,7 +73,7 @@ class Services(object):
|
|
|
64
73
|
|
|
65
74
|
|
|
66
75
|
class Client(object):
|
|
67
|
-
"""Tyba client class"""
|
|
76
|
+
"""Tyba valuation client class"""
|
|
68
77
|
|
|
69
78
|
DEFAULT_OPTIONS = {
|
|
70
79
|
'version': '0.1'
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from generation_models import PVModuleMermoudLejeune, ONDInverter, ONDEfficiencyCurve, ONDTemperatureDerateCurve, SolarResource, SolarResourceTimeSeries
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def read_pvsyst_file(path: str) -> dict:
|
|
8
|
+
try:
|
|
9
|
+
with open(path, "r", encoding="utf-8-sig") as f:
|
|
10
|
+
blob = {}
|
|
11
|
+
trace = [blob]
|
|
12
|
+
lines = [line for line in f.readlines() if "=" in line]
|
|
13
|
+
indentations = [len(line) - len(line.lstrip()) for line in lines]
|
|
14
|
+
for indentation, line in zip(indentations, lines):
|
|
15
|
+
if divmod(indentation, 2)[1] != 0:
|
|
16
|
+
raise PVSystFileError(f"invalid indentation at {line}")
|
|
17
|
+
structure = [(y - x) // 2 for x, y in zip(indentations[:-1], indentations[1:])]
|
|
18
|
+
structure.append(0)
|
|
19
|
+
for line, typ in zip(lines, structure):
|
|
20
|
+
stripped = line.strip()
|
|
21
|
+
k, v = stripped.split("=")
|
|
22
|
+
if typ == 0:
|
|
23
|
+
trace[-1][k] = v
|
|
24
|
+
elif typ == 1:
|
|
25
|
+
trace[-1][k] = {"type": v, "items": {}}
|
|
26
|
+
trace.append(trace[-1][k]["items"])
|
|
27
|
+
elif typ < 0:
|
|
28
|
+
trace[-1][k] = v
|
|
29
|
+
for _ in range(-1 * typ):
|
|
30
|
+
trace.pop()
|
|
31
|
+
else:
|
|
32
|
+
raise PVSystFileError(f"invalid structure at {line}")
|
|
33
|
+
except UnicodeDecodeError:
|
|
34
|
+
raise PVSystFileError(f"File not utf-8 encoded. Please encode the file to utf-8 and try again")
|
|
35
|
+
return blob
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PVSystFileError(ValueError):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def pv_module_from_pan(pan_file: str, bifacial_ground_clearance_height=1.0, bifacial_transmission_factor:float = 0.013):
|
|
43
|
+
pan_blob = read_pvsyst_file(pan_file)
|
|
44
|
+
data = pan_blob["PVObject_"]["items"]
|
|
45
|
+
commercial = data["PVObject_Commercial"]["items"]
|
|
46
|
+
if "PVObject_IAM" in data:
|
|
47
|
+
iam_points = [
|
|
48
|
+
v.split(",")
|
|
49
|
+
for k, v in data["PVObject_IAM"]["items"]["IAMProfile"]["items"].items()
|
|
50
|
+
if k.startswith("Point_")
|
|
51
|
+
]
|
|
52
|
+
iam_angles = [float(v[0]) for v in iam_points]
|
|
53
|
+
iam_values = [float(v[1]) for v in iam_points]
|
|
54
|
+
else:
|
|
55
|
+
iam_angles = None
|
|
56
|
+
iam_values = None
|
|
57
|
+
return PVModuleMermoudLejeune(
|
|
58
|
+
bifacial="BifacialityFactor" in data,
|
|
59
|
+
bifacial_transmission_factor=bifacial_transmission_factor,
|
|
60
|
+
bifaciality=float(data.get("BifacialityFactor", 0.65)),
|
|
61
|
+
bifacial_ground_clearance_height=bifacial_ground_clearance_height,
|
|
62
|
+
n_parallel=int(data["NCelP"]),
|
|
63
|
+
n_diodes=int(data["NDiode"]),
|
|
64
|
+
n_series=int(data["NCelS"]),
|
|
65
|
+
t_ref=float(data["TRef"]),
|
|
66
|
+
s_ref=float(data["GRef"]),
|
|
67
|
+
i_sc_ref=float(data["Isc"]),
|
|
68
|
+
v_oc_ref=float(data["Voc"]),
|
|
69
|
+
i_mp_ref=float(data["Imp"]),
|
|
70
|
+
v_mp_ref=float(data["Vmp"]),
|
|
71
|
+
alpha_sc=float(data["muISC"]) * 1e-3, # TODO: check units
|
|
72
|
+
beta_oc=float(data["muVocSpec"]) * 1e-3,
|
|
73
|
+
n_0=float(data["Gamma"]),
|
|
74
|
+
mu_n=float(data["muGamma"]),
|
|
75
|
+
r_sh_ref=float(data["RShunt"]),
|
|
76
|
+
r_s=float(data["RSerie"]),
|
|
77
|
+
r_sh_0=float(data["Rp_0"]),
|
|
78
|
+
r_sh_exp=float(data["Rp_Exp"]),
|
|
79
|
+
tech=data["Technol"],
|
|
80
|
+
length=float(commercial["Height"]),
|
|
81
|
+
width=float(commercial["Width"]),
|
|
82
|
+
# faiman cell temp model used by PVSyst
|
|
83
|
+
t_c_fa_alpha=float(data["Absorb"]),
|
|
84
|
+
# IAM
|
|
85
|
+
iam_c_cs_iam_value=iam_values,
|
|
86
|
+
iam_c_cs_inc_angle=iam_angles,
|
|
87
|
+
custom_d2_mu_tau=data.get("D2MuTau"),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def inverter_from_ond(ond_file: str, includes_xfmr: bool = True):
|
|
92
|
+
ond = read_pvsyst_file(ond_file)
|
|
93
|
+
data = ond['PVObject_']['items']
|
|
94
|
+
converter = data['Converter']['items']
|
|
95
|
+
voltage_curve_points = [float(v) for v in converter['VNomEff'].split(',') if v]
|
|
96
|
+
if len(voltage_curve_points) != 3:
|
|
97
|
+
raise NotImplementedError('OND Inverter only accepts voltage curves of length 3')
|
|
98
|
+
temp_derate_curve = ONDTemperatureDerateCurve(
|
|
99
|
+
ambient_temp=[-300, float(converter['TPMax']), float(converter['TPNom']), float(converter['TPLim1']), float(converter['TPLimAbs'])],
|
|
100
|
+
max_ac_power=[float(converter['PMaxOUT']) * 1e3, float(converter['PMaxOUT']) * 1e3, float(converter['PNomConv']) * 1e3, float(converter['PLim1']) * 1e3, float(converter.get('PlimAbs', 0.)) * 1e3]
|
|
101
|
+
)
|
|
102
|
+
raw_power_curves = [converter[f"ProfilPIOV{i}"]['items'] for i in [1, 2, 3]]
|
|
103
|
+
power_curves = []
|
|
104
|
+
for curve in raw_power_curves:
|
|
105
|
+
points = [[float(v) for v in curve[f"Point_{i}"].split(',')] for i in range(1, int(curve["NPtsEff"]) + 1)]
|
|
106
|
+
dc, ac = zip(*points)
|
|
107
|
+
power_curves.append(ONDEfficiencyCurve(dc_power=dc, ac_power=ac))
|
|
108
|
+
aux_loss = data.get('Aux_Loss')
|
|
109
|
+
aux_loss_threshold = data.get('Aux_Thresh')
|
|
110
|
+
if aux_loss is not None:
|
|
111
|
+
aux_loss = float(aux_loss)
|
|
112
|
+
aux_loss_threshold = float(aux_loss_threshold)
|
|
113
|
+
return ONDInverter(
|
|
114
|
+
mppt_low=float(converter['VMppMin']),
|
|
115
|
+
mppt_high=float(converter['VMPPMax']),
|
|
116
|
+
paco=float(converter['PMaxOUT']) * 1e3,
|
|
117
|
+
vdco=voltage_curve_points[1],
|
|
118
|
+
temp_derate_curve=temp_derate_curve,
|
|
119
|
+
nominal_voltages=voltage_curve_points,
|
|
120
|
+
power_curves=power_curves,
|
|
121
|
+
dc_turn_on=float(converter['PSeuil']),
|
|
122
|
+
pnt=float(data['Night_Loss']),
|
|
123
|
+
aux_loss=aux_loss,
|
|
124
|
+
aux_loss_threshold=aux_loss_threshold,
|
|
125
|
+
includes_xfmr=includes_xfmr
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
psm_column_map = {
|
|
130
|
+
"Year": "year",
|
|
131
|
+
"Month": "month",
|
|
132
|
+
"Day": "day",
|
|
133
|
+
"Hour": "hour",
|
|
134
|
+
"Minute": "minute",
|
|
135
|
+
"GHI": "gh",
|
|
136
|
+
"DNI": "dn",
|
|
137
|
+
"DHI": "df",
|
|
138
|
+
"POA": "poa",
|
|
139
|
+
"Temperature": "tdry",
|
|
140
|
+
# twet
|
|
141
|
+
"Dew Point": "tdew",
|
|
142
|
+
"Relative Humidity": "rhum",
|
|
143
|
+
"Pressure": "pres",
|
|
144
|
+
# Snow
|
|
145
|
+
"Surface Albedo": "alb",
|
|
146
|
+
# aod
|
|
147
|
+
"Wind Speed": "wspd",
|
|
148
|
+
"Wind Direction": "wdir",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def solar_resource_from_psm_csv(filename: str, typical: bool = True, monthly_albedo: t.Optional[t.List[float]] = None) -> SolarResource:
|
|
153
|
+
with open(filename) as f:
|
|
154
|
+
_meta = [f.readline().split(",") for _ in range(2)]
|
|
155
|
+
_data = pd.read_csv(f)
|
|
156
|
+
meta = {k: v for k, v in zip(*_meta)}
|
|
157
|
+
data = _data.rename(columns=psm_column_map)
|
|
158
|
+
return SolarResource(
|
|
159
|
+
latitude=float(meta["Latitude"]),
|
|
160
|
+
longitude=float(meta["Longitude"]),
|
|
161
|
+
elevation=float(meta["Elevation"]),
|
|
162
|
+
time_zone_offset=float(meta["Time Zone"]),
|
|
163
|
+
data=SolarResourceTimeSeries(**data.to_dict(orient="list")),
|
|
164
|
+
monthly_albedo=monthly_albedo,
|
|
165
|
+
typical=typical
|
|
166
|
+
)
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from io import StringIO
|
|
5
4
|
from dataclasses_json import config, dataclass_json
|
|
6
5
|
import pandas as pd
|
|
7
6
|
import typing as t
|
|
8
|
-
from .
|
|
7
|
+
from .io import read_pvsyst_file, psm_column_map
|
|
9
8
|
|
|
10
9
|
from tyba_client.utils import string_enum
|
|
11
10
|
|
|
@@ -347,7 +346,7 @@ class SolarResource(object):
|
|
|
347
346
|
_meta = [f.readline().split(",") for _ in range(2)]
|
|
348
347
|
_data = pd.read_csv(f)
|
|
349
348
|
meta = {k: v for k, v in zip(*_meta)}
|
|
350
|
-
data = _data.rename(columns=
|
|
349
|
+
data = _data.rename(columns=psm_column_map)
|
|
351
350
|
return cls(
|
|
352
351
|
latitude=float(meta["Latitude"]),
|
|
353
352
|
longitude=float(meta["Longitude"]),
|
|
@@ -357,29 +356,6 @@ class SolarResource(object):
|
|
|
357
356
|
)
|
|
358
357
|
|
|
359
358
|
|
|
360
|
-
_psm_data_map = {
|
|
361
|
-
"Year": "year",
|
|
362
|
-
"Month": "month",
|
|
363
|
-
"Day": "day",
|
|
364
|
-
"Hour": "hour",
|
|
365
|
-
"Minute": "minute",
|
|
366
|
-
"GHI": "gh",
|
|
367
|
-
"DNI": "dn",
|
|
368
|
-
"DHI": "df",
|
|
369
|
-
"POA": "poa",
|
|
370
|
-
"Temperature": "tdry",
|
|
371
|
-
# twet
|
|
372
|
-
"Dew Point": "tdew",
|
|
373
|
-
"Relative Humidity": "rhum",
|
|
374
|
-
"Pressure": "pres",
|
|
375
|
-
# Snow
|
|
376
|
-
"Surface Albedo": "alb",
|
|
377
|
-
# aod
|
|
378
|
-
"Wind Speed": "wspd",
|
|
379
|
-
"Wind Direction": "wdir",
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
359
|
@string_enum
|
|
384
360
|
class ArrayDegradationMode(str, Enum):
|
|
385
361
|
linear = "linear"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
from marshmallow import fields
|
|
5
|
+
|
|
6
|
+
T = t.TypeVar('T', bound=Enum)
|
|
7
|
+
def string_enum(cls: t.Type[T]) -> t.Type[T]:
|
|
8
|
+
"""
|
|
9
|
+
decorator to allow Enums to be used with dataclass_json
|
|
10
|
+
Stolen from:
|
|
11
|
+
https://github.com/lidatong/dataclasses-json/issues/101#issuecomment-506418278""
|
|
12
|
+
"""
|
|
13
|
+
class EnumField(fields.Field):
|
|
14
|
+
def _serialize(self, value, attr, obj, **kwargs):
|
|
15
|
+
return value.name
|
|
16
|
+
|
|
17
|
+
def _deserialize(self, value, attr, data, **kwargs):
|
|
18
|
+
return cls[value]
|
|
19
|
+
if (not hasattr(cls, '__metadata__')):
|
|
20
|
+
setattr(cls, '__metadata__', dict())
|
|
21
|
+
|
|
22
|
+
metadata = {
|
|
23
|
+
"dataclasses_json": {
|
|
24
|
+
"encoder": lambda v: v.name,
|
|
25
|
+
"decoder": lambda name: cls[name],
|
|
26
|
+
"mm_field": EnumField(),
|
|
27
|
+
}}
|
|
28
|
+
cls.__metadata__.update(metadata)
|
|
29
|
+
return cls
|
tyba_client-0.2.3/PKG-INFO
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: tyba-client
|
|
3
|
-
Version: 0.2.3
|
|
4
|
-
Summary: A Python API client for the Tyba Public API
|
|
5
|
-
License: MIT
|
|
6
|
-
Author: Tyler Nisonoff
|
|
7
|
-
Author-email: tyler@tybaenergy.com
|
|
8
|
-
Requires-Python: >=3.8,<4.0
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
-
Requires-Dist: dataclasses-json (>=0.5.4,<0.6.0)
|
|
16
|
-
Requires-Dist: generation-models (>=0.2.0,<0.3.0)
|
|
17
|
-
Requires-Dist: marshmallow (>=3.12.1,<4.0.0)
|
|
18
|
-
Requires-Dist: pandas (>=1.3.2,<2.0.0)
|
|
19
|
-
Requires-Dist: requests (>=2.25.1,<3.0.0)
|
|
20
|
-
Requires-Dist: structlog (>=23.1.0,<24.0.0)
|
|
21
|
-
Requires-Dist: tyba-financial-model (>=0.1.0,<0.2.0)
|
|
22
|
-
Description-Content-Type: text/markdown
|
|
23
|
-
|
|
24
|
-
# Tyba API Client
|
tyba_client-0.2.3/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# Tyba API Client
|
tyba_client-0.2.3/setup.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from setuptools import setup
|
|
3
|
-
|
|
4
|
-
packages = \
|
|
5
|
-
['tyba_client']
|
|
6
|
-
|
|
7
|
-
package_data = \
|
|
8
|
-
{'': ['*']}
|
|
9
|
-
|
|
10
|
-
install_requires = \
|
|
11
|
-
['dataclasses-json>=0.5.4,<0.6.0',
|
|
12
|
-
'generation-models>=0.2.0,<0.3.0',
|
|
13
|
-
'marshmallow>=3.12.1,<4.0.0',
|
|
14
|
-
'pandas>=1.3.2,<2.0.0',
|
|
15
|
-
'requests>=2.25.1,<3.0.0',
|
|
16
|
-
'structlog>=23.1.0,<24.0.0',
|
|
17
|
-
'tyba-financial-model>=0.1.0,<0.2.0']
|
|
18
|
-
|
|
19
|
-
setup_kwargs = {
|
|
20
|
-
'name': 'tyba-client',
|
|
21
|
-
'version': '0.2.3',
|
|
22
|
-
'description': 'A Python API client for the Tyba Public API',
|
|
23
|
-
'long_description': '# Tyba API Client',
|
|
24
|
-
'author': 'Tyler Nisonoff',
|
|
25
|
-
'author_email': 'tyler@tybaenergy.com',
|
|
26
|
-
'maintainer': 'None',
|
|
27
|
-
'maintainer_email': 'None',
|
|
28
|
-
'url': 'None',
|
|
29
|
-
'packages': packages,
|
|
30
|
-
'package_data': package_data,
|
|
31
|
-
'install_requires': install_requires,
|
|
32
|
-
'python_requires': '>=3.8,<4.0',
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
setup(**setup_kwargs)
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
import typing as t
|
|
3
|
-
|
|
4
|
-
from marshmallow import fields
|
|
5
|
-
|
|
6
|
-
T = t.TypeVar('T', bound=Enum)
|
|
7
|
-
def string_enum(cls: t.Type[T]) -> t.Type[T]:
|
|
8
|
-
"""
|
|
9
|
-
decorator to allow Enums to be used with dataclass_json
|
|
10
|
-
Stolen from:
|
|
11
|
-
https://github.com/lidatong/dataclasses-json/issues/101#issuecomment-506418278""
|
|
12
|
-
"""
|
|
13
|
-
class EnumField(fields.Field):
|
|
14
|
-
def _serialize(self, value, attr, obj, **kwargs):
|
|
15
|
-
return value.name
|
|
16
|
-
|
|
17
|
-
def _deserialize(self, value, attr, data, **kwargs):
|
|
18
|
-
return cls[value]
|
|
19
|
-
if (not hasattr(cls, '__metadata__')):
|
|
20
|
-
setattr(cls, '__metadata__', dict())
|
|
21
|
-
|
|
22
|
-
metadata = {
|
|
23
|
-
"dataclasses_json": {
|
|
24
|
-
"encoder": lambda v: v.name,
|
|
25
|
-
"decoder": lambda name: cls[name],
|
|
26
|
-
"mm_field": EnumField(),
|
|
27
|
-
}}
|
|
28
|
-
cls.__metadata__.update(metadata)
|
|
29
|
-
return cls
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class PVSystFileError(ValueError):
|
|
33
|
-
pass
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def read_pvsyst_file(path: str) -> dict:
|
|
37
|
-
with open(path, "r", encoding="utf-8-sig") as f:
|
|
38
|
-
blob = {}
|
|
39
|
-
trace = [blob]
|
|
40
|
-
lines = [line for line in f.readlines() if "=" in line]
|
|
41
|
-
indentations = [len(line) - len(line.lstrip()) for line in lines]
|
|
42
|
-
for indentation, line in zip(indentations, lines):
|
|
43
|
-
if divmod(indentation, 2)[1] != 0:
|
|
44
|
-
raise PVSystFileError(f"invalid indentation at {line}")
|
|
45
|
-
structure = [(y - x) // 2 for x, y in zip(indentations[:-1], indentations[1:])]
|
|
46
|
-
structure.append(0)
|
|
47
|
-
for line, typ in zip(lines, structure):
|
|
48
|
-
stripped = line.strip()
|
|
49
|
-
k, v = stripped.split("=")
|
|
50
|
-
if typ == 0:
|
|
51
|
-
trace[-1][k] = v
|
|
52
|
-
elif typ == 1:
|
|
53
|
-
trace[-1][k] = {"type": v, "items": {}}
|
|
54
|
-
trace.append(trace[-1][k]["items"])
|
|
55
|
-
elif typ < 0:
|
|
56
|
-
trace[-1][k] = v
|
|
57
|
-
for _ in range(-1 * typ):
|
|
58
|
-
trace.pop()
|
|
59
|
-
else:
|
|
60
|
-
raise PVSystFileError(f"invalid structure at {line}")
|
|
61
|
-
return blob
|
|
File without changes
|
|
File without changes
|