djgentelella 0.5.5__py3-none-any.whl → 0.5.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
djgentelella/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = '0.5.5'
1
+ __version__ = '0.5.6'
2
2
 
3
3
  if __name__ == '__main__':
4
4
  print(__version__)
@@ -0,0 +1,109 @@
1
+ import base64
2
+ import io
3
+ import json
4
+ import os
5
+ from base64 import b64encode, b64decode
6
+
7
+ from Crypto.Cipher import AES
8
+ from django.conf import settings
9
+ from django.db import models
10
+
11
+
12
+ def create_key(size=32):
13
+ key = os.urandom(size)
14
+ base64_encoded = base64.b64encode(key)
15
+ return base64_encoded.decode("utf-8")
16
+
17
+
18
+ def get_salt_session(size=16):
19
+ key = settings.SECRET_KEY.encode()
20
+ if len(key) > size:
21
+ return key[:size]
22
+ return key
23
+
24
+
25
+ def salt_encrypt(message, session_key=None):
26
+ if type(message) == str:
27
+ message = message.encode()
28
+ session_key = get_salt_session()
29
+ file_out = io.BytesIO()
30
+ cipher_aes = AES.new(session_key, AES.MODE_EAX)
31
+ ciphertext, tag = cipher_aes.encrypt_and_digest(message)
32
+ [file_out.write(x) for x in (cipher_aes.nonce, tag, ciphertext)]
33
+ file_out.seek(0)
34
+ return b64encode(file_out.read())
35
+
36
+
37
+ def salt_decrypt(message):
38
+ if message is None:
39
+ return None
40
+ raw_cipher_data = b64decode(message)
41
+ file_in = io.BytesIO(raw_cipher_data)
42
+ file_in.seek(0)
43
+
44
+ nonce, tag, ciphertext = [file_in.read(x) for x in (16, 16, -1)]
45
+ session_key = get_salt_session()
46
+ cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce)
47
+ decrypted = cipher_aes.decrypt_and_verify(ciphertext, tag)
48
+ return decrypted
49
+
50
+
51
+ class GTEncryptedText(models.TextField):
52
+ """
53
+ Encrypt data using AES and secure django key use in models like:
54
+ in models.py
55
+
56
+ class MyModel(models.Model):
57
+ my_secret = GTEncryptedText(null=True, blank=True)
58
+
59
+ """
60
+
61
+ def from_db_value(self, value, expression, connection):
62
+ if value is None:
63
+ return value
64
+ if isinstance(value, str):
65
+ value = value.encode()
66
+ return salt_decrypt(value)
67
+
68
+ def pre_save(self, model_instance, add):
69
+ field = getattr(model_instance, self.attname)
70
+ if field is None:
71
+ return None
72
+ dev = salt_encrypt(field)
73
+ if type(dev) == bytes:
74
+ dev = dev.decode()
75
+ return dev
76
+
77
+ def value_from_object(self, obj):
78
+ dev = super(GTEncryptedText, self).value_from_object(obj)
79
+ return dev.decode() if dev is not None else None
80
+
81
+
82
+ class GTEncryptedJSONField(models.JSONField):
83
+ """
84
+ Encrypt data using AES and secure django key use in models can store JSON objects:
85
+ in models.py
86
+
87
+ class MyModel(models.Model):
88
+ my_secret = GTEncryptedJSONField(null=True, blank=True)
89
+
90
+ """
91
+
92
+ def get_prep_value(self, value):
93
+ # encrypt the JSON before save to the database
94
+ if value is not None:
95
+ value = json.dumps(value)
96
+ encrypted_value = salt_encrypt(
97
+ super().get_prep_value(value).encode("utf-8")
98
+ )
99
+ return encrypted_value.decode("utf-8") # Store as text in DB
100
+ return value
101
+
102
+ def from_db_value(self, value, expression, connection):
103
+ # decrypt when loading from the database
104
+ if value is not None:
105
+ decrypted_value = salt_decrypt(value.encode("utf-8"))
106
+ return super().from_db_value(
107
+ decrypted_value.decode("utf-8"), expression, connection
108
+ )
109
+ return value
@@ -0,0 +1,87 @@
1
+ import datetime
2
+ import logging
3
+ import os
4
+ from pathlib import Path
5
+
6
+ from django.utils.text import slugify
7
+
8
+ logger = logging.getLogger("djgentelella")
9
+
10
+
11
+ def get_file_name(instance, name):
12
+ model_name = str(type(instance).__name__).lower()
13
+
14
+ return "%s-%s" % (
15
+ model_name,
16
+ name
17
+ )
18
+
19
+
20
+ def upload_files_by_model_and_dates(instance, filename):
21
+ """
22
+ Create a directory structure of the type model/date/file. Usage:
23
+
24
+ myfile = models.FileField(upload_to=upload_files_by_model_and_dates)
25
+ """
26
+ date = int(datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
27
+ path = Path(filename)
28
+ extension = path.suffix
29
+
30
+ if extension == ".zip":
31
+ name = path.stem
32
+ else:
33
+ name = get_file_name(instance, slugify(path.stem))
34
+
35
+ model_name = str(type(instance).__name__).lower()
36
+ return f"{model_name}/{date}/{name}{extension}"
37
+
38
+
39
+ def upload_files_by_model_and_month(instance, filename):
40
+ """
41
+ Create a directory structure of the type model/yearmonth/file. Usage:
42
+
43
+ myfile = models.FileField(upload_to=upload_files_by_model_and_dates)
44
+ """
45
+ dates = datetime.datetime.now().strftime("%Y%m")
46
+ path = Path(filename)
47
+ extension = path.suffix
48
+
49
+ if extension == ".zip":
50
+ name = path.stem
51
+ else:
52
+ name = get_file_name(instance, slugify(path.stem))
53
+
54
+ model_name = str(type(instance).__name__).lower()
55
+ return f"{model_name}/{dates}/{name}{extension}"
56
+
57
+
58
+ def delete_file_and_folder(file_field):
59
+ """
60
+ Delete the file associated with file_field and, if the containing folder is empty,
61
+ delete it.
62
+ """
63
+ if not file_field:
64
+ return
65
+
66
+ # Obtener la ruta absoluta del archivo
67
+ file_path = file_field.path
68
+ if os.path.exists(file_path):
69
+ try:
70
+ os.remove(file_path)
71
+ logger.info(f"Deleted file: {file_path}")
72
+ except Exception as e:
73
+ logger.error(f"Error deleting file: {file_path}", exc_info=e)
74
+
75
+ # Obtener la carpeta contenedora del archivo
76
+ directory = os.path.dirname(file_path)
77
+
78
+ # Intentar eliminar la carpeta, si está vacía.
79
+ if os.path.exists(directory):
80
+ # Listar el contenido de la carpeta
81
+ files = os.listdir(directory)
82
+ if not files:
83
+ try:
84
+ os.rmdir(directory)
85
+ logger.info(f"Folder created: {directory}")
86
+ except Exception as e:
87
+ logger.error(f"Error deleting folder: {directory}", exc_info=e)
@@ -69,3 +69,10 @@ class AnyPermissionByAction(BasePermission):
69
69
  return False
70
70
 
71
71
  return any_permission(request.user, perms)
72
+
73
+
74
+ def get_actions_by_perms(user, actions_list):
75
+ actions = {}
76
+ for action, perms in actions_list.items():
77
+ actions[action] = all_permission(user, perms)
78
+ return actions
@@ -31,3 +31,17 @@ class GTDateTimeField(DateTimeField):
31
31
  if not value and self.allow_empty_str:
32
32
  return None
33
33
  return super().to_internal_value(value)
34
+
35
+
36
+ class DateFieldWithEmptyString(DateField):
37
+ def to_internal_value(self, value):
38
+ if not value:
39
+ return None
40
+ return super(DateFieldWithEmptyString, self).to_internal_value(value)
41
+
42
+
43
+ class DateTimeFieldFieldWithEmptyString(DateTimeField):
44
+ def to_internal_value(self, value):
45
+ if not value:
46
+ return None
47
+ return super(DateTimeFieldFieldWithEmptyString, self).to_internal_value(value)
@@ -0,0 +1,8 @@
1
+ from rest_framework import serializers
2
+
3
+
4
+ class GTEncryptedTextField(serializers.CharField):
5
+ def to_representation(self, value):
6
+ if not value:
7
+ return ""
8
+ return str(value.decode())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: djgentelella
3
- Version: 0.5.5
3
+ Version: 0.5.6
4
4
  Summary: Help building extra widgets for forms and speciall methods to render forms in templates
5
5
  Author-email: Solvosoft <info@solvosoft.com>
6
6
  Maintainer-email: Luis Zarate Montero <luis.zarate@solvosoft.com>
@@ -377,6 +377,7 @@ Requires-Dist: Pillow
377
377
  Requires-Dist: djangorestframework>=3.13
378
378
  Requires-Dist: django>=4.2
379
379
  Requires-Dist: django-filter>=22.1
380
+ Requires-Dist: pycryptodome>=3.23.0
380
381
  Provides-Extra: dev
381
382
  Requires-Dist: pylp; extra == "dev"
382
383
  Requires-Dist: pylpconcat; extra == "dev"
@@ -1,4 +1,4 @@
1
- djgentelella/__init__.py,sha256=fSlji7vVD7VNU_XZIf0LtmkI5Mo7ox4Y-nqWwSr9JQk,73
1
+ djgentelella/__init__.py,sha256=CGHA797Gml3kD_XQHsmIsfocnbo03NlW5CDNC3lqSZs,73
2
2
  djgentelella/admin.py,sha256=1LhKD8iGGl-eHDVvZxH2JPdkSEhQEfT9trDYVaJkIe4,2999
3
3
  djgentelella/apps.py,sha256=5VEbpUv4b1Jh7gCRAQCe5TvpakRyndKwafjVj0z-Xfo,153
4
4
  djgentelella/chartjs.py,sha256=iNYoFnaIQoXsYInJFbSE2quWpjmDz5CuMKpVsvt4pRM,8572
@@ -8,6 +8,7 @@ djgentelella/groute.py,sha256=FPKKZBfm0UYAfMKmeLGhPBWqPXqazADjt7iKMruVmcE,799
8
8
  djgentelella/gtselects.py,sha256=4HiQGmV0OgdT4Eky0bsgudEEWBa3S0M8scP0RN-X4Mk,654
9
9
  djgentelella/models.py,sha256=TeDZhFca6Vdcfo0iU_AYMTFUShoScOPvMwX3c7XWwwA,7535
10
10
  djgentelella/models_manager.py,sha256=cpiL90mNBVO_BQTmrIUq5V1JQ6AedUwYKZSz1eZ0Eyg,1036
11
+ djgentelella/models_utils.py,sha256=KryCHnpZbSyZUB4BKNV4DECQZ7gv_Fi1KvsPssRIQgk,2473
11
12
  djgentelella/objectmanagement.py,sha256=y_dkSKIOfKKK9t1hZ8mwcM5RNxj0bChpKYvUn50mVzs,3121
12
13
  djgentelella/settings.py,sha256=7x7R6182YDd9SjGDNDceSwRcwYTVJgEIqU1hNtBmPZE,3298
13
14
  djgentelella/urls.py,sha256=Ht4AhWLYlY64hR-_D4N13vBmrD_7STi08V7z7c1Z-3Y,5427
@@ -54,6 +55,7 @@ djgentelella/fields/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
54
55
  djgentelella/fields/catalog.py,sha256=-9THXpjIrcMFy8M3uvPzxoJuwWmldOJhZEeRZ71GLPk,2551
55
56
  djgentelella/fields/drfdatetime.py,sha256=CpN5wT-NmaG8q8cKjwEuylhnlNHRqx-aIwdWqH9ngWw,2360
56
57
  djgentelella/fields/files.py,sha256=pezlWl7WYtSFE0DSBcOeVf4zZo9MUkm_TxQZYYN6TZ4,5538
58
+ djgentelella/fields/secure.py,sha256=osoFbA7P9Ae0y9mm7YMRppzqCz63LOs5MIdeIDJ2i2M,3195
57
59
  djgentelella/fields/tree.py,sha256=I_XxsnxXuXiJbeQSEEyni_AzoiYkNJuPE7LBO6mqrXs,2209
58
60
  djgentelella/firmador_digital/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
61
  djgentelella/firmador_digital/forms.py,sha256=OGsDG7csv-8CnEDy1dZUoFf5BBWEMlX46qtq4MhIReA,5347
@@ -116,15 +118,16 @@ djgentelella/notification/__init__.py,sha256=Xz_RootxxW07E9GVvn6Rqz9x-P_M7ZU1-Ww
116
118
  djgentelella/notification/base.py,sha256=VRxfoQwAgMVsWRBNAdh42JboRUQllXDfhqhMA89uMAQ,2760
117
119
  djgentelella/notification/serializer.py,sha256=ehLL7YO8vVehqk2xMQelHxuQsKWYVr1piYB7-oGeSnY,1989
118
120
  djgentelella/notification/widgets.py,sha256=EjmF6tXiHjme4ersZvUGbN79qoFwzSYGY_rgdvmAGiU,1752
119
- djgentelella/permission_management/__init__.py,sha256=JNtgNYdbOo4hn1m8jw_pNga0c2pSsiknZzR8WCV5h9U,2048
121
+ djgentelella/permission_management/__init__.py,sha256=8niXk7Xdde3zAItxdI9D40T-uvxjsRu6BdxuJdF6oRc,2233
120
122
  djgentelella/permission_management/forms.py,sha256=HfFUXcsMqr11ioIql67OOXcT87MxcWbOv4fRylib90k,1627
121
123
  djgentelella/permission_management/objinterface.py,sha256=b4a_Fa2NsQUNVtQs_2Vbe_HEB0tIMdQt5_ZMvl9F47Q,4926
122
124
  djgentelella/permission_management/views.py,sha256=XWaBD3i5kN7ADcbSYz4_Bzq2n0gSaJ7eQ_i9nKLXE_c,2104
123
- djgentelella/serializers/__init__.py,sha256=r-pQ1bBd-Y2V5am9Fd9FgPJCacICJvg-xuHqS8shOzQ,1307
125
+ djgentelella/serializers/__init__.py,sha256=Aboh7mxPBZmg2tGqoZ_m9W8Rm1FAI38msAS2thYad7g,1747
124
126
  djgentelella/serializers/calendar.py,sha256=57A9QOcBF6twCLJ4yC2uxhuFtolhDENpZ9Dv0sVWYQs,3461
125
127
  djgentelella/serializers/firmador_digital.py,sha256=YRPwV85gkggJC4uudvLZ-x_UVUb8l42ih4ckj5JNnS4,3870
126
128
  djgentelella/serializers/helper.py,sha256=xfBZDhI4sE0jRrtcfRFnnNtqtmMpagL2Q2uMkpFvyPQ,248
127
129
  djgentelella/serializers/paginators.py,sha256=1KOC9uHBguc6mT9g-QVRZolE6hIxRm9T4hUhOSa0E8Q,501
130
+ djgentelella/serializers/secure.py,sha256=rnTIJhPlzUlPgqoS_msNAxhlqdAYRQuHWp6PoAT8fhw,211
128
131
  djgentelella/serializers/selects.py,sha256=91zuRmtCy-ogSuFA_hni1E2U56wnlru8RuHch074llo,1360
129
132
  djgentelella/serializers/storyline.py,sha256=4RtDjlYEYbw69ibGexqOdsaIvyje3JmYWKFYQz8EFNM,1255
130
133
  djgentelella/serializers/storymap.py,sha256=HMoyoRxZocoO5S3BGQQ9SwuHtq32zaI2BJsUa9-V_G8,2777
@@ -1047,9 +1050,9 @@ djgentelella/widgets/trees.py,sha256=bV6s-w1cgYahS0aNU6xBrx-aISdlzDZ23BFt3_3hAu4
1047
1050
  djgentelella/widgets/wysiwyg.py,sha256=wHeMyYNVE8-lKJ06A-0DLDAcRNv3TOAOG_Wl9DYZqS0,609
1048
1051
  djgentelella/wysiwyg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1049
1052
  djgentelella/wysiwyg/views.py,sha256=tI7LjLhCnohdvIEOEEhB2Cu1zxRmdcOhYJJX4LBIZaA,1159
1050
- djgentelella-0.5.5.dist-info/AUTHORS,sha256=HyQoO-q7oXtavpNm7jaBVv8Vxx3c7yo33xkFkt4blkY,118
1051
- djgentelella-0.5.5.dist-info/LICENSE.txt,sha256=wDzqAntLQORAL6vQhVdzZyfsPVvFStZKtkct5DIZjK0,18047
1052
- djgentelella-0.5.5.dist-info/METADATA,sha256=eRln7Iyix_RiC-6CulRsz6OXIzxVUz3Km9vumPSOoDE,27544
1053
- djgentelella-0.5.5.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
1054
- djgentelella-0.5.5.dist-info/top_level.txt,sha256=88JbODVPV-P5q7ic25yMHt_FuuizaLzMyIaegpFa7Qk,13
1055
- djgentelella-0.5.5.dist-info/RECORD,,
1053
+ djgentelella-0.5.6.dist-info/AUTHORS,sha256=HyQoO-q7oXtavpNm7jaBVv8Vxx3c7yo33xkFkt4blkY,118
1054
+ djgentelella-0.5.6.dist-info/LICENSE.txt,sha256=wDzqAntLQORAL6vQhVdzZyfsPVvFStZKtkct5DIZjK0,18047
1055
+ djgentelella-0.5.6.dist-info/METADATA,sha256=gOpNj-hMQBjMNiXymMZi2tOgM29J79dOTVfaqR6Mlko,27580
1056
+ djgentelella-0.5.6.dist-info/WHEEL,sha256=hPN0AlP2dZM_3ZJZWP4WooepkmU9wzjGgCLCeFjkHLA,92
1057
+ djgentelella-0.5.6.dist-info/top_level.txt,sha256=88JbODVPV-P5q7ic25yMHt_FuuizaLzMyIaegpFa7Qk,13
1058
+ djgentelella-0.5.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.1)
2
+ Generator: bdist_wheel (0.46.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5