accrete 0.0.35__py3-none-any.whl → 0.0.37__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.
Files changed (31) hide show
  1. accrete/annotation.py +46 -0
  2. accrete/contrib/ui/__init__.py +12 -3
  3. accrete/contrib/ui/context.py +184 -257
  4. accrete/contrib/ui/elements.py +78 -2
  5. accrete/contrib/ui/filter.py +110 -44
  6. accrete/contrib/ui/static/css/accrete.css +128 -128
  7. accrete/contrib/ui/static/css/accrete.css.map +1 -1
  8. accrete/contrib/ui/static/css/accrete.scss +9 -9
  9. accrete/contrib/ui/static/js/filter.js +24 -0
  10. accrete/contrib/ui/static/js/htmx.min.js +1 -0
  11. accrete/contrib/ui/templates/ui/layout.html +134 -129
  12. accrete/contrib/ui/templates/ui/list.html +3 -3
  13. accrete/contrib/ui/templates/ui/partials/filter.html +29 -5
  14. accrete/contrib/ui/templates/ui/partials/header.html +7 -7
  15. accrete/contrib/ui/templates/ui/partials/pagination_detail.html +3 -3
  16. accrete/contrib/ui/templates/ui/partials/pagination_list.html +4 -4
  17. accrete/contrib/ui/templates/ui/table.html +18 -13
  18. accrete/contrib/ui/templatetags/accrete_ui.py +12 -1
  19. accrete/contrib/user/forms.py +0 -4
  20. accrete/contrib/user/templates/user/login.html +6 -12
  21. accrete/contrib/user/views.py +31 -21
  22. accrete/middleware.py +15 -0
  23. accrete/models.py +9 -7
  24. accrete/querystring.py +11 -8
  25. accrete/utils/models.py +14 -0
  26. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/METADATA +1 -1
  27. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/RECORD +29 -28
  28. accrete/contrib/ui/components.py +0 -96
  29. accrete/contrib/ui/querystring.py +0 -19
  30. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/WHEEL +0 -0
  31. {accrete-0.0.35.dist-info → accrete-0.0.37.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,16 @@
1
- from django.views.generic import View
1
+ from django.utils import timezone
2
2
  from django.contrib.auth.forms import AuthenticationForm
3
- from django.contrib.auth import logout, views, update_session_auth_hash
3
+ from django.contrib.auth import views, update_session_auth_hash
4
4
  from django.contrib.auth.decorators import login_required
5
+ from django.contrib.sessions.models import Session
5
6
  from django.contrib import messages
7
+ from django.db.models import Q
6
8
  from django.shortcuts import redirect, render, reverse, resolve_url
7
9
  from django.utils.translation import gettext_lazy as _
8
10
  from django.conf import settings
9
11
 
10
12
  from accrete.forms import save_form
11
- from accrete.contrib.ui import FormContext, DetailContext, ClientAction
13
+ from accrete.contrib import ui
12
14
  from .forms import UserForm, ChangePasswordForm, ChangeEmailForm
13
15
 
14
16
 
@@ -37,15 +39,17 @@ class LogoutView(views.LogoutView):
37
39
 
38
40
  @login_required()
39
41
  def user_detail(request):
40
- ctx = DetailContext(
41
- request.user, request.GET.dict(), paginate_by=0, breadcrumbs=[],
42
+ ctx = ui.DetailContext(
43
+ title=_('Preferences'),
44
+ object=request.user,
45
+ breadcrumbs=[],
42
46
  actions=[
43
- ClientAction(_('Edit'), url=reverse('user:edit')),
44
- ClientAction(_('Change E-Mail'), url=reverse('user:edit_email')),
45
- ClientAction(_('Change Password'), url=reverse('user:edit_password'))
47
+ ui.ClientAction(_('Edit'), url=reverse('user:edit')),
48
+ ui.ClientAction(_('Change E-Mail'), url=reverse('user:edit_email')),
49
+ ui.ClientAction(_('Change Password'), url=reverse('user:edit_password'))
46
50
  ]
47
51
  )
48
- return render(request, 'user/user_detail.html', ctx.dict())
52
+ return render(request, 'user/user_detail.html', ctx)
49
53
 
50
54
 
51
55
  @login_required()
@@ -58,7 +62,12 @@ def user_edit(request):
58
62
  form = save_form(UserForm(request.POST, instance=request.user))
59
63
  if form.is_saved:
60
64
  return redirect('user:detail')
61
- ctx = FormContext(request.user, form, request.GET.dict()).dict()
65
+ ctx = ui.FormContext(
66
+ title=_('Preferences'),
67
+ form=form,
68
+ form_id='form',
69
+ actions=ui.form_actions(reverse('user:detail'))
70
+ )
62
71
  return render(request, 'user/user_form.html', ctx)
63
72
 
64
73
 
@@ -75,12 +84,12 @@ def user_change_password(request):
75
84
  fail_silently=True
76
85
  )
77
86
  return redirect('user:detail')
78
- ctx = FormContext(
79
- request.user,
80
- form,
81
- request.GET.dict(),
87
+ ctx = ui.FormContext(
82
88
  title=_('Change Password'),
83
- ).dict()
89
+ form=form,
90
+ form_id='form',
91
+ actions=ui.form_actions(reverse('user:detail'))
92
+ )
84
93
  return render(request, 'user/change_password.html', ctx)
85
94
 
86
95
 
@@ -96,10 +105,11 @@ def user_change_email(request):
96
105
  fail_silently=True
97
106
  )
98
107
  return redirect('user:detail')
99
- ctx = FormContext(
100
- request.user,
101
- form,
102
- request.GET.dict(),
103
- title=_('Change Email')
104
- ).dict()
108
+
109
+ ctx = ui.FormContext(
110
+ title=_('Change Email'),
111
+ form=form,
112
+ form_id='form',
113
+ actions=ui.form_actions(reverse('user:detail'))
114
+ )
105
115
  return render(request, 'user/change_email.html', ctx)
accrete/middleware.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from django.utils.deprecation import MiddlewareMixin
2
2
  from django.conf import settings
3
+ from django.http.response import HttpResponseRedirect, HttpResponsePermanentRedirect
3
4
  from accrete.tenant import set_tenant, set_member
4
5
 
5
6
  from .models import Tenant
@@ -73,3 +74,17 @@ class TenantMiddleware(MiddlewareMixin):
73
74
  max_age=31536000
74
75
  )
75
76
  return response
77
+
78
+
79
+ class HtmxRedirectMiddleware(MiddlewareMixin):
80
+
81
+ @staticmethod
82
+ def process_response(request, response):
83
+ is_htmx = request.headers.get('HX-Request', 'false') == 'true'
84
+ is_redirect = isinstance(
85
+ response, (HttpResponseRedirect, HttpResponsePermanentRedirect)
86
+ )
87
+ if is_htmx and is_redirect:
88
+ response['HX-Redirect'] = response['Location']
89
+ response.status_code = 200
90
+ return response
accrete/models.py CHANGED
@@ -3,16 +3,17 @@ from django.conf import settings
3
3
  from django.utils.translation import gettext_lazy as _
4
4
  from django.contrib.auth.validators import UnicodeUsernameValidator
5
5
  from accrete.tenant import get_tenant
6
+ from accrete.annotation import AnnotationModelMixin, AnnotationManagerMixin
6
7
 
7
8
 
8
- class TenantManager(models.Manager):
9
+ class TenantManager(models.Manager, AnnotationManagerMixin):
9
10
 
10
11
  def get_queryset(self):
11
12
  queryset = super().get_queryset()
12
13
  tenant = get_tenant()
13
14
  if tenant:
14
15
  queryset = queryset.filter(tenant=tenant)
15
- return queryset
16
+ return self.add_annotations(queryset)
16
17
 
17
18
  def bulk_create(
18
19
  self,
@@ -25,10 +26,11 @@ class TenantManager(models.Manager):
25
26
  ):
26
27
  tenant = get_tenant()
27
28
  if tenant is None and not all(obj.tenant_id for obj in objs):
28
- raise ValueError('Tenant must be set for all objects when calling bulk_create')
29
- else:
30
- for obj in objs:
31
- obj.tenant_id = tenant.pk
29
+ raise ValueError(
30
+ 'Tenant must be set for all objects when calling bulk_create'
31
+ )
32
+ for obj in objs:
33
+ obj.tenant_id = tenant.pk
32
34
  return super().bulk_create(
33
35
  objs, batch_size=batch_size, ignore_conflicts=ignore_conflicts,
34
36
  update_conflicts=update_conflicts, update_fields=update_fields,
@@ -36,7 +38,7 @@ class TenantManager(models.Manager):
36
38
  )
37
39
 
38
40
 
39
- class TenantModel(models.Model):
41
+ class TenantModel(models.Model, AnnotationModelMixin):
40
42
 
41
43
  class Meta:
42
44
  abstract = True
accrete/querystring.py CHANGED
@@ -6,8 +6,14 @@ from django.db.models import Model, Q, QuerySet
6
6
  _logger = logging.getLogger(__name__)
7
7
 
8
8
 
9
- def filter_querystring(model: type[Model], query_string: str) -> QuerySet:
10
- return model.objects.filter(parse_querystring(model, query_string))
9
+ def filter_from_querystring(
10
+ model: type[Model], query_string: str, order: list[str] = None
11
+ ) -> QuerySet:
12
+
13
+ order = order or model._meta.ordering
14
+ return model.objects.filter(
15
+ parse_querystring(model, query_string)
16
+ ).order_by(*order).distinct()
11
17
 
12
18
 
13
19
  def parse_querystring(model: type[Model], query_string: str) -> Q:
@@ -23,7 +29,7 @@ def parse_querystring(model: type[Model], query_string: str) -> Q:
23
29
  invert = True
24
30
  term = term[1:]
25
31
 
26
- parts = term.split('_a_')
32
+ parts = term.split('__')
27
33
  if len(parts) == 1:
28
34
  expression = Q(**{term: value})
29
35
  return ~expression if invert else expression
@@ -31,10 +37,7 @@ def parse_querystring(model: type[Model], query_string: str) -> Q:
31
37
  rel_path = parts[0].rstrip('__')
32
38
  term = parts[1]
33
39
  rel_model = get_related_model(rel_path) if rel_path else model
34
- objects = rel_model.objects.annotate(**{
35
- annotation['name']: annotation['func']
36
- for annotation in rel_model.annotations
37
- }).filter(Q(**{term: value}))
40
+ objects = rel_model.objects.filter(Q(**{term: value}))
38
41
  expression = Q(**{
39
42
  f'{rel_path}{"__" if rel_path else ""}id__in': objects.values_list('id', flat=True)
40
43
  })
@@ -80,7 +83,7 @@ def parse_querystring(model: type[Model], query_string: str) -> Q:
80
83
  )
81
84
  return parsed_query
82
85
 
83
- query_data = json.loads(query_string)
86
+ query_data = json.loads(query_string.strip('?&='))
84
87
  if isinstance(query_data, dict):
85
88
  query_data = [query_data]
86
89
 
@@ -0,0 +1,14 @@
1
+ from django.db.models import Model
2
+
3
+
4
+ def get_related_model(model: type[Model], rel_path: str):
5
+ related_model = model
6
+ for part in rel_path.split('__'):
7
+ try:
8
+ related_model = related_model._meta.fields_map[part].related_model
9
+ except (AttributeError, KeyError):
10
+ try:
11
+ related_model = getattr(related_model, part).field.related_model
12
+ except AttributeError:
13
+ break
14
+ return related_model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: accrete
3
- Version: 0.0.35
3
+ Version: 0.0.37
4
4
  Summary: Django Shared Schema Multi Tenant
5
5
  Author-email: Benedikt Jilek <benedikt.jilek@pm.me>
6
6
  License: Copyright (c) 2023 Benedikt Jilek
@@ -1,12 +1,13 @@
1
1
  accrete/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  accrete/admin.py,sha256=MUYUmCFlGYPowiXTbwl4_Q6Cq0-neiL53WW4P76JCLs,1174
3
+ accrete/annotation.py,sha256=-nYqDQPOixkD9qdzVZDCIA4lETH2fAZcCSxNmaY2s80,1251
3
4
  accrete/apps.py,sha256=F7ynMLHJr_6bRujWtZVUzCliY2CGKiDvyUmL4F68L2E,146
4
5
  accrete/config.py,sha256=eJUbvyBO3DvAD6xkVKjTAzlXy7V7EK9bVyb91girfUs,299
5
6
  accrete/forms.py,sha256=nPDgSZao-vuVFRam2Py18yiet3Bar-A-qkWjwbeUDlg,11235
6
- accrete/middleware.py,sha256=ldnMI4Wv9p1JUyFX_sQSa6Hg4MgkXNbKaHqq5aRsD8s,2629
7
- accrete/models.py,sha256=OcxHnmFWity-Pfgj-jOhrxzZ44MdqmLxTH-xwrbtz7w,5974
7
+ accrete/middleware.py,sha256=RWeHHcYCfpVO4EnG5HMS52F1y5OKRzNCuidMeq6b0zY,3176
8
+ accrete/models.py,sha256=ruPKqNeKYzqSpYRhpQ12S82iJDYxy0EjSEtNSKWeRz4,6126
8
9
  accrete/queries.py,sha256=bchiqLzE1DmR9kvQ6Yyog6cNlYK_lxpztQMOx6Hy_0c,410
9
- accrete/querystring.py,sha256=wSb3sm0HlCsa9KN1EA1O-lrjra6DjnWUixGxNgI3e7U,3130
10
+ accrete/querystring.py,sha256=TEJ-PhNj1xfA5a2gL6QwD3r33CIcUpLjr5ju0RIgd_I,3140
10
11
  accrete/tenant.py,sha256=g3ZuTrQr2zqmIopNBRQeCmHEK2R3dlUme_hOV765J6U,1778
11
12
  accrete/tests.py,sha256=Agltbzwwh5htvq_Qi9vqvxutzmg_GwgPS_N19xJZRlw,7197
12
13
  accrete/views.py,sha256=9-sgCFe_CyG-wllAcIOLujyueiq66C-zg0U7Uf5Y2wU,2954
@@ -32,14 +33,12 @@ accrete/contrib/system_mail/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2
32
33
  accrete/contrib/system_mail/views.py,sha256=xc1IQHrsij7j33TUbo-_oewy3vs03pw_etpBWaMYJl0,63
33
34
  accrete/contrib/system_mail/migrations/0001_initial.py,sha256=6cwkkRXGjXvwXoMjjgmWmcPyXSTlUbhW1vMiHObk9MQ,1074
34
35
  accrete/contrib/system_mail/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- accrete/contrib/ui/__init__.py,sha256=OX-63-6FgiwOgPyrYuFKiuk9LR1fzbSP_VqaZ-bjja8,316
36
+ accrete/contrib/ui/__init__.py,sha256=D_QP3FW_4IzP2c_4WTWyuRIENDA1AFrSOXWL_FNeSMY,445
36
37
  accrete/contrib/ui/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
37
38
  accrete/contrib/ui/apps.py,sha256=E0ao2ox6PQ3ldfeR17FXJUUJuGiWjm2DPCxHbPXGzls,152
38
- accrete/contrib/ui/components.py,sha256=j6b-oK5vPxBtxV8KRo-0iI1QfwKMPOtQXvW-ZyzR5HY,2036
39
- accrete/contrib/ui/context.py,sha256=TUPjwhwAPahs29iTJMfDDNSZtZBWWdk4zIGGNReLHBg,9642
40
- accrete/contrib/ui/elements.py,sha256=as405cYGmyeCk_R8a8RNBjL8yNTQlUg6KLYQ9hWnqVA,75
41
- accrete/contrib/ui/filter.py,sha256=6EjHzTiqCwOjeiHNNmuvgj65VV-QCmZgCVKXAHxH5Bg,10650
42
- accrete/contrib/ui/querystring.py,sha256=vMP_4Hn6L3jCLf6_HIscZkPgkZGe9g9Y7GfI2zZYn2A,709
39
+ accrete/contrib/ui/context.py,sha256=lBX3Ga-54cRDnbmFT28afCypbtwg65qNtjKbiQz2jmI,6242
40
+ accrete/contrib/ui/elements.py,sha256=g6wksl7hHGJb3CKmkio5rWgcpr1Eyr2vxbKVD-59lkc,1570
41
+ accrete/contrib/ui/filter.py,sha256=39Kms9jS-ruuursyUbnazrM3DrBsMSEr81Lh6EelUqs,13047
43
42
  accrete/contrib/ui/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
44
43
  accrete/contrib/ui/urls.py,sha256=TUBlz_CGs9InTZoxM78GSnucA73I8knoh_obt12RUHM,186
45
44
  accrete/contrib/ui/views.py,sha256=WpBKMsxFFG8eG4IN7TW_TPE6i3OFF7gnLDTK7JMKti8,191
@@ -118,14 +117,15 @@ accrete/contrib/ui/static/bulma/sass/utilities/extends.sass,sha256=jb5Ipy_k6hBSF
118
117
  accrete/contrib/ui/static/bulma/sass/utilities/functions.sass,sha256=tRNFJ9yQTS-GZ6ouJ7EIWiFITL7irYR7srzKwzacvUA,4896
119
118
  accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.sass,sha256=JXNQHoF4YlBTUW6WSJ14_38SA9lbk6VkouzYURmlV8g,2691
120
119
  accrete/contrib/ui/static/bulma/sass/utilities/mixins.sass,sha256=oijSh2f1DPqod0W5op9iVEAFbC1huMqBIJmnHRx3exc,6291
121
- accrete/contrib/ui/static/css/accrete.css,sha256=seWy0cX31YB6IE26iqjLteZXxBpvQpo92EjLTnLRtCc,253314
120
+ accrete/contrib/ui/static/css/accrete.css,sha256=XflTQHcjaGSFHjtojR1t8MWSt2MRDEQPeWH5QuU1hIo,253378
122
121
  accrete/contrib/ui/static/css/accrete.css.bak,sha256=2RErGa8_tm8lFSamfW3UfqPgEVkPDejk2fh8IidW-B0,2359
123
- accrete/contrib/ui/static/css/accrete.css.map,sha256=ZiVnkZ0jHY-ehK-WSoboneSeKdYiY6x-Ge2aCLkEObs,55936
124
- accrete/contrib/ui/static/css/accrete.scss,sha256=dSW0qis_-2xPfbhx_6cghn5Fni0o-EX4pSdE-BLwnRg,5694
122
+ accrete/contrib/ui/static/css/accrete.css.map,sha256=eAGk5OIGglWaUl-B8ORQI24KwqZfYj_tkkTfEFr9MwU,55936
123
+ accrete/contrib/ui/static/css/accrete.scss,sha256=KCY4QZNNVtIvogyNYkSwxeFzCCHs1MD4-l-f5bL6QFM,5694
125
124
  accrete/contrib/ui/static/css/icons.css,sha256=IMdNurvI1cUwpGG8n3UXRkxl8MqP2Eqkvyh2maeyghw,6301
126
125
  accrete/contrib/ui/static/icons/Logo.svg,sha256=hGZuxrAa-LRpFavFiF8Lnc7X9OQcqmb6Xl_dxx-27hM,1861
127
126
  accrete/contrib/ui/static/icons/accrete.svg,sha256=CWHJKIgk3hxL7xIaFSz2j1cK-eF1TroCbjcF58bgOIs,1024
128
- accrete/contrib/ui/static/js/filter.js,sha256=gHNrUa51KWn472xXGhW7YO4zg27wJHTBsRdBphtFtgo,22061
127
+ accrete/contrib/ui/static/js/filter.js,sha256=wMK4rIx4J8ps3uB6J1z2wRAZ5MzkAN6D8EhFQiXybjM,22864
128
+ accrete/contrib/ui/static/js/htmx.min.js,sha256=s73PXHQYl6U2SLEgf_8EaaDWGQFCm6H26I-Y69hOZp4,47755
129
129
  accrete/contrib/ui/static/js/list.js,sha256=OX_81ifRmawE-1QBU5Qpq_E6sHiiNwIPleETAn9EOJw,4280
130
130
  accrete/contrib/ui/templates/django/forms/widgets/attrs.html,sha256=zNxjU4Ta_eWZkh1WhrF_VIwNZ0lZyl980gSSijUK51k,195
131
131
  accrete/contrib/ui/templates/django/forms/widgets/email.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48
@@ -136,31 +136,31 @@ accrete/contrib/ui/templates/django/forms/widgets/text.html,sha256=MSmLlQc7PsPoD
136
136
  accrete/contrib/ui/templates/django/forms/widgets/textarea.html,sha256=c9BTedqb3IkXLyVYd0p9pR8DFnsXCNGoxVBWZTk_Fic,278
137
137
  accrete/contrib/ui/templates/ui/detail.html,sha256=0sqsW_XbtLVIquXA8157ZWamnwRc4Wp-6_aZgZ5HN64,1051
138
138
  accrete/contrib/ui/templates/ui/form.html,sha256=kIIG4zzNbxb5fJAVr3lI05oa3yoxyAwstMeQWLa2Z0Y,409
139
- accrete/contrib/ui/templates/ui/layout.html,sha256=9-T8f3mZV6SJBQU6OVls-clKJAp6HgEZs0zguJfQyQw,8686
140
- accrete/contrib/ui/templates/ui/list.html,sha256=KmTPrXNS75FRSyb990VmMJ-r7B-nshMo8m3C9M1tHtg,1249
141
- accrete/contrib/ui/templates/ui/table.html,sha256=05KI_pVJx5ost4kRZmh7dbhEAsGymaGmzj8X40yOcME,3818
142
- accrete/contrib/ui/templates/ui/partials/filter.html,sha256=phofFaGKBt_A5Hv_8IVT58MRQYeiNZE-HjyBadZPXWs,2714
139
+ accrete/contrib/ui/templates/ui/layout.html,sha256=qxLTBR16uh3ed1qm_eC-GrFkN6RYZZx0k1bX07Dcfz4,9247
140
+ accrete/contrib/ui/templates/ui/list.html,sha256=dcPS1aSN9GlsljJsTNhKMqgPe8goF1uwknZ4DIbElLU,1212
141
+ accrete/contrib/ui/templates/ui/table.html,sha256=xVSFKrjO_2YkSEO3nspHqMj8mQU4ZV8GSAQFSsAk5SI,3868
142
+ accrete/contrib/ui/templates/ui/partials/filter.html,sha256=Y2-9fwV_sjrsu-Jvuh8hseku0mQTjsnePSHemps3Tqg,3936
143
143
  accrete/contrib/ui/templates/ui/partials/form_errors.html,sha256=1_TQvTdiejsn-43YSyp2YfnP52P-MFYb-HGY0DLm4oA,991
144
144
  accrete/contrib/ui/templates/ui/partials/form_modal.html,sha256=FFDfI5qjOCUBSGqDjBXa8tcqN2q94wOOCNFDaiyplHQ,1032
145
- accrete/contrib/ui/templates/ui/partials/header.html,sha256=X9oNlQuQUIm0-JS0u6Z3Fb1-PwDiTgqL58CBG3iEX6g,2927
145
+ accrete/contrib/ui/templates/ui/partials/header.html,sha256=Fr65wv84RgcjpRr1jRdGzChaX08SVhmFhcdEmkTBmyI,2859
146
146
  accrete/contrib/ui/templates/ui/partials/onchange_form.html,sha256=K5twTGqRUW1iM2dGtdWntjsJvJVo5EIzKxX2HK-H1yw,160
147
- accrete/contrib/ui/templates/ui/partials/pagination_detail.html,sha256=ee5d5wZHLgh8wARvKCVZ68KZf77w107GWNRi9qkpW80,938
148
- accrete/contrib/ui/templates/ui/partials/pagination_list.html,sha256=zdIHmK5W58IpYlW7QmMVpsh1ufUIxpPrDimq9uqaYFQ,1299
147
+ accrete/contrib/ui/templates/ui/partials/pagination_detail.html,sha256=58nA3X7Il0FAD4VcYyr7tTGWRiVf_FN1TkImmKEpKHU,1014
148
+ accrete/contrib/ui/templates/ui/partials/pagination_list.html,sha256=9h-I5PfjViomZjoeBUZIwmx7tgWodCs4bDtjOdsPQ8c,1379
149
149
  accrete/contrib/ui/templates/ui/partials/table_field.html,sha256=4oQw0na9UgHP8lo8KttyWBuDhCEeaZ8Vt9jJYJdZ5Gs,641
150
150
  accrete/contrib/ui/templates/ui/partials/table_field_float.html,sha256=GH_jFdpk8wEJXv4QfO6c3URYrZGGLeuSyWwWl2cWxwQ,45
151
151
  accrete/contrib/ui/templates/ui/partials/table_field_monetary.html,sha256=Wtod9vel2dD4vG8lUVLbtls4aU_2EG3p0E1QRRUSH10,166
152
152
  accrete/contrib/ui/templates/ui/partials/table_field_string.html,sha256=GH_jFdpk8wEJXv4QfO6c3URYrZGGLeuSyWwWl2cWxwQ,45
153
153
  accrete/contrib/ui/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
154
- accrete/contrib/ui/templatetags/accrete_ui.py,sha256=8Ryvwg8_DWYzCehZJTeiq66AvWLbcRTrAe5oxTk8Vzc,1320
154
+ accrete/contrib/ui/templatetags/accrete_ui.py,sha256=ttu2dN5xVMOaON1gyd6Dk22TGt3RQQr97YECrwVgDmw,1593
155
155
  accrete/contrib/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
156
  accrete/contrib/user/admin.py,sha256=YS4iApli7XUaIl9GsEJxys2j8sepX0by88omYHjff-E,85
157
157
  accrete/contrib/user/apps.py,sha256=oHDrAiHf-G57mZLyxqGJzRY2DbPprGFD-QgyVJG_ruI,156
158
- accrete/contrib/user/forms.py,sha256=kkPXxeaJWheNogd1-nBFN4kUxbh9v4b6ofF8-lgt6Vo,3298
158
+ accrete/contrib/user/forms.py,sha256=LVnRx9lcDZbSFn46zRndo1FR9qPvis5ivlXgTMp_IcM,3119
159
159
  accrete/contrib/user/middleware.py,sha256=qblcujwJsthopagyT-hPFq4HsMyGt-VvqZw5TQopBjk,403
160
160
  accrete/contrib/user/models.py,sha256=SFEXG9G-XY7Nuss7DT51abDv8BWLHKYJocOhQDI_1Lw,3926
161
161
  accrete/contrib/user/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
162
162
  accrete/contrib/user/urls.py,sha256=ktQJ3vZxDlKNUfzOxReeDLOduSdoW5z5Sz0LVFpxZGU,460
163
- accrete/contrib/user/views.py,sha256=uQ_gd97HubpA2vFMeAM9IKK0N0U9os03g3FAKU6pN_E,3523
163
+ accrete/contrib/user/views.py,sha256=82-RPqabxE6o7cglnfZmCUxNNRii4pmVFGiTThE4Zp0,3727
164
164
  accrete/contrib/user/locale/de/LC_MESSAGES/django.mo,sha256=p3rgUg6WltAVIMkQsjvjBqTsd_usLhSr1GH4Cyltc2c,433
165
165
  accrete/contrib/user/locale/de/LC_MESSAGES/django.po,sha256=f_Nxpo3HTm2L3f3zoHLfeWsZ-4IQp_EEVSku6TCZSvw,1870
166
166
  accrete/contrib/user/migrations/0001_initial.py,sha256=JWfM9PcMDfkJUdCjLWuWieGs6643qP0KdbCyr5uAZoY,2950
@@ -168,7 +168,7 @@ accrete/contrib/user/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
168
168
  accrete/contrib/user/templates/user/accrete_navbar_end_dropdown.html,sha256=vDFjrOZ95doOpdGYSCRlP8H-FfKUWpkYgYP8mOKcboY,481
169
169
  accrete/contrib/user/templates/user/change_email.html,sha256=i9ZgDH31awO9W87E6c3_MKgQNf1ff7EYQX1nEdPqRC8,995
170
170
  accrete/contrib/user/templates/user/change_password.html,sha256=wcwm5_tfPgs9V7VWdDQqhhyMpcIkiGsx2OIptF5fWMk,1498
171
- accrete/contrib/user/templates/user/login.html,sha256=qKUqF5WCkWPrbwQMaa05Ou7zkM_CeVrJzHsjsDWNPhQ,1644
171
+ accrete/contrib/user/templates/user/login.html,sha256=TuEH4uKK1CFV5X7nP3xPSzu2VJaoEn6APsHh-k6GiCc,1388
172
172
  accrete/contrib/user/templates/user/user_detail.html,sha256=SMkghUKVncVq3KFGBxADJf_wDnLYwmK4oDTKLO0Ejlo,1445
173
173
  accrete/contrib/user/templates/user/user_form.html,sha256=4o24KV940D_KsBP4YwBguwTFW9nYz1uz-RILs91M7Po,1935
174
174
  accrete/contrib/user_registration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -189,7 +189,8 @@ accrete/migrations/0002_initial.py,sha256=dFOM7kdHlx7pVAh8cTDlZMtciN4O9Z547HAzEK
189
189
  accrete/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
190
190
  accrete/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
191
191
  accrete/utils/dates.py,sha256=apM6kt6JhGrKgoT0jfav1W-8AUVTxNc9xt3fJQ2n0JI,1492
192
- accrete-0.0.35.dist-info/METADATA,sha256=eGPkkjrItDQrRTT61E0IftMPtR1TdTKY2b35D5qtogM,4892
193
- accrete-0.0.35.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
194
- accrete-0.0.35.dist-info/licenses/LICENSE,sha256=_7laeMIHnsd3Y2vJEXDYXq_PEXxIcjgJsGt8UIKTRWc,1057
195
- accrete-0.0.35.dist-info/RECORD,,
192
+ accrete/utils/models.py,sha256=qAdWovkztpvFZHY7VzK4hGIMUjBcA9iQtqYFgz7z2ro,474
193
+ accrete-0.0.37.dist-info/METADATA,sha256=DKhGve3m6OHxG8gFRrsGmdShLC6Ttph3HlvB6_5ACQU,4892
194
+ accrete-0.0.37.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
195
+ accrete-0.0.37.dist-info/licenses/LICENSE,sha256=_7laeMIHnsd3Y2vJEXDYXq_PEXxIcjgJsGt8UIKTRWc,1057
196
+ accrete-0.0.37.dist-info/RECORD,,
@@ -1,96 +0,0 @@
1
- from dataclasses import dataclass, field
2
- from enum import Enum
3
- from django.db.models import Model, QuerySet, Q
4
-
5
- DEFAULT_PAGINATE_BY = 40
6
-
7
-
8
- class ActionMethod(Enum):
9
-
10
- HREF = 'href'
11
- GET = 'hx-get'
12
- POST = 'hx-post'
13
- PUT = 'hx-put'
14
- DELETE = 'hx-delete'
15
-
16
-
17
- class TableFieldAlignment(Enum):
18
-
19
- LEFT = 'left'
20
- CENTER = 'center'
21
- RIGHT = 'right'
22
-
23
-
24
- class TableFieldType(Enum):
25
-
26
- NONE = ''
27
- STRING = '_string'
28
- MONETARY = '_monetary'
29
- FLOAT = '_float'
30
-
31
-
32
- @dataclass
33
- class TableField:
34
-
35
- label: str
36
- name: str
37
- alignment: TableFieldAlignment | Enum = TableFieldAlignment.LEFT
38
- header_alignment: TableFieldAlignment | Enum = None
39
- header_info: str = None
40
- field_type: TableFieldType | Enum = TableFieldType.NONE
41
- prefix: str = ''
42
- suffix: str = ''
43
- truncate_after: int = 0
44
- template: str = None
45
-
46
-
47
- @dataclass
48
- class BreadCrumb:
49
-
50
- name: str
51
- url: str
52
- add_url_params: bool = True
53
-
54
-
55
- class Icon(Enum):
56
-
57
- ADD = 'icon-add'
58
- EDIT = 'icon-edit'
59
- LIST = 'icon-list'
60
- OPEN_RELATED = 'icon-open-related'
61
- ENVELOPE = 'icon-envelope'
62
- CLEAR = 'icon-clear'
63
- BACKSPACE = 'icon-backspace'
64
- FILTER = 'icon-filter'
65
- DELETE_FILTER = 'icon-delete-filter'
66
- SELECT = 'icon-select'
67
-
68
-
69
-
70
- @dataclass
71
- class ClientAction:
72
-
73
- name: str
74
- url: str = ''
75
- method: ActionMethod = ActionMethod.HREF
76
- query_params: str = ''
77
- attrs: list[tuple[str, str]] = field(default_factory=list)
78
- submit: bool = False
79
- form_id: str = 'form'
80
- class_list: list[str] = field(default_factory=list)
81
- add_url_params: bool = False
82
- icon: Icon | type[Enum] = None
83
-
84
- def attrs_str(self):
85
- return ' '.join([f'{str(attr[0])}={str(attr[1])}' for attr in self.attrs])
86
-
87
-
88
- @dataclass
89
- class List:
90
-
91
- queryset: QuerySet
92
- title: str = None
93
- paginate_by: int = DEFAULT_PAGINATE_BY
94
- breadcrumbs: list[BreadCrumb] = field(default_factory=list)
95
- actions: list[ClientAction] = field(default_factory=list)
96
- fields: list[TableField] = field(default_factory=list)
@@ -1,19 +0,0 @@
1
- import json
2
-
3
-
4
- def load_querystring(get_params: dict) -> list:
5
- return json.loads(get_params.get('q', '[]'))
6
-
7
-
8
- def build_querystring(get_params: dict, extra_params: list[str] = None) -> str:
9
- querystring = f'?q={get_params.get("q", "[]")}'
10
- if paginate_by := get_params.get('paginate_by', False):
11
- querystring += f'&paginate_by={paginate_by}'
12
- if order_by := get_params.get('order_by', False):
13
- querystring += f'&order_by={order_by}'
14
- if crumbs := get_params.get('crumbs', False):
15
- querystring += f'&crumbs={crumbs}'
16
- for param in extra_params or []:
17
- if value := get_params.get(param, False):
18
- querystring += f'&{param}={value}'
19
- return querystring