datablender 0.0.1__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 (45) hide show
  1. datablender/__init__.py +68 -0
  2. datablender/base/__init__.py +283 -0
  3. datablender/base/connection.py +370 -0
  4. datablender/base/data.py +1856 -0
  5. datablender/base/dataConfiguration.py +701 -0
  6. datablender/base/dataElement.py +584 -0
  7. datablender/base/dataLogging.py +702 -0
  8. datablender/base/dataSets.py +233 -0
  9. datablender/base/directory.py +225 -0
  10. datablender/base/file.py +496 -0
  11. datablender/base/queryBuilder.py +1498 -0
  12. datablender/base/request.py +182 -0
  13. datablender/base/text.py +79 -0
  14. datablender/base/web.py +602 -0
  15. datablender/data/__init__.py +124 -0
  16. datablender/data/asyncDataProcess.py +1047 -0
  17. datablender/data/asyncDataServer.py +538 -0
  18. datablender/data/asyncDataSourceCore.py +1971 -0
  19. datablender/data/dataDirectory.py +1540 -0
  20. datablender/data/dataFile.py +635 -0
  21. datablender/data/dataProcess.py +639 -0
  22. datablender/data/dataServer.py +488 -0
  23. datablender/data/dataSource.py +647 -0
  24. datablender/data/dataSourceCore.py +1318 -0
  25. datablender/data/dataVersion.py +1526 -0
  26. datablender/data/directoryElementController.py +172 -0
  27. datablender/data/filesTable.py +219 -0
  28. datablender/database/__init__.py +26 -0
  29. datablender/database/database.py +1151 -0
  30. datablender/database/elementSQL.py +1093 -0
  31. datablender/database/extension.py +265 -0
  32. datablender/database/function.py +324 -0
  33. datablender/database/partition.py +26 -0
  34. datablender/database/role.py +385 -0
  35. datablender/database/schema.py +1009 -0
  36. datablender/database/table.py +2274 -0
  37. datablender/database/view.py +848 -0
  38. datablender/tests/__init__.py +8 -0
  39. datablender/tests/test_base.py +1604 -0
  40. datablender/tests/test_data.py +3346 -0
  41. datablender/tests/test_database.py +799 -0
  42. datablender-0.0.1.dist-info/METADATA +68 -0
  43. datablender-0.0.1.dist-info/RECORD +45 -0
  44. datablender-0.0.1.dist-info/WHEEL +5 -0
  45. datablender-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,68 @@
1
+ """
2
+
3
+ """
4
+
5
+ from datablender.base import (
6
+ File,
7
+ Connection,
8
+ readFile,
9
+ getNextID,
10
+ normalize_user_name,
11
+ Directory,
12
+ DirectoryElement,
13
+ ZipFile_,
14
+ Text,
15
+ formatText,
16
+ Request,
17
+ Bot,
18
+ BotAction,
19
+ WebElement,
20
+ QueryBuilder,
21
+ DataConfiguration,
22
+ DataLogging,
23
+ DataEventsTable,
24
+ DataElement,
25
+ Data,
26
+ AsyncConnection,
27
+ AsyncDataConfiguration,
28
+ AsyncRequest,
29
+ DataSets
30
+ )
31
+
32
+ from datablender.database import (
33
+ DatabaseElement,
34
+ SchemaElement,
35
+ Database,
36
+ Extension,
37
+ Role,
38
+ Schema,
39
+ Table,
40
+ View,
41
+ Function,
42
+ AsyncView,
43
+ AsyncTable,
44
+ AsyncDatabase
45
+ )
46
+
47
+ from datablender.data import (
48
+ DataVersion,
49
+ DataVersionColumn,
50
+ DataVersionTable,
51
+ DataVersionValue,
52
+ DataSource,
53
+ DataFetcher,
54
+ RawDataFile,
55
+ DirectoryElementController,
56
+ DataProcess,
57
+ FilesTable,
58
+ DataFile,
59
+ DataDirectory,
60
+ DataDirectoryElement,
61
+ DataZipFile,
62
+ DataSourceCore,
63
+ DataServer,
64
+ importData,
65
+ AsyncDataServer,
66
+ AsyncDataSource,
67
+ AsyncDataProcess
68
+ )
@@ -0,0 +1,283 @@
1
+ """
2
+
3
+ """
4
+ from typing import Callable,Tuple
5
+ import types
6
+
7
+ import os
8
+ import sys
9
+ import numpy
10
+ import pandas
11
+
12
+ import importlib
13
+ import importlib.util
14
+
15
+ def getDirectoryElementName(
16
+ path:str=None,
17
+ directory_name:str=None,
18
+ element_name:str=None
19
+ ) -> Tuple[str,str]:
20
+ """Get directory name and data directory element name from path.
21
+
22
+ Args:
23
+ path (str, optional): Path. Defaults to None.
24
+ directory_name (str, optional): Directory name. Defaults to None.
25
+ element_name (str, optional): Directory element name. Defaults to None.
26
+
27
+ Returns:
28
+ Tuple[str,str]: Directory name and directory element name.
29
+ """
30
+ if path:
31
+ return os.path.dirname(
32
+ os.path.normpath(path)
33
+ ),os.path.basename(
34
+ os.path.normpath(path)
35
+ )
36
+ elif directory_name and element_name:
37
+ return directory_name,element_name
38
+
39
+ from datablender.base.file import File,ZipFile_
40
+ from datablender.base.directory import Directory,DirectoryElement
41
+ from datablender.base.text import Text,formatText
42
+ from datablender.base.request import Request, AsyncRequest
43
+
44
+ def normalize_user_name(user_name:str):
45
+ return user_name.split(' ')[0]
46
+
47
+ def getNextID(elements:list,id_attribute_name:str='id') -> int:
48
+ """Get next id in a list of dictionnary.
49
+
50
+ Args:
51
+ elements (list): List of dictionnary.
52
+ id_attribute_name (str, optional): Id attribute name. Defaults to 'id'.
53
+
54
+ Returns:
55
+ int: Next id.
56
+ """
57
+ ids = numpy.append(
58
+ numpy.array([element[id_attribute_name] for element in elements]),
59
+ numpy.nan
60
+ )
61
+ df = pandas.DataFrame({
62
+ 'column':numpy.sort(ids),
63
+ 'ids':numpy.arange(1,ids.size+1,1)
64
+ })
65
+ return df.loc[df['column']!=df['ids'],'ids'].min()
66
+
67
+ def readFile(
68
+ path:str=None,
69
+ directory_name:str=None,
70
+ file_name:str=None,
71
+ **kwargs
72
+ ) -> pandas.DataFrame:
73
+ return File(
74
+ directory_name,
75
+ file_name,
76
+ path
77
+ ).read(
78
+ **kwargs
79
+ ).content
80
+
81
+ def getFunction(
82
+ function_name:str,
83
+ directory_name:str,
84
+ module:str='__init__',
85
+ code:str = None,
86
+ schema_name:str = None,
87
+ schema_type:str = None
88
+ ) -> Callable:
89
+
90
+ if module is None:
91
+ module = '__init__'
92
+
93
+ if directory_name is None:
94
+ directory_name = os.path.join(
95
+ os.getenv(
96
+ 'code_directory',
97
+ os.getcwd()
98
+ ),
99
+ schema_type,
100
+ schema_name
101
+ )
102
+ os.makedirs(
103
+ directory_name,
104
+ exist_ok=True
105
+ )
106
+
107
+ if code is not None:
108
+ File(
109
+ directory_name,
110
+ os.path.join(directory_name,'{}.py'.format(module))
111
+ ).write(code)
112
+
113
+ spec = importlib.util.spec_from_file_location(
114
+ module,
115
+ os.path.join(directory_name,'{}.py'.format(module))
116
+ )
117
+ module = importlib.util.module_from_spec(spec)
118
+ spec.loader.exec_module(module)
119
+ return getattr(module, function_name)
120
+
121
+ def getModule(
122
+ directory_name:str,
123
+ module:str='__init__',
124
+ package_name:str = None,
125
+ schema_name:str = None,
126
+ schema_type:str = None
127
+ ) -> types.ModuleType:
128
+
129
+ original_path = os.getcwd()
130
+
131
+ if package_name:
132
+ module = package_name
133
+ else:
134
+ if module is None:
135
+ module = '__init__'
136
+
137
+ if directory_name is None:
138
+ directory_name = os.path.join(
139
+ os.getenv(
140
+ 'CODE_DIRECTORY',
141
+ os.getcwd()
142
+ ),
143
+ schema_type,
144
+ schema_name
145
+ )
146
+ os.makedirs(
147
+ directory_name,
148
+ exist_ok=True
149
+ )
150
+
151
+ # os.chdir(directory_name)
152
+ sys.path.append(directory_name)
153
+
154
+ module = importlib.import_module(module)
155
+
156
+ # spec = importlib.util.spec_from_file_location(
157
+ # module,
158
+ # os.path.join(directory_name,'{}.py'.format(module))
159
+ # )
160
+ # module = importlib.util.module_from_spec(spec)
161
+ # spec.loader.exec_module(module)
162
+
163
+
164
+ sys.path.append(original_path)
165
+
166
+ return module
167
+
168
+ def manageQuery(
169
+ name:str=None,
170
+ query:str=None,
171
+ file_name:str=None,
172
+ directory_name:str=None,
173
+ schema_name:str=None,
174
+ schema_type:str= None,
175
+ data_config_is_active:bool=None
176
+ ) -> Tuple[str,str,str]:
177
+
178
+ if query is not None:
179
+ if data_config_is_active:
180
+ if file_name is None and directory_name is None and name is not None:
181
+ file_name = name+'.sql'
182
+ directory_name = os.path.join(
183
+ os.getenv(
184
+ 'CODE_DIRECTORY',
185
+ os.getcwd()
186
+ ),
187
+ schema_type,
188
+ schema_name,
189
+ 'queries'
190
+ )
191
+ os.makedirs(
192
+ directory_name,
193
+ exist_ok=True
194
+ )
195
+
196
+ File(
197
+ directory_name,
198
+ file_name
199
+ ).write(query)
200
+
201
+
202
+ # if os.path.isfile(os.path.join(os.path.abspath(directory_name),file_name)):
203
+ # query = File(
204
+ # directory_name,
205
+ # file_name
206
+ # ).read().content
207
+
208
+ # if directory_name is None:
209
+
210
+ # directory_name = os.getenv(
211
+ # 'CODE_DIRECTORY',
212
+ # os.getcwd()
213
+ # )
214
+
215
+ # if schema_name:
216
+ # new_directory_name = os.path.join(
217
+ # directory_name,
218
+ # schema_type,
219
+ # schema_name,
220
+ # 'queries'
221
+ # )
222
+ # if os.path.isdir(new_directory_name):
223
+ # directory_name = new_directory_name
224
+
225
+ # elif not os.path.isdir(directory_name):
226
+ # new_directory_name = os.path.join(
227
+ # os.getenv('CODE_DIRECTORY',os.getcwd()),
228
+ # directory_name
229
+ # )
230
+ # if os.path.isdir(new_directory_name):
231
+ # directory_name = new_directory_name
232
+
233
+ # if file_name is None:
234
+ # pass
235
+
236
+ # query = File(
237
+ # directory_name,
238
+ # file_name+'.sql' if '.' not in file_name else file_name
239
+ # ).read().content
240
+
241
+ elif file_name is not None:
242
+ pass
243
+
244
+ elif name is not None:
245
+ if directory_name is None:
246
+ directory_name=os.getenv(
247
+ 'CODE_DIRECTORY',
248
+ os.getcwd()
249
+ )
250
+ if schema_type:
251
+ directory_name = os.path.join(
252
+ directory_name,
253
+ schema_type
254
+ )
255
+ if schema_name:
256
+ directory_name = os.path.join(
257
+ directory_name,
258
+ schema_name
259
+ )
260
+
261
+ query = File(
262
+ os.path.join(
263
+ directory_name,
264
+ 'queries'
265
+ ),
266
+ name+'.sql'
267
+ ).read(encoding = 'utf-8').content
268
+
269
+ return query,file_name,directory_name
270
+
271
+ from datablender.base.web import Bot,BotAction,WebElement
272
+ from datablender.base.connection import Connection, AsyncConnection
273
+ from datablender.base.queryBuilder import QueryBuilder
274
+ from datablender.base.dataLogging import (
275
+ DataEventsTable,
276
+ DataLogging,
277
+ AsyncDataEventsTable,
278
+ AsyncDataLogging
279
+ )
280
+ from datablender.base.dataConfiguration import DataConfiguration,AsyncDataConfiguration
281
+ from datablender.base.dataElement import DataElement, AsyncDataElement
282
+ from datablender.base.data import Data
283
+ from datablender.base.dataSets import DataSets
@@ -0,0 +1,370 @@
1
+ """
2
+
3
+ """
4
+ from __future__ import annotations
5
+
6
+ import os
7
+ import psycopg2
8
+ import postgis
9
+ import sqlalchemy
10
+ import aiohttp
11
+ import asyncio
12
+ import asyncpg
13
+ from sqlalchemy.ext.asyncio import (
14
+ create_async_engine,
15
+ async_sessionmaker,
16
+ AsyncSession
17
+ )
18
+
19
+ from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT,ISOLATION_LEVEL_DEFAULT
20
+
21
+ from datablender.base import normalize_user_name
22
+
23
+ class Connection:
24
+ """Connection to a database.
25
+
26
+ Attributes:
27
+ ----------
28
+ host (str): Server's host.
29
+ port (str): Server's port.
30
+ database_name (str): Database name.
31
+ user_name (str): User name.
32
+ password (str): Password.
33
+ dialect (str): Dialect.
34
+ connection_string (str) : Connection string.
35
+ engine (object) : SQLAlchemy engine.
36
+ connection (object) : Connection.
37
+ cursor (object) : Connection's cursor.
38
+
39
+ Methods:
40
+ ----------
41
+ getConnection (self) -> None: Get the connection.
42
+ getCursor (self) -> None: Get the connection's cursor.
43
+ getEngine (self) -> None: Get SQLAlchemy engine.
44
+ connectActions (self) -> None: Get the connection, cursor and engine.
45
+ connect (self) -> None: Connect to the database.
46
+ close (self) -> None: a .
47
+ registerToPostgis (self) -> None: .
48
+ setIsolationLevel (self) -> None: .
49
+
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ host:str = 'localhost',
55
+ port:str = '5432',
56
+ database_name:str = 'postgres',
57
+ user_name:str = 'postgres',
58
+ password:str = None,
59
+ default_database:int = 'postgres',
60
+ **kwargs
61
+ ):
62
+ """Initiate the connection.
63
+
64
+ Args:
65
+ host (str, optional): Server's host. Defaults to 'localhost'.
66
+ port (str, optional): Server's port. Defaults to '5432'.
67
+ database_name (str, optional): Database name. Defaults to 'postgres'.
68
+ user_name (str, optional): User name. Defaults to 'postgres'.
69
+ password (str, optional): Password. Defaults to None.
70
+ dialect (str, optional): Dialect. Defaults to 'PostgreSQL'.
71
+ """
72
+ self.database_name = database_name
73
+ self.default_database = default_database
74
+
75
+
76
+ self.host = os.getenv('host',host)
77
+ self.port = os.getenv('port',port)
78
+ self.password = os.getenv('PASSWORD',password)
79
+
80
+ self.user_name = user_name if user_name != 'postgres' else normalize_user_name(
81
+ os.getenv(
82
+ 'username',
83
+ user_name
84
+ )
85
+ )
86
+
87
+ self.connect(**kwargs)
88
+
89
+ @property
90
+ def connection_string(self) -> str:
91
+ """Get connection string.
92
+
93
+ Returns:
94
+ str: Connection string.
95
+ """
96
+ return "postgresql+psycopg2://{}:{}@{}:{}/{}".format(
97
+ self.user_name,
98
+ self.password,
99
+ self.host,
100
+ self.port,
101
+ self.database_name
102
+ )
103
+
104
+ def getConnection(self) -> None:
105
+ """Get connection.
106
+ """
107
+ self.connection = psycopg2.connect(
108
+ database = self.database_name,
109
+ user = self.user_name,
110
+ host = self.host,
111
+ password = self.password,
112
+ port = self.port
113
+ )
114
+
115
+ def getCursor(self,**kwargs) -> None:
116
+ """Get connection's cursor.
117
+ """
118
+ self.cursor = self.connection.cursor(**kwargs)
119
+
120
+ def getEngine(self) -> None:
121
+ """Get SQLAlchemy engine.
122
+ """
123
+ self.engine = sqlalchemy.create_engine(
124
+ self.connection_string
125
+ )
126
+
127
+ def connect(self,**kwargs) -> None:
128
+ """Connect to the database.
129
+
130
+ Raises:
131
+ TypeError: _description_
132
+ TypeError: _description_
133
+
134
+ Returns:
135
+ object: self
136
+ """
137
+ self.getConnection()
138
+ self.getCursor(**kwargs)
139
+ self.getEngine()
140
+
141
+ def close(self) -> None:
142
+ """Close the connected object.
143
+ """
144
+ if self.cursor:
145
+ self.cursor.close()
146
+ if self.connection:
147
+ self.connection.close()
148
+ if self.engine:
149
+ self.engine.dispose()
150
+
151
+ def registerToPostgis(self) -> None:
152
+ """Register to Postgis.
153
+ """
154
+ postgis.register(self.cursor)
155
+
156
+ def setIsolationLevel(
157
+ self,
158
+ level:str = 'default'
159
+ ) -> None:
160
+ """Set the isolation level.
161
+
162
+ Args:
163
+ level (str, optional): Level to set. Defaults to auto.
164
+ """
165
+ self.connection.set_isolation_level(
166
+ {
167
+ 'auto':ISOLATION_LEVEL_AUTOCOMMIT,
168
+ 'default':ISOLATION_LEVEL_DEFAULT
169
+ }[level]
170
+ )
171
+
172
+ def setSchema(
173
+ self,
174
+ schema:str='public'
175
+ ) -> None:
176
+ """Set default schema.
177
+
178
+ Args:
179
+ schema (str, optional): Schema to set to default. Defaults to 'public'.
180
+ """
181
+ self.cursor.execute(
182
+ "SET search_path TO "+schema
183
+ )
184
+ self.connection.commit()
185
+
186
+ def setDatabase(
187
+ self,
188
+ database_name:str
189
+ ) -> None:
190
+ """Change the connection's database.
191
+
192
+ Args:
193
+ database_name (str): Database name.
194
+ """
195
+ self.close()
196
+ self.database_name = database_name
197
+ self.connect()
198
+
199
+ def getDefaultConnection(
200
+ self,
201
+ default_database_name:str=None
202
+ ) -> None:
203
+ """Change the database connection to the default one
204
+
205
+ Args:
206
+ default_database_name (str, optional): Name of the default database. Defaults to None.
207
+ """
208
+ if default_database_name:
209
+ self.default_database = default_database_name
210
+
211
+ self.setDatabase(
212
+ self.default_database
213
+ )
214
+
215
+ class AsyncConnection:
216
+ """Connection to a database.
217
+
218
+ Attributes:
219
+ ----------
220
+ host (str): Server's host.
221
+ port (str): Server's port.
222
+ database_name (str): Database name.
223
+ user_name (str): User name.
224
+ password (str): Password.
225
+ dialect (str): Dialect.
226
+ connection_string (str) : Connection string.
227
+ engine (object) : SQLAlchemy engine.
228
+ connection (object) : Connection.
229
+ cursor (object) : Connection's cursor.
230
+
231
+ Methods:
232
+ ----------
233
+ getConnection (self) -> None: Get the connection.
234
+ getCursor (self) -> None: Get the connection's cursor.
235
+ getEngine (self) -> None: Get SQLAlchemy engine.
236
+ connectActions (self) -> None: Get the connection, cursor and engine.
237
+ connect (self) -> None: Connect to the database.
238
+ close (self) -> None: a .
239
+ registerToPostgis (self) -> None: .
240
+ setIsolationLevel (self) -> None: .
241
+
242
+ """
243
+
244
+ def __init__(
245
+ self,
246
+ host:str = 'localhost',
247
+ port:str = '5432',
248
+ database_name:str = 'postgres',
249
+ user_name:str = 'postgres',
250
+ password:str = None,
251
+ default_database:int = 'postgres',
252
+ **kwargs
253
+ ):
254
+ """Initiate the connection.
255
+
256
+ Args:
257
+ host (str, optional): Server's host. Defaults to 'localhost'.
258
+ port (str, optional): Server's port. Defaults to '5432'.
259
+ database_name (str, optional): Database name. Defaults to 'postgres'.
260
+ user_name (str, optional): User name. Defaults to 'postgres'.
261
+ password (str, optional): Password. Defaults to None.
262
+ dialect (str, optional): Dialect. Defaults to 'PostgreSQL'.
263
+ """
264
+ self.database_name = database_name
265
+ self.default_database = default_database
266
+
267
+
268
+ self.host = os.getenv('host',host)
269
+ self.port = os.getenv('port',port)
270
+ self.password = os.getenv('PASSWORD',password)
271
+
272
+ self.user_name = user_name if user_name != 'postgres' else normalize_user_name(
273
+ os.getenv(
274
+ 'username',
275
+ user_name
276
+ )
277
+ )
278
+
279
+ @property
280
+ def connection_string(self) -> str:
281
+ """Get connection string.
282
+
283
+ Returns:
284
+ str: Connection string.
285
+ """
286
+ return "postgresql+asyncpg://{}:{}@{}:{}/{}".format(
287
+ self.user_name,
288
+ self.password,
289
+ self.host,
290
+ self.port,
291
+ self.database_name
292
+ )
293
+
294
+ async def connect(self,**kwargs) -> None:
295
+ """Connect to the database.
296
+
297
+ Raises:
298
+ TypeError: _description_
299
+ TypeError: _description_
300
+
301
+ Returns:
302
+ object: self
303
+ """
304
+ await self.getConnection()
305
+ self.getEngine()
306
+
307
+ async def getConnection(self) -> None:
308
+ """Get connection.
309
+ """
310
+ self.connection:asyncpg.connection.Connection = await asyncpg.connect(
311
+ database = self.database_name,
312
+ user = self.user_name,
313
+ host = self.host,
314
+ password = self.password,
315
+ port = self.port
316
+ )
317
+
318
+ def getEngine(self) -> None:
319
+ """Get SQLAlchemy engine.
320
+ """
321
+ self.engine = create_async_engine(
322
+ self.connection_string
323
+ )
324
+ self.async_session = async_sessionmaker(self.engine, expire_on_commit=False)
325
+
326
+ async def close(self) -> None:
327
+ await self.connection.close()
328
+ await self.engine.dispose()
329
+
330
+ async def setSchema(
331
+ self,
332
+ schema:str='public'
333
+ ) -> None:
334
+ """Set default schema.
335
+
336
+ Args:
337
+ schema (str, optional): Schema to set to default. Defaults to 'public'.
338
+ """
339
+ await self.connection.execute(
340
+ "SET search_path TO "+schema
341
+ )
342
+
343
+ async def setDatabase(
344
+ self,
345
+ database_name:str
346
+ ) -> None:
347
+ """Change the connection's database.
348
+
349
+ Args:
350
+ database_name (str): Database name.
351
+ """
352
+ await self.close()
353
+ self.database_name = database_name
354
+ await self.connect()
355
+
356
+ async def getDefaultConnection(
357
+ self,
358
+ default_database_name:str=None
359
+ ) -> None:
360
+ """Change the database connection to the default one
361
+
362
+ Args:
363
+ default_database_name (str, optional): Name of the default database. Defaults to None.
364
+ """
365
+ if default_database_name:
366
+ self.default_database = default_database_name
367
+
368
+ await self.setDatabase(
369
+ self.default_database
370
+ )