sdata 1.5.0__py3-none-any.whl

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 (98) hide show
  1. sdata/__init__.py +140 -0
  2. sdata/base.py +684 -0
  3. sdata/contrib/__init__.py +0 -0
  4. sdata/contrib/piexif/__init__.py +12 -0
  5. sdata/contrib/piexif/_common.py +94 -0
  6. sdata/contrib/piexif/_dump.py +346 -0
  7. sdata/contrib/piexif/_exceptions.py +2 -0
  8. sdata/contrib/piexif/_exif.py +638 -0
  9. sdata/contrib/piexif/_insert.py +60 -0
  10. sdata/contrib/piexif/_load.py +270 -0
  11. sdata/contrib/piexif/_remove.py +56 -0
  12. sdata/contrib/piexif/_transplant.py +45 -0
  13. sdata/contrib/piexif/_webp.py +240 -0
  14. sdata/contrib/piexif/helper.py +66 -0
  15. sdata/contrib/ranger/__init__.py +8 -0
  16. sdata/contrib/ranger/collections/__init__.py +0 -0
  17. sdata/contrib/ranger/collections/rangebucketmap.py +346 -0
  18. sdata/contrib/ranger/collections/rangemap.py +396 -0
  19. sdata/contrib/ranger/collections/rangeset.py +483 -0
  20. sdata/contrib/ranger/range/__init__.py +0 -0
  21. sdata/contrib/ranger/range/cut.py +285 -0
  22. sdata/contrib/ranger/range/range.py +644 -0
  23. sdata/contrib/sobol_seq.py +394 -0
  24. sdata/contrib/sortedcontainers/LICENSE +13 -0
  25. sdata/contrib/sortedcontainers/__init__.py +74 -0
  26. sdata/contrib/sortedcontainers/sorteddict.py +789 -0
  27. sdata/contrib/sortedcontainers/sortedlist.py +2646 -0
  28. sdata/contrib/sortedcontainers/sortedset.py +733 -0
  29. sdata/contrib/sqlitedict.py +598 -0
  30. sdata/contrib/timeflake/__init__.py +40 -0
  31. sdata/contrib/timeflake/flake.py +69 -0
  32. sdata/contrib/timeflake/utils.py +47 -0
  33. sdata/deprecated/__init__.py +10 -0
  34. sdata/deprecated/data.py +1503 -0
  35. sdata/did/__init__.py +129 -0
  36. sdata/did/_http.py +67 -0
  37. sdata/did/base58btc.py +49 -0
  38. sdata/did/did_github.py +152 -0
  39. sdata/did/did_peer4.py +155 -0
  40. sdata/did/did_web.py +222 -0
  41. sdata/did/diddoc.py +75 -0
  42. sdata/did/ed25519.py +225 -0
  43. sdata/did/eddsa.py +107 -0
  44. sdata/did/errors.py +40 -0
  45. sdata/did/issue_vc.py +193 -0
  46. sdata/did/jose.py +126 -0
  47. sdata/did/keys.py +104 -0
  48. sdata/did/utils_didkey.py +161 -0
  49. sdata/did/verify_vp.py +158 -0
  50. sdata/doe.py +101 -0
  51. sdata/dtypes.py +510 -0
  52. sdata/experiments/__init__.py +331 -0
  53. sdata/experiments/blob0.py +131 -0
  54. sdata/experiments/file.py +42 -0
  55. sdata/experiments/filegroup.py +144 -0
  56. sdata/experiments/image_old.py +313 -0
  57. sdata/experiments/ks2.py +15 -0
  58. sdata/experiments/lapshear.py +8 -0
  59. sdata/experiments/material.py +7 -0
  60. sdata/experiments/parameters_deprecated.py +394 -0
  61. sdata/experiments/workbook.py +226 -0
  62. sdata/format.py +125 -0
  63. sdata/imagemeta.py +533 -0
  64. sdata/interactive.py +118 -0
  65. sdata/iolib/__init__.py +51 -0
  66. sdata/iolib/hashtree.py +264 -0
  67. sdata/iolib/json1sqlitestore.py +554 -0
  68. sdata/iolib/jsonsqlitestore.py +704 -0
  69. sdata/iolib/pud.py +242 -0
  70. sdata/iolib/reader.py +262 -0
  71. sdata/iolib/writer.py +419 -0
  72. sdata/metadata.py +882 -0
  73. sdata/parameter.py +414 -0
  74. sdata/schema.py +273 -0
  75. sdata/sclass/__init__.py +111 -0
  76. sdata/sclass/bfo_classes.py +362 -0
  77. sdata/sclass/blob.py +362 -0
  78. sdata/sclass/content.py +87 -0
  79. sdata/sclass/dataframe.py +1251 -0
  80. sdata/sclass/dataframegroup.py +187 -0
  81. sdata/sclass/dependency_graph.py +85 -0
  82. sdata/sclass/filereference.py +124 -0
  83. sdata/sclass/image.py +218 -0
  84. sdata/sclass/lazy_namespace.py +155 -0
  85. sdata/sclass/process.py +383 -0
  86. sdata/sclass/process_graph.py +221 -0
  87. sdata/sclass/zipfile.py +135 -0
  88. sdata/semantic.py +346 -0
  89. sdata/suuid.py +136 -0
  90. sdata/timestamp.py +336 -0
  91. sdata/tools.py +14 -0
  92. sdata/units.py +602 -0
  93. sdata/vocab.py +149 -0
  94. sdata-1.5.0.dist-info/METADATA +264 -0
  95. sdata-1.5.0.dist-info/RECORD +98 -0
  96. sdata-1.5.0.dist-info/WHEEL +5 -0
  97. sdata-1.5.0.dist-info/licenses/LICENSE-MIT +21 -0
  98. sdata-1.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,331 @@
1
+ # -*-coding: utf-8-*-
2
+ import logging
3
+ logger = logging.getLogger("sdata")
4
+ import pandas as pd
5
+ import numpy as np
6
+ from sdata import Data
7
+ from sdata.deprecated.data import Sdata_Uuid_Exeption
8
+ import uuid
9
+
10
+ # import sdata.experiments.ks2
11
+ # import sdata.experiments.lapshear
12
+ # import sdata.experiments.material
13
+ #
14
+ # # ["name", "default_value", "dtype", "unit", "description", "required"]
15
+ # part_default_attributes = [
16
+ # ["part_id", None, "str", "-", "Part ID", True],
17
+ # ]
18
+
19
+
20
+ class TestProgram(Data):
21
+ """A sdata Testprogram
22
+
23
+ """
24
+ # Trotz "Test"-Präfix keine pytest-Testklasse (gilt via Vererbung auch für
25
+ # TestSeries/Test) -> unterdrückt PytestCollectionWarning.
26
+ __test__ = False
27
+
28
+ ATTRIBUTES = [
29
+ ['material_name', '', 'str', '-', 'Der Name des Materials.', '', False],
30
+ ['material_norm_name', '', 'str', '-', 'Der normative Name des Materials.', '', False],
31
+ ['material_number_norm', '', 'str', '-', 'Die normative Nummer des Materials.', '', False],
32
+ ['material_manifacturer', '', 'str', '-', 'Die Hersteller des Materials.', '', False],
33
+ ['material_grade', '', 'str', '-', 'Der Zustand des Materials.', '', False],
34
+ ['material_coating', '', 'str', '-', 'Die Beschichtung des Materials.', '', False],
35
+ ['material_density', '', 'float', 'g/cm^3', 'Die Dichte des Materials.', '', False],
36
+ ['material_production_date', '', 'str', '-', 'Das Produktionsdatum des Materials.', '', False],
37
+ ['responsible_person', '', 'str', '-', 'Verantwortlicher des Testprogramms.', '', False],
38
+ ['responsible_person_phone', '', 'str', '-', 'Telefonnummer des Verantwortlichen vom Testprogramms.', '', False],
39
+ ['responsible_person_email', '', 'str', '-', 'Emailadressen des Verantwortlichen vom Testprogramms.', '', False],
40
+ ['responsible_person_organisation', '', 'str', '-', 'Organisation des Verantwortlichen vom Testprogramms.', '', False],
41
+ ['notes', '', 'str', '-', 'Notizen zum Testprogramm.', '', False],
42
+ ]
43
+
44
+ def __init__(self, **kwargs):
45
+ """
46
+
47
+ :param kwargs:
48
+ """
49
+ Data.__init__(self, **kwargs)
50
+ self.metadata.add(name=self.SDATA_CLASS, value=self.__class__.__name__, dtype="str")
51
+
52
+ # if kwargs.get("uuid_testprogram") is not None:
53
+ # try:
54
+ # self.uuid_testprogram = kwargs.get("uuid_testprogram") # store given uuid str or generate a new uuid
55
+ # except Sdata_Uuid_Exeption as exp:
56
+ # if self.auto_correct is True:
57
+ # logger.warning("got invald uuid -> generate a new uuid")
58
+ # self.uuid_testprogram = uuid.uuid4().hex
59
+ # else:
60
+ # raise
61
+
62
+ self.name_testprogram = self.name
63
+ self.uuid_testprogram = self.uuid
64
+
65
+ if kwargs.get("name_testprogram") is not None:
66
+ self._set_name(kwargs.get("name_testprogram"))
67
+ if kwargs.get("uuid_testprogram") is not None:
68
+ self._set_uuid(kwargs.get("uuid_testprogram"))
69
+
70
+ for attrlist in self.ATTRIBUTES:
71
+ self.metadata.set_attr(attrlist[0], attrlist[1],
72
+ dtype=attrlist[2],
73
+ unit=attrlist[3],
74
+ description=attrlist[4],
75
+ label=attrlist[5],
76
+ required=attrlist[6])
77
+
78
+
79
+ def gen_testseries(self, **kwargs):
80
+ """generate TestSeries instance
81
+
82
+ :param kwargs:
83
+ :return: TestSeries()
84
+ """
85
+ class_name = kwargs.get("class_name", TestSeries)
86
+ if "class_name" in kwargs:
87
+ kwargs.pop("class_name")
88
+
89
+ ts = class_name(**kwargs)
90
+ ts.project = self.project
91
+ ts.name_testprogram = self.name_testprogram
92
+ ts.uuid_testprogram = self.uuid_testprogram
93
+ ts.metadata.add(self.SDATA_PARENT, self.uuid)
94
+ return ts
95
+
96
+ @property
97
+ def longname(self):
98
+ return "|".join([self.name_testprogram])
99
+
100
+ class TestSeries(TestProgram):
101
+ """A sdata TestSeries
102
+
103
+ """
104
+
105
+ SDATA_TESTPROGRAM_NAME = "!sdata_testprogram_name"
106
+ SDATA_TESTPROGRAM_UUID = "!sdata_testprogram_uuid"
107
+
108
+ SDATA_TESTTYPE = "!sdata_testtype"
109
+
110
+
111
+
112
+ def __init__(self, **kwargs):
113
+ """Test Series
114
+
115
+ :param kwargs:
116
+ """
117
+ TestProgram.__init__(self, **kwargs)
118
+
119
+ self.metadata.add(name=self.SDATA_CLASS, value=self.__class__.__name__, dtype="str")
120
+ self.metadata.add(name=self.SDATA_TESTPROGRAM_NAME, value="N.N.", dtype="str", description="name of the testprogram")
121
+ self.metadata.add(name=self.SDATA_TESTPROGRAM_UUID, value="", dtype="str", description="uuid of the testprogram")
122
+ self.metadata.add(name=self.SDATA_TESTTYPE, value="", dtype="str", description="test type")
123
+
124
+ # if kwargs.get("name_testprogram") is not None:
125
+ # self.name_testprogram = kwargs.get("name_testprogram")
126
+ #
127
+ # if kwargs.get("uuid_testseries") is not None:
128
+ # try:
129
+ # self.uuid_testseries = kwargs.get("uuid_testseries") # store given uuid str or generate a new uuid
130
+ # except Sdata_Uuid_Exeption as exp:
131
+ # if self.auto_correct is True:
132
+ # logger.warning("got invald uuid -> generate a new uuid")
133
+ # self.uuid_testseries = uuid.uuid4().hex
134
+ # else:
135
+ # raise
136
+
137
+ self.uuid_testseries = self.uuid
138
+ self.name_testseries = self.name
139
+
140
+ # if kwargs.get("name") is not None:
141
+ # self._set_name(kwargs.get("name"))
142
+ # if kwargs.get("uuid") is not None:
143
+ # self._set_uuid(kwargs.get("uuid"))
144
+
145
+ # if kwargs.get("name_testseries") is not None:
146
+ # self.name_testseries = kwargs.get("name_testseries")
147
+
148
+ if kwargs.get("testtype") is not None:
149
+ self._set_testtype(kwargs.get("testtype"))
150
+ if kwargs.get("name_testseries") is not None:
151
+ self._set_name(kwargs.get("name_testseries"))
152
+ if kwargs.get("uuid_testseries") is not None:
153
+ self._set_uuid(kwargs.get("uuid_testseries"))
154
+ if kwargs.get("name") is not None:
155
+ self._set_name(kwargs.get("name"))
156
+ if kwargs.get("uuid") is not None:
157
+ self._set_uuid(kwargs.get("uuid"))
158
+ if kwargs.get("name_testprogram") is not None:
159
+ self._set_name_testprogram(kwargs.get("name_testprogram"))
160
+ if kwargs.get("uuid_testprogram") is not None:
161
+ self._set_uuid_testprogram(kwargs.get("uuid_testprogram"))
162
+
163
+ uuid_testseries = property(fget=TestProgram._get_uuid, fset=TestProgram._set_uuid, doc="uuid of the testseries")
164
+ name_testseries = property(fget=TestProgram._get_name, fset=TestProgram._set_name, doc="name of the testseries")
165
+
166
+ def _get_uuid_testprogram(self):
167
+ return self.metadata.get(self.SDATA_TESTPROGRAM_UUID).value
168
+ # return self._uuid
169
+
170
+ def _set_uuid_testprogram(self, value):
171
+ if isinstance(value, str):
172
+ try:
173
+ uuid.UUID(value)
174
+ self.metadata.set_attr(self.SDATA_TESTPROGRAM_UUID, uuid.UUID(value).hex)
175
+ except ValueError as exp:
176
+ logger.warning("data.uuid: %s" % exp)
177
+ raise Sdata_Uuid_Exeption("got invalid uuid str '{}'".format(str(value)))
178
+ elif isinstance(value, uuid.UUID):
179
+ self.metadata.set_attr(self.SDATA_TESTPROGRAM_UUID, value.hex)
180
+ else:
181
+ logger.error("Data.uuid: invalid uuid '{}'".format(value))
182
+ raise Exception("Data.uuid: invalid uuid '{}'".format(value))
183
+
184
+ uuid_testprogram = property(fget=_get_uuid_testprogram, fset=_set_uuid_testprogram, doc="uuid of the testprogram")
185
+
186
+ def _get_name_testprogram(self):
187
+ # return self._name
188
+ return self.metadata.get(self.SDATA_TESTPROGRAM_NAME).value
189
+
190
+ def _set_name_testprogram(self, value):
191
+ if isinstance(value, str):
192
+ try:
193
+ self.metadata.set_attr(self.SDATA_TESTPROGRAM_NAME, str(value)[:256])
194
+ except ValueError as exp:
195
+ logger.warning("data.name_testprogram: %s" % exp)
196
+ else:
197
+ # self._name = str(value)[:256]
198
+ self.metadata.set_attr(self.SDATA_TESTPROGRAM_NAME, str(value)[:256])
199
+
200
+ name_testprogram = property(fget=_get_name_testprogram, fset=_set_name_testprogram, doc="name of the testprogram")
201
+
202
+ def _get_testtype(self):
203
+ return self.metadata.get(self.SDATA_TESTTYPE).value
204
+
205
+ def _set_testtype(self, value):
206
+ if isinstance(value, str):
207
+ try:
208
+ self.metadata.set_attr(self.SDATA_TESTTYPE, str(value)[:256])
209
+ except ValueError as exp:
210
+ logger.warning("data.testtype: %s" % exp)
211
+ else:
212
+ self.metadata.set_attr(self.SDATA_TESTTYPE, str(value)[:256])
213
+
214
+ testtype = property(fget=_get_testtype, fset=_set_testtype, doc="testtype of the testseries")
215
+
216
+ def gen_test(self, **kwargs):
217
+ """generate Test instance
218
+
219
+ :param kwargs:
220
+ :return: Test()
221
+ """
222
+ class_name = kwargs.get("class_name", Test)
223
+ if "class_name" in kwargs:
224
+ kwargs.pop("class_name")
225
+
226
+ t = class_name(**kwargs)
227
+ t.name_testprogram = self.name_testprogram
228
+ t.uuid_testprogram = self.uuid_testprogram
229
+ t.name_testseries = self.name_testseries
230
+ t.uuid_testseries = self.uuid_testseries
231
+ t.project = self.project
232
+ t.testtype = self.testtype
233
+ t.metadata.add(self.SDATA_PARENT, self.uuid)
234
+
235
+ return t
236
+
237
+ @property
238
+ def longname(self):
239
+ return "|".join([self.name_testprogram, self.name])
240
+
241
+
242
+ class Test(TestSeries):
243
+ """A sdata Test
244
+
245
+ """
246
+
247
+ SDATA_TESTSERIES_NAME = "!sdata_testseries_name"
248
+ SDATA_TESTSERIES_UUID = "!sdata_testseries_uuid"
249
+
250
+
251
+ def __init__(self, **kwargs):
252
+ """Test
253
+
254
+ :param kwargs:
255
+ """
256
+ TestSeries.__init__(self, **kwargs)
257
+ self.metadata.add(name=self.SDATA_TESTSERIES_NAME, value="N.N.", dtype="str", description="name of the testseries")
258
+ self.metadata.add(name=self.SDATA_TESTSERIES_UUID, value="", dtype="str", description="uuid of the testseries")
259
+
260
+ if kwargs.get("name") is not None:
261
+ self._set_name(kwargs.get("name"))
262
+ else:
263
+ self._set_name("N.N.")
264
+ if kwargs.get("testtype") is not None:
265
+ # print("testtype", kwargs.get("testtype"), self._set_testtype)
266
+ self._set_testtype(kwargs.get("testtype"))
267
+ if kwargs.get("name_testseries") is not None:
268
+ self._set_name_testseries(kwargs.get("name_testseries"))
269
+ if kwargs.get("uuid_testseries") is not None:
270
+ self._set_uuid_testseries(kwargs.get("uuid_testseries"))
271
+ if kwargs.get("name_testprogram") is not None:
272
+ self._set_name_testprogram(kwargs.get("name_testprogram"))
273
+ if kwargs.get("uuid_testprogram") is not None:
274
+ # print("uuid_testprogram", kwargs.get("uuid_testprogram"), self._set_uuid_testprogram)
275
+ self._set_uuid_testprogram(kwargs.get("uuid_testprogram"))
276
+
277
+
278
+ def _get_uuid_testseries(self):
279
+ return self.metadata.get(self.SDATA_TESTSERIES_UUID).value
280
+
281
+ def _set_uuid_testseries(self, value):
282
+ if isinstance(value, str):
283
+ try:
284
+ uuid.UUID(value)
285
+ self.metadata.set_attr(self.SDATA_TESTSERIES_UUID, uuid.UUID(value).hex)
286
+ except ValueError as exp:
287
+ logger.warning("data.uuid: %s" % exp)
288
+ raise Sdata_Uuid_Exeption("got invalid uuid str '{}'".format(str(value)))
289
+ elif isinstance(value, uuid.UUID):
290
+ self.metadata.set_attr(self.SDATA_TESTSERIES_UUID, value.hex)
291
+ else:
292
+ logger.error("Data.uuid: invalid uuid '{}'".format(value))
293
+ raise Exception("Data.uuid: invalid uuid '{}'".format(value))
294
+
295
+ uuid_testseries = property(fget=_get_uuid_testseries, fset=_set_uuid_testseries, doc="uuid of the testseries")
296
+
297
+ def _get_name_testseries(self):
298
+ return self.metadata.get(self.SDATA_TESTSERIES_NAME).value
299
+
300
+ def _set_name_testseries(self, value):
301
+ if isinstance(value, str):
302
+ try:
303
+ self.metadata.set_attr(self.SDATA_TESTSERIES_NAME, str(value)[:256])
304
+ except ValueError as exp:
305
+ logger.warning("data.name_testseries: %s" % exp)
306
+ else:
307
+ self.metadata.set_attr(self.SDATA_TESTSERIES_NAME, str(value)[:256])
308
+
309
+ name_testseries = property(fget=_get_name_testseries, fset=_set_name_testseries, doc="name of the testseries")
310
+
311
+ def _get_testtype(self):
312
+ return self.metadata.get(self.SDATA_TESTTYPE).value
313
+
314
+ def _set_testtype(self, value):
315
+ if isinstance(value, str):
316
+ try:
317
+ self.metadata.set_attr(self.SDATA_TESTTYPE, str(value)[:256])
318
+ except ValueError as exp:
319
+ logger.warning("data.testtype: %s" % exp)
320
+ else:
321
+ self.metadata.set_attr(self.SDATA_TESTTYPE, str(value)[:256])
322
+
323
+ testtype = property(fget=_get_testtype, fset=_set_testtype, doc="testtype of the testseries")
324
+
325
+ @property
326
+ def longname(self):
327
+ return "|".join([self.name_testprogram, self.name_testseries, self.name])
328
+
329
+
330
+
331
+ __all__ = []
@@ -0,0 +1,131 @@
1
+ # -*-coding: utf-8-*-
2
+
3
+ import logging
4
+ import collections
5
+ import pandas as pd
6
+ import numpy as np
7
+ from sdata.timestamp import TimeStamp
8
+ from sdata.base import Base
9
+ from sdata.metadata import Metadata, Attribute
10
+ import json
11
+ import os
12
+ import hashlib
13
+
14
+
15
+ class Blob(Base):
16
+ """Binary Large Object as reference
17
+
18
+ .. warning::
19
+
20
+ highly experimental"""
21
+
22
+ VAULT_TYPES = ["filesystem", "hdf5", "db", "www"]
23
+
24
+ def __init__(self, **kwargs):
25
+ """Binary Large Object
26
+
27
+ :param name:
28
+ :param url:
29
+ :param filetype:
30
+ :param vault:
31
+ :param kwargs:
32
+
33
+ """
34
+ super().__init__(**kwargs)
35
+ self.metadata.add("blob_name", kwargs.get("name", ""))
36
+ self.metadata.add("blob_type", "unknown")
37
+ self.url = kwargs.get("url", "")
38
+ self.metadata.add("blob_sha1", self.sha1)
39
+ self.metadata.add("blob_md5", self.md5)
40
+ self.metadata.add("vault", kwargs.get("vault", ""))
41
+ self.metadata.add("blob_filetype", kwargs.get("filetype", "unknown"))
42
+
43
+
44
+ def _get_url(self):
45
+ # return self._name
46
+ return self.metadata.get("blob_url").value
47
+
48
+ def _set_url(self, value):
49
+ if isinstance(value, str):
50
+ try:
51
+ self.metadata.set_attr("blob_url", str(value)[:256])
52
+ except ValueError as exp:
53
+ logging.warning("blob.url: %s" % exp)
54
+ else:
55
+ # self._name = str(value)[:256]
56
+ self.metadata.set_attr("blob_url", str(value)[:256])
57
+
58
+ url = property(fget=_get_url, fset=_set_url, doc="url of the blob")
59
+
60
+ def exists(self, vault="filesystem"):
61
+ """Test whether a object under the blob.url exists.
62
+
63
+ :param vault:
64
+ :return:
65
+ """
66
+ if vault == "filesystem":
67
+ if os.path.exists(self.url):
68
+ return True
69
+ else:
70
+ return False
71
+ else:
72
+ return False
73
+
74
+ @property
75
+ def sha1(self):
76
+ """calculate the sha1 hash of the blob
77
+
78
+ :return: sha1
79
+ """
80
+ hash = hashlib.sha1()
81
+ if self.url and os.path.exists(self.url):
82
+ with open(self.url, "rb") as fh:
83
+ self.update_hash(fh, hash)
84
+ return hash.hexdigest()
85
+ else:
86
+ logging.error("can't open external url '{}'".format(str(self.url)))
87
+
88
+ @property
89
+ def md5(self):
90
+ """calculate the md5 hash of the blob
91
+
92
+ :return: sha1
93
+ """
94
+ hash = hashlib.md5()
95
+ if self.url and os.path.exists(self.url):
96
+ with open(self.url, "rb") as fh:
97
+ self.update_hash(fh, hash)
98
+ return hash.hexdigest()
99
+ else:
100
+ logging.error("can't open external url '{}'".format(str(self.url)))
101
+
102
+ def update_hash(self, fh, hashobject, buffer_size = 65536):
103
+ """A hash represents the object used to calculate a checksum of a
104
+ string of information.
105
+
106
+ .. code-block:: python
107
+
108
+ hashobject = hashlib.md5()
109
+ df = pd.DataFrame([1,2,3])
110
+ url = "/tmp/blob.csv"
111
+ df.to_csv(url)
112
+ blob = sdata.Blob(url=url)
113
+ fh = open(url, "rb")
114
+ blob.update_hash(fh, hashobject)
115
+ hashobject.hexdigest()
116
+
117
+ :param fh: file handle
118
+ :param hashobject: hash object, e.g. hashlib.sha1()
119
+ :param buffer_size: buffer size (default buffer_size=65536)
120
+ :return: hashobject
121
+ """
122
+
123
+
124
+ with fh as f:
125
+ while True:
126
+ data = f.read(buffer_size)
127
+ if not data:
128
+ break
129
+ hash.update(data)
130
+ return hash
131
+
@@ -0,0 +1,42 @@
1
+ from sdata import SUUID, Base
2
+ import os
3
+
4
+ class File(Base):
5
+ def __init__(self, **kwargs):
6
+ super().__init__(**kwargs)
7
+
8
+ def exists(self, path):
9
+ filepath = os.path.join(path, self.name)
10
+ return os.path.exists(path)
11
+
12
+ class Directory(Base):
13
+ def __init__(self, **kwargs):
14
+ super().__init__(**kwargs)
15
+ self.path = kwargs.get("path", os.getcwd())
16
+ self.files = {}
17
+
18
+ def register_files(self, path):
19
+ self.path = path
20
+ for f in os.listdir(self.path):
21
+ self.add(File(name=f))
22
+
23
+ def add(self, f):
24
+ """
25
+
26
+ """
27
+ self.files[f.name] = f
28
+
29
+ def ls(self):
30
+ """
31
+
32
+ """
33
+ return list(self.files.keys())
34
+
35
+ if __name__ == '__main__':
36
+ f = File(name="a.txt")
37
+ print(f)
38
+
39
+ d = Directory(name="/folder")
40
+ d.add(f)
41
+ print(d.ls())
42
+ print(d)
@@ -0,0 +1,144 @@
1
+ import logging
2
+ from typing import Any, Dict, List, Optional
3
+ from sdata.base import Base
4
+ logger = logging.getLogger(__name__)
5
+
6
+ class FileGroup(Base):
7
+ """
8
+ A derived class from Base that manages a group of files.
9
+ Stores file metadata in self.data['files'] as a dictionary where:
10
+ - Keys are unique file identifiers (e.g., filenames or custom keys).
11
+ - Values are dictionaries containing 'name', 'url' (relative or absolute path),
12
+ 'filetype' (e.g., 'pdf', 'txt'), and 'reference' (e.g., file content as bytes,
13
+ or a file handle, or any other reference to the file data).
14
+ """
15
+
16
+ def __init__(self, **kwargs: Any) -> None:
17
+ """
18
+ Initialize FileGroup.
19
+
20
+ :param kwargs: Keyword arguments passed to Base.__init__.
21
+ """
22
+ super().__init__(**kwargs)
23
+ # Initialize the files dictionary if not already present in data
24
+ if 'files' not in self.data:
25
+ self.data['files'] = {}
26
+
27
+ def add_file(
28
+ self,
29
+ key: str,
30
+ name: str,
31
+ url: str,
32
+ filetype: str,
33
+ reference: Any,
34
+ overwrite: bool = False
35
+ ) -> None:
36
+ """
37
+ Add a file's metadata to the group.
38
+
39
+ :param key: Unique key for the file (e.g., filename or custom identifier).
40
+ :param name: The name of the file.
41
+ :param url: Relative or absolute path to the file.
42
+ :param filetype: The type of the file (e.g., 'pdf', 'csv', 'jpg').
43
+ :param reference: Reference to the file, e.g., bytes content, file object, or other data.
44
+ :param overwrite: If True, overwrite existing entry with the same key.
45
+ :raises ValueError: If key already exists and overwrite is False.
46
+ """
47
+ if key in self.data['files'] and not overwrite:
48
+ raise ValueError(f"File with key '{key}' already exists. Set overwrite=True to replace.")
49
+
50
+ self.data['files'][key] = {
51
+ 'name': name,
52
+ 'url': url,
53
+ 'filetype': filetype,
54
+ 'reference': reference
55
+ }
56
+ logger.debug(f"Added file '{key}' to {self.sname}")
57
+
58
+ def get_file(self, key: str) -> Optional[Dict[str, Any]]:
59
+ """
60
+ Retrieve a file's metadata by key.
61
+
62
+ :param key: The unique key for the file.
63
+ :return: Dictionary with file metadata if found, else None.
64
+ """
65
+ return self.data['files'].get(key)
66
+
67
+ def remove_file(self, key: str) -> None:
68
+ """
69
+ Remove a file from the group by key.
70
+
71
+ :param key: The unique key for the file.
72
+ :raises KeyError: If the key does not exist.
73
+ """
74
+ if key not in self.data['files']:
75
+ raise KeyError(f"File with key '{key}' not found.")
76
+ del self.data['files'][key]
77
+ logger.debug(f"Removed file '{key}' from {self.sname}")
78
+
79
+ def list_files(self) -> List[str]:
80
+ """
81
+ List all file keys in the group.
82
+
83
+ :return: List of file keys.
84
+ """
85
+ return list(self.data['files'].keys())
86
+
87
+ def to_dict(self) -> Dict[str, Any]:
88
+ """
89
+ Extend Base.to_dict to include the files in data.
90
+ Note: If 'reference' contains non-serializable objects (e.g., file handles),
91
+ they may need custom handling for serialization.
92
+ """
93
+ return super().to_dict()
94
+
95
+ @classmethod
96
+ def from_dict(cls, d: Dict[str, Any]) -> 'FileGroup':
97
+ """
98
+ Create a FileGroup instance from a dictionary.
99
+ Assumes 'data' contains 'files' if previously serialized.
100
+ """
101
+ instance = super().from_dict(d)
102
+ # Ensure 'files' is initialized if missing
103
+ if 'files' not in instance.data:
104
+ instance.data['files'] = {}
105
+ return instance
106
+
107
+ if __name__ == '__main__':
108
+ logging.basicConfig(level=logging.DEBUG)
109
+
110
+ # Erstelle eine FileGroup-Instanz (z.B. für ein Projekt "Dokumentensammlung")
111
+ group = FileGroup(name="Dokumentensammlung", description="Eine Sammlung von PDF- und Text-Dateien.")
112
+
113
+ # Füge Dateien hinzu (hier mit Beispieldaten; 'reference' könnte z.B. Bytes oder ein File-Objekt sein)
114
+ group.add_file(
115
+ key="doc1",
116
+ name="Bericht.pdf",
117
+ url="/pfad/zum/bericht.pdf", # Relativ- oder Absolutpfad
118
+ filetype="pdf",
119
+ reference=b"Beispiel-Inhalt als Bytes" # Hier Bytes als Platzhalter; könnte auch ein File-Handle sein
120
+ )
121
+
122
+ group.add_file(
123
+ key="doc2",
124
+ name="Notizen.txt",
125
+ url="C:\\Dokumente\\notizen.txt", # Absolutpfad-Beispiel
126
+ filetype="txt",
127
+ reference="Text-Inhalt als String" # String als Referenz
128
+ )
129
+
130
+ # Versuche, eine Datei zu überschreiben (mit overwrite=True)
131
+ group.add_file(
132
+ key="doc1",
133
+ name="Aktualisierter_Bericht.pdf",
134
+ url="/neuer/pfad/zum/bericht.pdf",
135
+ filetype="pdf",
136
+ reference=b"Neuer Inhalt",
137
+ overwrite=True
138
+ )
139
+
140
+ # Liste alle Datei-Keys auf
141
+ print("Verfügbare Dateien:", group.list_files()) # Ausgabe: ['doc1', 'doc2']
142
+
143
+ # Hole Metadaten einer spezifischen Datei
144
+ file_info = group.get_file("doc1")