backio 1.0.0__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.
- backio-1.0.0/LICENSE +21 -0
- backio-1.0.0/PKG-INFO +25 -0
- backio-1.0.0/README.md +4 -0
- backio-1.0.0/pyproject.toml +3 -0
- backio-1.0.0/setup.cfg +34 -0
- backio-1.0.0/src/backio/__init__.py +3 -0
- backio-1.0.0/src/backio/io.py +846 -0
- backio-1.0.0/src/backio.egg-info/PKG-INFO +25 -0
- backio-1.0.0/src/backio.egg-info/SOURCES.txt +11 -0
- backio-1.0.0/src/backio.egg-info/dependency_links.txt +1 -0
- backio-1.0.0/src/backio.egg-info/requires.txt +5 -0
- backio-1.0.0/src/backio.egg-info/top_level.txt +1 -0
backio-1.0.0/LICENSE
ADDED
|
@@ -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.
|
backio-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: backio
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Literp Fundamental IO Lib
|
|
5
|
+
Home-page: https://github.com/asinerum/backio
|
|
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: embedize>=1.0.4
|
|
16
|
+
Requires-Dist: formatize>=1.0.4
|
|
17
|
+
Requires-Dist: fastapi>=0.122.1
|
|
18
|
+
Requires-Dist: python-jose>=3.5.0
|
|
19
|
+
Requires-Dist: pydantic>=2.12.5
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
Detailed tips, tricks, and examples, can be found at project's repository
|
|
23
|
+
https://github.com/asinerum/backio
|
|
24
|
+
|
|
25
|
+
(C) 2026 Asinerum Conlang Project
|
backio-1.0.0/README.md
ADDED
backio-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = backio
|
|
3
|
+
version = 1.0.0
|
|
4
|
+
author = Asinerum Conlang Project
|
|
5
|
+
author_email = asinerum.com@gmail.com
|
|
6
|
+
description = Literp Fundamental IO Lib
|
|
7
|
+
long_description = file: README.md
|
|
8
|
+
long_description_content_type = text/markdown
|
|
9
|
+
url = https://github.com/asinerum/backio
|
|
10
|
+
license = MIT
|
|
11
|
+
classifiers =
|
|
12
|
+
Programming Language :: Python :: 3
|
|
13
|
+
License :: OSI Approved :: MIT License
|
|
14
|
+
Operating System :: OS Independent
|
|
15
|
+
|
|
16
|
+
[options]
|
|
17
|
+
package_dir =
|
|
18
|
+
= src
|
|
19
|
+
packages = find:
|
|
20
|
+
python_requires = >=3.7
|
|
21
|
+
install_requires =
|
|
22
|
+
embedize >= 1.0.4
|
|
23
|
+
formatize >= 1.0.4
|
|
24
|
+
fastapi >= 0.122.1
|
|
25
|
+
python-jose >= 3.5.0
|
|
26
|
+
pydantic >= 2.12.5
|
|
27
|
+
|
|
28
|
+
[options.packages.find]
|
|
29
|
+
where = src
|
|
30
|
+
|
|
31
|
+
[egg_info]
|
|
32
|
+
tag_build =
|
|
33
|
+
tag_date = 0
|
|
34
|
+
|
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
from typing import Optional, Callable
|
|
3
|
+
from jose import JWTError, jwt
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from fastapi import Response
|
|
6
|
+
import sys, os, json, types
|
|
7
|
+
import inspect
|
|
8
|
+
|
|
9
|
+
from formatize.const import *
|
|
10
|
+
|
|
11
|
+
from formatize.form import\
|
|
12
|
+
ADMIN, ACCOUNTING,\
|
|
13
|
+
APP_PATH, DBA_PATH,\
|
|
14
|
+
PARAMS, EMPTY, SPACE,\
|
|
15
|
+
add_models_from_yaml,\
|
|
16
|
+
add_consts_from_yaml,\
|
|
17
|
+
get_modules_from_yaml,\
|
|
18
|
+
param_load_from_request,\
|
|
19
|
+
data_load_from_json_file,\
|
|
20
|
+
data_load_from_yaml_file,\
|
|
21
|
+
function_module2,\
|
|
22
|
+
function_module,\
|
|
23
|
+
import_module,\
|
|
24
|
+
get_module,\
|
|
25
|
+
load_yaml,\
|
|
26
|
+
dump_yaml,\
|
|
27
|
+
dump_csv,\
|
|
28
|
+
popattr,\
|
|
29
|
+
popfunc,\
|
|
30
|
+
uid,\
|
|
31
|
+
sha,\
|
|
32
|
+
lds,\
|
|
33
|
+
hstr,\
|
|
34
|
+
dicts,\
|
|
35
|
+
integer,\
|
|
36
|
+
default,\
|
|
37
|
+
flatten,\
|
|
38
|
+
extract,\
|
|
39
|
+
to_upper,\
|
|
40
|
+
err_input,\
|
|
41
|
+
is_numeric,\
|
|
42
|
+
str_to_list,\
|
|
43
|
+
check_list,\
|
|
44
|
+
merge_list,\
|
|
45
|
+
data_dict,\
|
|
46
|
+
sort_dict,\
|
|
47
|
+
dict_sort,\
|
|
48
|
+
dict_assign,\
|
|
49
|
+
parse_doc_date,\
|
|
50
|
+
prepare_model,\
|
|
51
|
+
prepare_info,\
|
|
52
|
+
prepare_password,\
|
|
53
|
+
prepare_worktime,\
|
|
54
|
+
prepare_workinterval,\
|
|
55
|
+
prepare_hash_password
|
|
56
|
+
|
|
57
|
+
from embedize import\
|
|
58
|
+
ARRAY,\
|
|
59
|
+
ERROR,\
|
|
60
|
+
RESULT,\
|
|
61
|
+
UNKNOWN,\
|
|
62
|
+
BADZONEID,\
|
|
63
|
+
ERRORINPUT,\
|
|
64
|
+
NOROWCOUNT,\
|
|
65
|
+
DEFPASSWORD,\
|
|
66
|
+
ADMINZONENAME,\
|
|
67
|
+
ADMINUSERNAME,\
|
|
68
|
+
ADMINZONE,\
|
|
69
|
+
ADMINUSER,\
|
|
70
|
+
NOENTRIES,\
|
|
71
|
+
NOBALANCE,\
|
|
72
|
+
COL_ID,\
|
|
73
|
+
COL_UUID,\
|
|
74
|
+
COL_NAME,\
|
|
75
|
+
COL_ZONE,\
|
|
76
|
+
COL_ZADM,\
|
|
77
|
+
COL_PASS,\
|
|
78
|
+
COL_OPWD,\
|
|
79
|
+
COL_NPWD,\
|
|
80
|
+
COL_INFO,\
|
|
81
|
+
COL_ZONEID,\
|
|
82
|
+
COL_ZONEIDN,\
|
|
83
|
+
COL_CONUSER,\
|
|
84
|
+
COL_USERIDN,\
|
|
85
|
+
COL_CREATOR,\
|
|
86
|
+
COL_MODIFIER,\
|
|
87
|
+
COL_CREDIT,\
|
|
88
|
+
COL_DEBIT,\
|
|
89
|
+
COL_DATED,\
|
|
90
|
+
COL_USER,\
|
|
91
|
+
COL_CODE,\
|
|
92
|
+
COL_ACCOUNT,\
|
|
93
|
+
COL_ACCOUNTCODE,\
|
|
94
|
+
duckdb,\
|
|
95
|
+
sqlite3,\
|
|
96
|
+
set_engine,\
|
|
97
|
+
set_connect,\
|
|
98
|
+
connect_duckdb,\
|
|
99
|
+
connect_sqlite,\
|
|
100
|
+
error_unexpect,\
|
|
101
|
+
error_database,\
|
|
102
|
+
dicts_items,\
|
|
103
|
+
dict_result,\
|
|
104
|
+
check_admin,\
|
|
105
|
+
get_zoneid,\
|
|
106
|
+
zonedb,\
|
|
107
|
+
callup,\
|
|
108
|
+
batch,\
|
|
109
|
+
db_exist,\
|
|
110
|
+
first_row,\
|
|
111
|
+
import_zone_database,\
|
|
112
|
+
export_zone_database,\
|
|
113
|
+
export_database_tables,\
|
|
114
|
+
resultset_first_row,\
|
|
115
|
+
resultset_is_empty,\
|
|
116
|
+
select_value,\
|
|
117
|
+
select_dict,\
|
|
118
|
+
select_list,\
|
|
119
|
+
delete_one,\
|
|
120
|
+
insert_one,\
|
|
121
|
+
update_one,\
|
|
122
|
+
delete_many,\
|
|
123
|
+
insert_many,\
|
|
124
|
+
update_many,\
|
|
125
|
+
create_admin_zones,\
|
|
126
|
+
create_admin_users,\
|
|
127
|
+
duck_create_admin_zones,\
|
|
128
|
+
duck_create_admin_users,\
|
|
129
|
+
create_zone_roles,\
|
|
130
|
+
create_zone_specs,\
|
|
131
|
+
create_zone_specroles,\
|
|
132
|
+
create_zone_userroles,\
|
|
133
|
+
create_zone_userspecs,\
|
|
134
|
+
create_zone_groups,\
|
|
135
|
+
create_zone_roots,\
|
|
136
|
+
create_zone_accounts,\
|
|
137
|
+
create_zone_subs,\
|
|
138
|
+
create_zone_items,\
|
|
139
|
+
create_zone_txs,\
|
|
140
|
+
duck_create_zone_roles,\
|
|
141
|
+
duck_create_zone_specs,\
|
|
142
|
+
duck_create_zone_specroles,\
|
|
143
|
+
duck_create_zone_userspecs,\
|
|
144
|
+
duck_create_zone_userroles,\
|
|
145
|
+
duck_create_zone_groups,\
|
|
146
|
+
duck_create_zone_roots,\
|
|
147
|
+
duck_create_zone_accounts,\
|
|
148
|
+
duck_create_zone_subs,\
|
|
149
|
+
duck_create_zone_items,\
|
|
150
|
+
duck_create_zone_txs,\
|
|
151
|
+
multi_holders
|
|
152
|
+
|
|
153
|
+
LIBPATH = 'lib.db.query'
|
|
154
|
+
|
|
155
|
+
ROLE_WORKING = 'User login required'
|
|
156
|
+
ROLE_ADMIN = 'System admin privileges required'
|
|
157
|
+
ROLE_MANAGER = 'Client admin or system moderator or registered user access only'
|
|
158
|
+
ROLE_MODERATOR = 'System moderator privileges required'
|
|
159
|
+
ROLE_ZONE_ADMIN = 'Client admin privileges required'
|
|
160
|
+
ROLE_ZONE_MANAGER = 'Client admin or system moderator privileges required'
|
|
161
|
+
ROLE_ZONE_STAFF = 'Personnel login required'
|
|
162
|
+
ROLE_ADDITIONAL = 'Additional privileges required'
|
|
163
|
+
ROLE_CREATOR = 'Action dedicated for system admin on system zone and system moderator on client zones'
|
|
164
|
+
ROLE_USER = 'Action not authorized'
|
|
165
|
+
|
|
166
|
+
ROLE_FinancialRecordkeeping = 100100
|
|
167
|
+
ROLE_TransactionProcessing = 100150
|
|
168
|
+
ROLE_FinancialReporting = 100200
|
|
169
|
+
ROLE_AccountReconciliation = 100250
|
|
170
|
+
ROLE_TaxCompliance = 100300
|
|
171
|
+
ROLE_BudgetingForecasting = 100350
|
|
172
|
+
ROLE_Auditing = 100400
|
|
173
|
+
ROLE_FinancialAnalysis = 100450
|
|
174
|
+
ROLE_Compliance = 100500
|
|
175
|
+
ROLE_CostManagement = 100550
|
|
176
|
+
ROLE_AccountsPayableReceivable = 100600
|
|
177
|
+
ROLE_PayrollProcessing = 100650
|
|
178
|
+
ROLE_CommunicationCollaboration = 100700
|
|
179
|
+
ROLE_RecordReportReviewing = 100900
|
|
180
|
+
|
|
181
|
+
SPEC_StaffAccountant = 101100
|
|
182
|
+
SPEC_AccountingAssistant = 101150
|
|
183
|
+
SPEC_AccountingClerk = 101200
|
|
184
|
+
SPEC_FinancialAccountant = 101250
|
|
185
|
+
SPEC_TaxAccountant = 101300
|
|
186
|
+
SPEC_AuditAccountant = 101350
|
|
187
|
+
SPEC_ChiefAccountant = 101800
|
|
188
|
+
SPEC_FinancialOfficer = 101850
|
|
189
|
+
SPEC_Reviewer = 101900
|
|
190
|
+
|
|
191
|
+
## Overrode by IO.YAML config vars
|
|
192
|
+
edata_spec_specs = [SPEC_FinancialOfficer]
|
|
193
|
+
edata_role_specs = [SPEC_ChiefAccountant, SPEC_FinancialOfficer]
|
|
194
|
+
edata_sub_specs = [SPEC_StaffAccountant, SPEC_AccountingClerk, SPEC_FinancialAccountant, SPEC_TaxAccountant, SPEC_ChiefAccountant, SPEC_FinancialOfficer]
|
|
195
|
+
edata_txp_roles = [ROLE_TransactionProcessing]
|
|
196
|
+
edata_frk_roles = [ROLE_FinancialRecordkeeping]
|
|
197
|
+
|
|
198
|
+
ENV_SECRET = 'SECRET_KEY'
|
|
199
|
+
TOKENTYPE = 'token_type'
|
|
200
|
+
TOKEN = 'access_token'
|
|
201
|
+
RTOKEN = 'refresh_token'
|
|
202
|
+
TOKENID = 'sub'
|
|
203
|
+
BEARER = 'Bearer'
|
|
204
|
+
USERNAME = 'user'
|
|
205
|
+
MESSAGE = 'message'
|
|
206
|
+
|
|
207
|
+
ERR_DBASE = 'Database already exists'
|
|
208
|
+
ERR_NODBASE = 'Database does not exist'
|
|
209
|
+
ERR_OLDPASS = 'Incorrect given password'
|
|
210
|
+
ERR_USERNAME = 'Incorrect user name or status'
|
|
211
|
+
ERR_USERPASS = 'Incorrect user login password'
|
|
212
|
+
ERR_VALIDATES = 'Could not validate credentials'
|
|
213
|
+
ERR_AUTHLOGIN = 'Not authorized or login failed'
|
|
214
|
+
ERR_BADZONEID = 'Invalid zone ID'
|
|
215
|
+
|
|
216
|
+
ERR_INPUT = 'Invalid input parameters'
|
|
217
|
+
ERR_FUNCTION = 'App module or function not found'
|
|
218
|
+
ERR_JSONDATA = 'JSON input data processing failed'
|
|
219
|
+
ERR_LOADJSON = 'JSON data loading failed'
|
|
220
|
+
ERR_LOADYAML = 'YAML data loading failed'
|
|
221
|
+
|
|
222
|
+
JAFARG = 'afarg'
|
|
223
|
+
JMODEL = 'model'
|
|
224
|
+
JDFUNC = 'dfunc'
|
|
225
|
+
JDBMOD = 'dbmod'
|
|
226
|
+
JDBQUE = 'dbque'
|
|
227
|
+
JQUEID = 'queid'
|
|
228
|
+
|
|
229
|
+
json_role = lambda module: f'lib/json/{module}/role.json'
|
|
230
|
+
json_account = lambda module: f'lib/json/{module}/account.json'
|
|
231
|
+
yaml_transaction = lambda module: f'lib/yaml/{module}/transaction.yaml'
|
|
232
|
+
yaml_report = lambda module: f'lib/yaml/{module}/report.yaml'
|
|
233
|
+
yaml_rich = lambda module: f'lib/yaml/{module}/rich.yaml'
|
|
234
|
+
|
|
235
|
+
get_userid = lambda params: params.get(COL_USER)
|
|
236
|
+
get_idname = lambda params: params.get(COL_NAME) or params.get(COL_ID)
|
|
237
|
+
get_nameid = lambda params: params.get(COL_ID) or params.get(COL_NAME)
|
|
238
|
+
|
|
239
|
+
pseudo = lambda data: (lambda anything=None: data)
|
|
240
|
+
do_none = lambda access_token=None, param=None, ok=True: (ok, None, None)
|
|
241
|
+
|
|
242
|
+
popthis = lambda attr: popattr(this, attr)
|
|
243
|
+
get_attr = lambda attr: (lambda object: object.get(attr))
|
|
244
|
+
|
|
245
|
+
getfunc = lambda funcname, field, defval=None, finval=None, pop=popthis, module=None: pop(x[funcname].get(field) if type(x:=(y:=module or module_access).get(function_module2(y, funcname))) is dict and funcname in x else defval) or finval
|
|
246
|
+
|
|
247
|
+
get_afunc = lambda funcname, field='afunc', defval=None, finval=None, pop=popthis: getfunc(funcname, field, defval, finval, pop)
|
|
248
|
+
get_pfunc = lambda funcname, defval=get_idname, field='afarg': defval if not (x:=get_afunc(funcname, field=field, pop=hstr)) else (lambda p: p.get(x))
|
|
249
|
+
get_wfunc = lambda funcname, defval=prepare_password: get_afunc(funcname, field='wfunc', finval=defval if defval else prepare_password)
|
|
250
|
+
get_ifunc = lambda funcname, defval=prepare_info: get_afunc(funcname, field='ifunc', finval=defval if defval else prepare_info)
|
|
251
|
+
get_dfunc = lambda funcname, defval=select_dict: get_afunc(funcname, field='dfunc', finval=defval if defval else select_dict)
|
|
252
|
+
get_model = lambda funcname, defval=None, afunc=get_afunc: afunc(funcname, field='model', defval=defval, finval=popthis('DbUni'))
|
|
253
|
+
get_token = lambda funcname, defval=None: get_pfunc(funcname, defval=defval, field='token')
|
|
254
|
+
get_bfunc = lambda funcname, defval=None: get_afunc(funcname, field='bfunc', finval=defval)
|
|
255
|
+
get_rfunc = lambda funcname, defval=None: get_afunc(funcname, field='rfunc', finval=defval)
|
|
256
|
+
get_mfunc = lambda funcname, defval=None: get_afunc(funcname, field='mfunc', finval=defval)
|
|
257
|
+
get_minfo = lambda funcname, defval=None: get_afunc(funcname, field='minfo', finval=defval)
|
|
258
|
+
get_mdele = lambda funcname, defval=None: get_afunc(funcname, field='mdele', finval=defval)
|
|
259
|
+
get_erole = lambda funcname, defval=None: get_afunc(funcname, field='erole', finval=defval)
|
|
260
|
+
get_edata = lambda funcname, defval=None: get_afunc(funcname, field='edata', finval=defval)
|
|
261
|
+
get_build = lambda funcname, defval=None: get_afunc(funcname, field='build', defval=defval, finval=popthis('DbUni'))
|
|
262
|
+
get_query = lambda funcname, querypath, libpath=LIBPATH: popattr(get_module(querypath, path=f'{libpath}.'), funcname)
|
|
263
|
+
get_qpath = lambda funcname, defval=None, field='qpath': get_afunc(funcname, field=field, defval=EMPTY, finval=defval, pop=hstr)
|
|
264
|
+
get_rpath = lambda funcname, defval=None: get_qpath(funcname, defval, field='rpath')
|
|
265
|
+
get_rquid = lambda funcname, defval=None: get_qpath(funcname, defval, field='rquid')
|
|
266
|
+
get_afarg = lambda funcname, defval=None: get_qpath(funcname, defval, field='afarg')
|
|
267
|
+
get_mprep = lambda funcname, defval=None: get_afunc(funcname, field='mprep', finval=defval)
|
|
268
|
+
get_ufunc = lambda funcname, defval=None: get_afunc(funcname, field='ufunc', finval=defval)
|
|
269
|
+
get_cfunc = lambda funcname, defval=None: get_afunc(funcname, field='cfunc', finval=defval)
|
|
270
|
+
get_cemsg = lambda funcname, defval=None: get_afunc(funcname, field='cemsg', finval=defval)
|
|
271
|
+
get_cattr = lambda funcname, defval=None: get_qpath(funcname, defval, field='cattr')
|
|
272
|
+
get_vfunc = lambda funcname, defval=None: get_afunc(funcname, field='vfunc', finval=defval)
|
|
273
|
+
get_vattr = lambda funcname, defval=None: get_qpath(funcname, defval, field='vattr')
|
|
274
|
+
|
|
275
|
+
pick_afunc = lambda funcname, field='afunc', defval=None, finval=None, pop=popthis: getfunc(funcname, field, defval, finval, pop, module=module_dbase)
|
|
276
|
+
pick_dfunc = lambda funcname, defval=select_dict: pick_afunc(funcname, field='dfunc', finval=defval if defval else select_dict)
|
|
277
|
+
pick_qpath = lambda funcname, defval=None, field='qpath': pick_afunc(funcname, field=field, defval=EMPTY, finval=defval, pop=hstr)
|
|
278
|
+
pick_layer = lambda funcname, defval=None: x if (x:=integer(pick_qpath(funcname, defval, field='layer'))) > 0 else defval
|
|
279
|
+
pick_ufunc = lambda funcname, defval=None: pick_afunc(funcname, field='ufunc', finval=defval)
|
|
280
|
+
pick_model = lambda funcname, defval=None: get_model(funcname, defval, afunc=pick_afunc)
|
|
281
|
+
pick_rpath = lambda funcname, defval=None: pick_qpath(funcname, defval, field='rpath')
|
|
282
|
+
pick_rquid = lambda funcname, defval=None: pick_qpath(funcname, defval, field='rquid')
|
|
283
|
+
|
|
284
|
+
check_idname = lambda model, idname: False if not idname else (str(idname) if not is_numeric(idname, int) else str(idname)).strip().lower() in [str(model.id).lower(), str(model.name).lower()]
|
|
285
|
+
check_nameid = lambda obj, idname: False if not idname else (str(idname) if not is_numeric(idname, int) else str(idname)).strip().lower() in [str(obj.get('name')).lower(),str(obj.get('id')).lower()]
|
|
286
|
+
check_nameids = lambda obj, idnames: False if type(idnames) is not list else check_list([str(x).lower() for x in idnames], [str(obj.get('name')).lower(),str(obj.get('id')).lower()])
|
|
287
|
+
check_idnames = lambda lst, idnames: False if type(idnames) is not list else check_list([str(x).strip().lower() for x in idnames], [str(x).strip().lower() for x in lst])
|
|
288
|
+
|
|
289
|
+
get_yaml_querid = lambda module, querid: data_load_from_yaml_file(yaml_rich(module))[PARAMS].get(querid)
|
|
290
|
+
get_rich_query = lambda params, que='querid', mod='module': '' if not (mod:=params.get(mod)) or not (que:=params.get(que)) else get_yaml_querid(mod,que)
|
|
291
|
+
get_rich_retquery = lambda params, que='requerid', mod='module': get_rich_query(params, que, mod)
|
|
292
|
+
|
|
293
|
+
def access_function(caller: str, model: BaseModel=None, dfunc: Callable=None, layer=5, access=True) -> tuple:
|
|
294
|
+
model = model or get_model(caller) if access else pick_model(caller)
|
|
295
|
+
dfunc = dfunc or get_dfunc(caller) if access else pick_dfunc(caller)
|
|
296
|
+
query = get_query(caller, get_qpath(caller) if access else pick_qpath(caller))
|
|
297
|
+
retquery = get_query(get_rquid(caller), get_rpath(caller)) if access else get_query(pick_rquid(caller), pick_rpath(caller))
|
|
298
|
+
dbfunc = lambda modelval, zoneid=None, query=query: dfunc(query=query, retquery=retquery, model=modelval, db_path=zonedb(zoneid), layer=layer)
|
|
299
|
+
valid = model and dfunc and query and True
|
|
300
|
+
return dbfunc, valid, query
|
|
301
|
+
|
|
302
|
+
def dbase_function(caller: str, model: BaseModel=None, dfunc: Callable=None, layer=5) -> tuple:
|
|
303
|
+
return access_function(caller=caller, model=model, dfunc=dfunc, layer=layer, access=False)
|
|
304
|
+
|
|
305
|
+
def access_function0(*a, **kw):
|
|
306
|
+
f, v, q = access_function(*a, **kw)
|
|
307
|
+
return f if v else None
|
|
308
|
+
|
|
309
|
+
def dbase_function0(*a, **kw):
|
|
310
|
+
f, v, q = dbase_function(*a, **kw)
|
|
311
|
+
return f if v else None
|
|
312
|
+
|
|
313
|
+
""" Usage
|
|
314
|
+
def select_system_moderator(model, zoneid=None):
|
|
315
|
+
f, v, q = dbase_function(callup(1), layer=4)
|
|
316
|
+
return f(model, zoneid) if v else None
|
|
317
|
+
select_system_moderator(DbUser(name='admin'))
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
def create_module(name: str):
|
|
321
|
+
new_module = types.ModuleType(name)
|
|
322
|
+
sys.modules[name] = new_module
|
|
323
|
+
return sys.modules[name]
|
|
324
|
+
|
|
325
|
+
def create_dbase_functions(moduledict: dict):
|
|
326
|
+
for item in moduledict:
|
|
327
|
+
exec(
|
|
328
|
+
f'''def {item} (model=[], zoneid=None, layer={pick_layer(item) or 4}):
|
|
329
|
+
f, v, q = dbase_function(callup(1), layer=layer)
|
|
330
|
+
return f(model, zoneid) if v else None''',
|
|
331
|
+
globals())
|
|
332
|
+
|
|
333
|
+
dbexist = lambda zoneid=0: db_exist(zonedb(zoneid))
|
|
334
|
+
errnodb = lambda: error_database(ERR_NODBASE)
|
|
335
|
+
errdb = lambda: error_database(ERR_DBASE)
|
|
336
|
+
|
|
337
|
+
genesis_zone = lambda: DbZone(**{'id':ADMINZONE, 'name':ADMINZONENAME, 'active':True})
|
|
338
|
+
genesis_user = lambda adminpass=DEFPASSWORD: DbUser(**{'zone':ADMINZONE, 'name':ADMINUSERNAME, 'hpwd':prepare_hash_password(adminpass), 'active':True, 'zadmin':True, 'creator':None})
|
|
339
|
+
adminzone = lambda zone: str(zone).strip().upper() in [str(ADMINZONE), str(ADMINZONENAME)]
|
|
340
|
+
|
|
341
|
+
base_origins = lambda base, ports: [base]+[base+':'+str(x) for x in ports] if type(base) is not list else base+[y+':'+str(x) for x in ports for y in base]
|
|
342
|
+
checkonly = lambda params={}: params.get('checkonly')
|
|
343
|
+
je = lambda data: data.get(ERROR)
|
|
344
|
+
|
|
345
|
+
load_data = lambda jsonfile: data_load_from_json_file(jsonfile)
|
|
346
|
+
get_roles = lambda module, data: data[PARAMS]['modules'][module]['roles']
|
|
347
|
+
get_specs = lambda module, data: data[PARAMS]['modules'][module]['specs']
|
|
348
|
+
|
|
349
|
+
get_groups = lambda module, data: data[PARAMS]['modules'][module]['accounts']
|
|
350
|
+
get_accounts = lambda groupcode, groups: groups[groupcode]['data']
|
|
351
|
+
|
|
352
|
+
group_roots = lambda groupcode, groups: [{'id': x['id'], 'grup': groups[groupcode]['id'], 'code': str(x['code']) if x['code'] else str(x['id']), 'name': x['name'], 'note': x['note'], 'active': x['active']} for x in get_accounts(groupcode, groups)]
|
|
353
|
+
root_accounts = lambda groupcode, groups: [{'id': x['id'], 'root': x['id'], 'code': str(x['code']) if x['code'] else str(x['id']), 'name': x['name'], 'note': x['note'], 'subs': [{'id': y['id'], 'root': x['id'], 'code': y['code'] if y['code'] else str(y['id']), 'name': y['name'], 'note': y['note'], 'active': y['active']} for y in x['subs']], 'active': x['active']} for x in get_accounts(groupcode, groups)]
|
|
354
|
+
code_accounts = lambda groupcode, groups: flatten([row['subs'] if row['subs'] else [{'id': row['id'], 'root': row['id'], 'code': str(row['id']), 'name': row['name'], 'note': row['note'], 'active': row['active']}] for row in root_accounts(groupcode, groups)])
|
|
355
|
+
|
|
356
|
+
groups_scalar_data = lambda groups: [{'id':groups[x]['id'], 'code':x, 'name':groups[x]['name']} for x in groups]
|
|
357
|
+
roots_scalar_data = lambda groups: flatten([group_roots(x, groups) for x in groups])
|
|
358
|
+
accounts_scalar_data = lambda groups: flatten([code_accounts(x, groups) for x in groups])
|
|
359
|
+
|
|
360
|
+
check_user_at_spec = lambda user, spec, zoneid: not resultset_is_empty(select_userspec(DbUserspec(user=user, spec=spec), zoneid))
|
|
361
|
+
check_user_at_role = lambda user, role, zoneid: not resultset_is_empty(select_userrole(DbUserrole(user=user, role=role), zoneid))
|
|
362
|
+
check_user_of_specs = lambda user, specs, zoneid: check_list(select_user_specs(DbUserspec(user=user), zoneid)[RESULT], specs)
|
|
363
|
+
check_user_of_roles = lambda user, roles, zoneid: check_list(select_user_roles(DbUserrole(user=user), zoneid)[RESULT], roles)
|
|
364
|
+
|
|
365
|
+
under_specs = lambda user, specs: user.active and check_user_of_specs(user.id, specs, user.zone)
|
|
366
|
+
under_roles = lambda user, roles: user.active and check_user_of_roles(user.id, roles, user.zone)
|
|
367
|
+
granted_specs = lambda user, spec: user.active and check_user_at_spec(user.id, spec, user.zone)
|
|
368
|
+
granted_roles = lambda user, role: user.active and check_user_at_role(user.id, role, user.zone)
|
|
369
|
+
|
|
370
|
+
is_under_specs = under_specs
|
|
371
|
+
is_under_roles = under_roles
|
|
372
|
+
is_granted_specs = granted_specs
|
|
373
|
+
is_granted_roles = granted_roles
|
|
374
|
+
|
|
375
|
+
MODULE = ACCOUNTING
|
|
376
|
+
JSON_ROLE = json_role(MODULE)
|
|
377
|
+
JSON_ACCOUNT = json_account(MODULE)
|
|
378
|
+
YAML_TRANSACTION = yaml_transaction(MODULE)
|
|
379
|
+
YAML_REPORT = yaml_report(MODULE)
|
|
380
|
+
YAML_RICH = yaml_rich(MODULE)
|
|
381
|
+
|
|
382
|
+
load_roles = lambda: load_data(JSON_ROLE)
|
|
383
|
+
load_accounts = lambda: load_data(JSON_ACCOUNT)
|
|
384
|
+
|
|
385
|
+
roles_lst = lambda json=None: get_roles(MODULE, json if json else load_roles())
|
|
386
|
+
specs_lst = lambda json=None: get_specs(MODULE, json if json else load_roles())
|
|
387
|
+
roles_specs_lst = lambda json=None: [roles_lst(json), specs_lst(json)]
|
|
388
|
+
|
|
389
|
+
specs_inc_roles = lambda data=None: [{'spec': s['id'], 'roles': s['roles']} for s in (data if data else roles_specs_lst())[1]]
|
|
390
|
+
specs_roles_cls = lambda data=None: [DbSpecrole(spec=x['spec'], role=y) for x in specs_inc_roles(data) for y in x['roles']]
|
|
391
|
+
|
|
392
|
+
roles_cls = lambda data=None: [DbRole(**r) for r in (data if data else roles_specs_lst())[0]]
|
|
393
|
+
specs_cls = lambda data=None: [DbSpec(**s) for s in (data if data else roles_specs_lst())[1]]
|
|
394
|
+
roles_specs_cls = lambda data=None: (roles_cls(data), specs_cls(data))
|
|
395
|
+
|
|
396
|
+
role_and_spec_models = roles_specs_cls
|
|
397
|
+
specrole_pair_models = specs_roles_cls
|
|
398
|
+
|
|
399
|
+
account_groups = lambda: get_groups(MODULE, load_accounts())
|
|
400
|
+
|
|
401
|
+
groups_data = lambda: groups_scalar_data(account_groups())
|
|
402
|
+
roots_data = lambda: roots_scalar_data(account_groups())
|
|
403
|
+
accounts_data = lambda: accounts_scalar_data(account_groups())
|
|
404
|
+
|
|
405
|
+
account_group_models = lambda: [DbGroup(**r) for r in groups_data()]
|
|
406
|
+
accounts_root_models = lambda: [DbRoot(**r) for r in roots_data()]
|
|
407
|
+
plain_account_models = lambda: [DbAccount(**r) for r in accounts_data()]
|
|
408
|
+
|
|
409
|
+
dicts_userspecs = lambda us: [{'user': us.user, 'spec': x} for x in us.specs]
|
|
410
|
+
dicts_userroles = lambda ur: [{'user': ur.user, 'role': x} for x in ur.roles]
|
|
411
|
+
|
|
412
|
+
execmany_uni_model = lambda model, BuildModel: prepare_model(model, BuildModel, DbUni)
|
|
413
|
+
execmany_userspec_model = lambda model: DbUni(user=model.user, array=dicts_userspecs(model))
|
|
414
|
+
execmany_userrole_model = lambda model: DbUni(user=model.user, array=dicts_userroles(model))
|
|
415
|
+
execmany_accountitem_model = lambda model: DbUni(array=dicts_items(model))
|
|
416
|
+
|
|
417
|
+
dicts_specs = lambda specs: prepare_model(dicts(specs), DbSpec, DbUni)
|
|
418
|
+
dicts_roles = lambda roles: prepare_model(dicts(roles), DbRole, DbUni)
|
|
419
|
+
dicts_specroles = lambda specroles: prepare_model(dicts(specroles), DbSpecrole, DbUni)
|
|
420
|
+
dicts_accounts = lambda accounts: prepare_model(dicts(accounts), DbAccount, DbUni)
|
|
421
|
+
dicts_groups = lambda groups: prepare_model(dicts(groups), DbGroup, DbUni)
|
|
422
|
+
dicts_roots = lambda roots: prepare_model(dicts(roots), DbRoot, DbUni)
|
|
423
|
+
|
|
424
|
+
## INIT BEGIN
|
|
425
|
+
|
|
426
|
+
try:
|
|
427
|
+
this = sys.modules[__name__]
|
|
428
|
+
add_models_from_yaml(this)
|
|
429
|
+
add_consts_from_yaml(this)
|
|
430
|
+
add_consts_from_yaml(this, 'dbase.yaml') ## module_dbase
|
|
431
|
+
add_consts_from_yaml(this, 'access.yaml') ## module_access
|
|
432
|
+
create_dbase_functions(module_dbase[ADMIN])
|
|
433
|
+
create_dbase_functions(module_dbase[ACCOUNTING])
|
|
434
|
+
app_consts = data_load_from_yaml_file()[PARAMS]
|
|
435
|
+
except:
|
|
436
|
+
print('Literp environment required, program exits')
|
|
437
|
+
sys.exit()
|
|
438
|
+
|
|
439
|
+
## INIT END
|
|
440
|
+
|
|
441
|
+
token_expire_minutes = app_consts.setdefault('token_expire_minutes', token_expire_minutes)
|
|
442
|
+
NOINIT = app_consts.get('NOINIT')
|
|
443
|
+
|
|
444
|
+
login_user = select_check_login_user
|
|
445
|
+
working_login_user = select_working_login_user
|
|
446
|
+
system_moderator = select_system_moderator
|
|
447
|
+
system_admin = select_system_admin
|
|
448
|
+
zone_admin = select_zone_admin
|
|
449
|
+
zone_admin_or_very_user = select_zone_admin_or_very_user
|
|
450
|
+
system_moderator_or_zone_admin = select_system_moderator_or_zone_admin
|
|
451
|
+
system_moderator_or_zone_admin_or_very_user = select_system_moderator_or_zone_admin_or_very_user
|
|
452
|
+
|
|
453
|
+
## BASIC IO
|
|
454
|
+
|
|
455
|
+
def dump_database_tables(zoneidn: str, access_token: str) -> dict:
|
|
456
|
+
ok, x, x = user_is_zone_admin(access_token, zoneidn)
|
|
457
|
+
if not ok: return error_unexpect(f'EXPORT TABLES: {ROLE_ZONE_ADMIN}')
|
|
458
|
+
return export_database_tables(zoneidn)
|
|
459
|
+
|
|
460
|
+
def dump_zone_database(zoneidn: str, table: str, access_token: str, idr: list=[]) -> dict:
|
|
461
|
+
ok, x, x = user_is_zone_admin(access_token, zoneidn)
|
|
462
|
+
if not ok: return error_unexpect(f'EXPORT DATABASE: {ROLE_ZONE_ADMIN}')
|
|
463
|
+
return export_zone_database(zoneidn, table, idr=idr)
|
|
464
|
+
|
|
465
|
+
def load_zone_database(csvdata: str, zoneidn: str, table: str, access_token: str) -> dict:
|
|
466
|
+
ok, x, x = user_is_zone_admin(access_token, zoneidn)
|
|
467
|
+
if not ok: return error_unexpect(f'IMPORT DATABASE: {ROLE_ZONE_ADMIN}')
|
|
468
|
+
return import_zone_database(zoneidn, table, {'data': csvdata})
|
|
469
|
+
|
|
470
|
+
def init_admin_database(adminpass: str=DEFPASSWORD) -> dict:
|
|
471
|
+
if NOINIT: return error_unexpect('NOINIT flag is set', 'Install')
|
|
472
|
+
task = batch([create_admin_zones, create_admin_users])
|
|
473
|
+
if je(task): return task
|
|
474
|
+
owner = insert_zone(genesis_zone())
|
|
475
|
+
if je(owner): return owner
|
|
476
|
+
admin = insert_user(genesis_user(adminpass))
|
|
477
|
+
if je(admin): return admin
|
|
478
|
+
return dict_result([{'Zone':owner, 'User':admin}])
|
|
479
|
+
|
|
480
|
+
def init_zone_database(zoneid: int, access_token: str) -> dict:
|
|
481
|
+
ok, x, x = user_is_system_admin(access_token)
|
|
482
|
+
if not ok: return error_unexpect(f'INSTALL CLIENT DATABASE: {ROLE_ADMIN}')
|
|
483
|
+
if get_zoneid(zoneid) in [ADMINZONE, None]: return error_database(ERR_INPUT)
|
|
484
|
+
if client_port_not_allowed(zoneid): return error_database(ERR_BADZONEID)
|
|
485
|
+
task = batch([
|
|
486
|
+
create_zone_roles,
|
|
487
|
+
create_zone_specs,
|
|
488
|
+
create_zone_specroles,
|
|
489
|
+
create_zone_userspecs,
|
|
490
|
+
create_zone_userroles,
|
|
491
|
+
create_zone_groups,
|
|
492
|
+
create_zone_roots,
|
|
493
|
+
create_zone_accounts,
|
|
494
|
+
create_zone_subs,
|
|
495
|
+
create_zone_items,
|
|
496
|
+
create_zone_txs,
|
|
497
|
+
], zonedb(zoneid))
|
|
498
|
+
if je(task): return task
|
|
499
|
+
return dict_result([{}])
|
|
500
|
+
|
|
501
|
+
def init_accounting_database(zoneid: int, access_token: str) -> dict:
|
|
502
|
+
ok, x, x = user_is_system_admin(access_token)
|
|
503
|
+
if not ok: return error_unexpect(f'INSTALL MODULE DATABASE: {ROLE_ADMIN}')
|
|
504
|
+
if get_zoneid(zoneid) in [ADMINZONE, None]: return error_database(ERR_INPUT)
|
|
505
|
+
if not dbexist(zoneid): return errnodb()
|
|
506
|
+
roles, specs = role_and_spec_models()
|
|
507
|
+
specroles = specrole_pair_models()
|
|
508
|
+
groups = account_group_models()
|
|
509
|
+
roots = accounts_root_models()
|
|
510
|
+
accounts = plain_account_models()
|
|
511
|
+
task = insert_zone_roles(dicts_roles(roles), zoneid)
|
|
512
|
+
if je(task): return task
|
|
513
|
+
task = insert_zone_specs(dicts_specs(specs), zoneid)
|
|
514
|
+
if je(task): return task
|
|
515
|
+
task = insert_zone_specroles(dicts_specroles(specroles), zoneid)
|
|
516
|
+
if je(task): return task
|
|
517
|
+
task = insert_zone_groups(dicts_groups(groups), zoneid)
|
|
518
|
+
if je(task): return task
|
|
519
|
+
task = insert_zone_roots(dicts_roots(roots), zoneid)
|
|
520
|
+
if je(task): return task
|
|
521
|
+
task = insert_zone_accounts(dicts_accounts(accounts), zoneid)
|
|
522
|
+
if je(task): return task
|
|
523
|
+
return dict_result([{}])
|
|
524
|
+
|
|
525
|
+
## CREDENTIALS
|
|
526
|
+
|
|
527
|
+
def user_under_specs(access_token: str, specs: list) -> bool:
|
|
528
|
+
user = get_current_user(access_token)
|
|
529
|
+
return under_specs(user, specs)
|
|
530
|
+
|
|
531
|
+
def user_under_roles(access_token: str, roles: list) -> bool:
|
|
532
|
+
user = get_current_user(access_token)
|
|
533
|
+
return under_roles(user, roles)
|
|
534
|
+
|
|
535
|
+
def user_granted_spec(access_token: str, spec: int) -> bool:
|
|
536
|
+
user = get_current_user(access_token)
|
|
537
|
+
return granted_specs(user, spec)
|
|
538
|
+
|
|
539
|
+
def user_granted_role(access_token: str, role: int) -> bool:
|
|
540
|
+
user = get_current_user(access_token)
|
|
541
|
+
return granted_roles(user, role)
|
|
542
|
+
|
|
543
|
+
def user_is_under_specs(access_token: str, specs: list) -> (bool, int, int):
|
|
544
|
+
user = get_current_user(access_token)
|
|
545
|
+
return under_specs(user, specs), user.id, user.zone
|
|
546
|
+
|
|
547
|
+
def user_is_under_roles(access_token: str, roles: list) -> (bool, int, int):
|
|
548
|
+
user = get_current_user(access_token)
|
|
549
|
+
return under_roles(user, roles), user.id, user.zone
|
|
550
|
+
|
|
551
|
+
def user_is_working(access_token: str, addict=None) -> (bool, int, int):
|
|
552
|
+
user = get_current_user(access_token)
|
|
553
|
+
return user.found, user.id, user.zone
|
|
554
|
+
|
|
555
|
+
def user_is_system_admin(access_token: str, addict=None) -> (bool, int, int):
|
|
556
|
+
user = get_current_user(access_token, system_admin)
|
|
557
|
+
return user.found, user.id, user.zone
|
|
558
|
+
|
|
559
|
+
def user_is_system_moderator(access_token: str, addict=None) -> (bool, int, int):
|
|
560
|
+
user = get_current_user(access_token, system_moderator)
|
|
561
|
+
return user.found, user.id, user.zone
|
|
562
|
+
|
|
563
|
+
def user_is_zone_admin(access_token: str, zoneidn: str) -> (bool, int, int):
|
|
564
|
+
user = get_current_user(access_token, zone_admin, {COL_ZONEIDN: zoneidn, COL_CONUSER: None})
|
|
565
|
+
return user.found, user.id, user.zoneidn
|
|
566
|
+
|
|
567
|
+
def user_is_ones_admin(access_token: str, conuser: str) -> (bool, int, int):
|
|
568
|
+
user = get_current_user(access_token, zone_admin, {COL_CONUSER: conuser, COL_ZONEIDN: None})
|
|
569
|
+
return user.found, user.id, user.zoneidn
|
|
570
|
+
|
|
571
|
+
user_is_zone_admin_of = user_is_ones_admin
|
|
572
|
+
|
|
573
|
+
def user_is_zone_manager(access_token: str, zoneidn: str) -> (bool, int, int):
|
|
574
|
+
user = get_current_user(access_token, system_moderator_or_zone_admin, {COL_ZONEIDN: zoneidn, COL_CONUSER: None})
|
|
575
|
+
return user.found, user.id, user.zoneidn
|
|
576
|
+
|
|
577
|
+
def user_is_ones_manager(access_token: str, conuser: str) -> (bool, int, int):
|
|
578
|
+
user = get_current_user(access_token, system_moderator_or_zone_admin, {COL_CONUSER: conuser, COL_ZONEIDN: None})
|
|
579
|
+
return user.found, user.id, user.zoneidn
|
|
580
|
+
|
|
581
|
+
user_is_zone_manager_of = user_is_ones_manager
|
|
582
|
+
|
|
583
|
+
def user_can_controll(access_token: str, useridn: str) -> (bool, int, int):
|
|
584
|
+
user = get_current_user(access_token, zone_admin_or_very_user, {COL_USERIDN: useridn, COL_ZONEIDN: None})
|
|
585
|
+
return user.found, user.id, user.zoneidn
|
|
586
|
+
|
|
587
|
+
def user_can_access(access_token: str, useridn: str) -> (bool, int, int):
|
|
588
|
+
user = get_current_user(access_token, system_moderator_or_zone_admin_or_very_user, {COL_USERIDN: useridn, COL_ZONEIDN: None})
|
|
589
|
+
return user.found, user.id, user.zoneidn
|
|
590
|
+
|
|
591
|
+
## Derivatives
|
|
592
|
+
def user_is_zone_creator(access_token: str, zoneidn: str) -> (bool, int, int):
|
|
593
|
+
return user_is_system_admin(access_token) if adminzone(zoneidn) else user_is_system_moderator(access_token)
|
|
594
|
+
|
|
595
|
+
def self_is_admin(access_token: str, addict=None) -> (bool, int, int):
|
|
596
|
+
user = get_current_user(access_token)
|
|
597
|
+
return user.zadmin, user.id, user.zoneidn
|
|
598
|
+
|
|
599
|
+
def self_is_manager(access_token: str, addict=None) -> (bool, int, int):
|
|
600
|
+
user = get_current_user(access_token)
|
|
601
|
+
return (user.zadmin or user.zone==ADMINZONE), user.id, user.zoneidn
|
|
602
|
+
|
|
603
|
+
def self_is_creator(access_token: str, addict=None) -> (bool, int, int):
|
|
604
|
+
user = get_current_user(access_token)
|
|
605
|
+
return (user.zone==ADMINZONE), user.id, user.zoneidn
|
|
606
|
+
|
|
607
|
+
## ACTIONS
|
|
608
|
+
|
|
609
|
+
def check_login(hpwd: str, idname: str) -> (bool, int, int):
|
|
610
|
+
data = first_row(login_user(DbUser(id=integer(idname), name=idname, hpwd=hpwd)))
|
|
611
|
+
return (True, data[COL_ID], data[COL_ZONE]) if data else (False, None, None)
|
|
612
|
+
|
|
613
|
+
def get_user(model: DbUser, task: Callable=working_login_user, addict: dict={}) -> dict:
|
|
614
|
+
if addict:
|
|
615
|
+
model = model.model_dump()
|
|
616
|
+
model.update(addict)
|
|
617
|
+
model = DbUser(**model)
|
|
618
|
+
return first_row(task(model))
|
|
619
|
+
|
|
620
|
+
def get_current_user(access_token: str, task: Callable=working_login_user, addict: dict={}) -> User:
|
|
621
|
+
credentials_exception = User(name=ERR_VALIDATES)
|
|
622
|
+
other_fatal_exception = User(name=ERR_AUTHLOGIN)
|
|
623
|
+
try:
|
|
624
|
+
payload = jwt.decode(access_token, os.environ[ENV_SECRET], algorithms=[token_crypto_algorithm])
|
|
625
|
+
username = payload.get(TOKENID)
|
|
626
|
+
if not username:
|
|
627
|
+
return credentials_exception
|
|
628
|
+
user = get_user(DbUser(name=username), task, addict)
|
|
629
|
+
if not user:
|
|
630
|
+
return credentials_exception
|
|
631
|
+
user = User(**user)
|
|
632
|
+
user.found = True
|
|
633
|
+
return user
|
|
634
|
+
except JWTError:
|
|
635
|
+
return credentials_exception
|
|
636
|
+
except Exception:
|
|
637
|
+
return other_fatal_exception
|
|
638
|
+
|
|
639
|
+
def authenticate_user(username: str, md5_password: str) -> dict:
|
|
640
|
+
user = get_user(DbUser(name=username))
|
|
641
|
+
if not user:
|
|
642
|
+
return {ERROR: ERR_USERNAME}
|
|
643
|
+
if not verify_password(md5_password, user[COL_PASS]):
|
|
644
|
+
return {ERROR: ERR_USERPASS}
|
|
645
|
+
return user
|
|
646
|
+
|
|
647
|
+
def verify_password(md5_password: str, sha256_password: str) -> bool:
|
|
648
|
+
return sha(md5_password)==sha256_password
|
|
649
|
+
|
|
650
|
+
def create_access_token(data: dict, expires_delta: Optional[timedelta]=None, algorithm: str=token_crypto_algorithm, expire_minutes: int=token_expire_minutes) -> str:
|
|
651
|
+
to_encode = data.copy()
|
|
652
|
+
if expires_delta:
|
|
653
|
+
expire = datetime.utcnow() + expires_delta
|
|
654
|
+
else:
|
|
655
|
+
expire = datetime.utcnow() + timedelta(minutes=expire_minutes)
|
|
656
|
+
to_encode['exp'] = expire
|
|
657
|
+
return jwt.encode(to_encode, os.environ[ENV_SECRET], algorithm=algorithm)
|
|
658
|
+
|
|
659
|
+
def login(username: str, md5_password: str) -> Token.model_dump:
|
|
660
|
+
user = authenticate_user(username, md5_password)
|
|
661
|
+
if user.get(ERROR):
|
|
662
|
+
return {TOKEN: ERROR, TOKENTYPE: user[ERROR]}
|
|
663
|
+
access_token_expires = timedelta(minutes=token_expire_minutes)
|
|
664
|
+
access_token = create_access_token(
|
|
665
|
+
data={TOKENID: user[COL_NAME]},
|
|
666
|
+
expires_delta=access_token_expires,
|
|
667
|
+
algorithm=token_crypto_algorithm,
|
|
668
|
+
expire_minutes=token_expire_minutes)
|
|
669
|
+
return Token(user=user[COL_NAME], id=user[COL_ID], zone=user[COL_ZONE], zadmin=user[COL_ZADM], access_token=access_token, token_type=BEARER).model_dump()
|
|
670
|
+
|
|
671
|
+
## MISC
|
|
672
|
+
|
|
673
|
+
def http_allowed_origins(url: str=cors_base_url) -> list:
|
|
674
|
+
zones = existing_zones_list()
|
|
675
|
+
addrs = base_origins(url, zones)
|
|
676
|
+
olist = merge_list(addrs, cors_allowed_origins())
|
|
677
|
+
lanbase = app_consts.get('cors_lan_url')
|
|
678
|
+
wanbase = app_consts.get('cors_wan_url')
|
|
679
|
+
lanbases = [] if not lanbase else base_origins(lanbase, zones)
|
|
680
|
+
wanbases = [] if not wanbase else base_origins(wanbase, zones)
|
|
681
|
+
return merge_list(olist, lanbases+wanbases)
|
|
682
|
+
|
|
683
|
+
def existing_zones_list() -> list:
|
|
684
|
+
defzones = [port_for_admin, port_for_api]
|
|
685
|
+
extzones = select_all_zone_ids()
|
|
686
|
+
if resultset_is_empty(extzones): return defzones
|
|
687
|
+
return merge_list(defzones, extzones[RESULT])
|
|
688
|
+
|
|
689
|
+
def get_params(jsondata: str) -> dict:
|
|
690
|
+
if je(data:=param_load_from_request(jsondata)): raise ValueError(ERR_JSONDATA)
|
|
691
|
+
return data[PARAMS]
|
|
692
|
+
|
|
693
|
+
## API BASIS
|
|
694
|
+
|
|
695
|
+
def hello() -> dict:
|
|
696
|
+
return {MESSAGE: 'cool' if dbexist() else 'poor'}
|
|
697
|
+
|
|
698
|
+
def hello_zone(zoneid: int) -> dict:
|
|
699
|
+
return {MESSAGE: 'great' if dbexist(zoneid) else 'sucks'}
|
|
700
|
+
|
|
701
|
+
def what_module(modules: dict, funcname: str) -> dict:
|
|
702
|
+
return {MESSAGE: function_module(modules, funcname)}
|
|
703
|
+
|
|
704
|
+
def do_login(username: str, md5_password: str, response: Response) -> Token:
|
|
705
|
+
res = login(username, md5_password)
|
|
706
|
+
response.set_cookie(key=TOKEN, value=res[TOKEN])
|
|
707
|
+
return res
|
|
708
|
+
|
|
709
|
+
def do_logout(response: Response) -> dict:
|
|
710
|
+
response.set_cookie(key=TOKEN, value='', expires=0)
|
|
711
|
+
return {MESSAGE: 'You are logged out'}
|
|
712
|
+
|
|
713
|
+
## API IO
|
|
714
|
+
|
|
715
|
+
def set_afunc(funcname: str, value: any, field: str='edata') -> bool:
|
|
716
|
+
module = function_module2(module_access, funcname)
|
|
717
|
+
if not module: return False
|
|
718
|
+
dict_assign(module_access, f'{module}.{funcname}.{field}', value=value, add=True)
|
|
719
|
+
return True
|
|
720
|
+
|
|
721
|
+
def update_afunc(field: str='edata') -> dict:
|
|
722
|
+
data = load_yaml(f'{field}.yaml')[field]
|
|
723
|
+
for f in data: set_afunc(f, data[f][field], field)
|
|
724
|
+
return module_access
|
|
725
|
+
|
|
726
|
+
def sort_module_access():
|
|
727
|
+
modules = app_consts.get('modules')
|
|
728
|
+
for module in modules: dict_sort(module_access, module)
|
|
729
|
+
|
|
730
|
+
## DB BASIS
|
|
731
|
+
|
|
732
|
+
def wrapvals(model: DbTxs) -> list:
|
|
733
|
+
data = model.data ## [model] list
|
|
734
|
+
if (count:=len(data))<2: return False
|
|
735
|
+
vals = []
|
|
736
|
+
for i in range (count):
|
|
737
|
+
data[i].uuid = model.uuid if i==0 else uid()
|
|
738
|
+
data[i].tx = model.uuid
|
|
739
|
+
data[i].dated = model.dated
|
|
740
|
+
data[i].creator = model.creator
|
|
741
|
+
vals.append(data[i])
|
|
742
|
+
valdicts = dicts(vals) ## [{}] list
|
|
743
|
+
if lds(valdicts, COL_DEBIT) != lds(valdicts, COL_CREDIT): return None
|
|
744
|
+
return vals ## [model] list
|
|
745
|
+
|
|
746
|
+
def wraptx(model: DbTxs) -> dict:
|
|
747
|
+
vals = wrapvals(model)
|
|
748
|
+
if vals is False: raise(Exception(NOENTRIES))
|
|
749
|
+
if vals is None: raise(Exception(NOBALANCE))
|
|
750
|
+
model = DbUni(array=dicts(vals))
|
|
751
|
+
return model
|
|
752
|
+
|
|
753
|
+
def bind_values(query: str, modelval: DbUni(), vfunc: Callable, vattr: str, sfunc: Callable=str_to_list, outdict: bool=False) -> tuple:
|
|
754
|
+
values = sfunc(getattr(modelval, vattr))
|
|
755
|
+
query = vfunc(query, values)
|
|
756
|
+
return {'query': query, 'values': values} if outdict else (query, values)
|
|
757
|
+
|
|
758
|
+
## HTTP
|
|
759
|
+
|
|
760
|
+
def reload_edata(edata: dict=EDATA):
|
|
761
|
+
for e in edata:
|
|
762
|
+
globals()[e] = EDATA[e]
|
|
763
|
+
setattr(this, e, EDATA[e])
|
|
764
|
+
|
|
765
|
+
def prepare_inputs(params: dict, passfunc: Callable=prepare_password) -> dict:
|
|
766
|
+
passfunc(params, COL_PASS)
|
|
767
|
+
passfunc(params, COL_OPWD)
|
|
768
|
+
passfunc(params, COL_NPWD)
|
|
769
|
+
return params
|
|
770
|
+
|
|
771
|
+
def prepare_params(params: dict, userid: int, quelibid: str=None, dbfuncid: str=None, modelid: str=None, moduleid: str=None, InfoModel: DbUni=None, infofunc: Callable=prepare_info) -> dict:
|
|
772
|
+
params[COL_CREATOR] = userid
|
|
773
|
+
params[COL_MODIFIER] = userid
|
|
774
|
+
if modelid: params[JMODEL] = modelid
|
|
775
|
+
if moduleid: params[JDBMOD] = moduleid
|
|
776
|
+
if quelibid: params[JDBQUE] = quelibid
|
|
777
|
+
if dbfuncid: params[JDFUNC] = dbfuncid
|
|
778
|
+
params['query'] = get_rich_query(params)
|
|
779
|
+
params['retquery'] = get_rich_retquery(params)
|
|
780
|
+
params[COL_DATED] = parse_doc_date(params.get(COL_DATED))
|
|
781
|
+
if infofunc and InfoModel: infofunc(params, COL_INFO, InfoModel)
|
|
782
|
+
return params
|
|
783
|
+
|
|
784
|
+
def prepare_param_array(params: dict, userid: int) -> dict:
|
|
785
|
+
if params.get(ARRAY):
|
|
786
|
+
params[ARRAY] = [prepare_params(x, userid) for x in params[ARRAY]]
|
|
787
|
+
return params
|
|
788
|
+
|
|
789
|
+
def prepare_jsondata(jsondata: str, back: bool=False, layer: int=3, jafarg: any=None):
|
|
790
|
+
params = get_params(jsondata)
|
|
791
|
+
default(params, JAFARG, jafarg)
|
|
792
|
+
default(params, JMODEL, 'DbUni')
|
|
793
|
+
default(params, JDBQUE, 'select')
|
|
794
|
+
default(params, JDBMOD, 'accounting')
|
|
795
|
+
default(params, JDFUNC, 'select_dict')
|
|
796
|
+
default(params, JQUEID, callup(layer))
|
|
797
|
+
default(params, COL_UUID, uid())
|
|
798
|
+
if not back: return params
|
|
799
|
+
return json.dumps(params)
|
|
800
|
+
|
|
801
|
+
def appdb(
|
|
802
|
+
access_token,
|
|
803
|
+
jsondata: str,
|
|
804
|
+
modelid: str='DbUni',
|
|
805
|
+
InfoModel: any=None,
|
|
806
|
+
DelModel: any=None,
|
|
807
|
+
pwdfunc: Callable=None,
|
|
808
|
+
prefunc: Callable=None,
|
|
809
|
+
delfunc: Callable=None,
|
|
810
|
+
parfunc: Callable=get_idname,
|
|
811
|
+
errfunc: Callable=error_unexpect,
|
|
812
|
+
caller: str=None) -> dict: ## QueryIdName
|
|
813
|
+
if not caller: caller = callup() ## FName
|
|
814
|
+
model = get_model(caller, defval=modelid)
|
|
815
|
+
afunc = get_afunc(caller, finval=do_none)
|
|
816
|
+
erole = get_erole(caller)
|
|
817
|
+
edata = get_edata(caller)
|
|
818
|
+
qpath = get_qpath(caller)
|
|
819
|
+
dfunc = get_dfunc(caller)
|
|
820
|
+
build = get_build(caller)
|
|
821
|
+
mfunc = get_mfunc(caller)
|
|
822
|
+
dbfunc, valid, query = access_function(caller, model, dfunc)
|
|
823
|
+
if not valid: return errfunc(f'{ERR_FUNCTION}: {caller}')
|
|
824
|
+
pwdfunc = get_wfunc(caller, defval=pwdfunc)
|
|
825
|
+
prefunc = get_ifunc(caller, defval=prefunc)
|
|
826
|
+
parfunc = get_pfunc(caller, defval=parfunc)
|
|
827
|
+
delfunc = get_bfunc(caller, defval=delfunc)
|
|
828
|
+
DelModel = get_mdele(caller, defval=DelModel)
|
|
829
|
+
InfoModel = get_minfo(caller, defval=InfoModel)
|
|
830
|
+
access_token = get_token(caller, defval=access_token)
|
|
831
|
+
prepare_inputs(params:=prepare_jsondata(jsondata), passfunc=pwdfunc or prepare_password)
|
|
832
|
+
ok, userid, zoneid = afunc(access_token if not callable(access_token) else access_token(params), edata or (parfunc(params) if parfunc else None))
|
|
833
|
+
if not ok: return errfunc(f'{this.MSG.get(caller) or to_upper(caller)}: {erole}{SPACE+str(edata) if edata else ""}')
|
|
834
|
+
if checkonly(params): return dict_result([{}])
|
|
835
|
+
prepare_params(params, userid, InfoModel=InfoModel, infofunc=prefunc)
|
|
836
|
+
prepare_param_array(params, userid) ## prepare for ExecuteMany data action
|
|
837
|
+
if (bad:=get_cfunc(caller)) and bad(params.get(get_cattr(caller))): return errfunc(get_cemsg(caller))
|
|
838
|
+
if delfunc and DelModel: delfunc(DelModel(name=get_idname(params)), zoneid)
|
|
839
|
+
if mfunc: return dbfunc(mfunc(params.get(ARRAY), build, DbUni), zoneid)
|
|
840
|
+
values = model(**params)
|
|
841
|
+
if up:=get_ufunc(caller): up(values)
|
|
842
|
+
if pm:=get_mprep(caller): values = pm(values)
|
|
843
|
+
query, values = bind_values(query, values, vfunc, get_vattr(caller)) if (vfunc:=get_vfunc(caller)) else (query, values)
|
|
844
|
+
return dbfunc(values, zoneid, query=query)
|
|
845
|
+
|
|
846
|
+
reload_edata()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: backio
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Literp Fundamental IO Lib
|
|
5
|
+
Home-page: https://github.com/asinerum/backio
|
|
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: embedize>=1.0.4
|
|
16
|
+
Requires-Dist: formatize>=1.0.4
|
|
17
|
+
Requires-Dist: fastapi>=0.122.1
|
|
18
|
+
Requires-Dist: python-jose>=3.5.0
|
|
19
|
+
Requires-Dist: pydantic>=2.12.5
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
Detailed tips, tricks, and examples, can be found at project's repository
|
|
23
|
+
https://github.com/asinerum/backio
|
|
24
|
+
|
|
25
|
+
(C) 2026 Asinerum Conlang Project
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.cfg
|
|
5
|
+
src/backio/__init__.py
|
|
6
|
+
src/backio/io.py
|
|
7
|
+
src/backio.egg-info/PKG-INFO
|
|
8
|
+
src/backio.egg-info/SOURCES.txt
|
|
9
|
+
src/backio.egg-info/dependency_links.txt
|
|
10
|
+
src/backio.egg-info/requires.txt
|
|
11
|
+
src/backio.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
backio
|