tina4-python 0.1.52__tar.gz → 0.2.64__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.
- {tina4_python-0.1.52 → tina4_python-0.2.64}/PKG-INFO +182 -26
- {tina4_python-0.1.52 → tina4_python-0.2.64}/README.md +176 -23
- {tina4_python-0.1.52 → tina4_python-0.2.64}/pyproject.toml +8 -2
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Auth.py +44 -6
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Constant.py +5 -1
- tina4_python-0.2.64/tina4_python/Database.py +573 -0
- tina4_python-0.2.64/tina4_python/DatabaseResult.py +101 -0
- tina4_python-0.2.64/tina4_python/Debug.py +91 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Env.py +10 -5
- tina4_python-0.2.64/tina4_python/MiddleWare.py +75 -0
- tina4_python-0.2.64/tina4_python/Migration.py +99 -0
- tina4_python-0.2.64/tina4_python/ORM.py +519 -0
- tina4_python-0.2.64/tina4_python/Queue.py +495 -0
- tina4_python-0.2.64/tina4_python/Request.py +18 -0
- tina4_python-0.2.64/tina4_python/Response.py +98 -0
- tina4_python-0.2.64/tina4_python/Router.py +385 -0
- tina4_python-0.2.64/tina4_python/Session.py +252 -0
- tina4_python-0.2.64/tina4_python/ShellColors.py +20 -0
- tina4_python-0.2.64/tina4_python/Swagger.py +228 -0
- tina4_python-0.2.64/tina4_python/Template.py +91 -0
- tina4_python-0.2.64/tina4_python/Webserver.py +385 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/__init__.py +128 -37
- tina4_python-0.2.64/tina4_python/public/favicon.ico +0 -0
- tina4_python-0.2.64/tina4_python/public/images/403.png +0 -0
- tina4_python-0.2.64/tina4_python/public/images/404.png +0 -0
- tina4_python-0.2.64/tina4_python/public/images/500.png +0 -0
- tina4_python-0.2.64/tina4_python/public/images/logo.png +0 -0
- tina4_python-0.2.64/tina4_python/public/js/tina4helper.js +397 -0
- tina4_python-0.2.64/tina4_python/public/swagger/index.html +90 -0
- tina4_python-0.2.64/tina4_python/public/swagger/oauth2-redirect.html +63 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/templates/errors/403.twig +1 -2
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/templates/errors/404.twig +1 -2
- tina4_python-0.2.64/tina4_python/templates/errors/500.twig +11 -0
- tina4_python-0.1.52/tina4_python/Debug.py +0 -33
- tina4_python-0.1.52/tina4_python/Request.py +0 -21
- tina4_python-0.1.52/tina4_python/Response.py +0 -40
- tina4_python-0.1.52/tina4_python/Router.py +0 -208
- tina4_python-0.1.52/tina4_python/Session.py +0 -115
- tina4_python-0.1.52/tina4_python/Template.py +0 -47
- tina4_python-0.1.52/tina4_python/Webserver.py +0 -225
- tina4_python-0.1.52/tina4_python/public/favicon.ico +0 -0
- tina4_python-0.1.52/tina4_python/public/images/403.png +0 -0
- tina4_python-0.1.52/tina4_python/public/images/404.png +0 -0
- tina4_python-0.1.52/tina4_python/public/images/logo.png +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Localization.py +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Messages.py +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/messages.pot +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/public/css/readme.md +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/public/images/readme.md +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/public/js/readme.md +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/templates/readme.md +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: tina4-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.64
|
|
4
4
|
Summary: Tina4Python - This is not another framework for Python
|
|
5
5
|
Author: Andre van Zuydam
|
|
6
6
|
Author-email: andrevanzuydam@gmail.com
|
|
@@ -9,9 +9,12 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
-
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Dist: Jinja2 (>=3.1.4,<4.0.0)
|
|
14
|
+
Requires-Dist: bcrypt (>=4.1.3,<5.0.0)
|
|
13
15
|
Requires-Dist: cryptography (>=42.0.5,<43.0.0)
|
|
14
16
|
Requires-Dist: libsass (>=0.22.0,<0.23.0)
|
|
17
|
+
Requires-Dist: litequeue (>=0.9,<0.10)
|
|
15
18
|
Requires-Dist: mypy (>=0.991,<0.992)
|
|
16
19
|
Requires-Dist: pyjwt (>=2.8.0,<3.0.0)
|
|
17
20
|
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
|
@@ -56,6 +59,22 @@ If you are developing on Tina4, make sure you copy the public folder from tina4_
|
|
|
56
59
|
|
|
57
60
|
### Installation
|
|
58
61
|
|
|
62
|
+
Virtual environment
|
|
63
|
+
|
|
64
|
+
Linux / Mac
|
|
65
|
+
```bash
|
|
66
|
+
python3 -m venv venv
|
|
67
|
+
source ./venv/bin/activate
|
|
68
|
+
pip install poetry
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Windows
|
|
72
|
+
```bash
|
|
73
|
+
python -m venv venv
|
|
74
|
+
.\venv\Scripts\activate
|
|
75
|
+
pip install poetry
|
|
76
|
+
```
|
|
77
|
+
|
|
59
78
|
#### Windows
|
|
60
79
|
|
|
61
80
|
1.) Install Poetry:
|
|
@@ -158,10 +177,10 @@ from tina4_python.Router import get
|
|
|
158
177
|
from tina4_python.Response import Response
|
|
159
178
|
|
|
160
179
|
@get("/hello/{name}")
|
|
161
|
-
async def greet(**params):
|
|
180
|
+
async def greet(**params): #(request, response)
|
|
162
181
|
name = params['request'].params['name']
|
|
163
|
-
return params['response'](f"Hello, {name}!")
|
|
164
|
-
|
|
182
|
+
return params['response'](f"Hello, {name}!") # return response()
|
|
183
|
+
````
|
|
165
184
|
|
|
166
185
|
This code creates a route for a GET request to `/hello/{name}`, where `name` is a parameter in the URL. The function `greet` accepts this parameter and responds with a personalized greeting.
|
|
167
186
|
|
|
@@ -201,6 +220,16 @@ async def capture_get(request, response):
|
|
|
201
220
|
@post("/capture")
|
|
202
221
|
async def capture_post(request, response):
|
|
203
222
|
return response(request.body)
|
|
223
|
+
|
|
224
|
+
@get("/simple")
|
|
225
|
+
async def simple_get(request, response):
|
|
226
|
+
print("<h1>Hello World!</h1>")
|
|
227
|
+
|
|
228
|
+
@get("/redirect")
|
|
229
|
+
async def simple_get(request, response):
|
|
230
|
+
|
|
231
|
+
return response.redirect("/test")
|
|
232
|
+
|
|
204
233
|
```
|
|
205
234
|
|
|
206
235
|
In your ```src/__init__.py``` add the following code
|
|
@@ -224,33 +253,150 @@ For ease of use you can supply an `API_KEY` param to your .env with a secret of
|
|
|
224
253
|
API_KEY=somehash
|
|
225
254
|
```
|
|
226
255
|
|
|
256
|
+
### Suppressing the default webservice
|
|
257
|
+
|
|
258
|
+
You may want to use your Tina4 project as a library in another project so suppressing the default webservice is probably needed.
|
|
259
|
+
|
|
260
|
+
```.env
|
|
261
|
+
TINA4_DEFAULT_WEBSERVER="False"
|
|
262
|
+
```
|
|
263
|
+
or in your `__init__.py` file
|
|
264
|
+
```python
|
|
265
|
+
import os
|
|
266
|
+
os.environ["TINA4_DEFAULT_WEBSERVER"] = "False"
|
|
267
|
+
```
|
|
268
|
+
Additionally, it will require you to rename `src` to the name of your package
|
|
227
269
|
|
|
228
270
|
### Features
|
|
229
|
-
| Completed | To Do
|
|
230
|
-
|
|
231
|
-
| Python pip package |
|
|
232
|
-
| Basic environment handling |
|
|
233
|
-
|
|
|
234
|
-
| Enhanced
|
|
235
|
-
| CSS Support |
|
|
236
|
-
| Image Support |
|
|
237
|
-
| Localization |
|
|
238
|
-
| Error Pages |
|
|
239
|
-
| Template handling |
|
|
240
|
-
| Form posting |
|
|
241
|
-
|
|
|
271
|
+
| Completed | To Do |
|
|
272
|
+
|----------------------------|-------|
|
|
273
|
+
| Python pip package | |
|
|
274
|
+
| Basic environment handling | |
|
|
275
|
+
| Routing with Swagger | |
|
|
276
|
+
| Enhanced Routing | |
|
|
277
|
+
| CSS Support | |
|
|
278
|
+
| Image Support | |
|
|
279
|
+
| Localization | |
|
|
280
|
+
| Error Pages | |
|
|
281
|
+
| Template handling | |
|
|
282
|
+
| Form posting | |
|
|
283
|
+
| Migrations | |
|
|
284
|
+
| Colored Debugging | |
|
|
285
|
+
| Database Abstraction | |
|
|
286
|
+
|
|
287
|
+
### Database
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
|
|
291
|
+
Various databases initialised:
|
|
292
|
+
|
|
293
|
+
dba = Database("sqlite3:test.db", "username", "password")
|
|
294
|
+
dba = Database("mysql:localhost/3306:myschema", "username", "password")
|
|
295
|
+
dba = Database("postgres:localhost/5432:myschema", "username", "password")
|
|
296
|
+
dba = Database("firebird:localhost/3050:/home/database/FIREBIRD.FDB", "username", "password")
|
|
297
|
+
|
|
298
|
+
NoSQL support (Still to be developed):
|
|
299
|
+
|
|
300
|
+
dba = Database("mongodb:localhost/27017:mycollection", "username", "password")
|
|
301
|
+
dba = Database("firebase:https://your_storage.firebaseio.com", "username", "password")
|
|
302
|
+
|
|
303
|
+
Fetching records and passing data as parameters, limit and skip specified:
|
|
304
|
+
|
|
305
|
+
records = dba.fetch("select * from table where something = ? and something2 = ?", params=["something", "something2"], limit=10, skip=5)
|
|
306
|
+
|
|
307
|
+
print (records)
|
|
308
|
+
print (records.to_json())
|
|
309
|
+
{
|
|
310
|
+
id : 1
|
|
311
|
+
something: "something",
|
|
312
|
+
something2: "something2"
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
print(records[0].id)
|
|
316
|
+
1
|
|
317
|
+
|
|
318
|
+
record = dba.fetch_one("select * from table where something = ? and something2 = ?", params=["something", "something2"])
|
|
319
|
+
|
|
320
|
+
print(record.id)
|
|
321
|
+
|
|
322
|
+
print (records.to_json())
|
|
323
|
+
1
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
Executing sql queries:
|
|
327
|
+
|
|
328
|
+
dba.execute ("update table set something = ? and something2 = ? where id = ?", params=["something", "something2", 1])
|
|
329
|
+
dba.execute ("delete from table where id = ?", params=[1])
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
Starting a transaction:
|
|
333
|
+
|
|
334
|
+
dba.start_transaction()
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
Rollback a transaction:
|
|
338
|
+
|
|
339
|
+
dba.roll_back()
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
Commit a transaction:
|
|
343
|
+
|
|
344
|
+
dba.commit()
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
Select method (Still in development):
|
|
348
|
+
|
|
349
|
+
dba.select(["id", "something", "something2"], "table_name", filter={"id": 1}, limit=10, skip=0)
|
|
350
|
+
dba.select(["id", "something", "something2"], "table_name", filter=[{"id": 1}, {"id": 2}], limit=10, skip=0)
|
|
351
|
+
dba.select(["id", "something", "sum(id)"])
|
|
352
|
+
.from(["table"])
|
|
353
|
+
.join(["tabel2"])
|
|
354
|
+
.on([""])
|
|
355
|
+
.and([{"id": 1}])
|
|
356
|
+
.where({"id" : 2}, "id = ?", [{"id": 2}])
|
|
357
|
+
.having()
|
|
358
|
+
.group_by()
|
|
359
|
+
.order_by(["id"])
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
Updating records - records will be found by primary key specified and then updated:
|
|
363
|
+
|
|
364
|
+
dba.update(table_name="table_name", records.fromJSON(json_string))
|
|
365
|
+
dba.update(table_name="table_name", records, primary_key="id")
|
|
366
|
+
dba.update(table_name="table_name", record, primary_key="id") # primary key implied by first key value pair
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
Inserting records - pass a dictionary for a single record and a list of dictionaries for multiple records:
|
|
370
|
+
|
|
371
|
+
dba.insert(table_name="table_name", dba.from_json(json_string))
|
|
372
|
+
dba.insert(table_name="table_name", {"id": 1, "something": "hello", "something2": "world"})
|
|
373
|
+
dba.insert(table_name="table_name", [{"id": 1, "something": "hello", "something2": "world"},
|
|
374
|
+
{"id": 2, "something": "hello2", "something2": "world2"}])
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
Deleting records - specify table name. Records can either be found by primary key or filter:
|
|
378
|
+
|
|
379
|
+
dba.delete("table_name", record, primary_key="id")
|
|
380
|
+
dba.delete("table_name", filter={"id": 1})
|
|
381
|
+
dba.delete("table_name", filter=[{"id": 1}, {"id": 2}])
|
|
382
|
+
|
|
383
|
+
Migration updates:
|
|
384
|
+
|
|
385
|
+
- record count added
|
|
386
|
+
- Database result object updated
|
|
387
|
+
```
|
|
242
388
|
|
|
243
389
|
### Building and Deployment
|
|
244
390
|
|
|
245
391
|
#### Using Python
|
|
246
392
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
393
|
+
Building the package without poetry - you will need to request an API key:
|
|
394
|
+
```bash
|
|
395
|
+
python3 -m pip install --upgrade build
|
|
396
|
+
python3 -m build
|
|
397
|
+
python3 -m pip install --upgrade twine
|
|
398
|
+
python3 -m twine upload dist/*
|
|
399
|
+
```
|
|
254
400
|
|
|
255
401
|
#### Using Poetry
|
|
256
402
|
|
|
@@ -276,3 +422,13 @@ Flake8 Code tests
|
|
|
276
422
|
poetry run flake8 ./tina4_python
|
|
277
423
|
```
|
|
278
424
|
|
|
425
|
+
### Using queues
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
docker run -d --hostname=my-kafka --name=some-kafka -p 9092:9092 apache/kafka
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
docker run -d --hostname=my-rabbit --name=some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3
|
|
433
|
+
```
|
|
434
|
+
|
|
@@ -36,6 +36,22 @@ If you are developing on Tina4, make sure you copy the public folder from tina4_
|
|
|
36
36
|
|
|
37
37
|
### Installation
|
|
38
38
|
|
|
39
|
+
Virtual environment
|
|
40
|
+
|
|
41
|
+
Linux / Mac
|
|
42
|
+
```bash
|
|
43
|
+
python3 -m venv venv
|
|
44
|
+
source ./venv/bin/activate
|
|
45
|
+
pip install poetry
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Windows
|
|
49
|
+
```bash
|
|
50
|
+
python -m venv venv
|
|
51
|
+
.\venv\Scripts\activate
|
|
52
|
+
pip install poetry
|
|
53
|
+
```
|
|
54
|
+
|
|
39
55
|
#### Windows
|
|
40
56
|
|
|
41
57
|
1.) Install Poetry:
|
|
@@ -138,10 +154,10 @@ from tina4_python.Router import get
|
|
|
138
154
|
from tina4_python.Response import Response
|
|
139
155
|
|
|
140
156
|
@get("/hello/{name}")
|
|
141
|
-
async def greet(**params):
|
|
157
|
+
async def greet(**params): #(request, response)
|
|
142
158
|
name = params['request'].params['name']
|
|
143
|
-
return params['response'](f"Hello, {name}!")
|
|
144
|
-
|
|
159
|
+
return params['response'](f"Hello, {name}!") # return response()
|
|
160
|
+
````
|
|
145
161
|
|
|
146
162
|
This code creates a route for a GET request to `/hello/{name}`, where `name` is a parameter in the URL. The function `greet` accepts this parameter and responds with a personalized greeting.
|
|
147
163
|
|
|
@@ -181,6 +197,16 @@ async def capture_get(request, response):
|
|
|
181
197
|
@post("/capture")
|
|
182
198
|
async def capture_post(request, response):
|
|
183
199
|
return response(request.body)
|
|
200
|
+
|
|
201
|
+
@get("/simple")
|
|
202
|
+
async def simple_get(request, response):
|
|
203
|
+
print("<h1>Hello World!</h1>")
|
|
204
|
+
|
|
205
|
+
@get("/redirect")
|
|
206
|
+
async def simple_get(request, response):
|
|
207
|
+
|
|
208
|
+
return response.redirect("/test")
|
|
209
|
+
|
|
184
210
|
```
|
|
185
211
|
|
|
186
212
|
In your ```src/__init__.py``` add the following code
|
|
@@ -204,33 +230,150 @@ For ease of use you can supply an `API_KEY` param to your .env with a secret of
|
|
|
204
230
|
API_KEY=somehash
|
|
205
231
|
```
|
|
206
232
|
|
|
233
|
+
### Suppressing the default webservice
|
|
234
|
+
|
|
235
|
+
You may want to use your Tina4 project as a library in another project so suppressing the default webservice is probably needed.
|
|
236
|
+
|
|
237
|
+
```.env
|
|
238
|
+
TINA4_DEFAULT_WEBSERVER="False"
|
|
239
|
+
```
|
|
240
|
+
or in your `__init__.py` file
|
|
241
|
+
```python
|
|
242
|
+
import os
|
|
243
|
+
os.environ["TINA4_DEFAULT_WEBSERVER"] = "False"
|
|
244
|
+
```
|
|
245
|
+
Additionally, it will require you to rename `src` to the name of your package
|
|
207
246
|
|
|
208
247
|
### Features
|
|
209
|
-
| Completed | To Do
|
|
210
|
-
|
|
211
|
-
| Python pip package |
|
|
212
|
-
| Basic environment handling |
|
|
213
|
-
|
|
|
214
|
-
| Enhanced
|
|
215
|
-
| CSS Support |
|
|
216
|
-
| Image Support |
|
|
217
|
-
| Localization |
|
|
218
|
-
| Error Pages |
|
|
219
|
-
| Template handling |
|
|
220
|
-
| Form posting |
|
|
221
|
-
|
|
|
248
|
+
| Completed | To Do |
|
|
249
|
+
|----------------------------|-------|
|
|
250
|
+
| Python pip package | |
|
|
251
|
+
| Basic environment handling | |
|
|
252
|
+
| Routing with Swagger | |
|
|
253
|
+
| Enhanced Routing | |
|
|
254
|
+
| CSS Support | |
|
|
255
|
+
| Image Support | |
|
|
256
|
+
| Localization | |
|
|
257
|
+
| Error Pages | |
|
|
258
|
+
| Template handling | |
|
|
259
|
+
| Form posting | |
|
|
260
|
+
| Migrations | |
|
|
261
|
+
| Colored Debugging | |
|
|
262
|
+
| Database Abstraction | |
|
|
263
|
+
|
|
264
|
+
### Database
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
|
|
268
|
+
Various databases initialised:
|
|
269
|
+
|
|
270
|
+
dba = Database("sqlite3:test.db", "username", "password")
|
|
271
|
+
dba = Database("mysql:localhost/3306:myschema", "username", "password")
|
|
272
|
+
dba = Database("postgres:localhost/5432:myschema", "username", "password")
|
|
273
|
+
dba = Database("firebird:localhost/3050:/home/database/FIREBIRD.FDB", "username", "password")
|
|
274
|
+
|
|
275
|
+
NoSQL support (Still to be developed):
|
|
276
|
+
|
|
277
|
+
dba = Database("mongodb:localhost/27017:mycollection", "username", "password")
|
|
278
|
+
dba = Database("firebase:https://your_storage.firebaseio.com", "username", "password")
|
|
279
|
+
|
|
280
|
+
Fetching records and passing data as parameters, limit and skip specified:
|
|
281
|
+
|
|
282
|
+
records = dba.fetch("select * from table where something = ? and something2 = ?", params=["something", "something2"], limit=10, skip=5)
|
|
283
|
+
|
|
284
|
+
print (records)
|
|
285
|
+
print (records.to_json())
|
|
286
|
+
{
|
|
287
|
+
id : 1
|
|
288
|
+
something: "something",
|
|
289
|
+
something2: "something2"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
print(records[0].id)
|
|
293
|
+
1
|
|
294
|
+
|
|
295
|
+
record = dba.fetch_one("select * from table where something = ? and something2 = ?", params=["something", "something2"])
|
|
296
|
+
|
|
297
|
+
print(record.id)
|
|
298
|
+
|
|
299
|
+
print (records.to_json())
|
|
300
|
+
1
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
Executing sql queries:
|
|
304
|
+
|
|
305
|
+
dba.execute ("update table set something = ? and something2 = ? where id = ?", params=["something", "something2", 1])
|
|
306
|
+
dba.execute ("delete from table where id = ?", params=[1])
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
Starting a transaction:
|
|
310
|
+
|
|
311
|
+
dba.start_transaction()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
Rollback a transaction:
|
|
315
|
+
|
|
316
|
+
dba.roll_back()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
Commit a transaction:
|
|
320
|
+
|
|
321
|
+
dba.commit()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
Select method (Still in development):
|
|
325
|
+
|
|
326
|
+
dba.select(["id", "something", "something2"], "table_name", filter={"id": 1}, limit=10, skip=0)
|
|
327
|
+
dba.select(["id", "something", "something2"], "table_name", filter=[{"id": 1}, {"id": 2}], limit=10, skip=0)
|
|
328
|
+
dba.select(["id", "something", "sum(id)"])
|
|
329
|
+
.from(["table"])
|
|
330
|
+
.join(["tabel2"])
|
|
331
|
+
.on([""])
|
|
332
|
+
.and([{"id": 1}])
|
|
333
|
+
.where({"id" : 2}, "id = ?", [{"id": 2}])
|
|
334
|
+
.having()
|
|
335
|
+
.group_by()
|
|
336
|
+
.order_by(["id"])
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
Updating records - records will be found by primary key specified and then updated:
|
|
340
|
+
|
|
341
|
+
dba.update(table_name="table_name", records.fromJSON(json_string))
|
|
342
|
+
dba.update(table_name="table_name", records, primary_key="id")
|
|
343
|
+
dba.update(table_name="table_name", record, primary_key="id") # primary key implied by first key value pair
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
Inserting records - pass a dictionary for a single record and a list of dictionaries for multiple records:
|
|
347
|
+
|
|
348
|
+
dba.insert(table_name="table_name", dba.from_json(json_string))
|
|
349
|
+
dba.insert(table_name="table_name", {"id": 1, "something": "hello", "something2": "world"})
|
|
350
|
+
dba.insert(table_name="table_name", [{"id": 1, "something": "hello", "something2": "world"},
|
|
351
|
+
{"id": 2, "something": "hello2", "something2": "world2"}])
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
Deleting records - specify table name. Records can either be found by primary key or filter:
|
|
355
|
+
|
|
356
|
+
dba.delete("table_name", record, primary_key="id")
|
|
357
|
+
dba.delete("table_name", filter={"id": 1})
|
|
358
|
+
dba.delete("table_name", filter=[{"id": 1}, {"id": 2}])
|
|
359
|
+
|
|
360
|
+
Migration updates:
|
|
361
|
+
|
|
362
|
+
- record count added
|
|
363
|
+
- Database result object updated
|
|
364
|
+
```
|
|
222
365
|
|
|
223
366
|
### Building and Deployment
|
|
224
367
|
|
|
225
368
|
#### Using Python
|
|
226
369
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
370
|
+
Building the package without poetry - you will need to request an API key:
|
|
371
|
+
```bash
|
|
372
|
+
python3 -m pip install --upgrade build
|
|
373
|
+
python3 -m build
|
|
374
|
+
python3 -m pip install --upgrade twine
|
|
375
|
+
python3 -m twine upload dist/*
|
|
376
|
+
```
|
|
234
377
|
|
|
235
378
|
#### Using Poetry
|
|
236
379
|
|
|
@@ -255,3 +398,13 @@ Flake8 Code tests
|
|
|
255
398
|
```
|
|
256
399
|
poetry run flake8 ./tina4_python
|
|
257
400
|
```
|
|
401
|
+
|
|
402
|
+
### Using queues
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
docker run -d --hostname=my-kafka --name=some-kafka -p 9092:9092 apache/kafka
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
docker run -d --hostname=my-rabbit --name=some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3
|
|
410
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tina4-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.64"
|
|
4
4
|
description = "Tina4Python - This is not another framework for Python"
|
|
5
5
|
authors = ["Andre van Zuydam <andrevanzuydam@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -9,18 +9,24 @@ readme = "README.md"
|
|
|
9
9
|
|
|
10
10
|
[tool.poetry.dependencies]
|
|
11
11
|
python = "^3.10"
|
|
12
|
-
Jinja2 = "^3.
|
|
12
|
+
Jinja2 = "^3.1.4"
|
|
13
13
|
mypy = "^0.991"
|
|
14
14
|
libsass = "^0.22.0"
|
|
15
15
|
python-dotenv = "^1.0.1"
|
|
16
16
|
pyjwt = "^2.8.0"
|
|
17
17
|
cryptography = "^42.0.5"
|
|
18
18
|
watchdog = "^4.0.0"
|
|
19
|
+
bcrypt = "^4.1.3"
|
|
20
|
+
litequeue = "^0.9"
|
|
19
21
|
|
|
20
22
|
[tool.poetry.group.dev.dependencies]
|
|
21
23
|
flake8 = "^7.0.0"
|
|
22
24
|
pytest = "^7.2.0"
|
|
23
25
|
jurigged = "^0.5.3"
|
|
26
|
+
safety = "^3.2.8"
|
|
27
|
+
ruff = "^0.7.0"
|
|
28
|
+
mysql-connector-python = "^9.1.0"
|
|
29
|
+
psycopg2-binary = "^2.9.10"
|
|
24
30
|
|
|
25
31
|
[build-system]
|
|
26
32
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
# flake8: noqa: E501
|
|
7
7
|
import datetime
|
|
8
8
|
import os
|
|
9
|
-
|
|
10
9
|
import jwt
|
|
10
|
+
import bcrypt
|
|
11
|
+
from json import JSONEncoder
|
|
11
12
|
from cryptography import x509
|
|
12
13
|
from cryptography.x509 import NameOID
|
|
13
14
|
from cryptography.hazmat.primitives import serialization, hashes
|
|
@@ -15,6 +16,15 @@ from cryptography.hazmat.primitives.asymmetric import rsa
|
|
|
15
16
|
from cryptography.hazmat.backends import default_backend
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
class AuthJSONSerializer(JSONEncoder):
|
|
20
|
+
def default(self, o):
|
|
21
|
+
if isinstance(o, datetime.datetime):
|
|
22
|
+
return o.isoformat()
|
|
23
|
+
else:
|
|
24
|
+
raise TypeError(f'Object of type {o.__class__.__name__} '
|
|
25
|
+
f'is not JSON serializable')
|
|
26
|
+
|
|
27
|
+
|
|
18
28
|
class Auth:
|
|
19
29
|
secret = None
|
|
20
30
|
private_key = None
|
|
@@ -22,6 +32,28 @@ class Auth:
|
|
|
22
32
|
loaded_private_key = None
|
|
23
33
|
loaded_public_key = None
|
|
24
34
|
|
|
35
|
+
def hash_password(self, text):
|
|
36
|
+
"""
|
|
37
|
+
Generates a Bcrypt password hash
|
|
38
|
+
:param text:
|
|
39
|
+
:return:
|
|
40
|
+
"""
|
|
41
|
+
password_bytes = text.encode('utf-8')
|
|
42
|
+
# Generate a salt
|
|
43
|
+
salt = bcrypt.gensalt()
|
|
44
|
+
# Hash the password
|
|
45
|
+
return bcrypt.hashpw(password_bytes, salt).decode('utf-8')
|
|
46
|
+
|
|
47
|
+
def check_password(self, password_hash, text):
|
|
48
|
+
"""
|
|
49
|
+
Checks a Bcrypt password hash
|
|
50
|
+
:param password_hash:
|
|
51
|
+
:param text:
|
|
52
|
+
:return:
|
|
53
|
+
"""
|
|
54
|
+
password_bytes = text.encode('utf-8')
|
|
55
|
+
return bcrypt.checkpw(password_bytes, password_hash.encode('utf-8'))
|
|
56
|
+
|
|
25
57
|
def load_private_key(self):
|
|
26
58
|
if self.loaded_private_key:
|
|
27
59
|
return self.loaded_private_key
|
|
@@ -104,16 +136,22 @@ class Auth:
|
|
|
104
136
|
with open(self.self_signed, "wb") as f:
|
|
105
137
|
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
|
106
138
|
|
|
107
|
-
def get_token(self, payload_data):
|
|
139
|
+
def get_token(self, payload_data, expiry_minutes=0):
|
|
108
140
|
private_key = self.load_private_key()
|
|
109
141
|
now = datetime.datetime.now()
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
|
|
143
|
+
if not "expires" in payload_data:
|
|
144
|
+
token_limit_minutes = int(os.environ.get("TINA4_TOKEN_LIMIT", 2))
|
|
145
|
+
if expiry_minutes != 0:
|
|
146
|
+
token_limit_minutes = expiry_minutes
|
|
147
|
+
expiry_time = now + datetime.timedelta(minutes=token_limit_minutes)
|
|
148
|
+
payload_data["expires"] = expiry_time.isoformat()
|
|
149
|
+
|
|
113
150
|
token = jwt.encode(
|
|
114
151
|
payload=payload_data,
|
|
115
152
|
key=private_key,
|
|
116
|
-
algorithm='RS256'
|
|
153
|
+
algorithm='RS256',
|
|
154
|
+
json_encoder=AuthJSONSerializer
|
|
117
155
|
)
|
|
118
156
|
|
|
119
157
|
return token
|
|
@@ -26,10 +26,14 @@ HTTP_BAD_REQUEST = 400
|
|
|
26
26
|
HTTP_UNAUTHORIZED = 401
|
|
27
27
|
HTTP_FORBIDDEN = 403
|
|
28
28
|
HTTP_NOT_FOUND = 404
|
|
29
|
+
HTTP_REDIRECT = 302
|
|
30
|
+
HTTP_SERVER_ERROR = 500
|
|
29
31
|
|
|
30
32
|
LOOKUP_HTTP_CODE = {HTTP_OK: "OK", HTTP_CREATED: "Created", HTTP_BAD_REQUEST: "Bad Request",
|
|
31
33
|
HTTP_PARTIAL_CONTENT: "Partial Content", HTTP_UNAUTHORIZED: "Unauthorized",
|
|
32
|
-
HTTP_FORBIDDEN: "Forbidden", HTTP_NOT_FOUND: "Not Found", HTTP_NO_CONTENT: "No Content"
|
|
34
|
+
HTTP_FORBIDDEN: "Forbidden", HTTP_NOT_FOUND: "Not Found", HTTP_NO_CONTENT: "No Content",
|
|
35
|
+
HTTP_REDIRECT: "Redirect",
|
|
36
|
+
HTTP_SERVER_ERROR: "Internal Server Error"}
|
|
33
37
|
|
|
34
38
|
TEXT_HTML = "text/html"
|
|
35
39
|
TEXT_CSS = "text/css"
|