etlup 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.
etlup-0.0.1/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ play.py
3
+ env_personal.py
4
+ .idea/
5
+ .VSCodeCounter/
6
+ .vscode/
7
+ file_uploads/
8
+ webdavtest.py
9
+ tempdb.sqlite
10
+ dev-key
11
+ dev-key.pub
12
+ etlcharta/
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Hayden Swanson <hayden_swanson22@yahoo.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
etlup-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,44 @@
1
+ Metadata-Version: 2.3
2
+ Name: etlup
3
+ Version: 0.0.1
4
+ Project-URL: Documentation, https://github.com/Hayden Swanson/etlup#readme
5
+ Project-URL: Issues, https://github.com/Hayden Swanson/etlup/issues
6
+ Project-URL: Source, https://github.com/Hayden Swanson/etlup
7
+ Author-email: Hayden Swanson <hayden_swanson22@yahoo.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE.txt
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
19
+ Requires-Python: >=3.8
20
+ Requires-Dist: matplotlib==3.7.2
21
+ Requires-Dist: pydantic==2.5.3
22
+ Description-Content-Type: text/markdown
23
+
24
+ # etlup
25
+
26
+ [![PyPI - Version](https://img.shields.io/pypi/v/etlup.svg)](https://pypi.org/project/etlup)
27
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/etlup.svg)](https://pypi.org/project/etlup)
28
+
29
+ -----
30
+
31
+ ## Table of Contents
32
+
33
+ - [Installation](#installation)
34
+ - [License](#license)
35
+
36
+ ## Installation
37
+
38
+ ```console
39
+ pip install etlup
40
+ ```
41
+
42
+ ## License
43
+
44
+ `etlup` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
etlup-0.0.1/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # etlup
2
+
3
+ [![PyPI - Version](https://img.shields.io/pypi/v/etlup.svg)](https://pypi.org/project/etlup)
4
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/etlup.svg)](https://pypi.org/project/etlup)
5
+
6
+ -----
7
+
8
+ ## Table of Contents
9
+
10
+ - [Installation](#installation)
11
+ - [License](#license)
12
+
13
+ ## Installation
14
+
15
+ ```console
16
+ pip install etlup
17
+ ```
18
+
19
+ ## License
20
+
21
+ `etlup` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
@@ -0,0 +1,3 @@
1
+
2
+ #to use the package as a module, if etlup becomes stable I will switch it or if we do more packages
3
+ from etlup.src import etlup
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "etlup"
7
+ dynamic = ["version"]
8
+ description = ''
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ keywords = []
13
+ authors = [
14
+ { name = "Hayden Swanson", email = "hayden_swanson22@yahoo.com" },
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Programming Language :: Python",
19
+ "Programming Language :: Python :: 3.8",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: Implementation :: CPython",
25
+ "Programming Language :: Python :: Implementation :: PyPy",
26
+ ]
27
+ dependencies = [
28
+ "pydantic==2.5.3",
29
+ "matplotlib==3.7.2"
30
+ ]
31
+
32
+ [project.urls]
33
+ Documentation = "https://github.com/Hayden Swanson/etlup#readme"
34
+ Issues = "https://github.com/Hayden Swanson/etlup/issues"
35
+ Source = "https://github.com/Hayden Swanson/etlup"
36
+
37
+ [tool.hatch.version]
38
+ path = "src/etlup/__about__.py"
39
+
40
+ [tool.hatch.envs.types]
41
+ extra-dependencies = [
42
+ "mypy>=1.0.0",
43
+ ]
44
+ [tool.hatch.envs.types.scripts]
45
+ check = "mypy --install-types --non-interactive {args:src/etlup tests}"
46
+
47
+ [tool.coverage.run]
48
+ source_pkgs = ["etlup", "tests"]
49
+ branch = true
50
+ parallel = true
51
+ omit = [
52
+ "src/etlup/__about__.py",
53
+ ]
54
+
55
+ [tool.coverage.paths]
56
+ etlup = ["src/etlup", "*/etlup/src/etlup"]
57
+ tests = ["tests", "*/etlup/tests"]
58
+
59
+ [tool.coverage.report]
60
+ exclude_lines = [
61
+ "no cov",
62
+ "if __name__ == .__main__.:",
63
+ "if TYPE_CHECKING:",
64
+ ]
@@ -0,0 +1,4 @@
1
+ # SPDX-FileCopyrightText: 2024-present Hayden Swanson <hayden_swanson22@yahoo.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ __version__ = "0.0.1"
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: 2024-present Hayden Swanson <hayden_swanson22@yahoo.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
@@ -0,0 +1,286 @@
1
+ """
2
+ Contains the code for ensuring data follows a predifined format and uses Pydantic
3
+
4
+ --------------------CONSTRUCTION DATA---------------------
5
+ - ConstructionBase
6
+ - all assembly and test types inherit from this base class, contains the needed and shared fields and validation for every assembly/test (user, measurement date, etc...)
7
+
8
+ - Subclasses of ConstructionBase (assembly and test types!)
9
+ - type field overwritten to ensure it is the correct assembly or test type
10
+ - data field so the data can be of a defined format as well
11
+ * data_cache method takes the data field and trims any fat to put in the database for fast lookup (code to visualize the data will use this)
12
+
13
+ ----------------OTHER DATA NOT IMPLMEMENTED----------------
14
+ """
15
+
16
+ from typing import Union, List
17
+ from typing_extensions import Literal
18
+ from pydantic import BaseModel, field_validator, model_validator, Field, AwareDatetime, AliasChoices, RootModel, ConfigDict
19
+ import numpy as np
20
+ import json
21
+ from _ctypes import PyObj_FromPtr
22
+ import re
23
+ import json
24
+ from datetime import datetime
25
+ import pytz
26
+ from typing import get_args
27
+
28
+ #-----------------------------------------------------------#
29
+ def get_constr_model(constr_type: str):
30
+ """
31
+ Returns a single construction model class based on type
32
+ """
33
+ #loop through all subclasses of ConstructionBase
34
+ for ConstrSubclass in ConstructionBase.get_subclasses():
35
+ #get all the types the construction class is defined for
36
+ model_constr_types = get_args(ConstrSubclass.model_fields['type'].annotation)
37
+ #check if the supplied type is part of it
38
+ if constr_type in model_constr_types:
39
+ return ConstrSubclass
40
+
41
+
42
+
43
+ def validate_datetime(meas_date: datetime) -> datetime:
44
+ #if measurement date has tz information, convert from that TZ to UTC time!
45
+ if not isinstance(meas_date, datetime):
46
+ raise ValueError(f"Inputted date is not a datetime object it is {type(meas_date)}")
47
+ #is a datetime object...
48
+ if meas_date.tzinfo is None:
49
+ #need to do this otherwise it has a default for no timezone given!
50
+ raise ValueError("Measurement data has no time zone information, see https://en.wikipedia.org/wiki/ISO_8601")
51
+
52
+ #converts time to UTC time
53
+ return meas_date.astimezone(pytz.utc)
54
+
55
+ class NoIndent(object):
56
+ """ Value wrapper. """
57
+ def __init__(self, value, max_length=None):
58
+ self.value = value
59
+ self.max_length = max_length
60
+
61
+
62
+ class DocumentationEncoder(json.JSONEncoder):
63
+ FORMAT_SPEC = '@@{}@@'
64
+ regex = re.compile(FORMAT_SPEC.format(r'(\d+)'))
65
+
66
+ def __init__(self, **kwargs):
67
+ # Save copy of any keyword argument values needed for use here.
68
+ self.__sort_keys = kwargs.get('sort_keys', None)
69
+ super(DocumentationEncoder, self).__init__(**kwargs)
70
+
71
+ def default(self, obj):
72
+ return (self.FORMAT_SPEC.format(id(obj)) if isinstance(obj, NoIndent)
73
+ else super(DocumentationEncoder, self).default(obj))
74
+
75
+ def encode(self, obj):
76
+ format_spec = self.FORMAT_SPEC # Local var to expedite access.
77
+ json_repr = super(DocumentationEncoder, self).encode(obj) # Default JSON.
78
+
79
+ # Replace any marked-up object ids in the JSON repr with the
80
+ # value returned from the json.dumps() of the corresponding
81
+ # wrapped Python object.
82
+ for match in self.regex.finditer(json_repr):
83
+ # see https://stackoverflow.com/a/15012814/355230
84
+ _id = int(match.group(1))
85
+ no_indent = PyObj_FromPtr(_id)
86
+ if no_indent.max_length is not None and isinstance(no_indent.value, list) and len(str(no_indent.value)) > no_indent.max_length:
87
+ truncated_value = str(no_indent.value)[0:no_indent.max_length]
88
+ json_obj_repr = truncated_value + '...'
89
+ else:
90
+ json_obj_repr = json.dumps(no_indent.value, sort_keys=self.__sort_keys)
91
+
92
+ # Replace the matched id string with json formatted representation
93
+ # of the corresponding Python object.
94
+ json_repr = json_repr.replace(
95
+ '"{}"'.format(format_spec.format(_id)), json_obj_repr)
96
+
97
+ return '\n'+json_repr #\n to get rid of weird indent in html :)
98
+
99
+ #----------------------SUBMODELS------------------------#
100
+ class SensorVendorTestData(BaseModel):
101
+ vendor_leakage_current_uA: Union[None,float] = Field(validation_alias=AliasChoices('vendor_leakage_current_uA','Vendor Leakage Current [uA]'))
102
+ vendor_breakdown_voltage_V: Union[None,float] = Field(validation_alias=AliasChoices('vendor_breakdown_voltage_V','Vendor Breakdown Voltage [V]'))
103
+ vendor_category: Union[None,Literal["BAD", "GOOD", "MEDIUM"]] = Field(validation_alias=AliasChoices('vendor_category','Vendor Category'))
104
+ current: Union[None,List] = Field(validation_alias=AliasChoices('current','Current'))
105
+ voltage: Union[None,List] = Field(validation_alias=AliasChoices('voltage','Voltage'))
106
+
107
+
108
+ class GantryPickAndPlaceData(BaseModel):
109
+ target: List[float]
110
+ actual: List[float]
111
+ delta: List[float]
112
+
113
+ @field_validator('*')
114
+ @classmethod
115
+ def length_check(cls, v):
116
+ if len(v) != 4:
117
+ raise ValueError("The required length is 4 for target, actual and delta. It is [x, y, z, rot]")
118
+ return v
119
+
120
+
121
+ #---------------------------------------- BASE CONSTRUCTION MODELS ---------------------------------------#
122
+ class ConstrHelperMixin:
123
+ @classmethod
124
+ def get_examples(cls):
125
+ json_schema = cls.model_config.get("json_schema_extra")
126
+ return [json.dumps(examp, cls=DocumentationEncoder, indent=2) for examp in json_schema.get("examples", []) if json_schema]
127
+
128
+ class ConstructionBase(BaseModel, ConstrHelperMixin):
129
+ measurement_date: AwareDatetime #force times to have timezone
130
+ location: str
131
+ user_created: str
132
+
133
+ @field_validator('*')
134
+ @classmethod
135
+ def empty_str_to_none(cls, v):
136
+ if isinstance(v, str) and v.strip() == '':
137
+ return None
138
+ return v
139
+
140
+ @classmethod
141
+ def get_subclasses(cls):
142
+ return tuple(cls.__subclasses__())
143
+
144
+ @field_validator("measurement_date")
145
+ @classmethod
146
+ def validate_measurement_date(cls, v):
147
+ return validate_datetime(v)
148
+
149
+ #------------------------------------------------------#
150
+
151
+ class SensorVendorTest(ConstructionBase):
152
+ model_config = ConfigDict(json_schema_extra={
153
+ 'examples': [
154
+ {
155
+ "component": "TYL4U001",
156
+ "type": "Sensor Vendor Test",
157
+ "measurement_date": "2023-01-01T12:00:00+01:00",
158
+ "location": "FBK",
159
+ "user_created": "fsiviero",
160
+ "data": {
161
+ "vendor_leakage_current_uA": 158.176,
162
+ "vendor_breakdown_voltage_V": 282.0,
163
+ "vendor_category": "BAD",
164
+ "current": NoIndent([1.7981099942332435e-9, 2.1325199384136795e-9,2.3892399170222234e-9,2.6674600306364482e-9,2.9917699428949618e-9,3.3739500082674567e-9,3.8820999748168106e-9,4.47047021623348e-9,5.163700134147575e-9,5.982729867071157e-9,6.888820180961375e-9,7.861340023396224e-9,8.972140363994185e-9,9.177109738800482e-9,1.1357499829500739e-8,1.3345699656497345e-8,1.591829956737456e-8,1.9567799824926624e-8,2.4187400526898273e-8,2.9501000753384687e-8,4.2371500086346714e-8,5.5717400755384006e-6,0.000025144199753412977,0.000034218599466839805,0.000040536098822485656,0.00004414160139276646,0.000045468401367543265,0.00005196220081415959,0.00006523320189444348,0.00007574760093120858,0.00008891220204532146,0.00011379199713701382,0.00013335900439415127,0.00015114799316506833,0.0001657230022829026,0.00018318099319003522,0.00020124799630139023,0.00021330300660338253,0.00022421199537348002,0.00023484700068365782,0.00024567899527028203,0.00025691199698485434,0.0002710630069486797,0.00028676798683591187,0.0003011419903486967,0.00031354298698715866,0.0003255319898016751,0.00033718798658810556,0.00034998898627236485,0.00037184200482442975,0.0003914310073014349,0.0004061759973410517,0.00042100698919966817,0.0004358369915280491,0.0004506719997152686,0.00046582298818975687,0.0004808040102943778,0.0004961600061506033,0.0005116279935464263,0.0005280390032567084,0.000550133001524955,0.000574567005969584], max_length=40),
165
+ "voltage": NoIndent([0.0,2.0,4.0,6.0,8.0,10.0,12.0,14.0,16.0,18.0,20.0,22.0,23.0,25.0,26.0,28.0,30.0,32.0,34.0,36.0,38.0,40.0,41.0,43.0,45.0,47.0,49.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,85.0,90.0,95.0,100.0,105.0,110.0,115.0,120.0,125.0,130.0,135.0,140.0,145.0,150.0,155.0,160.0,165.0,170.0,175.0,180.0,185.0,190.0,195.0,200.0,205.0,210.0,215.0,220.0,225.0,230.0,235.0,240.0,245.0,250.0,255.0,260.0,265.0,270.0,275.0,280.0], max_length=40)
166
+ }
167
+ }
168
+ ],
169
+ })
170
+
171
+ type: Literal['Sensor Vendor Test']
172
+ component: str
173
+ data: SensorVendorTestData
174
+
175
+ def calc_data_cache(self):
176
+ return self.data
177
+
178
+
179
+ class ModuleETROCStatus(ConstructionBase):
180
+ model_config = ConfigDict(json_schema_extra={
181
+ 'examples': [
182
+ {
183
+ "component": "PE0001",
184
+ "type": "etroc_status",
185
+ "measurement_date": "2023-01-01T12:00:00+01:00",
186
+ "location": "BU",
187
+ "user_created": "hayden",
188
+ "data": NoIndent(np.ones((16,16), dtype=int).tolist(), max_length=60)
189
+ }
190
+ ],
191
+ })
192
+ type: Literal['etroc_status']
193
+ component: str
194
+ data: List[List[int]]
195
+
196
+ @field_validator('data')
197
+ @classmethod
198
+ def length_check(cls, v):
199
+ v_arr = np.array(v)
200
+ if v_arr.shape != (16,16):
201
+ raise ValueError(f"Your array is not the correct shape, it should be 16x16, you gave: {v_arr.shape}")
202
+
203
+ def calc_data_cache(self):
204
+ return self.data
205
+
206
+
207
+ #---------------------ASSEMBLY----------------------#
208
+
209
+ class SubassemblyAlignment(ConstructionBase):
210
+ model_config = ConfigDict(json_schema_extra={
211
+ 'examples': [
212
+ {
213
+ "compoenent": "PS0001",
214
+ "type": "subassembly alignment",
215
+ "measurement_date": "2023-01-01T12:00:00+01:00",
216
+ "location": "BU",
217
+ "user_created": "hayden",
218
+ "data": {
219
+ "target": NoIndent([639.141118, 287.244992, 64.009534,-0.048954]),
220
+ "actual": NoIndent([639.141118, 287.244992, 64.009534,-0.048954]),
221
+ "delta": NoIndent([639.141118, 287.244992, 64.009534,-0.048954])
222
+ }
223
+ },
224
+ ],
225
+ })
226
+ type: Literal['subassembly alignment']
227
+ component: str
228
+ data: GantryPickAndPlaceData
229
+
230
+ def calc_data_cache(self):
231
+ return self.data
232
+
233
+ class GantryPickPlace(ConstructionBase):
234
+ model_config = ConfigDict(json_schema_extra={
235
+ 'examples': [
236
+ {
237
+ "module": "PBU0001",
238
+ "compoenent": "PE0001",
239
+ "component_pos": 1,
240
+ "type": "pick and place survey precure",
241
+ "measurement_date": "2023-01-01T12:00:00+01:00",
242
+ "location": "BU",
243
+ "user_created": "hayden",
244
+ "data": {
245
+ "target": NoIndent([639.141118, 287.244992, 64.009534,-0.048954]),
246
+ "actual": NoIndent([639.141118, 287.244992, 64.009534,-0.048954]),
247
+ "delta": NoIndent([639.141118, 287.244992, 64.009534,-0.048954])
248
+ }
249
+ },
250
+ {
251
+ "module": "PBU0001",
252
+ "compoenent": "PE0001",
253
+ "component_pos": 1,
254
+ "type": "pick and place survey postcure",
255
+ "measurement_date": "2023-01-01T12:00:00+01:00",
256
+ "location": "BU",
257
+ "user_created": "hayden",
258
+ "data": {
259
+ "target": NoIndent([639.141118, 287.244992, 64.009534,-0.048954]),
260
+ "actual": NoIndent([639.141118, 287.244992, 64.009534,-0.048954]),
261
+ "delta": NoIndent([639.141118, 287.244992, 64.009534,-0.048954])
262
+ }
263
+ }
264
+ ],
265
+ })
266
+ type: Literal['pick and place survey precure', 'pick and place survey postcure']
267
+ module: str
268
+ component: str
269
+ component_pos: int
270
+ data: GantryPickAndPlaceData
271
+
272
+ def calc_data_cache(self):
273
+ print("whoops")
274
+ return self.data
275
+
276
+ def display_data(self):
277
+ #take the data and make a plot or calculation
278
+ pass
279
+
280
+ def as_html_card(self):
281
+ pass
282
+
283
+ #-------------------------Model for list of construction objects-------------------------------#
284
+ class ConstructionCong(RootModel): #cong for conglomerate
285
+ #https://github.com/pydantic/pydantic/issues/3947
286
+ root: List[Union[ConstructionBase.get_subclasses()]] = Field(..., discriminator='type') #could try Union[Type[ConstructionBase]] later
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: 2024-present Hayden Swanson <hayden_swanson22@yahoo.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT