accrete 0.0.27__py3-none-any.whl → 0.0.28__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.
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.9 on 2024-01-27 07:50
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('sequence', '0001_initial'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='sequence',
15
+ name='name',
16
+ field=models.CharField(max_length=255, verbose_name='Name'),
17
+ ),
18
+ ]
@@ -21,7 +21,7 @@ class Sequence(TenantModel):
21
21
 
22
22
  name = models.CharField(
23
23
  verbose_name=_('Name'),
24
- max_length=50,
24
+ max_length=255,
25
25
  )
26
26
 
27
27
  nextval = models.PositiveBigIntegerField(
@@ -9,11 +9,11 @@ def get_nextval(name, create_if_none=True):
9
9
  tenant = get_tenant()
10
10
  with transaction.atomic():
11
11
  seq = Sequence.objects.filter(
12
- tenant=tenant.id, name=name
12
+ tenant=tenant, name=name
13
13
  ).select_for_update().first()
14
14
 
15
15
  if seq is None and not create_if_none:
16
- raise AttributeError(f'No sequence for {name} found!')
16
+ raise ValueError(f'Sequence "{name}" does not exist.')
17
17
  elif seq is None:
18
18
  seq = Sequence(name=name, tenant=tenant)
19
19
  seq.save()
@@ -1,15 +1,22 @@
1
1
  import logging
2
- from celery import shared_task
3
-
2
+ from celery import shared_task, current_app
3
+ from celery.schedules import crontab
4
4
  from django.core import mail
5
5
  from django.db import transaction
6
6
  from django.utils.html import strip_tags
7
-
8
7
  from .models import SystemMail
9
8
 
10
9
  _logger = logging.getLogger(__name__)
11
10
 
12
11
 
12
+ @current_app.on_after_finalize.connect
13
+ def setup_periodic_tasks(sender, **kwargs):
14
+ sender.add_periodic_task(
15
+ crontab(hour='*', minute='*/5'),
16
+ run_mail_queue.s()
17
+ )
18
+
19
+
13
20
  @shared_task()
14
21
  def run_mail_queue():
15
22
  with transaction.atomic():
@@ -6,9 +6,10 @@ from .context import (
6
6
  )
7
7
  from .components import (
8
8
  ClientAction,
9
+ ActionMethod,
9
10
  BreadCrumb,
10
11
  TableField,
11
12
  TableFieldAlignment,
12
13
  TableFieldType
13
14
  )
14
- from .querystring import load_querystring, build_url_params
15
+ from .querystring import load_querystring, build_querystring
@@ -1,5 +1,17 @@
1
1
  from dataclasses import dataclass, field
2
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'
3
15
 
4
16
 
5
17
  class TableFieldAlignment(Enum):
@@ -37,6 +49,7 @@ class BreadCrumb:
37
49
 
38
50
  name: str
39
51
  url: str
52
+ add_url_params: bool = True
40
53
 
41
54
 
42
55
  @dataclass
@@ -44,8 +57,24 @@ class ClientAction:
44
57
 
45
58
  name: str
46
59
  url: str = ''
60
+ method: ActionMethod = ActionMethod.HREF
47
61
  query_params: str = ''
48
62
  attrs: list[tuple[str, str]] = field(default_factory=list)
49
63
  submit: bool = False
50
64
  form_id: str = 'form'
51
65
  class_list: list[str] = field(default_factory=list)
66
+ add_url_params: bool = True
67
+
68
+ def attrs_str(self):
69
+ return ' '.join(['='.join(attr) for attr in self.attrs])
70
+
71
+
72
+ @dataclass
73
+ class List:
74
+
75
+ queryset: QuerySet
76
+ title: str = None
77
+ paginate_by: int = DEFAULT_PAGINATE_BY
78
+ breadcrumbs: list[BreadCrumb] = field(default_factory=list)
79
+ actions: list[ClientAction] = field(default_factory=list)
80
+ fields: list[TableField] = field(default_factory=list)
@@ -7,7 +7,7 @@ from django.core.paginator import Paginator
7
7
  from django.forms import Form, ModelForm
8
8
  from accrete.querystring import parse_querystring
9
9
 
10
- from .querystring import load_querystring, build_url_params
10
+ from .querystring import load_querystring, build_querystring
11
11
  from .components import ClientAction, BreadCrumb, TableField
12
12
  from .filter import Filter
13
13
 
@@ -43,16 +43,16 @@ class ListContext:
43
43
 
44
44
  order = self.order_by or self.model._meta.ordering
45
45
 
46
- pks = self.model.objects.filter(
47
- parse_querystring(self.model, self.get_params.get('q', '[]'))
48
- ).distinct().values_list('pk', flat=True)
46
+ # pks = self.model.objects.filter(
47
+ # parse_querystring(self.model, self.get_params.get('q', '[]'))
48
+ # ).distinct().values_list('pk', flat=True)
49
49
 
50
50
  queryset = self.model.objects.select_related(
51
51
  *self.select_related
52
52
  ).prefetch_related(
53
53
  *self.prefetch_related
54
54
  ).filter(
55
- Q(pk__in=pks)
55
+ parse_querystring(self.model, self.get_params.get('q', '[]'))
56
56
  ).annotate(
57
57
  **self.get_annotations()
58
58
  ).order_by(
@@ -110,7 +110,7 @@ class ListContext:
110
110
  'default_filter_term': self.default_filter_term,
111
111
  'breadcrumbs': self.breadcrumbs,
112
112
  'querystring': load_querystring(self.get_params),
113
- 'url_params': build_url_params(self.get_params),
113
+ 'url_params': build_querystring(self.get_params),
114
114
  'actions': self.actions,
115
115
  'fields': self.fields
116
116
  }
@@ -209,7 +209,7 @@ class DetailContext:
209
209
  'paginate_by': paginate_by,
210
210
  'detail_pagination': False,
211
211
  'breadcrumbs': self.breadcrumbs,
212
- 'url_params': build_url_params(self.get_params, ['page']),
212
+ 'url_params': build_querystring(self.get_params, ['page']),
213
213
  'actions': self.actions
214
214
  }
215
215
  if self.paginate_by > 0:
@@ -230,6 +230,7 @@ class FormContext:
230
230
  add_default_actions: bool = True
231
231
  discard_url: str = None
232
232
  actions: list[ClientAction] = field(default_factory=list)
233
+ breadcrumbs: list[BreadCrumb] = field(default_factory=list)
233
234
 
234
235
  def get_default_form_actions(self):
235
236
  actions = [
@@ -268,17 +269,18 @@ class FormContext:
268
269
  return self.title
269
270
  try:
270
271
  int(self.model.pk)
271
- return f'Edit {self.model}'
272
+ return _('Edit')
272
273
  except TypeError:
273
- return f'Add {self.model._meta.verbose_name}'
274
+ return _('Add')
274
275
 
275
276
  def dict(self):
276
277
  ctx = {
277
278
  'title': self.get_title(),
278
279
  'form': self.form,
279
280
  'form_id': self.form_id,
280
- 'url_params': build_url_params(self.get_params, ['page']),
281
- 'actions': []
281
+ 'url_params': build_querystring(self.get_params, ['page']),
282
+ 'actions': [],
283
+ 'breadcrumbs': self.breadcrumbs,
282
284
  }
283
285
  if self.add_default_actions:
284
286
  ctx.update({'actions': self.get_default_form_actions()})
File without changes
@@ -115,6 +115,28 @@ class Filter:
115
115
  ]
116
116
  }
117
117
 
118
+ def get_choice_int_query_term(self, label, param, choices):
119
+ choices = [(choice[0], str(choice[1])) for choice in choices]
120
+ return {
121
+ 'label': str(label),
122
+ 'param': param,
123
+ 'params': [
124
+ {
125
+ 'label': str(self.LABEL_EXACT),
126
+ 'data_type': 'selection',
127
+ 'choices': choices,
128
+ 'param': 'exact'
129
+ },
130
+ {
131
+ 'label': str(self.LABEL_EXACT_NOT),
132
+ 'data_type': 'selection',
133
+ 'choices': choices,
134
+ 'invert': True,
135
+ 'param': 'exact'
136
+ }
137
+ ]
138
+ }
139
+
118
140
  def get_int_query_term(self, label, param):
119
141
  return {
120
142
  'label': str(label),
@@ -258,6 +280,8 @@ class Filter:
258
280
  label, param, self.cast_decimal_places_to_step(field.decimal_places or 0)
259
281
  )
260
282
  elif internal_type in self.query_int_fields:
283
+ if field.choices:
284
+ return self.get_choice_int_query_term(label, param, field.choices)
261
285
  return self.get_int_query_term(label, param)
262
286
  elif internal_type in self.query_boolean_fields:
263
287
  return self.get_boolean_query_term(label, param)
@@ -5,7 +5,7 @@ def load_querystring(get_params: dict) -> list:
5
5
  return json.loads(get_params.get('q', '[]'))
6
6
 
7
7
 
8
- def build_url_params(get_params: dict, extra_params: list[str] = None) -> str:
8
+ def build_querystring(get_params: dict, extra_params: list[str] = None) -> str:
9
9
  querystring = f'?q={get_params.get("q", "[]")}'
10
10
  if paginate_by := get_params.get('paginate_by', False):
11
11
  querystring += f'&paginate_by={paginate_by}'
@@ -229,6 +229,9 @@ function appendAfterTag(queryTagContainer) {
229
229
  function resetInput() {
230
230
  const input = document.getElementById('query-input');
231
231
  const queryParam = getParamElement(input.getAttribute('data-default-term'))
232
+ if (!queryParam) {
233
+ return
234
+ }
232
235
  const queryLabel = getCompleteQueryText(queryParam);
233
236
  const queryNotificationLabel = document.getElementById('query-label');
234
237
  queryNotificationLabel.innerText = queryLabel;
@@ -93,12 +93,14 @@
93
93
  {% if action.submit %}
94
94
  <input class="button is-fullwidth {{ action.class_list|join:' ' }}"
95
95
  type="submit" form={{ action.form_id }} value="{{ action.name }}"
96
- {% for attr in action.attrs %}{{ attr }}{% endfor %}
96
+ {{ action.attrs_str }}
97
97
  >
98
98
  {% else %}
99
99
  <a class="button is-fullwidth {{ action.class_list|join:' ' }}"
100
- href="{{ action.url }}{{ url_params|default_if_none:'?' }}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
101
- {% for attr, val in action.attrs %}{{ attr }}="{{ val }}"{% endfor %}
100
+ {% if action.url %}
101
+ {{ action.method.value }}="{{ action.url }}{% if action.add_url_params %}{{ url_params|default_if_none:'?' }}{% endif %}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
102
+ {% endif %}
103
+ {{ action.attrs_str }}
102
104
  >{{ action.name }}
103
105
  </a>
104
106
  {% endif %}
@@ -7,7 +7,7 @@
7
7
  <nav id="breadcrumbs" class="breadcrumb" aria-label="breadcrumbs" style="white-space: unset; word-break: break-word">
8
8
  <ul>
9
9
  {% for crumb in breadcrumbs %}
10
- <li><a class="is-underlined" hx-get="{{ crumb.url|default_if_none:'#' }}{{ url_params }}"
10
+ <li><a class="is-underlined" hx-get="{{ crumb.url|default_if_none:'#' }}{% if crumb.add_url_params %}{{ url_params }}{% endif %}"
11
11
  hx-target="body" hx-push-url="true" aria-current="page"
12
12
  >{{ crumb.name }}</a></li>
13
13
  {% endfor %}
accrete/forms.py CHANGED
@@ -292,7 +292,7 @@ class TenantModelForm(One2ManyModelForm):
292
292
  return self.instance
293
293
 
294
294
 
295
- def save_form(form, reraise=False):
295
+ def save_form(form, reraise=False, log=True):
296
296
  form.is_saved = False
297
297
  try:
298
298
  if form.is_valid():
@@ -300,10 +300,11 @@ def save_form(form, reraise=False):
300
300
  form.save()
301
301
  form.is_saved = True
302
302
  except Exception as e:
303
- error_id = str(uuid4())[:8]
304
- _logger.exception(f'{error_id}: {e}')
305
- form.save_error = e
306
- form.save_error_id = error_id
303
+ form.save_error = repr(e)
304
+ if log:
305
+ error_id = str(uuid4())[:8]
306
+ _logger.exception(f'{error_id}: {e}')
307
+ form.save_error_id = error_id
307
308
  if reraise:
308
309
  raise e
309
310
  return form
accrete/utils/dates.py CHANGED
@@ -9,7 +9,6 @@ def current_year_start() -> datetime:
9
9
  first_day = now.replace(
10
10
  month=1, day=1, hour=0, minute=0, second=0, microsecond=0
11
11
  )
12
- # calendar.monthrange(date.year, date.month)[1]
13
12
  return first_day
14
13
 
15
14
 
@@ -23,7 +22,6 @@ def current_year_end() -> datetime:
23
22
 
24
23
  def current_month_start() -> datetime:
25
24
  now = timezone.now()
26
- monthrange = calendar.monthrange(now.year, now.month)
27
25
  first_day = now.replace(
28
26
  day=1, hour=0, minute=0, second=0, microsecond=0
29
27
  )
@@ -37,3 +35,13 @@ def current_month_end() -> datetime:
37
35
  day=monthrange[1], hour=23, minute=59, second=59, microsecond=999999
38
36
  )
39
37
  return last_day
38
+
39
+
40
+ def next_relative_month_start(start_date: date) -> date:
41
+ monthrange = calendar.monthrange(start_date.year, start_date.month)
42
+ return start_date.replace(day=monthrange[1]) + timedelta(days=1)
43
+
44
+
45
+ def next_relative_year_start(start_date: date) -> date:
46
+ current = current_year_end().date().replace(year=start_date.year)
47
+ return current + relativedelta(days=1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: accrete
3
- Version: 0.0.27
3
+ Version: 0.0.28
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
@@ -35,6 +35,7 @@ Requires-Dist: django>=4.2
35
35
  Provides-Extra: contrib
36
36
  Requires-Dist: celery>=5.3.4; extra == 'contrib'
37
37
  Requires-Dist: django-celery-beat; extra == 'contrib'
38
+ Requires-Dist: sqlalchemy; extra == 'contrib'
38
39
  Provides-Extra: dev
39
40
  Requires-Dist: build; extra == 'dev'
40
41
  Requires-Dist: pytest>=7.0; extra == 'dev'
@@ -3,7 +3,7 @@ accrete/admin.py,sha256=MUYUmCFlGYPowiXTbwl4_Q6Cq0-neiL53WW4P76JCLs,1174
3
3
  accrete/apps.py,sha256=F7ynMLHJr_6bRujWtZVUzCliY2CGKiDvyUmL4F68L2E,146
4
4
  accrete/config.py,sha256=eJUbvyBO3DvAD6xkVKjTAzlXy7V7EK9bVyb91girfUs,299
5
5
  accrete/decorators.py,sha256=vM8GuDHLzwEpHmTGafG-xwMddKvuYFIhe-FrUJqL1_4,678
6
- accrete/forms.py,sha256=dvRrDRNMTTls4dNL0UbU3EjE5x7omkqTnyvAXtWIbq0,11172
6
+ accrete/forms.py,sha256=PsM13oQ84U3eJ0b6d5fhCNZCKGyKobMao_2M_1Q4Ucs,11216
7
7
  accrete/middleware.py,sha256=ldnMI4Wv9p1JUyFX_sQSa6Hg4MgkXNbKaHqq5aRsD8s,2629
8
8
  accrete/models.py,sha256=OcxHnmFWity-Pfgj-jOhrxzZ44MdqmLxTH-xwrbtz7w,5974
9
9
  accrete/queries.py,sha256=bchiqLzE1DmR9kvQ6Yyog6cNlYK_lxpztQMOx6Hy_0c,410
@@ -16,29 +16,31 @@ accrete/contrib/sequence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
16
16
  accrete/contrib/sequence/admin.py,sha256=mTjab5cVklRUIQcSrsUo-_JgtXEsSdcFj_gfWhlStS4,273
17
17
  accrete/contrib/sequence/apps.py,sha256=2SalOz9piCrbOPudCh0grN1eojN9kEC4-jcNzBmfqEk,164
18
18
  accrete/contrib/sequence/forms.py,sha256=cdjLIytH7WlJK-B2Y6xRRPjOijkK36XpqUuIe4yLLj0,248
19
- accrete/contrib/sequence/models.py,sha256=zXK0aCdsDiXQU_51UZBElL6DUvnMdKEAITxPX57NNEI,886
20
- accrete/contrib/sequence/queries.py,sha256=ikDK70V07tVS5M81bzMtWCyvpdhmg51Ioh-SgweCtgA,686
19
+ accrete/contrib/sequence/models.py,sha256=UEuPvg1StovPW1n9yF-f31nBq4aTj5zpfS10oqcf60E,887
20
+ accrete/contrib/sequence/queries.py,sha256=VT4pbYKzVAEE_8oPOaavdcCPi-zPiX1BwdVviWh8XKM,683
21
21
  accrete/contrib/sequence/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
22
22
  accrete/contrib/sequence/views.py,sha256=xc1IQHrsij7j33TUbo-_oewy3vs03pw_etpBWaMYJl0,63
23
23
  accrete/contrib/sequence/migrations/0001_initial.py,sha256=iAR_hhGN2wDAk40IS9PwEsm7iYqfgasoKRrTLFEpOY8,1352
24
+ accrete/contrib/sequence/migrations/0002_alter_sequence_name.py,sha256=70tpxJ0_edWpzj69PxV73m3CwI7Hy5x1YK8m6_2niP4,398
24
25
  accrete/contrib/sequence/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
26
  accrete/contrib/system_mail/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
27
  accrete/contrib/system_mail/admin.py,sha256=9hXwwfZn446juyRoBOygLWm12X6N9waRC-1LHBLrgZk,227
27
28
  accrete/contrib/system_mail/apps.py,sha256=yIWgsa5GV8NPOJBtDZQJljSZ5v_mOP6VJrTfo_HkLF8,169
28
29
  accrete/contrib/system_mail/forms.py,sha256=baRoj0YQvc-b83y69dbxVgCN5BWoQsco_YRNqqbubY8,723
29
30
  accrete/contrib/system_mail/models.py,sha256=mTx8YAtyyrUE2B8iKQDyJVsUqN1EGHg8XKgdoWibZ9g,880
30
- accrete/contrib/system_mail/tasks.py,sha256=xfAX49wIxYuWDZBuPrWyQt3GPpukK0xMjHtUsgEsMkI,1089
31
+ accrete/contrib/system_mail/tasks.py,sha256=W3q40AxcjGFt7wtuvblQ_WFYIaipYF-WWkDYGvbkYhQ,1326
31
32
  accrete/contrib/system_mail/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
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=uoJqqJzMOpjp3d3Wi3oiobBp2Cw9w57rBiQNBq8stp0,287
36
+ accrete/contrib/ui/__init__.py,sha256=VuqzzKYHSxxEeps7dOcQ4u0vOdgrpMnkwPXq8n6xEgY,306
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=87GGgjPNVLSSkFaS52JBGcmKzAfPWV5jFJozNqlz4rk,966
39
- accrete/contrib/ui/context.py,sha256=m7A8TmkrwDl1_ubuB3nYbzwE_aRRuF5136mt8EDxqzA,9425
40
- accrete/contrib/ui/filter.py,sha256=YKLXQaaXfpkUIcqBhzL1_LGOBuwGkOmJUKBU1_gX73Y,11749
41
- accrete/contrib/ui/querystring.py,sha256=InQoEnNu3H9-2wnqLUpn7seQmgJ91_-xKOtVW6WZc_o,708
39
+ accrete/contrib/ui/components.py,sha256=9GrutNvfPT5QS2x8Ae3lIGWx7sSRx12uh52BqwysL1w,1670
40
+ accrete/contrib/ui/context.py,sha256=CI0GGIFvYPAu7v_8wKvrvFyWFae2t3sfiKtokus6rlk,9552
41
+ accrete/contrib/ui/elements.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ accrete/contrib/ui/filter.py,sha256=OmrNWElKuVRjwSbD7GEus1erZmHwcu-I8NzCMbQovu8,12586
43
+ accrete/contrib/ui/querystring.py,sha256=vMP_4Hn6L3jCLf6_HIscZkPgkZGe9g9Y7GfI2zZYn2A,709
42
44
  accrete/contrib/ui/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
43
45
  accrete/contrib/ui/urls.py,sha256=TUBlz_CGs9InTZoxM78GSnucA73I8knoh_obt12RUHM,186
44
46
  accrete/contrib/ui/views.py,sha256=WpBKMsxFFG8eG4IN7TW_TPE6i3OFF7gnLDTK7JMKti8,191
@@ -124,7 +126,7 @@ accrete/contrib/ui/static/css/accrete.scss,sha256=-ASM1TVXcT48Z0-VVHmESBn9wbIaKM
124
126
  accrete/contrib/ui/static/css/icons.css,sha256=ZaS9ChqKlwvEgrHbRXhplE3JohWEYmJefVnfggx9-gY,4739
125
127
  accrete/contrib/ui/static/icons/Logo.svg,sha256=hGZuxrAa-LRpFavFiF8Lnc7X9OQcqmb6Xl_dxx-27hM,1861
126
128
  accrete/contrib/ui/static/icons/accrete.svg,sha256=CWHJKIgk3hxL7xIaFSz2j1cK-eF1TroCbjcF58bgOIs,1024
127
- accrete/contrib/ui/static/js/filter.js,sha256=t1bvDGEaKeS1TOAZbjWIsk9TlmgTf_MgDsNguqExDkU,24089
129
+ accrete/contrib/ui/static/js/filter.js,sha256=7HUBHon2UB5SP0G1NH6fHqxdWw5CuQE8VvJvzDRNYCU,24133
128
130
  accrete/contrib/ui/static/js/list.js,sha256=OX_81ifRmawE-1QBU5Qpq_E6sHiiNwIPleETAn9EOJw,4280
129
131
  accrete/contrib/ui/static/js/navbar.js,sha256=9QGZfPgGWjCBZhZhrRf983hoPnRlwQP-Pl73c2vISYs,628
130
132
  accrete/contrib/ui/static/js/utils.js,sha256=6RKh3EJ57gx5UIjBcSaeZEs7lZdLvyYT9VAQ-WqlKSk,173
@@ -137,13 +139,13 @@ accrete/contrib/ui/templates/django/forms/widgets/text.html,sha256=MSmLlQc7PsPoD
137
139
  accrete/contrib/ui/templates/django/forms/widgets/textarea.html,sha256=c9BTedqb3IkXLyVYd0p9pR8DFnsXCNGoxVBWZTk_Fic,278
138
140
  accrete/contrib/ui/templates/ui/detail.html,sha256=Ix3WpJ3FsYuvUfi0ivGcA6Bd8IpGO1wpG4eXJFofK8k,1047
139
141
  accrete/contrib/ui/templates/ui/form.html,sha256=HpcZCDU_ur_Zf5gpnH_F8OV6qfOYPl6Ecqg6w5RIayQ,405
140
- accrete/contrib/ui/templates/ui/layout.html,sha256=s2OuO4P43RPb27pxo9qW4Fyv1OjI5Gue9olKxCICF7k,8128
142
+ accrete/contrib/ui/templates/ui/layout.html,sha256=U_ZLxWn-Wg1Ta2Tjev7oMlNSpvfblmLhJbtc83hDWVc,8222
141
143
  accrete/contrib/ui/templates/ui/list.html,sha256=d35PALSg9Zr9ErMD4z9XvyXRJPkZEhB5IoOT6LXh73s,661
142
144
  accrete/contrib/ui/templates/ui/table.html,sha256=68EG0v78UXWx1YQtJWlqDmXx-7db6DU5kru2FM2kvkA,2762
143
145
  accrete/contrib/ui/templates/ui/partials/filter.html,sha256=phuMVykVDGo3urTGGHeA1aLALoSdsZHYEJYLi3rkfo0,2686
144
146
  accrete/contrib/ui/templates/ui/partials/form_errors.html,sha256=1_TQvTdiejsn-43YSyp2YfnP52P-MFYb-HGY0DLm4oA,991
145
147
  accrete/contrib/ui/templates/ui/partials/form_modal.html,sha256=FFDfI5qjOCUBSGqDjBXa8tcqN2q94wOOCNFDaiyplHQ,1032
146
- accrete/contrib/ui/templates/ui/partials/header.html,sha256=QcUX4PUwomEbcmsLFRMbkzoTlE272p7bF3o_ORP4NV4,2795
148
+ accrete/contrib/ui/templates/ui/partials/header.html,sha256=KVsYaXn5C8UQykMv4H5zFONretq8tjxLdwcgrwAYdvk,2835
147
149
  accrete/contrib/ui/templates/ui/partials/onchange_form.html,sha256=K5twTGqRUW1iM2dGtdWntjsJvJVo5EIzKxX2HK-H1yw,160
148
150
  accrete/contrib/ui/templates/ui/partials/pagination_detail.html,sha256=ee5d5wZHLgh8wARvKCVZ68KZf77w107GWNRi9qkpW80,938
149
151
  accrete/contrib/ui/templates/ui/partials/pagination_list.html,sha256=zdIHmK5W58IpYlW7QmMVpsh1ufUIxpPrDimq9uqaYFQ,1299
@@ -189,8 +191,8 @@ accrete/migrations/0001_initial.py,sha256=azThbc8otEhxJwo8BIgOt5eC30mxXhKJLBAazZ
189
191
  accrete/migrations/0002_initial.py,sha256=dFOM7kdHlx7pVAh8cTDlZMtciN4O9Z547HAzEKnygZE,1628
190
192
  accrete/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
191
193
  accrete/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
192
- accrete/utils/dates.py,sha256=yuf8bqhdjOBarxFFl2hqepbbbuFHQgoe2BNar6-4n8g,1068
193
- accrete-0.0.27.dist-info/METADATA,sha256=7HV17v5wSbukr6SIJfTeb9aOEA2Wgo3dns6gcmiphg4,4846
194
- accrete-0.0.27.dist-info/WHEEL,sha256=mRYSEL3Ih6g5a_CVMIcwiF__0Ae4_gLYh01YFNwiq1k,87
195
- accrete-0.0.27.dist-info/licenses/LICENSE,sha256=_7laeMIHnsd3Y2vJEXDYXq_PEXxIcjgJsGt8UIKTRWc,1057
196
- accrete-0.0.27.dist-info/RECORD,,
194
+ accrete/utils/dates.py,sha256=iqMqEcW1ZV5yg9TjVN01-Pa24vgdcO-_u2x_sXSvMPI,1329
195
+ accrete-0.0.28.dist-info/METADATA,sha256=SO405O8tJ2JxsxLxj7WrfExs10ujpFideOP3o_6ksKQ,4892
196
+ accrete-0.0.28.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
197
+ accrete-0.0.28.dist-info/licenses/LICENSE,sha256=_7laeMIHnsd3Y2vJEXDYXq_PEXxIcjgJsGt8UIKTRWc,1057
198
+ accrete-0.0.28.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.21.0
2
+ Generator: hatchling 1.21.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any