cardo-python-utils 0.4.2__tar.gz → 0.5.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.
- cardo_python_utils-0.5.0/MANIFEST.in +5 -0
- {cardo_python_utils-0.4.2/cardo_python_utils.egg-info → cardo_python_utils-0.5.0}/PKG-INFO +27 -24
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/README.rst +2 -4
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0/cardo_python_utils.egg-info}/PKG-INFO +27 -24
- cardo_python_utils-0.5.0/cardo_python_utils.egg-info/SOURCES.txt +74 -0
- cardo_python_utils-0.5.0/cardo_python_utils.egg-info/requires.txt +20 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/cardo_python_utils.egg-info/top_level.txt +1 -0
- cardo_python_utils-0.5.0/pyproject.toml +74 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/choices.py +5 -4
- cardo_python_utils-0.5.0/python_utils/django/README.md +227 -0
- cardo_python_utils-0.5.0/python_utils/django/__init__.py +1 -0
- cardo_python_utils-0.5.0/python_utils/django/admin/__init__.py +0 -0
- cardo_python_utils-0.5.0/python_utils/django/admin/auth.py +82 -0
- cardo_python_utils-0.5.0/python_utils/django/admin/templates/__init__.py +3 -0
- cardo_python_utils-0.5.0/python_utils/django/admin/templates/user_groups_changelist.html +7 -0
- cardo_python_utils-0.5.0/python_utils/django/admin/user_group.py +159 -0
- cardo_python_utils-0.5.0/python_utils/django/admin/views.py +24 -0
- cardo_python_utils-0.5.0/python_utils/django/api/__init__.py +0 -0
- cardo_python_utils-0.5.0/python_utils/django/api/drf.py +93 -0
- cardo_python_utils-0.5.0/python_utils/django/api/ninja.py +153 -0
- cardo_python_utils-0.5.0/python_utils/django/api/utils.py +130 -0
- cardo_python_utils-0.5.0/python_utils/django/apps.py +6 -0
- cardo_python_utils-0.5.0/python_utils/django/auth/service.py +202 -0
- cardo_python_utils-0.5.0/python_utils/django/celery/__init__.py +4 -0
- cardo_python_utils-0.5.0/python_utils/django/celery/tenant_aware_database_scheduler.py +207 -0
- cardo_python_utils-0.5.0/python_utils/django/celery/tenant_aware_task.py +115 -0
- cardo_python_utils-0.5.0/python_utils/django/db/__init__.py +0 -0
- cardo_python_utils-0.5.0/python_utils/django/db/alias.py +22 -0
- cardo_python_utils-0.5.0/python_utils/django/db/routers.py +11 -0
- cardo_python_utils-0.5.0/python_utils/django/db/transaction.py +26 -0
- cardo_python_utils-0.5.0/python_utils/django/db/utils.py +66 -0
- cardo_python_utils-0.5.0/python_utils/django/management/__init__.py +0 -0
- cardo_python_utils-0.5.0/python_utils/django/management/commands/__init__.py +0 -0
- cardo_python_utils-0.5.0/python_utils/django/management/commands/migrateall.py +25 -0
- cardo_python_utils-0.5.0/python_utils/django/management/commands/shell.py +32 -0
- cardo_python_utils-0.5.0/python_utils/django/management/commands/showmigrations.py +37 -0
- cardo_python_utils-0.5.0/python_utils/django/management/commands/tenant_aware_command.py +74 -0
- cardo_python_utils-0.5.0/python_utils/django/middleware/__init__.py +6 -0
- cardo_python_utils-0.5.0/python_utils/django/middleware/tenant_aware_http_middleware.py +108 -0
- cardo_python_utils-0.5.0/python_utils/django/middleware/tenant_aware_websocket_middleware.py +129 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0001_initial.py +115 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0001_initial_squashed_0005_alter_userrole_id.py +153 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0002_auto_20220120_1617.py +18 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0003_auto_20220513_1025.py +18 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0004_auto_20220817_1526.py +23 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0005_alter_userrole_id.py +18 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0006_userrole_organization_and_more.py +31 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0007_user_demo.py +15 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/0008_delete_userrole.py +25 -0
- cardo_python_utils-0.5.0/python_utils/django/migrations/__init__.py +0 -0
- cardo_python_utils-0.5.0/python_utils/django/models/__init__.py +3 -0
- cardo_python_utils-0.5.0/python_utils/django/models/user.py +22 -0
- cardo_python_utils-0.5.0/python_utils/django/models/user_group.py +22 -0
- cardo_python_utils-0.5.0/python_utils/django/oidc_settings.py +134 -0
- cardo_python_utils-0.5.0/python_utils/django/redis/__init__.py +4 -0
- cardo_python_utils-0.5.0/python_utils/django/redis/key_function.py +5 -0
- cardo_python_utils-0.5.0/python_utils/django/settings.py +22 -0
- cardo_python_utils-0.5.0/python_utils/django/storage/__init__.py +6 -0
- cardo_python_utils-0.5.0/python_utils/django/storage/tenant_aware_storage.py +86 -0
- cardo_python_utils-0.5.0/python_utils/django/tenant_context.py +101 -0
- cardo_python_utils-0.5.0/python_utils/django/tests/__init__.py +9 -0
- cardo_python_utils-0.5.0/python_utils/django/tests/conftest.py +101 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/django_utils.py +0 -40
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/esma_choices.py +21 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/text.py +5 -4
- cardo_python_utils-0.5.0/setup.cfg +4 -0
- cardo_python_utils-0.4.2/MANIFEST.in +0 -3
- cardo_python_utils-0.4.2/cardo_python_utils.egg-info/SOURCES.txt +0 -24
- cardo_python_utils-0.4.2/cardo_python_utils.egg-info/requires.txt +0 -18
- cardo_python_utils-0.4.2/python_utils/pandas_utils.py +0 -143
- cardo_python_utils-0.4.2/python_utils/rest.py +0 -37
- cardo_python_utils-0.4.2/setup.cfg +0 -44
- cardo_python_utils-0.4.2/setup.py +0 -7
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/LICENSE +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/cardo_python_utils.egg-info/dependency_links.txt +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/__init__.py +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/data_structures.py +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/db.py +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/exceptions.py +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/imports.py +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/math.py +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/time.py +0 -0
- {cardo_python_utils-0.4.2 → cardo_python_utils-0.5.0}/python_utils/types_hinting.py +0 -0
|
@@ -1,40 +1,45 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cardo-python-utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Python library enhanced with a wide range of functions for different scenarios.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Author-email: CardoAI <hello@cardoai.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/CardoAI/cardo-python-utils
|
|
8
|
+
Project-URL: Repository, https://github.com/CardoAI/cardo-python-utils.git
|
|
9
|
+
Project-URL: Issues, https://github.com/CardoAI/cardo-python-utils/issues
|
|
10
|
+
Keywords: utilities,helpers,django
|
|
9
11
|
Classifier: Environment :: Web Environment
|
|
10
12
|
Classifier: Framework :: Django
|
|
11
13
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved ::
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
15
|
Classifier: Operating System :: OS Independent
|
|
14
16
|
Classifier: Programming Language :: Python
|
|
15
17
|
Classifier: Programming Language :: Python :: 3
|
|
16
18
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
22
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
23
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
22
24
|
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/x-rst
|
|
23
26
|
License-File: LICENSE
|
|
24
|
-
Provides-Extra:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Requires-Dist: djangorestframework; extra == "rest"
|
|
31
|
-
Requires-Dist: requests; extra == "rest"
|
|
27
|
+
Provides-Extra: django-keycloak
|
|
28
|
+
Requires-Dist: PyJWT>=2.10.1; extra == "django-keycloak"
|
|
29
|
+
Requires-Dist: mozilla-django-oidc>=4.0.1; extra == "django-keycloak"
|
|
30
|
+
Requires-Dist: requests; extra == "django-keycloak"
|
|
31
|
+
Provides-Extra: django-keycloak-groups
|
|
32
|
+
Requires-Dist: python-keycloak>=5.8.1; extra == "django-keycloak-groups"
|
|
32
33
|
Provides-Extra: all
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist: djangorestframework; extra == "all"
|
|
34
|
+
Requires-Dist: PyJWT>=2.10.1; extra == "all"
|
|
35
|
+
Requires-Dist: mozilla-django-oidc>=4.0.1; extra == "all"
|
|
36
|
+
Requires-Dist: python-keycloak>=5.8.1; extra == "all"
|
|
37
37
|
Requires-Dist: requests; extra == "all"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-django>=4.5; extra == "dev"
|
|
41
|
+
Requires-Dist: coverage>=6.0; extra == "dev"
|
|
42
|
+
Requires-Dist: tox>=3.25; extra == "dev"
|
|
38
43
|
Dynamic: license-file
|
|
39
44
|
|
|
40
45
|
============================
|
|
@@ -50,9 +55,7 @@ Main utils:
|
|
|
50
55
|
* data_structures
|
|
51
56
|
* db
|
|
52
57
|
* django
|
|
53
|
-
* django_rest
|
|
54
58
|
* math
|
|
55
|
-
* pandas
|
|
56
59
|
* exception
|
|
57
60
|
* choices
|
|
58
61
|
|
|
@@ -64,7 +67,7 @@ Quick start
|
|
|
64
67
|
from python_utils.time import date_range
|
|
65
68
|
date_range(start_date, end_date)
|
|
66
69
|
|
|
67
|
-
Although the library provides some utility functions related to other libraries like django
|
|
70
|
+
Although the library provides some utility functions related to other libraries like django, it does not install any dependencies automatically.
|
|
68
71
|
This means, you can install the library even if you do not use these libraries, but keep in mind that in this case you cannot use the
|
|
69
72
|
functions that depend on them.
|
|
70
73
|
|
|
@@ -74,7 +77,7 @@ You can also chose to install the dependencies alongside the library, including
|
|
|
74
77
|
|
|
75
78
|
Tests
|
|
76
79
|
-----
|
|
77
|
-
The library has a
|
|
80
|
+
The library has a high coverage by tests. If you want to see tests in action:
|
|
78
81
|
|
|
79
82
|
1. Inside venv, run ``pip install -r tests/requirements.txt``
|
|
80
83
|
|
|
@@ -11,9 +11,7 @@ Main utils:
|
|
|
11
11
|
* data_structures
|
|
12
12
|
* db
|
|
13
13
|
* django
|
|
14
|
-
* django_rest
|
|
15
14
|
* math
|
|
16
|
-
* pandas
|
|
17
15
|
* exception
|
|
18
16
|
* choices
|
|
19
17
|
|
|
@@ -25,7 +23,7 @@ Quick start
|
|
|
25
23
|
from python_utils.time import date_range
|
|
26
24
|
date_range(start_date, end_date)
|
|
27
25
|
|
|
28
|
-
Although the library provides some utility functions related to other libraries like django
|
|
26
|
+
Although the library provides some utility functions related to other libraries like django, it does not install any dependencies automatically.
|
|
29
27
|
This means, you can install the library even if you do not use these libraries, but keep in mind that in this case you cannot use the
|
|
30
28
|
functions that depend on them.
|
|
31
29
|
|
|
@@ -35,7 +33,7 @@ You can also chose to install the dependencies alongside the library, including
|
|
|
35
33
|
|
|
36
34
|
Tests
|
|
37
35
|
-----
|
|
38
|
-
The library has a
|
|
36
|
+
The library has a high coverage by tests. If you want to see tests in action:
|
|
39
37
|
|
|
40
38
|
1. Inside venv, run ``pip install -r tests/requirements.txt``
|
|
41
39
|
|
|
@@ -1,40 +1,45 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cardo-python-utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Python library enhanced with a wide range of functions for different scenarios.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Author-email: CardoAI <hello@cardoai.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/CardoAI/cardo-python-utils
|
|
8
|
+
Project-URL: Repository, https://github.com/CardoAI/cardo-python-utils.git
|
|
9
|
+
Project-URL: Issues, https://github.com/CardoAI/cardo-python-utils/issues
|
|
10
|
+
Keywords: utilities,helpers,django
|
|
9
11
|
Classifier: Environment :: Web Environment
|
|
10
12
|
Classifier: Framework :: Django
|
|
11
13
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved ::
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
15
|
Classifier: Operating System :: OS Independent
|
|
14
16
|
Classifier: Programming Language :: Python
|
|
15
17
|
Classifier: Programming Language :: Python :: 3
|
|
16
18
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
22
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
23
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
22
24
|
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/x-rst
|
|
23
26
|
License-File: LICENSE
|
|
24
|
-
Provides-Extra:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Requires-Dist: djangorestframework; extra == "rest"
|
|
31
|
-
Requires-Dist: requests; extra == "rest"
|
|
27
|
+
Provides-Extra: django-keycloak
|
|
28
|
+
Requires-Dist: PyJWT>=2.10.1; extra == "django-keycloak"
|
|
29
|
+
Requires-Dist: mozilla-django-oidc>=4.0.1; extra == "django-keycloak"
|
|
30
|
+
Requires-Dist: requests; extra == "django-keycloak"
|
|
31
|
+
Provides-Extra: django-keycloak-groups
|
|
32
|
+
Requires-Dist: python-keycloak>=5.8.1; extra == "django-keycloak-groups"
|
|
32
33
|
Provides-Extra: all
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist: djangorestframework; extra == "all"
|
|
34
|
+
Requires-Dist: PyJWT>=2.10.1; extra == "all"
|
|
35
|
+
Requires-Dist: mozilla-django-oidc>=4.0.1; extra == "all"
|
|
36
|
+
Requires-Dist: python-keycloak>=5.8.1; extra == "all"
|
|
37
37
|
Requires-Dist: requests; extra == "all"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-django>=4.5; extra == "dev"
|
|
41
|
+
Requires-Dist: coverage>=6.0; extra == "dev"
|
|
42
|
+
Requires-Dist: tox>=3.25; extra == "dev"
|
|
38
43
|
Dynamic: license-file
|
|
39
44
|
|
|
40
45
|
============================
|
|
@@ -50,9 +55,7 @@ Main utils:
|
|
|
50
55
|
* data_structures
|
|
51
56
|
* db
|
|
52
57
|
* django
|
|
53
|
-
* django_rest
|
|
54
58
|
* math
|
|
55
|
-
* pandas
|
|
56
59
|
* exception
|
|
57
60
|
* choices
|
|
58
61
|
|
|
@@ -64,7 +67,7 @@ Quick start
|
|
|
64
67
|
from python_utils.time import date_range
|
|
65
68
|
date_range(start_date, end_date)
|
|
66
69
|
|
|
67
|
-
Although the library provides some utility functions related to other libraries like django
|
|
70
|
+
Although the library provides some utility functions related to other libraries like django, it does not install any dependencies automatically.
|
|
68
71
|
This means, you can install the library even if you do not use these libraries, but keep in mind that in this case you cannot use the
|
|
69
72
|
functions that depend on them.
|
|
70
73
|
|
|
@@ -74,7 +77,7 @@ You can also chose to install the dependencies alongside the library, including
|
|
|
74
77
|
|
|
75
78
|
Tests
|
|
76
79
|
-----
|
|
77
|
-
The library has a
|
|
80
|
+
The library has a high coverage by tests. If you want to see tests in action:
|
|
78
81
|
|
|
79
82
|
1. Inside venv, run ``pip install -r tests/requirements.txt``
|
|
80
83
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.rst
|
|
4
|
+
pyproject.toml
|
|
5
|
+
cardo_python_utils.egg-info/PKG-INFO
|
|
6
|
+
cardo_python_utils.egg-info/SOURCES.txt
|
|
7
|
+
cardo_python_utils.egg-info/dependency_links.txt
|
|
8
|
+
cardo_python_utils.egg-info/requires.txt
|
|
9
|
+
cardo_python_utils.egg-info/top_level.txt
|
|
10
|
+
python_utils/__init__.py
|
|
11
|
+
python_utils/choices.py
|
|
12
|
+
python_utils/data_structures.py
|
|
13
|
+
python_utils/db.py
|
|
14
|
+
python_utils/django_utils.py
|
|
15
|
+
python_utils/esma_choices.py
|
|
16
|
+
python_utils/exceptions.py
|
|
17
|
+
python_utils/imports.py
|
|
18
|
+
python_utils/math.py
|
|
19
|
+
python_utils/text.py
|
|
20
|
+
python_utils/time.py
|
|
21
|
+
python_utils/types_hinting.py
|
|
22
|
+
python_utils/django/README.md
|
|
23
|
+
python_utils/django/__init__.py
|
|
24
|
+
python_utils/django/apps.py
|
|
25
|
+
python_utils/django/oidc_settings.py
|
|
26
|
+
python_utils/django/settings.py
|
|
27
|
+
python_utils/django/tenant_context.py
|
|
28
|
+
python_utils/django/admin/__init__.py
|
|
29
|
+
python_utils/django/admin/auth.py
|
|
30
|
+
python_utils/django/admin/user_group.py
|
|
31
|
+
python_utils/django/admin/views.py
|
|
32
|
+
python_utils/django/admin/templates/__init__.py
|
|
33
|
+
python_utils/django/admin/templates/user_groups_changelist.html
|
|
34
|
+
python_utils/django/api/__init__.py
|
|
35
|
+
python_utils/django/api/drf.py
|
|
36
|
+
python_utils/django/api/ninja.py
|
|
37
|
+
python_utils/django/api/utils.py
|
|
38
|
+
python_utils/django/auth/service.py
|
|
39
|
+
python_utils/django/celery/__init__.py
|
|
40
|
+
python_utils/django/celery/tenant_aware_database_scheduler.py
|
|
41
|
+
python_utils/django/celery/tenant_aware_task.py
|
|
42
|
+
python_utils/django/db/__init__.py
|
|
43
|
+
python_utils/django/db/alias.py
|
|
44
|
+
python_utils/django/db/routers.py
|
|
45
|
+
python_utils/django/db/transaction.py
|
|
46
|
+
python_utils/django/db/utils.py
|
|
47
|
+
python_utils/django/management/__init__.py
|
|
48
|
+
python_utils/django/management/commands/__init__.py
|
|
49
|
+
python_utils/django/management/commands/migrateall.py
|
|
50
|
+
python_utils/django/management/commands/shell.py
|
|
51
|
+
python_utils/django/management/commands/showmigrations.py
|
|
52
|
+
python_utils/django/management/commands/tenant_aware_command.py
|
|
53
|
+
python_utils/django/middleware/__init__.py
|
|
54
|
+
python_utils/django/middleware/tenant_aware_http_middleware.py
|
|
55
|
+
python_utils/django/middleware/tenant_aware_websocket_middleware.py
|
|
56
|
+
python_utils/django/migrations/0001_initial.py
|
|
57
|
+
python_utils/django/migrations/0001_initial_squashed_0005_alter_userrole_id.py
|
|
58
|
+
python_utils/django/migrations/0002_auto_20220120_1617.py
|
|
59
|
+
python_utils/django/migrations/0003_auto_20220513_1025.py
|
|
60
|
+
python_utils/django/migrations/0004_auto_20220817_1526.py
|
|
61
|
+
python_utils/django/migrations/0005_alter_userrole_id.py
|
|
62
|
+
python_utils/django/migrations/0006_userrole_organization_and_more.py
|
|
63
|
+
python_utils/django/migrations/0007_user_demo.py
|
|
64
|
+
python_utils/django/migrations/0008_delete_userrole.py
|
|
65
|
+
python_utils/django/migrations/__init__.py
|
|
66
|
+
python_utils/django/models/__init__.py
|
|
67
|
+
python_utils/django/models/user.py
|
|
68
|
+
python_utils/django/models/user_group.py
|
|
69
|
+
python_utils/django/redis/__init__.py
|
|
70
|
+
python_utils/django/redis/key_function.py
|
|
71
|
+
python_utils/django/storage/__init__.py
|
|
72
|
+
python_utils/django/storage/tenant_aware_storage.py
|
|
73
|
+
python_utils/django/tests/__init__.py
|
|
74
|
+
python_utils/django/tests/conftest.py
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
[all]
|
|
3
|
+
PyJWT>=2.10.1
|
|
4
|
+
mozilla-django-oidc>=4.0.1
|
|
5
|
+
python-keycloak>=5.8.1
|
|
6
|
+
requests
|
|
7
|
+
|
|
8
|
+
[dev]
|
|
9
|
+
pytest>=7.0
|
|
10
|
+
pytest-django>=4.5
|
|
11
|
+
coverage>=6.0
|
|
12
|
+
tox>=3.25
|
|
13
|
+
|
|
14
|
+
[django-keycloak]
|
|
15
|
+
PyJWT>=2.10.1
|
|
16
|
+
mozilla-django-oidc>=4.0.1
|
|
17
|
+
requests
|
|
18
|
+
|
|
19
|
+
[django-keycloak-groups]
|
|
20
|
+
python-keycloak>=5.8.1
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cardo-python-utils"
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
description = "Python library enhanced with a wide range of functions for different scenarios."
|
|
9
|
+
readme = "README.rst"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "CardoAI", email = "hello@cardoai.com"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["utilities", "helpers", "django"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Environment :: Web Environment",
|
|
18
|
+
"Framework :: Django",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
29
|
+
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
30
|
+
]
|
|
31
|
+
dependencies = []
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
django-keycloak = [
|
|
35
|
+
"PyJWT>=2.10.1",
|
|
36
|
+
"mozilla-django-oidc>=4.0.1",
|
|
37
|
+
"requests",
|
|
38
|
+
]
|
|
39
|
+
django-keycloak-groups = [
|
|
40
|
+
"python-keycloak>=5.8.1",
|
|
41
|
+
]
|
|
42
|
+
all = [
|
|
43
|
+
"PyJWT>=2.10.1",
|
|
44
|
+
"mozilla-django-oidc>=4.0.1",
|
|
45
|
+
"python-keycloak>=5.8.1",
|
|
46
|
+
"requests",
|
|
47
|
+
]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=7.0",
|
|
50
|
+
"pytest-django>=4.5",
|
|
51
|
+
"coverage>=6.0",
|
|
52
|
+
"tox>=3.25",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
Homepage = "https://github.com/CardoAI/cardo-python-utils"
|
|
57
|
+
Repository = "https://github.com/CardoAI/cardo-python-utils.git"
|
|
58
|
+
Issues = "https://github.com/CardoAI/cardo-python-utils/issues"
|
|
59
|
+
|
|
60
|
+
[tool.setuptools]
|
|
61
|
+
include-package-data = true
|
|
62
|
+
|
|
63
|
+
[tool.setuptools.packages.find]
|
|
64
|
+
where = ["."]
|
|
65
|
+
exclude = ["tests", "tests.*"]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
DJANGO_SETTINGS_MODULE = "tests.settings"
|
|
69
|
+
django_find_project = false
|
|
70
|
+
python_files = ["tests.py", "test_*.py", "*_tests.py"]
|
|
71
|
+
addopts = "--strict-markers --no-migrations --reuse-db --log-cli-level=INFO --doctest-modules"
|
|
72
|
+
|
|
73
|
+
[tool.tox]
|
|
74
|
+
legacy_tox_ini = "tox.ini"
|
|
@@ -9,6 +9,7 @@ It adds some useful methods to the Enum class, such as:
|
|
|
9
9
|
|
|
10
10
|
from abc import ABC, abstractmethod
|
|
11
11
|
from enum import EnumMeta, Enum
|
|
12
|
+
from typing import Union
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class IChoice(ABC):
|
|
@@ -41,7 +42,7 @@ class ChoiceEnumMeta(EnumMeta, IChoice, ABC):
|
|
|
41
42
|
|
|
42
43
|
return new_cls
|
|
43
44
|
|
|
44
|
-
def __contains__(cls, item: int
|
|
45
|
+
def __contains__(cls, item: Union[int, str]) -> bool:
|
|
45
46
|
if isinstance(item, int):
|
|
46
47
|
member_values = [v.value[0] for v in cls.__members__.values()]
|
|
47
48
|
elif isinstance(item, str):
|
|
@@ -75,12 +76,12 @@ class ChoiceEnum(Enum, metaclass=ChoiceEnumMeta):
|
|
|
75
76
|
return {elm.value[0]: elm.value[1] for elm in cls}
|
|
76
77
|
|
|
77
78
|
@classmethod
|
|
78
|
-
def get_by_value(cls, value: str
|
|
79
|
-
value_index = 0 if
|
|
79
|
+
def get_by_value(cls, value: Union[str, int]):
|
|
80
|
+
value_index = 0 if isinstance(value, int) else 1
|
|
80
81
|
return next((v for v in cls.__members__.values() if v.value[value_index] == value), None)
|
|
81
82
|
|
|
82
83
|
@classmethod
|
|
83
|
-
def list_as(cls, item_type) -> list[int
|
|
84
|
+
def list_as(cls, item_type) -> list[Union[int, str]]:
|
|
84
85
|
if item_type not in [int, str]:
|
|
85
86
|
raise TypeError('Invalid item type')
|
|
86
87
|
return list(map(item_type, cls))
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
This package provides utilities for facilitating IDP communication and multi-tenancy support.
|
|
2
|
+
|
|
3
|
+
# Usage
|
|
4
|
+
|
|
5
|
+
## Environment variables to set
|
|
6
|
+
|
|
7
|
+
- AWS_STORAGE_TENANT_BUCKET_NAMES
|
|
8
|
+
- _This variable should be set if separate tenant buckets are needed._
|
|
9
|
+
- A JSON dictionary where each key is the tenant name and the value is the bucket name.
|
|
10
|
+
- DATABASE_CONFIG
|
|
11
|
+
- A JSON dictionary where each key is the tenant name and the value is a dict with the datase config.
|
|
12
|
+
- If multiple 'DATABASE_CONFIG'-prefixed variables are set, they will be merged into a single dictionary.
|
|
13
|
+
- KEYCLOAK_CONFIDENTIAL_CLIENT_ID
|
|
14
|
+
- The id of the confidential client of the backend service
|
|
15
|
+
- KEYCLOAK_CONFIDENTIAL_CLIENT_SERVICE_ACCOUNT_TOKEN_FILE_PATHS
|
|
16
|
+
- A JSON dictionary where each key is the tenant name and the value is the file path of the service account token for the confidential client of that tenant
|
|
17
|
+
|
|
18
|
+
## settings.py file
|
|
19
|
+
|
|
20
|
+
### For multi-tenancy
|
|
21
|
+
|
|
22
|
+
```python3
|
|
23
|
+
INSTALLED_APPS = [
|
|
24
|
+
...
|
|
25
|
+
"python_utils.django",
|
|
26
|
+
...
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
MIDDLEWARE = [
|
|
30
|
+
"python_utils.django.middleware.TenantAwareHttpMiddleware",
|
|
31
|
+
...
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
AUTH_USER_MODEL = "idp_user.User"
|
|
35
|
+
|
|
36
|
+
# Include the database configuration for each tenant in the DATABASES setting.
|
|
37
|
+
# You can use the get_database_configs() function from python_utils.django.db.utils as a helper.
|
|
38
|
+
from python_utils.django.db.utils import get_database_configs
|
|
39
|
+
|
|
40
|
+
for tenant, tenant_db_config in get_database_configs().items():
|
|
41
|
+
DATABASES[tenant] = {
|
|
42
|
+
"ENGINE": "django.db.backends.postgresql",
|
|
43
|
+
"NAME": tenant_db_config["name"],
|
|
44
|
+
"USER": tenant_db_config["user"],
|
|
45
|
+
"PASSWORD": tenant_db_config["password"],
|
|
46
|
+
"HOST": tenant_db_config["host"],
|
|
47
|
+
"PORT": tenant_db_config.get("port", 5432),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# If you want to override the database alias to use for local development (when DEBUG is True).
|
|
51
|
+
# By default, the first database defined in DATABASES is used.
|
|
52
|
+
DEVELOPMENT_TENANT = "development"
|
|
53
|
+
|
|
54
|
+
# This is required to use the tenant context when routing database queries
|
|
55
|
+
DATABASE_ROUTERS = ["python_utils.django.db.routers.TenantAwareRouter"]
|
|
56
|
+
|
|
57
|
+
# If using celery, set the task class to TenantAwareTask:
|
|
58
|
+
CELERY_TASK_CLS = "python_utils.django.celery.TenantAwareTask"
|
|
59
|
+
|
|
60
|
+
# If using Redis caching, configure the cache backend as follows:
|
|
61
|
+
CACHES = {
|
|
62
|
+
"default": {
|
|
63
|
+
"BACKEND": "django_redis.cache.RedisCache",
|
|
64
|
+
"LOCATION": REDIS_LOCATION,
|
|
65
|
+
"KEY_FUNCTION": "python_utils.django.redis.make_tenant_aware_key",
|
|
66
|
+
**OPTIONS,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# If using Django Storages with S3, and separate tenant buckets are needed,
|
|
71
|
+
# configure the storage backends as follows:
|
|
72
|
+
STORAGES = {
|
|
73
|
+
"default": {
|
|
74
|
+
"BACKEND": "python_utils.django.storage.TenantAwarePrivateS3Storage",
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# If you want to exclude certain paths from tenant processing, use TENANT_AWARE_EXCLUDED_PATHS:
|
|
79
|
+
# They are considered as prefixes, so all paths starting with the given strings will be excluded.
|
|
80
|
+
TENANT_AWARE_EXCLUDED_PATHS = ("/some/path",)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### For OIDC auth
|
|
84
|
+
|
|
85
|
+
```python3
|
|
86
|
+
from python_utils.django.admin.templates import TEMPLATE_PATH
|
|
87
|
+
|
|
88
|
+
INSTALLED_APPS.append("mozilla_django_oidc")
|
|
89
|
+
TEMPLATES[0]["DIRS"].append(TEMPLATE_PATH)
|
|
90
|
+
|
|
91
|
+
JWT_AUDIENCE = "myapp"
|
|
92
|
+
JWT_SCOPE_PREFIX = "myapp"
|
|
93
|
+
|
|
94
|
+
# If using DRF
|
|
95
|
+
REST_FRAMEWORK.update(
|
|
96
|
+
{
|
|
97
|
+
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
|
98
|
+
"DEFAULT_AUTHENTICATION_CLASSES": ("python_utils.django.api.drf.AuthenticationBackend",),
|
|
99
|
+
"DEFAULT_PERMISSION_CLASSES": (
|
|
100
|
+
"rest_framework.permissions.IsAuthenticated",
|
|
101
|
+
"python_utils.django.api.drf.HasScope",
|
|
102
|
+
),
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Admin auth backends
|
|
107
|
+
AUTHENTICATION_BACKENDS = [
|
|
108
|
+
"django.contrib.auth.backends.ModelBackend",
|
|
109
|
+
"python_utils.django.admin.auth.AdminAuthenticationBackend",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
# If user groups are used for Row Level Security (RLS)
|
|
113
|
+
KEYCLOAK_USER_GROUP_MODEL = "myapp.UserGroup"
|
|
114
|
+
|
|
115
|
+
KEYCLOAK_CONFIDENTIAL_CLIENT_ID = os.getenv("KEYCLOAK_CONFIDENTIAL_CLIENT_ID", f"{JWT_AUDIENCE}_confidential")
|
|
116
|
+
|
|
117
|
+
OIDC_RP_CLIENT_ID = KEYCLOAK_CONFIDENTIAL_CLIENT_ID
|
|
118
|
+
OIDC_RP_SIGN_ALGO = "RS256"
|
|
119
|
+
OIDC_CREATE_USER = True
|
|
120
|
+
OIDC_AUTHENTICATE_CLASS = "python_utils.django.admin.views.TenantAwareOIDCAuthenticationRequestView"
|
|
121
|
+
|
|
122
|
+
LOGIN_REDIRECT_URL = "/admin"
|
|
123
|
+
SESSION_COOKIE_AGE = 60 * 30 # 30 minutes
|
|
124
|
+
SESSION_SAVE_EVERY_REQUEST = True # Extend session on each request
|
|
125
|
+
|
|
126
|
+
# If using django-easy-audit
|
|
127
|
+
|
|
128
|
+
from python_utils.django.db.alias import DynamicDatabaseAlias
|
|
129
|
+
DJANGO_EASY_AUDIT_DATABASE_ALIAS = DynamicDatabaseAlias()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## urls.py file
|
|
133
|
+
|
|
134
|
+
The views of the `mozilla-django-oidc` package need to be exposed as well, for the OIDC auth:
|
|
135
|
+
|
|
136
|
+
```python3
|
|
137
|
+
urlpatterns.append(path("oidc/", include("mozilla_django_oidc.urls")))
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## admin.py file
|
|
141
|
+
|
|
142
|
+
The Django Admin Panel needs to be configured to automatically redirect to the OIDC login page:
|
|
143
|
+
|
|
144
|
+
```python3
|
|
145
|
+
from python_utils.django.admin.auth import has_admin_site_permission
|
|
146
|
+
from python_utils.django.admin.views import TenantAwareOIDCAuthenticationRequestView
|
|
147
|
+
|
|
148
|
+
admin.site.login = TenantAwareOIDCAuthenticationRequestView.as_view()
|
|
149
|
+
admin.site.has_permission = has_admin_site_permission
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## With django-ninja
|
|
153
|
+
|
|
154
|
+
If using `django-ninja`, apart from the settings configured above, auth utils are provided in the django/api/ninja.py module.
|
|
155
|
+
|
|
156
|
+
## Atomic Transactions
|
|
157
|
+
|
|
158
|
+
Django's `transaction.atomic` uses the default database. To make it tenant-aware, use `tenant_atomic`.
|
|
159
|
+
If `transaction.on_commit` is used, make sure to pass the tenant as DB alias as well:
|
|
160
|
+
|
|
161
|
+
```python3
|
|
162
|
+
from python_utils.django.db.transaction import tenant_atomic
|
|
163
|
+
|
|
164
|
+
@tenant_atomic
|
|
165
|
+
def my_function():
|
|
166
|
+
# Some logic
|
|
167
|
+
|
|
168
|
+
transaction.on_commit(do_smth, using=TenantContext.get())
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Explicit database connection
|
|
172
|
+
|
|
173
|
+
If using django.db.connection anywhere in the code, you need to change that to get a tenant-aware connection:
|
|
174
|
+
|
|
175
|
+
```python3
|
|
176
|
+
from python_utils.django.db.utils import get_connection
|
|
177
|
+
from python_utils.django.tenant_context import TenantContext
|
|
178
|
+
|
|
179
|
+
connection = get_connection(TenantContext.get())
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Django Shell
|
|
183
|
+
|
|
184
|
+
This library overrides the shell command of Django, so that it requires the `tenant` arg.
|
|
185
|
+
This way, the shell is automatically initialized with the context set to the tenant.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
./manage.py shell --tenant=tenant1
|
|
189
|
+
|
|
190
|
+
Starting shell for tenant: -tenant=tenant
|
|
191
|
+
>>>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Testing
|
|
195
|
+
|
|
196
|
+
In order for tests to work, create the following autouse fixtures:
|
|
197
|
+
|
|
198
|
+
```python3
|
|
199
|
+
import pytest
|
|
200
|
+
|
|
201
|
+
from python_utils.django.tenant_context import TenantContext
|
|
202
|
+
|
|
203
|
+
# Add this, if the test utils of the package are needed
|
|
204
|
+
pytest_plugins = ["python_utils.django.tests"]
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
208
|
+
def test_database() -> str:
|
|
209
|
+
return "default"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
213
|
+
def set_tenant_context_session(test_database):
|
|
214
|
+
"""Set tenant context for the entire test session."""
|
|
215
|
+
TenantContext.set(test_database)
|
|
216
|
+
yield
|
|
217
|
+
TenantContext.clear()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@pytest.fixture(autouse=True)
|
|
221
|
+
def set_tenant_context(set_tenant_context_session, test_database):
|
|
222
|
+
"""Ensure tenant context is set for each test (depends on session fixture)."""
|
|
223
|
+
# Re-set in case it was cleared between tests
|
|
224
|
+
if not TenantContext.is_set():
|
|
225
|
+
TenantContext.set(test_database)
|
|
226
|
+
yield
|
|
227
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
default_app_config = "python_utils.django.apps.DjangoAppConfig"
|
|
File without changes
|