embedize 1.0.2__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.
embedize/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .dcore import *
2
+ from .dbs import *
3
+
4
+ __version__ = "1.0.2"
embedize/dbs.py ADDED
@@ -0,0 +1,231 @@
1
+ def full_year (column, yyyy):
2
+ return f"(CAST(STRFTIME('%Y', {column}) AS INT) = CAST({yyyy} AS INT))"
3
+
4
+ def year_month (column, yyyy, mm):
5
+ return f"(CAST(STRFTIME('%Y', {column}) AS INT) = CAST({yyyy} AS INT) AND CAST(STRFTIME('%m', {column}) AS INT) = CAST({mm} AS INT))"
6
+
7
+ def date_between (column, dd1, dd2):
8
+ return f'(DATE({column}) BETWEEN DATE({dd1}) AND DATE({dd2}))'
9
+
10
+ def moderators_of_system ():
11
+ return f'(SELECT id FROM users WHERE zone=0)'
12
+
13
+ def admins_of_system ():
14
+ return f'(SELECT id FROM users WHERE zone=0 AND zadmin=1)'
15
+
16
+ def items_of_sub (sub_placeholder):
17
+ return f'(SELECT id FROM items WHERE sub={sub_placeholder})'
18
+
19
+ def subs_of_account (account_placeholder):
20
+ return f'(SELECT id FROM subs WHERE account={account_placeholder})'
21
+
22
+ def column_setting (column, cplaceholder, placeholder):
23
+ return f"{column} = CASE WHEN {cplaceholder} != '' THEN {placeholder} ELSE {column} END"
24
+
25
+ def close_items_listing (limit, offset):
26
+ return f'ORDER BY CAST(subs.account AS VARCHAR), subs.code, items.head LIMIT {limit} OFFSET {offset}'
27
+
28
+ def close_subs_listing (limit, offset):
29
+ return f'ORDER BY CAST(subs.account AS VARCHAR), subs.code LIMIT {limit} OFFSET {offset}'
30
+
31
+ def accounts_of_code_like (code_placeholder):
32
+ return f"(SELECT id FROM accounts WHERE code LIKE '%'||{code_placeholder}||'%')"
33
+
34
+ def check_sub_identify (id_placeholder, account_placeholder, code_placeholder):
35
+ return f'(id={id_placeholder} OR (account={account_placeholder} AND code=UPPER(TRIM({code_placeholder}))))'
36
+
37
+ def check_sub_acc_like (placeholder):
38
+ return f"CAST(subs.account AS VARCHAR) LIKE '%'||{placeholder}||'%'"
39
+
40
+ def check_uid_identify (id_placeholder, uuid_placeholder):
41
+ return f'(id={id_placeholder} OR uuid={uuid_placeholder})'
42
+
43
+ def check_uid_identifies (ids_placeholder, uuids_placeholder):
44
+ return f'(id IN ({ids_placeholder}) OR uuid IN ({uuids_placeholder}))'
45
+
46
+ def check_item_uid_identify (id_placeholder, uuid_placeholder):
47
+ return f'(items.id={id_placeholder} OR items.uuid={uuid_placeholder})'
48
+
49
+ def check_item_uid_identifies (ids_placeholder, uuids_placeholder):
50
+ return f'(items.id IN ({ids_placeholder}) OR items.uuid IN ({uuids_placeholder}))'
51
+
52
+ def role_exists (id_placeholder):
53
+ return f'EXISTS (SELECT 1 FROM roles WHERE id={id_placeholder})'
54
+
55
+ def specrole_exists (spec_placeholder, role_placeholder):
56
+ return f'EXISTS (SELECT 1 FROM specroles WHERE spec={spec_placeholder} AND role={role_placeholder})'
57
+
58
+ def userrole_exists (user_placeholder, role_placeholder):
59
+ return f'EXISTS (SELECT 1 FROM userroles WHERE user={user_placeholder} AND role={role_placeholder})'
60
+
61
+ def spec_exists (id_placeholder):
62
+ return f'EXISTS (SELECT 1 FROM specs WHERE id={id_placeholder})'
63
+
64
+ def userspec_exists (user_placeholder, spec_placeholder):
65
+ return f'EXISTS (SELECT 1 FROM userspecs WHERE user={user_placeholder} AND spec={spec_placeholder})'
66
+
67
+ def group_exists (id_placeholder):
68
+ return f'EXISTS (SELECT 1 FROM groups WHERE id={id_placeholder})'
69
+
70
+ group_id_exists = group_exists
71
+
72
+ def root_exists (id_placeholder, code_placeholder):
73
+ return f'EXISTS (SELECT 1 FROM roots WHERE id={id_placeholder} OR code=UPPER(TRIM({code_placeholder})))'
74
+
75
+ def root_id_exists (id_placeholder):
76
+ return f'EXISTS (SELECT 1 FROM roots WHERE id={id_placeholder})'
77
+
78
+ def account_exists (id_placeholder, code_placeholder):
79
+ return f'EXISTS (SELECT 1 FROM accounts WHERE id={id_placeholder} OR code=UPPER(TRIM({code_placeholder})))'
80
+
81
+ def account_id_exists (id_placeholder):
82
+ return f'EXISTS (SELECT 1 FROM accounts WHERE id={id_placeholder})'
83
+
84
+ def sub_exists (code_placeholder, account_placeholder):
85
+ return f'EXISTS (SELECT 1 FROM subs, accounts WHERE subs.code=UPPER(TRIM({code_placeholder})) AND subs.account={account_placeholder} AND subs.account=accounts.id)'
86
+
87
+ def sub_id_exists (id_placeholder):
88
+ return f'EXISTS (SELECT 1 FROM subs WHERE id={id_placeholder})'
89
+
90
+ def zone_exists (id_placeholder, name_placeholder):
91
+ return f'EXISTS (SELECT 1 FROM zones WHERE id={id_placeholder} OR name=UPPER(TRIM({name_placeholder})))'
92
+
93
+ def zone_id_exists (id_placeholder):
94
+ return f'EXISTS (SELECT 1 FROM zones WHERE id={id_placeholder})'
95
+
96
+ def attach_user_exists (id_placeholder):
97
+ return f'EXISTS (SELECT 1 FROM admin.users WHERE id={id_placeholder})'
98
+
99
+ def attach_user_check (idname_placeholder):
100
+ return f'(SELECT id FROM admin.users WHERE {check_idname(idname_placeholder)})'
101
+
102
+ def user_name_exists (name_placeholder):
103
+ return f'EXISTS (SELECT 1 FROM users WHERE name=LOWER(TRIM({name_placeholder})))'
104
+
105
+ def user_id_exists (id_placeholder):
106
+ return f'EXISTS (SELECT 1 FROM users WHERE id={id_placeholder})'
107
+
108
+ def check_obj_active (active_placeholder):
109
+ return f'(obj.active = (CASE WHEN {active_placeholder} IN (0,1) THEN {active_placeholder} ELSE obj.active END))'
110
+
111
+ def check_obj_zone_name (name_placeholder):
112
+ return f'(obj.zone = (SELECT id FROM zones WHERE name=UPPER(TRIM({name_placeholder})) LIMIT 1))'
113
+
114
+ def check_obj_zoneidn (id_placeholder, name_placeholder):
115
+ return f'(obj.zone = (SELECT id FROM zones WHERE {check_zone_id_or_name(id_placeholder, name_placeholder)} LIMIT 1))'
116
+
117
+ def check_user_id_or_name (id_placeholder, name_placeholder):
118
+ return f'(id={id_placeholder} OR name=LOWER(TRIM({name_placeholder})))'
119
+
120
+ def check_strict_user_id_or_name (id_placeholder, name_placeholder):
121
+ return f'(users.id={id_placeholder} OR users.name=LOWER(TRIM({name_placeholder})))'
122
+
123
+ def check_strict_user_worktime (worktime_placeholder):
124
+ return f"(users.workend IS NULL OR DATE({worktime_placeholder}) BETWEEN IFNULL(DATE(users.workbegin), DATE('1900-01-01')) AND DATE(users.workend))"
125
+
126
+ def check_strict_user_working (worktime_placeholder):
127
+ return f'({check_strict_user_active()} AND {check_strict_user_worktime(worktime_placeholder)})'
128
+
129
+ def check_strict_user_active ():
130
+ return f'(users.active=1 AND users.zone IN (SELECT id FROM zones WHERE active=1))'
131
+
132
+ def check_zone_id_or_name (id_placeholder, name_placeholder):
133
+ return f'(id={id_placeholder} OR name=UPPER(TRIM({name_placeholder})))'
134
+
135
+ def set_user_note (cnote_placeholder, note_placeholder, modifier_placeholder):
136
+ return f"note = CASE WHEN {cnote_placeholder} != '' THEN (IFNULL(note,'') || '\n' || {note_placeholder} || ' [uid.' || IFNULL({modifier_placeholder},'N/A') || '] ' || DATETIME('now')) ELSE note END"
137
+
138
+ def set_user_modify (modifier_placeholder):
139
+ return f"modified=(DATETIME('now')), modifier={modifier_placeholder}"
140
+
141
+ set_modifier_value = set_user_modify
142
+
143
+ def check_user_id (id_placeholder, name_placeholder):
144
+ return f'((user={id_placeholder} OR users.name=LOWER(TRIM({name_placeholder}))) AND user=users.id)'
145
+
146
+ def check_user_in_specs (user_placeholder, name_placeholder):
147
+ return f'({check_user_id(user_placeholder, name_placeholder)} AND spec=specs.id)'
148
+
149
+ def check_user_in_roles (user_placeholder, name_placeholder):
150
+ return f'({check_user_id(user_placeholder, name_placeholder)} AND role=roles.id)'
151
+
152
+ def check_user_spec (user_placeholder, name_placeholder, spec_placeholder):
153
+ return f'({check_user_id(user_placeholder, name_placeholder)} AND spec={spec_placeholder})'
154
+
155
+ def check_user_role (user_placeholder, name_placeholder, role_placeholder):
156
+ return f'({check_user_id(user_placeholder, name_placeholder)} AND role={role_placeholder})'
157
+
158
+ def check_user_spec_in_specs (user_placeholder, name_placeholder, spec_placeholder):
159
+ return f'({check_user_id(user_placeholder, name_placeholder)} AND spec={spec_placeholder} AND spec=specs.id)'
160
+
161
+ def check_user_role_in_roles (user_placeholder, name_placeholder, role_placeholder):
162
+ return f'({check_user_id(user_placeholder, name_placeholder)} AND role={role_placeholder} AND role=roles.id)'
163
+
164
+ check_id_or_uuid = check_uid_identify
165
+
166
+ def check_id_or_name (id_placeholder, name_placeholder):
167
+ return f'(id={id_placeholder} OR LOWER(TRIM(name))=LOWER(TRIM({name_placeholder})))'
168
+
169
+ def check_iduuid (placeholder):
170
+ return f'(TRIM(CAST(id AS VARCHAR))=TRIM({placeholder}) OR LOWER(TRIM(uuid))=LOWER(TRIM({placeholder})))'
171
+
172
+ def check_idname (placeholder):
173
+ return f'(TRIM(CAST(id AS VARCHAR))=TRIM({placeholder}) OR LOWER(TRIM(name))=LOWER(TRIM({placeholder})))'
174
+
175
+ def check_strict_idname (placeholder):
176
+ return f'(TRIM(CAST(users.id AS VARCHAR))=TRIM({placeholder}) OR LOWER(TRIM(users.name))=LOWER(TRIM({placeholder})))'
177
+
178
+ def check_ones_admin (zone_placeholder, user_placeholder):
179
+ return f'(zadmin=1 AND zone IN ({zone_of_zoneuser(zone_placeholder,user_placeholder)}))'
180
+
181
+ def check_ones_admin_or_moderator (zone_placeholder, user_placeholder):
182
+ return f'(zone=0 OR {check_ones_admin(zone_placeholder,user_placeholder)})'
183
+
184
+ check_admin_or_moderator_of = check_ones_admin_or_moderator
185
+
186
+ check_admin_of = check_ones_admin
187
+
188
+ def zone_of_user (user_placeholder):
189
+ return f'(SELECT zone FROM users WHERE {check_idname(user_placeholder)})'
190
+
191
+ def zone_by_user (user_placeholder):
192
+ return f'(SELECT id FROM zones WHERE id IN {zone_of_user(user_placeholder)})'
193
+
194
+ def zone_by_idname (idn_placeholder):
195
+ return f'(SELECT id FROM zones WHERE id={idn_placeholder} OR name=UPPER(TRIM({idn_placeholder})) LIMIT 1)'
196
+
197
+ def zone_of_zoneuser (zone_placeholder, user_placeholder):
198
+ return f'(SELECT id FROM zones WHERE {check_idname(zone_placeholder)} OR id IN {zone_of_user(user_placeholder)})'
199
+
200
+ def zone_case_idname (zone_placeholder):
201
+ return f"(CASE WHEN {zone_placeholder} != '' THEN (SELECT id FROM zones WHERE {check_idname(zone_placeholder)}) ELSE zone END)"
202
+
203
+ def get_zoneidn_by_useridn (zone_placeholder, user_placeholder):
204
+ return f"(CASE WHEN {user_placeholder} != '' THEN {zone_by_user(user_placeholder)} ELSE {zone_case_idname(zone_placeholder)} END)"
205
+
206
+ def zone_case_system (zone_placeholder, zone='zone'):
207
+ return f"(CASE WHEN TRIM({zone_placeholder})='0' OR UPPER(TRIM({zone_placeholder}))='ADMIN' THEN {zone} ELSE (SELECT id FROM zones WHERE {check_idname(zone_placeholder)} LIMIT 1) END)"
208
+
209
+ def increase_id (table):
210
+ return f'(SELECT IFNULL(MAX(id),0)+1 FROM {table})'
211
+
212
+ def search_idname (table, placeholder, fields='*'):
213
+ return f"SELECT {fields} FROM {table} WHERE (CAST(id AS VARCHAR) LIKE '%'||{placeholder}||'%' OR name LIKE '%'||{placeholder}||'%')"
214
+
215
+ def limit_offset (limnum, setnum):
216
+ return f'{limit(limnum)} {offset(setnum)}'
217
+
218
+ def offset (num):
219
+ return f'\n OFFSET {num}'
220
+
221
+ def limit (num):
222
+ return f'\n LIMIT {num}'
223
+
224
+ def multi_holders (query, lst):
225
+ return query.replace('??', ','.join(['(?)' for x in lst]))
226
+
227
+ def uuid_value ():
228
+ return "lower(hex(randomblob(4)))||'-'||lower(hex(randomblob(2)))||'-4'||substr(lower(hex(randomblob(2))),2)||'-'||substr('89ab',abs(random())%4+1,1)||substr(lower(hex(randomblob(2))),2)||'-'||lower(hex(randomblob(6)))"
229
+
230
+ def empty_json ():
231
+ return '{}'
embedize/dcore.py ADDED
@@ -0,0 +1,622 @@
1
+ import os
2
+ import re
3
+ import sys
4
+ import json
5
+ import duckdb
6
+ import sqlite3
7
+ import inspect
8
+ from typing import Callable
9
+ from pydantic import BaseModel
10
+
11
+ DBENGINE = 'SQLite'
12
+ ADMINDB = 'db/admin.db'
13
+ DUCKDBENGINE = 'DUCK'
14
+ ADMINDUCKDB = 'db/dadmin.db'
15
+
16
+ ADMINDBNAME = 'admin'
17
+ ADMINZONENAME = 'ADMIN'
18
+ ADMINUSERNAME = 'admin'
19
+ ADMINZONE = 0
20
+ ADMINUSER = 1
21
+ DEFPASSWORD = '123456'
22
+ ENCODING = 'ISO-8859-1'
23
+ ENCODINGSIG = 'utf-8-sig'
24
+ PLACEHOLDER = r'\:\w+\b'
25
+ COLUUID = 'uuid'
26
+ ARRAY = 'array'
27
+
28
+ JOURPRAGMA = 'PRAGMA journal_mode=WAL'
29
+ FKEYPRAGMA = 'PRAGMA foreign_keys=1'
30
+ TEMPPRAGMA = 'PRAGMA temp_store=2'
31
+
32
+ ## GLOBAL CONST
33
+
34
+ ERROR = 'error'
35
+ ACTION = 'action'
36
+ RESULT = 'result'
37
+
38
+ UNKNOWN = 'Unknown'
39
+ DBERROR = 'DatabaseError'
40
+ UNERROR = 'UnexpectedError'
41
+
42
+ BADZONEID = 'Zone ID not allowed'
43
+ ERRORINPUT = 'Invalid function parameters'
44
+ NOROWCOUNT = 'Input not verified, no updates were made'
45
+
46
+ NOENTRIES = 'A financial transaction must be recorded with at least 2 entries'
47
+ NOBALANCE = 'The total value of debits must equal the total value of credits'
48
+
49
+ ## GLOBAL COLUMN
50
+
51
+ COL_ACCOUNT = 'account'
52
+ COL_ACTIVE = 'active'
53
+ COL_CODE = 'code'
54
+ COL_CREATED = 'created'
55
+ COL_CREATOR = 'creator'
56
+ COL_CREDIT = 'credit'
57
+ COL_DATED = 'dated'
58
+ COL_DEBIT = 'debit'
59
+ COL_GROUP = 'grup'
60
+ COL_ID = 'id'
61
+ COL_INFO = 'info'
62
+ COL_ITEM = 'item'
63
+ COL_MODIFIED = 'modified'
64
+ COL_MODIFIER = 'modifier'
65
+ COL_NAME = 'name'
66
+ COL_NOTE = 'note'
67
+ COL_REF = 'ref'
68
+ COL_ROLE = 'role'
69
+ COL_ROOT = 'root'
70
+ COL_SPEC = 'spec'
71
+ COL_SUB = 'sub'
72
+ COL_TX = 'tx'
73
+ COL_USER = 'user'
74
+ COL_UUID = 'uuid'
75
+ COL_WORKBEGIN = 'workbegin'
76
+ COL_WORKEND = 'workend'
77
+ COL_ZADM = 'zadmin'
78
+ COL_ZONE = 'zone'
79
+
80
+ COL_ZONEID = 'zoneid'
81
+ COL_ZONEIDN = 'zoneidn'
82
+ COL_CONUSER = 'conuser'
83
+ COL_USERIDN = 'useridn'
84
+
85
+ COL_PASS = 'hpwd'
86
+ COL_OPWD = 'oldpass'
87
+ COL_NPWD = 'newpass'
88
+
89
+ COL_ACCOUNTCODE = 'accountcode'
90
+ COL_WORKTIME = 'worktime'
91
+
92
+ COL_OFFSET = 'offset'
93
+ COL_LIMIT = 'limit'
94
+
95
+ QUERY_ZONEID = 'SELECT id FROM zones WHERE CAST(id AS VARCHAR)=TRIM(:zoneidn) OR name=UPPER(TRIM(:zoneidn)) LIMIT 1'
96
+
97
+ class Execute (BaseModel):
98
+ func: Callable=None
99
+ data: dict={'query':'','values':{},'retquery':None,'retvalues':{},'db_path':None,'many':False,'script':False,'aid':UNKNOWN,'engine':None,'connect':None,'pandas':False}
100
+ check: dict={'err_stat':None,'err_func':None,'err_message':''}
101
+
102
+ def set_engine (engine):
103
+ return setattr(sys.modules[__name__], 'sys_engine', engine)
104
+
105
+ def set_connect (connect):
106
+ return setattr(sys.modules[__name__], 'sys_connect', connect)
107
+
108
+ def say_engine ():
109
+ return print(sys_engine)
110
+
111
+ def say_connect ():
112
+ return print(sys_connect)
113
+
114
+ def is_select (query, check='select'):
115
+ return str(query).strip().lower().startswith(check)
116
+
117
+ def admindb (engine):
118
+ return ADMINDB if engine in [sqlite3, None] else (ADMINDUCKDB if engine in [duckdb] else None)
119
+
120
+ def dbprefix (engine, defval='x'):
121
+ return '' if engine in [sqlite3, None] else ('d' if engine in [duckdb] else defval)
122
+
123
+ def get_zonedb (zoneidn, engine):
124
+ return f"{dbprefix(engine)}{ADMINDBNAME if (zoneid:=get_zoneid(zoneidn, engine=engine))==ADMINZONE else (str(zoneid) if zoneid is not None else '')}"
125
+
126
+ def get_zoneid (zoneidn, engine=None):
127
+ return ADMINZONE if str(zoneidn).upper() in [ADMINZONENAME, str(ADMINZONE)] else check_admin(QUERY_ZONEID, {COL_ZONEIDN: str(zoneidn)}, engine=(engine or sys_engine))
128
+
129
+ def get_aid (layer=4):
130
+ return callup(layer)
131
+
132
+ def callup (layer=2):
133
+ return inspect.stack()[layer][3]
134
+
135
+ def kwarg (**kw):
136
+ return kw
137
+
138
+ def dict_item (modifier, active, cancel, id, uuid):
139
+ return locals()
140
+
141
+ def dicts_items (it):
142
+ return [dict_item(it.modifier, it.active, it.cancel, x, None) for x in it.ids] if it.ids else [dict_item(it.modifier, it.active, it.cancel, None, x) for x in it.uuids]
143
+
144
+ def select_value (query, vals={}, index=0, engine=None):
145
+ return check_admin(query, vals, index, engine=(engine or sys_engine))
146
+
147
+ def first_row (resultset, defrow=None):
148
+ return resultset_first_row(resultset) if not resultset_is_empty(resultset) else defrow
149
+
150
+ def reform_model (model):
151
+ return model if type(model) in [dict, list, tuple, str, int, float] else model.model_dump()
152
+
153
+ def regexp (pattern, text):
154
+ return 1 if re.search(pattern, text) else 0
155
+
156
+ ## SQLITE vs DUCKDB
157
+
158
+ def sql_replace (text, pat, rep):
159
+ return re.sub(re.escape(pat), rep, text, flags=re.IGNORECASE)
160
+
161
+ def sql_date_replace (text):
162
+ return re.sub(r'DATE\((.*?)\)', r'CAST(\1 AS DATE)', text, flags=re.IGNORECASE)
163
+
164
+ def sql_uni_replace (text: str, replaces: dict={}) -> str:
165
+ for key, val in replaces.items(): text = sql_replace(text, key, val)
166
+ return text
167
+
168
+ def sql_duck_replace (text: str, replaces: dict={}) -> str:
169
+ return sql_date_replace(sql_uni_replace(text, replaces))
170
+
171
+ def check_valid_naming (column='name'):
172
+ return f"({column} NOT REGEXP '^[0-9]+$')"
173
+
174
+ def check_valid_naming2 (column='name'):
175
+ return f"(NOT REGEXP_MATCHES({column}, '^[0-9]+$'))"
176
+
177
+ def duck_replaces ():
178
+ return {
179
+ check_valid_naming(): check_valid_naming2(),
180
+ "DATETIME('now')": 'now()',
181
+ "DATE('now')": 'CAST(now() AS DATE)',
182
+ 'AUTOINCREMENT': ''
183
+ }
184
+
185
+ ## STORED FUNCTIONS
186
+
187
+ def json_extract (json_str, path):
188
+ try:
189
+ json_data = json.loads(json_str)
190
+ path = path.split('$.')[-1]
191
+ path_components = path.split('.')
192
+ value = json_data
193
+ for component in path_components:
194
+ value = value.get(component)
195
+ return value
196
+ except (json.JSONDecodeError, AttributeError, TypeError):
197
+ return None
198
+
199
+ def update_placeholders (query: str, engine=None, holdermark: str=':', holder: str=PLACEHOLDER) -> str:
200
+ engine = engine or sys_engine
201
+ if type(query) not in [str]: return None
202
+ if engine in [sqlite3, None]: return query
203
+ if engine in [duckdb]: query = sql_duck_replace(query, duck_replaces())
204
+ newmark = '$' if engine in [duckdb] else holdermark
205
+ subs = re.findall(holder, query)
206
+ items = iter(str(e) for e in subs)
207
+ newquery = re.sub(holder, lambda lm: f'{newmark}{next(items)[1:]}', query)
208
+ return newquery
209
+
210
+ def update_values (query: str, values: dict, engine=None, holder: str=PLACEHOLDER) -> dict:
211
+ engine = engine or sys_engine
212
+ if type(query) not in [str]: return None
213
+ if engine in [sqlite3, None]: return values
214
+ if type(values) not in [dict, list, tuple]: return values
215
+ def update(object):
216
+ if type(object) not in [dict]: return object
217
+ subs = list(set(re.findall(holder, query)))
218
+ subs = [sub[1:] for sub in subs]
219
+ return {key: value for key, value in object.items() if key in subs}
220
+ if type(values) in [list, tuple]:
221
+ newvalues = [update(value) for value in list(values)]
222
+ else:
223
+ newvalues = update(values)
224
+ return newvalues
225
+
226
+ def get_rowcount (cursor, engine=None):
227
+ engine = engine or sys_engine
228
+ try:
229
+ if engine in [duckdb]:
230
+ (rowcount,) = cursor.fetchone()
231
+ else:
232
+ rowcount = cursor.rowcount
233
+ except:
234
+ rowcount = cursor.rowcount
235
+ return rowcount
236
+
237
+ def attach (cursor, engine=None):
238
+ try:
239
+ cursor.execute(f"ATTACH DATABASE '{admindb(engine or sys_engine)}' AS admin")
240
+ except:
241
+ pass
242
+
243
+ def connect_duckdb (db_path: str):
244
+ conn = duckdb.connect(db_path)
245
+ return conn
246
+
247
+ def connect_sqlite (db_path: str):
248
+ return connect_db(db_path)
249
+
250
+ def connect_db (db_path: str):
251
+ conn = sqlite3.connect(db_path)
252
+ conn.create_function('json_extract', 2, json_extract)
253
+ conn.create_function('regexp_matches', 2, regexp)
254
+ conn.create_function('regexp', 2, regexp)
255
+ conn.execute(JOURPRAGMA)
256
+ conn.execute(TEMPPRAGMA)
257
+ conn.execute(FKEYPRAGMA)
258
+ return conn
259
+
260
+ set_engine(sqlite3)
261
+
262
+ set_connect(connect_sqlite)
263
+
264
+ def zonedb (zoneid: any, engine=None) -> str:
265
+ engine = engine or sys_engine
266
+ zoneid = get_zonedb(zoneid, engine=engine)
267
+ return None if zoneid is None else f'db/{zoneid}.db'
268
+
269
+ def getdb (zoneid: any, engine=None) -> str:
270
+ engine = engine or sys_engine
271
+ db = zonedb(zoneid, engine=engine)
272
+ if not db: raise(ValueError(f'DataError: no such database: {zoneid}'))
273
+ return db
274
+
275
+ def import_zone_database (zoneid: any, table: str, csvdata: any, engine=None) -> dict:
276
+ import io
277
+ import csv
278
+ conn = None
279
+ res = dict_result(aid=callup(1))
280
+ engine = engine or sys_engine
281
+ try:
282
+ if type(csvdata) is str:
283
+ with open(csvdata, 'r', encoding=ENCODINGSIG) as file:
284
+ reader = csv.reader(file)
285
+ headers = next(reader)
286
+ rows = [tuple(row) for row in reader]
287
+ elif type(csvdata) is dict:
288
+ reader = csv.reader(io.StringIO(csvdata.get('data')))
289
+ headers = next(reader)
290
+ rows = [tuple(row) for row in reader]
291
+ else:
292
+ res[ERROR] = f'ReadError: no valid data loaded'
293
+ return res
294
+ placeholders = ','.join(['?' for _ in headers])
295
+ query = f"INSERT INTO {table} ({','.join(headers)}) VALUES ({placeholders})"
296
+ conn = engine.connect(getdb(zoneid, engine=engine))
297
+ cursor = conn.cursor()
298
+ cursor.executemany(query, list(filter(None, rows)))
299
+ except Exception as e:
300
+ res[ERROR] = f'ActionError: {e}'
301
+ finally:
302
+ if conn:
303
+ conn.commit()
304
+ conn.close()
305
+ return res
306
+
307
+ def export_zone_database (zoneid: any, table: str, csv_path: str=None, idr: list=[], engine=None) -> dict:
308
+ import io
309
+ import csv
310
+ conn = None
311
+ res = dict_result(aid=callup(1))
312
+ where = '' if not idr else f'WHERE id BETWEEN ? AND ?'
313
+ query = f'SELECT * FROM {table} {where}'
314
+ engine = engine or sys_engine
315
+ try:
316
+ conn = engine.connect(getdb(zoneid, engine=engine))
317
+ cursor = conn.cursor()
318
+ cursor.execute(query, idr if where else [])
319
+ rows = cursor.fetchall()
320
+ headers = [description[0] for description in cursor.description]
321
+ def csvwrite(output, headers, rows):
322
+ writer = csv.writer(output)
323
+ writer.writerow(headers)
324
+ writer.writerows(rows)
325
+ if csv_path:
326
+ with open(csv_path, 'w', newline='', encoding=ENCODINGSIG) as output:
327
+ csvwrite(output, headers, rows)
328
+ res[RESULT] = [{'file': csv_path}]
329
+ else:
330
+ output = io.StringIO()
331
+ csvwrite(output, headers, rows)
332
+ res[RESULT] = [{'data': output.getvalue()}]
333
+ except Exception as e:
334
+ res[ERROR] = f'ActionError: {e}'
335
+ finally:
336
+ if conn:
337
+ conn.close()
338
+ return res
339
+
340
+ def export_database_tables (zoneid: any, engine=None) -> dict:
341
+ conn = None
342
+ res = dict_result(aid=callup(1))
343
+ engine = engine or sys_engine
344
+ if engine in [duckdb]:
345
+ query = "SELECT table_name FROM duckdb_tables"
346
+ elif engine in [sqlite3, None]:
347
+ query = "SELECT name FROM sqlite_master WHERE type='table'"
348
+ else:
349
+ query = ""
350
+ try:
351
+ conn = engine.connect(getdb(zoneid, engine=engine))
352
+ cursor = conn.cursor()
353
+ cursor.execute(query)
354
+ tables = cursor.fetchall()
355
+ tables = [table[0] for table in tables]
356
+ res[RESULT] = tables
357
+ except Exception as e:
358
+ res[ERROR] = f'ActionError: {e}'
359
+ finally:
360
+ if conn:
361
+ conn.close()
362
+ return res
363
+
364
+ def check_admin (query: str, values: dict={}, index=0, engine=None) -> dict:
365
+ engine = engine or sys_engine
366
+ conn = None
367
+ try:
368
+ conn = engine.connect(getdb(ADMINDBNAME, engine=engine))
369
+ cursor = conn.cursor()
370
+ cursor.execute(update_placeholders(query, engine), update_values(query, values, engine))
371
+ rows = cursor.fetchone()
372
+ return None if not rows else rows[index]
373
+ except Exception as e:
374
+ return None
375
+ finally:
376
+ if conn:
377
+ conn.close()
378
+
379
+ def out_pandas (conn, query: str, values: dict={}, simplelist: bool=False, dataindex: int=0):
380
+ import pandas as pd
381
+ df = pd.read_sql_query(query, conn, params=values)
382
+ if simplelist: return df.iloc[:,dataindex]
383
+ return df
384
+
385
+ ## CORE FUNCTIONS
386
+
387
+ def execute_table_create_insert_update (db_path: str, query: str, values: dict={}, retquery: str=None, retvalues: dict={}, many: bool=False, script: bool=False, aid: str=UNKNOWN, engine=None, connect=None, pandas: bool=False) -> dict:
388
+ engine = engine or sys_engine
389
+ connect = connect or sys_connect
390
+ conn = None
391
+ try:
392
+ conn = connect(db_path)
393
+ cursor = conn.cursor()
394
+ attach(cursor, engine=engine)
395
+ if script and engine in [sqlite3, None]:
396
+ cursor.executescript(query)
397
+ else:
398
+ newquery = update_placeholders(query, engine)
399
+ newvalues = update_values(query, values, engine)
400
+ if many:
401
+ cursor.executemany(newquery, newvalues)
402
+ else:
403
+ cursor.execute(newquery, newvalues)
404
+ rowcount = get_rowcount(cursor, engine)
405
+ conn.commit()
406
+ if rowcount==0:
407
+ return error_database(NOROWCOUNT)
408
+ if retquery is None:
409
+ return dict_result(aid=aid)
410
+ elif not is_select(retquery):
411
+ return dict_result(err=ERRORINPUT, aid=aid)
412
+ else:
413
+ if pandas:
414
+ return dict_result(out_pandas(conn, update_placeholders(retquery, engine), update_values(retquery, retvalues, engine)), aid=aid)
415
+ else:
416
+ return fetch_table_as_list_of_dict(db_path, retquery, retvalues, aid=aid, engine=engine, connect=connect)
417
+ except engine.Error as e:
418
+ conn.rollback()
419
+ return error_database(e)
420
+ except Exception as e:
421
+ return error_unexpect(e)
422
+ finally:
423
+ if conn:
424
+ conn.close()
425
+
426
+ def fetch_table_as_list_of_dict (db_path: str, query: str, values: dict={}, retquery=None, retvalues=None, many: bool=False, script: bool=False, aid: str=UNKNOWN, engine=None, connect=None, pandas: bool=False) -> dict:
427
+ engine = engine or sys_engine
428
+ connect = connect or sys_connect
429
+ conn = None
430
+ try:
431
+ conn = connect(db_path)
432
+ cursor = conn.cursor()
433
+ attach(cursor, engine=engine)
434
+ newquery = update_placeholders(query, engine)
435
+ newvalues = update_values(query, values, engine)
436
+ if pandas:
437
+ return dict_result(out_pandas(conn, newquery, newvalues), aid=aid)
438
+ cursor.execute(newquery, newvalues)
439
+ rows = cursor.fetchall()
440
+ columns = [desc[0] for desc in cursor.description]
441
+ list_of_dicts = [dict(zip(columns, row)) for row in rows]
442
+ return dict_result(result=list_of_dicts, aid=aid)
443
+ except engine.Error as e:
444
+ return error_database(e)
445
+ except Exception as e:
446
+ return error_unexpect(e)
447
+ finally:
448
+ if conn:
449
+ conn.close()
450
+
451
+ def fetch_table_as_simple_list (db_path: str, query: str, values: dict={}, retquery=None, retvalues=None, many: bool=False, script: bool=False, aid: str=UNKNOWN, engine=None, connect=None, pandas: bool=False) -> dict:
452
+ engine = engine or sys_engine
453
+ connect = connect or sys_connect
454
+ conn = None
455
+ try:
456
+ conn = connect(db_path)
457
+ cursor = conn.cursor()
458
+ attach(cursor, engine=engine)
459
+ newquery = update_placeholders(query, engine)
460
+ newvalues = update_values(query, values, engine)
461
+ if pandas:
462
+ return dict_result(out_pandas(conn, newquery, newvalues, simplelist=True), aid=aid)
463
+ cursor.execute(newquery, newvalues)
464
+ rows = cursor.fetchall()
465
+ rows = [row[0] for row in rows]
466
+ return dict_result(result=rows, aid=aid)
467
+ except engine.Error as e:
468
+ return error_database(e)
469
+ except Exception as e:
470
+ return error_unexpect(e)
471
+ finally:
472
+ if conn:
473
+ conn.close()
474
+
475
+ def resultset_first_row (result: dict) -> dict:
476
+ try:
477
+ row = result[RESULT][0]
478
+ if type(row) is dict: return row
479
+ return {}
480
+ except:
481
+ return {}
482
+
483
+ def resultset_is_empty (result: dict) -> bool:
484
+ try:
485
+ return (result.get(ERROR) is not None) or (not result.get(RESULT))
486
+ except:
487
+ return True
488
+
489
+ def dict_result (result: list=[], err: str=None, scope: str=UNKNOWN, aid: str=UNKNOWN) -> dict:
490
+ if err is None:
491
+ return {ACTION: aid, ERROR: None, RESULT: result}
492
+ else:
493
+ return {ACTION: aid, ERROR: f"{scope}: {err}", RESULT: []}
494
+
495
+ def error_database (err: str, aid: str=UNKNOWN) -> dict:
496
+ return dict_result([], err, DBERROR, aid=aid)
497
+
498
+ def error_unexpect (err: str, aid: str=UNKNOWN) -> dict:
499
+ return dict_result([], err, UNERROR, aid=aid)
500
+
501
+ def db_exist (db_path: str, engine=None) -> bool:
502
+ engine = engine or sys_engine
503
+ try:
504
+ if os.path.isfile(db_path):
505
+ if os.path.getsize(db_path)>100:
506
+ with open(db_path,'r', encoding=ENCODING) as f:
507
+ header = f.read(100)
508
+ if engine in [sqlite3, None]: return header.startswith(DBENGINE)
509
+ if engine in [duckdb]: return header[8:].startswith(DUCKDBENGINE)
510
+ return False
511
+ except:
512
+ return False
513
+
514
+ def setquery (model: Execute, query: str, aid: str=UNKNOWN):
515
+ model.data['query'] = query
516
+ model.data['aid'] = aid
517
+
518
+ def setretquery (model: Execute, query: str):
519
+ model.data['retquery'] = query
520
+
521
+ def setvals (model: Execute, vals: dict={}):
522
+ model.data['values'] = vals
523
+
524
+ def setretvals (model: Execute, vals: dict={}):
525
+ model.data['retvalues'] = vals
526
+
527
+ def setmany (model: Execute, val: bool=False):
528
+ model.data['many'] = val
529
+
530
+ def setscript (model: Execute, val: bool=False):
531
+ model.data['script'] = val
532
+
533
+ def setpandas (model: Execute, val: bool=False):
534
+ model.data['pandas'] = val
535
+
536
+ def setengine (model: Execute, val=None):
537
+ model.data['engine'] = val or sys_engine
538
+
539
+ def setconnect (model: Execute, val=None):
540
+ model.data['connect'] = val or sys_connect
541
+
542
+ def setdbpath (model: Execute, val: str):
543
+ model.data['db_path'] = val
544
+
545
+ def db_execute (db_caller: Execute) -> dict:
546
+ if db_caller.check['err_stat']: return db_caller.check['err_func'](db_caller.check['err_message'])
547
+ return db_caller.func(**db_caller.data)
548
+
549
+ def batch (queries: list, db_path: str=None, engine=None, connect=None) -> dict:
550
+ engine = engine or sys_engine
551
+ connect = connect or sys_connect
552
+ if not db_path: db_path = admindb(engine)
553
+ caller = Execute(func=execute_table_create_insert_update)
554
+ setquery(caller, ';'.join(queries))
555
+ setscript(caller, True)
556
+ setengine(caller, engine)
557
+ setconnect(caller, connect)
558
+ setdbpath(caller, db_path)
559
+ return db_execute(caller)
560
+
561
+ db_insert = execute_table_create_insert_update
562
+
563
+ db_select = fetch_table_as_list_of_dict
564
+
565
+ db_list = fetch_table_as_simple_list
566
+
567
+ def select_dict (query: str, model: BaseModel, db_path: str=None, func=db_select, layer=4, engine=None, connect=None, pandas: bool=False, **kw) -> dict:
568
+ engine = engine or sys_engine
569
+ connect = connect or sys_connect
570
+ if not db_path: db_path = admindb(engine)
571
+ caller = Execute(func=func)
572
+ setquery(caller, query, aid=get_aid(layer))
573
+ setvals(caller, reform_model(model))
574
+ setengine(caller, engine)
575
+ setconnect(caller, connect)
576
+ setdbpath(caller, db_path)
577
+ setpandas(caller, pandas)
578
+ return db_execute(caller)
579
+
580
+ def select_list (query: str, model: BaseModel, db_path: str=None, engine=None, connect=None, pandas: bool=False, **kw) -> dict:
581
+ engine = engine or sys_engine
582
+ connect = connect or sys_connect
583
+ return select_dict(func=db_list, query=query, model=model, db_path=db_path, engine=engine, connect=connect, pandas=pandas)
584
+
585
+ def insert_one (query: str, model: BaseModel, retquery: str='', db_path: str=None, layer=4, engine=None, connect=None, pandas: bool=False, **kw) -> dict:
586
+ engine = engine or sys_engine
587
+ connect = connect or sys_connect
588
+ if not db_path: db_path = admindb(engine)
589
+ caller = Execute(func=db_insert)
590
+ setquery(caller, query, aid=get_aid(layer))
591
+ setretquery(caller, retquery)
592
+ data = reform_model(model)
593
+ if type(data) is dict:
594
+ if data.get(ARRAY):
595
+ data[COL_LIMIT] = len(model.array)
596
+ setvals(caller, model.array)
597
+ setmany(caller, True)
598
+ else:
599
+ setvals(caller, data)
600
+ setretvals(caller, data) ##limit
601
+ else:
602
+ setvals(caller, data)
603
+ setretvals(caller, kw.get('retvalues'))
604
+ setengine(caller, engine)
605
+ setconnect(caller, connect)
606
+ setdbpath(caller, db_path)
607
+ setpandas(caller, pandas)
608
+ return db_execute(caller)
609
+
610
+ create_one = insert_one
611
+
612
+ update_one = insert_one
613
+
614
+ delete_one = insert_one
615
+
616
+ insert_many = insert_one
617
+
618
+ create_many = insert_many
619
+
620
+ update_many = insert_many
621
+
622
+ delete_many = insert_many
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: embedize
3
+ Version: 1.0.2
4
+ Summary: SQLite and DuckDB Tiny Lib
5
+ Home-page: https://github.com/asinerum/embedize
6
+ Author: Asinerum Conlang Project
7
+ Author-email: asinerum.com@gmail.com
8
+ License: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: duckdb>=1.4.2
16
+ Requires-Dist: pydantic>=2.12.5
17
+ Requires-Dist: pyyaml>=6.0.3
18
+ Dynamic: license-file
19
+
20
+ Detailed tips, tricks, and examples, can be found at project's repository
21
+ https://github.com/asinerum/embedize
22
+
23
+ (C) 2026 Asinerum Conlang Project
@@ -0,0 +1,8 @@
1
+ embedize/__init__.py,sha256=gt_wGaSvvHy20spPijrYxSHcA2-Tbsc5krdubVcymMo,67
2
+ embedize/dbs.py,sha256=eAtQZ3XFs2y4l1HP4uQlUp1_PvFgssLeo8RqLuS-wbQ,10848
3
+ embedize/dcore.py,sha256=yatqRv3Hbhsc0brwY9KQco3tbHseoV-phxYnPzr8sCA,19997
4
+ embedize-1.0.2.dist-info/licenses/LICENSE,sha256=4npUbkrpgB6lqMiYYeUxZAP4SOkjVSwK8-7jW60mxvw,1081
5
+ embedize-1.0.2.dist-info/METADATA,sha256=-Vg7oIIZjYaA6RJf0eOQqb20ZvSK_D9En4jSLgSI2eM,732
6
+ embedize-1.0.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
7
+ embedize-1.0.2.dist-info/top_level.txt,sha256=iA0uN4RappjE38h_K_x_sjVZsyNY05nDSRfMcy9f_L4,9
8
+ embedize-1.0.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Asinerum Conlang Project
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.
@@ -0,0 +1 @@
1
+ embedize