tina4-python 0.2.165__tar.gz → 0.2.166__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.
Files changed (56) hide show
  1. {tina4_python-0.2.165 → tina4_python-0.2.166}/PKG-INFO +1 -1
  2. {tina4_python-0.2.165 → tina4_python-0.2.166}/pyproject.toml +1 -1
  3. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Database.py +29 -20
  4. {tina4_python-0.2.165 → tina4_python-0.2.166}/.gitignore +0 -0
  5. {tina4_python-0.2.165 → tina4_python-0.2.166}/README.md +0 -0
  6. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Api.py +0 -0
  7. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Auth.py +0 -0
  8. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/CRUD.py +0 -0
  9. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Constant.py +0 -0
  10. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/DatabaseResult.py +0 -0
  11. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/DatabaseTypes.py +0 -0
  12. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Debug.py +0 -0
  13. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Env.py +0 -0
  14. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/FieldTypes.py +0 -0
  15. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/HtmlElement.py +0 -0
  16. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Localization.py +0 -0
  17. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Messages.py +0 -0
  18. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/MiddleWare.py +0 -0
  19. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Migration.py +0 -0
  20. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/ORM.py +0 -0
  21. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Queue.py +0 -0
  22. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Request.py +0 -0
  23. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Response.py +0 -0
  24. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Router.py +0 -0
  25. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Session.py +0 -0
  26. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/ShellColors.py +0 -0
  27. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Swagger.py +0 -0
  28. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Template.py +0 -0
  29. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Testing.py +0 -0
  30. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/WSDL.py +0 -0
  31. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Webserver.py +0 -0
  32. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/Websocket.py +0 -0
  33. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/__init__.py +0 -0
  34. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/cli.py +0 -0
  35. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/messages.pot +0 -0
  36. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/css/readme.md +0 -0
  37. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/favicon.ico +0 -0
  38. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/images/403.png +0 -0
  39. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/images/404.png +0 -0
  40. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/images/500.png +0 -0
  41. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/images/logo.png +0 -0
  42. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/images/readme.md +0 -0
  43. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/js/readme.md +0 -0
  44. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/js/reconnecting-websocket.js +0 -0
  45. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/js/tina4helper.js +0 -0
  46. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/swagger/index.html +0 -0
  47. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  48. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/templates/components/crud.twig +0 -0
  49. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/templates/errors/403.twig +0 -0
  50. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/templates/errors/404.twig +0 -0
  51. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/templates/errors/500.twig +0 -0
  52. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/templates/readme.md +0 -0
  53. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  54. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  55. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  56. {tina4_python-0.2.165 → tina4_python-0.2.166}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 0.2.165
3
+ Version: 0.2.166
4
4
  Summary: Tina4Python - This is not another framework for Python
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  Requires-Python: <4.0,>=3.12
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tina4-python"
3
- version = "0.2.165"
3
+ version = "0.2.166"
4
4
  description = "Tina4Python - This is not another framework for Python"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam",email = "andrevanzuydam@gmail.com"}
@@ -11,8 +11,7 @@ import sys
11
11
  import importlib
12
12
  import json
13
13
  from decimal import Decimal
14
- from tina4_python import Debug, Constant
15
- from tina4_python.Constant import TINA4_LOG_ERROR
14
+ from tina4_python import Debug
16
15
  from tina4_python.DatabaseResult import DatabaseResult
17
16
  from tina4_python.DatabaseTypes import *
18
17
  from tina4_python.FieldTypes import get_field_type_values
@@ -41,7 +40,7 @@ class Database:
41
40
  raise Exception("Database connection string is missing, try declaring DATABASE_PATH in the .env file.")
42
41
 
43
42
  self.database_module = importlib.import_module(params[0])
44
- except Exception:
43
+ except Exception as e:
45
44
  install_message = "Please implement " + params[0] + " in Database.py and make a pull request!"
46
45
  if params[0] == SQLITE:
47
46
  install_message = "Your python is missing the sqlite3 module, please reinstall or update"
@@ -54,7 +53,7 @@ class Database:
54
53
  elif params[0] == MSSQL:
55
54
  install_message = "Your python is missing the mssql module, please install with " + MSSQL_INSTALL
56
55
 
57
- sys.exit("Could not load database driver for " + params[0] + "\n" + install_message)
56
+ sys.exit("Could not load database driver for " + params[0] + "\n" + install_message+ "\n"+str(e))
58
57
 
59
58
 
60
59
  self.database_engine = params[0]
@@ -186,7 +185,7 @@ class Database:
186
185
 
187
186
  def get_next_id(self, table_name, column_name="id"):
188
187
  """
189
- Gets the next id using max method in sql for databases which don't have good sequences
188
+ Gets the next id using max method in SQL for databases which don't have good sequences
190
189
  :param str table_name: Name of the table
191
190
  :param str column_name: Name of the column in that table to increment
192
191
  :return: int : The next id in the sequence
@@ -294,7 +293,7 @@ class Database:
294
293
  cols = []
295
294
  for c in raw:
296
295
  name = c.strip().split()[-1].split(".")[-1]
297
- name = re.sub(r'^[`"\[\(].*[`"\]\)]$', '', name).strip('`"[]')
296
+ name = re.sub(r'^[`"\[(].*[`"\])]$', '', name).strip('`"[]')
298
297
  if name and name != "*":
299
298
  cols.append(name)
300
299
 
@@ -326,18 +325,28 @@ class Database:
326
325
  counter.close()
327
326
 
328
327
  # 4. FINAL PAGINATION – applied AFTER the filter
329
- if self.database_engine == "FIREBIRD":
328
+ if self.database_engine == FIREBIRD:
330
329
  final_sql = f"SELECT FIRST {limit} SKIP {skip} * FROM ({final_sql}) AS t"
331
- elif self.database_engine in ("MYSQL", "SQLITE"):
330
+ elif self.database_engine in (MYSQL, SQLITE):
332
331
  final_sql = f"SELECT * FROM ({final_sql}) AS t LIMIT {limit} OFFSET {skip}"
333
- elif self.database_engine == "POSTGRES":
332
+ elif self.database_engine == POSTGRES:
334
333
  final_sql = f"SELECT * FROM ({final_sql}) AS t LIMIT {limit} OFFSET {skip}"
335
- elif self.database_engine == "MSSQL":
336
- # MSSQL needs ORDER BY for OFFSET/FETCH
334
+ elif self.database_engine == MSSQL:
337
335
  inner = final_sql.strip()
338
- if not re.search(r"\border\s+by\b", inner, re.I):
339
- inner += " ORDER BY (SELECT NULL)"
340
- final_sql = f"SELECT * FROM ({inner}) AS t OFFSET {skip} ROWS FETCH NEXT {limit} ROWS ONLY"
336
+ # Detect and extract ORDER BY if present
337
+ order_by_match = re.search(r"(?i)\border\s+by\s+.+?$", inner, re.DOTALL)
338
+ has_order_by = order_by_match is not None
339
+
340
+ # Clean inner query: remove trailing ORDER BY if it exists
341
+ if has_order_by:
342
+ inner_clean = re.sub(r"(?i)\s+order\s+by\s+.+?$", "", inner, flags=re.DOTALL).strip()
343
+ order_by_part = order_by_match.group(0)
344
+ else:
345
+ inner_clean = inner
346
+ order_by_part = "ORDER BY (SELECT NULL)"
347
+
348
+ # Build final paginated query
349
+ final_sql = f"SELECT * FROM ({inner_clean}) AS t {order_by_part} OFFSET {skip} ROWS FETCH NEXT {limit} ROWS ONLY"
341
350
  else:
342
351
  final_sql = f"SELECT * FROM ({final_sql}) AS t LIMIT {limit} OFFSET {skip}"
343
352
 
@@ -356,7 +365,7 @@ class Database:
356
365
 
357
366
  def fetch_one(self, sql, params=[], skip=0):
358
367
  """
359
- Fetch a single record based on a sql statement, take note that BLOB and byte record data is converted into base64 automatically
368
+ Fetch a single record based on a SQL statement, take note that BLOB and byte record data is converted into base64 automatically
360
369
  :param str sql: A plain SQL statement or one with params in it designated by ?
361
370
  :param list params: A list of params in order of precedence
362
371
  :param int skip: Offset of records to skip
@@ -384,8 +393,8 @@ class Database:
384
393
 
385
394
  def parse_place_holders(self, sql):
386
395
  """
387
- Sanitizes a sql statement to replace param chars with the appropriate placeholders
388
- MYSQL expects %s and firebird, posgres and sqlite expect ?
396
+ Sanitizes a SQL statement to replace param chars with the appropriate placeholders
397
+ MYSQL expects %s and firebird, PostgresSQL and sqlite expect ?
389
398
  :param sql:
390
399
  :return:
391
400
  """
@@ -396,7 +405,7 @@ class Database:
396
405
 
397
406
  def execute(self, sql, params=None):
398
407
  """
399
- Execute a query based on a sql statement
408
+ Execute a query based on a SQL statement
400
409
  :param str sql: A plain SQL statement or one with params in it designated by ?
401
410
  :param list params: A list of params in order of precedence
402
411
  :return: DatabaseResult
@@ -431,7 +440,7 @@ class Database:
431
440
 
432
441
  def execute_many(self, sql, params=None):
433
442
  """
434
- Execute a query based on a single sql statement with a different number of params
443
+ Execute a query based on a single SQL statement with a different number of params
435
444
  :param sql: A plain SQL statement or one with params in it designated by ?
436
445
  :param params: A list of params in order of precedence
437
446
  :return: DatabaseResult
@@ -513,7 +522,7 @@ class Database:
513
522
 
514
523
  def sanitize(self, record):
515
524
  """
516
- Changes dictionaries and list values into json for updating and inserting
525
+ Changes dictionaries and list values into JSON for updating and inserting
517
526
  :param record:
518
527
  :return:
519
528
  """
File without changes