lino 25.5.0__py3-none-any.whl → 25.5.1__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.
lino/__init__.py CHANGED
@@ -31,7 +31,7 @@ from django import VERSION
31
31
  from django.apps import AppConfig
32
32
  from django.conf import settings
33
33
  import warnings
34
- __version__ = '25.5.0'
34
+ __version__ = '25.5.1'
35
35
 
36
36
  # import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
37
37
 
lino/core/actions.py CHANGED
@@ -31,18 +31,12 @@ from django.core.exceptions import BadRequest
31
31
 
32
32
  from django.apps import apps
33
33
 
34
- get_models = apps.get_models
35
-
36
34
 
37
35
  def discover_choosers():
38
36
  logger.debug("Discovering choosers for database fields...")
39
- # ~ logger.debug("Instantiate model reports...")
40
- for model in get_models():
41
- # ~ n = 0
42
- allfields = model._meta.fields
43
- for field in allfields:
37
+ for model in apps.get_models():
38
+ for field in model._meta.fields:
44
39
  check_for_chooser(model, field)
45
- # ~ logger.debug("Discovered %d choosers in model %s.",n,model)
46
40
 
47
41
 
48
42
  def resolve_layout(cls, k, spec, layout_class, **options):
lino/core/actors.py CHANGED
@@ -17,10 +17,8 @@ from django.db import models
17
17
  from django.conf import settings
18
18
  from django.utils.translation import gettext_lazy as _
19
19
  from django.utils.html import format_html, mark_safe, SafeString
20
- from django.core.exceptions import BadRequest
21
20
 
22
21
  from lino import logger
23
- from lino.utils import isiterable
24
22
  from lino.utils import MissingRow
25
23
  from lino.core import fields
26
24
  from lino.core import actions
@@ -532,7 +530,11 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
532
530
  @classmethod
533
531
  def get_chooser_for_field(cls, fieldname):
534
532
  d = getattr(cls, "_choosers_dict", {})
535
- return d.get(fieldname, None)
533
+ ch = d.get(fieldname, None)
534
+ if ch is not None:
535
+ return ch
536
+ if cls.model is not None:
537
+ return cls.model.get_chooser_for_field(fieldname)
536
538
 
537
539
  # @classmethod
538
540
  # def inject_field(cls, name, fld):
lino/core/choicelists.py CHANGED
@@ -1,25 +1,19 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2008-2023 Rumma & Ko Ltd
2
+ # Copyright 2008-2025 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
  """
5
5
  Defines the classes :class:`Choice` and :class:`ChoiceList`. See
6
6
  :doc:`/dev/choicelists`.
7
7
  """
8
8
 
9
- from future.utils import with_metaclass
10
-
11
9
  import warnings
12
-
13
10
  from django.utils.translation import gettext_lazy as _
14
11
  from django.utils.functional import lazy
15
-
16
12
  # from django.utils.deconstruct import deconstructible
17
13
  from django.db import models
18
14
  from django.conf import settings
19
- from django.db.models import NOT_PROVIDED
20
15
  from django.db.migrations.serializer import BaseSerializer
21
16
  from django.db.migrations.writer import MigrationWriter
22
-
23
17
  from lino.utils import MissingRow
24
18
  from lino.core.utils import resolve_field
25
19
  from lino.core import actions
@@ -33,8 +27,9 @@ STRICT = True
33
27
  VALUE_FIELD = models.CharField(_("value"), max_length=20)
34
28
  VALUE_FIELD.attname = "value"
35
29
 
36
-
37
30
  # @deconstructible
31
+
32
+
38
33
  class Choice(fields.TableRow):
39
34
  """
40
35
  A constant value whose string representation depends on the current language
@@ -345,7 +340,7 @@ class PointingChoice(Choice):
345
340
  return not isinstance(self.get_object(severe=False), MissingRow)
346
341
 
347
342
 
348
- class ChoiceList(with_metaclass(ChoiceListMeta, tables.AbstractTable)):
343
+ class ChoiceList(tables.AbstractTable, metaclass=ChoiceListMeta):
349
344
  """
350
345
  User-defined choice lists must inherit from this base class.
351
346
 
@@ -743,12 +738,14 @@ class ChoiceList(with_metaclass(ChoiceListMeta, tables.AbstractTable)):
743
738
 
744
739
  Override this to customize the display text of choices.
745
740
 
741
+ Usage example: :class:`lino.modlib.system.DisplayColors`.
742
+
746
743
  :class:`lino.modlib.users.UserGroups` and
747
744
  :class:`lino.modlib.cv.models.CefLevel` used to do this before
748
745
  we had the :attr:`ChoiceList.show_values` option.
749
746
 
750
- This must be lazily translatable because the return value is also used
751
- to build the `choices` attribute of ChoiceListFields on this choicelist.
747
+ The return value must be lazily translatable because it is also used to
748
+ build the `choices` attribute of ChoiceListFields on this choicelist.
752
749
 
753
750
  Note that Django's `lazy` function has a list of "resultclasses" that
754
751
  are used "so that the automatic forcing of the lazy evaluation code is
lino/core/dbtables.py CHANGED
@@ -228,13 +228,13 @@ class Table(AbstractTable):
228
228
  return qs.model.objects.none()
229
229
  return qs.filter(flt)
230
230
 
231
- @classmethod
232
- def get_chooser_for_field(self, fieldname):
233
- ch = super().get_chooser_for_field(fieldname)
234
- if ch is not None:
235
- return ch
236
- if self.model is not None:
237
- return self.model.get_chooser_for_field(fieldname)
231
+ # @classmethod
232
+ # def get_chooser_for_field(self, fieldname):
233
+ # ch = super().get_chooser_for_field(fieldname)
234
+ # if ch is not None:
235
+ # return ch
236
+ # if self.model is not None:
237
+ # return self.model.get_chooser_for_field(fieldname)
238
238
 
239
239
  @classmethod
240
240
  def column_choices(self):
lino/core/elems.py CHANGED
@@ -61,6 +61,7 @@ from lino.core import tables
61
61
  from lino.core.gfks import GenericForeignKey
62
62
  from lino.utils.format_date import fds
63
63
  from lino.modlib.users.utils import get_user_profile
64
+ from lino.modlib.gfks.fields import GenericForeignKeyIdField
64
65
 
65
66
  # from etgen import etree
66
67
  from etgen.html2rst import html2rst
@@ -2550,6 +2551,7 @@ class TabPanel(Panel):
2550
2551
 
2551
2552
  _FIELD2ELEM = [
2552
2553
  # (dd.Constant, ConstantElement),
2554
+ (GenericForeignKeyIdField, ComplexRemoteComboFieldElement),
2553
2555
  (fields.RecurrenceField, RecurrenceElement),
2554
2556
  (fields.DelayedHtmlBox, DisplayElement),
2555
2557
  (fields.HtmlBox, HtmlBoxElement),
@@ -2605,6 +2607,9 @@ def field2elem(layout_handle, field, **kw):
2605
2607
  holder = layout_handle.layout.get_chooser_holder()
2606
2608
  ch = holder.get_chooser_for_field(field.name)
2607
2609
 
2610
+ # if field.name == "answer":
2611
+ # print(f"20250511 {field} {ch} {field.choices}")
2612
+
2608
2613
  if ch:
2609
2614
  kw.update(can_create_choice=ch.can_create_choice)
2610
2615
  if ch.can_create_choice or not ch.force_selection:
@@ -2619,6 +2624,7 @@ def field2elem(layout_handle, field, **kw):
2619
2624
  return ForeignKeyElement(layout_handle, field, **kw)
2620
2625
  else:
2621
2626
  return ComplexRemoteComboFieldElement(layout_handle, field, **kw)
2627
+
2622
2628
  if field.choices:
2623
2629
  if isinstance(field, choicelists.ChoiceListField):
2624
2630
  if field.choicelist.preferred_width is None:
lino/core/fields.py CHANGED
@@ -8,7 +8,6 @@ related to fields.
8
8
 
9
9
  #fmt: off
10
10
 
11
- from lino import logger
12
11
  import datetime
13
12
  from decimal import Decimal
14
13
 
@@ -22,6 +21,7 @@ from django.core.exceptions import FieldDoesNotExist
22
21
  from django.db.models.fields import NOT_PROVIDED
23
22
  from django.utils.functional import cached_property
24
23
 
24
+ from lino import logger
25
25
  from lino.utils.html import E, forcetext, tostring, SafeString, escape, mark_safe
26
26
 
27
27
  from lino.core.utils import (
@@ -38,8 +38,8 @@ from lino.utils import isiterable
38
38
  from lino.utils import get_class_attr
39
39
  from lino.utils import IncompleteDate
40
40
  from lino.utils import quantities
41
- from lino.utils import choosers
42
41
  from lino.utils.quantities import Duration
42
+ from lino.modlib.gfks.fields import GenericForeignKeyIdField
43
43
 
44
44
  from .signals import pre_ui_save
45
45
 
@@ -543,7 +543,7 @@ VFIELD_ATTRIBS = frozenset(
543
543
  """to_python choices save_form_data
544
544
  value_to_string max_length remote_field
545
545
  max_digits verbose_name decimal_places wildcard_data_elem
546
- blank""".split()
546
+ blank choices""".split()
547
547
  )
548
548
 
549
549
 
@@ -618,6 +618,7 @@ class VirtualField(FakeField):
618
618
  "Invalid return type spec {} for {} : {}".format(
619
619
  f, self, e)
620
620
  )
621
+ self.field = f
621
622
 
622
623
  if isinstance(f, FakeField):
623
624
  # sortable_by = f.sortable_by
@@ -628,6 +629,11 @@ class VirtualField(FakeField):
628
629
  # if sortable_by and isinstance(sortable_by, list):
629
630
  # sortable_by = sortable_by[0]
630
631
 
632
+ # if isinstance(f, VirtualField):
633
+ # delegate = f.return_type
634
+ # else:
635
+ # delegate = f
636
+
631
637
  if isinstance(f, models.ForeignKey):
632
638
  f.remote_field.model = resolve_model(f.remote_field.model)
633
639
  set_default_verbose_name(f)
@@ -1147,6 +1153,7 @@ class DummyField(FakeField):
1147
1153
 
1148
1154
  # choices = []
1149
1155
  # primary_key = False
1156
+ field = None # Used e.g. to test whether it's a dummy field
1150
1157
 
1151
1158
  def __init__(self, dummy_value=None):
1152
1159
  super().__init__()
@@ -1266,6 +1273,13 @@ class TableRow(object):
1266
1273
 
1267
1274
  """
1268
1275
 
1276
+ @classmethod
1277
+ def get_chooser_for_field(cls, fieldname):
1278
+ d = getattr(cls, "_choosers_dict", {})
1279
+ # if fieldname.endswith("__municipality"):
1280
+ # print("20200425 Model.get_chooser_for_field", cls, fieldname, d)
1281
+ return d.get(fieldname, None)
1282
+
1269
1283
  @classmethod
1270
1284
  def setup_parameters(cls, params):
1271
1285
  """Inheritable hook for defining parameters for every actor on this model.
@@ -1597,6 +1611,7 @@ def pointer_factory(cls, othermodel, *args, **kw):
1597
1611
 
1598
1612
 
1599
1613
  def make_remote_field(model, name):
1614
+ from lino.utils import choosers
1600
1615
  parts = name.split("__")
1601
1616
  if len(parts) == 1:
1602
1617
  return
@@ -1815,7 +1830,6 @@ def choices_for_field(ar, holder, field):
1815
1830
  t = m.get_default_table()
1816
1831
  # qs = t.create_request(request=ar.request).data_iterator
1817
1832
  qs = t.create_request(parent=ar).data_iterator
1818
-
1819
1833
  # logger.info('20120710 choices_view(FK) %s --> %s', t, qs.query)
1820
1834
 
1821
1835
  def row2dict(obj, d):
@@ -1823,6 +1837,18 @@ def choices_for_field(ar, holder, field):
1823
1837
  obj, ar, field)
1824
1838
  d[constants.CHOICES_VALUE_FIELD] = obj.pk
1825
1839
  return d
1840
+ # elif isinstance(field, GenericForeignKeyIdField):
1841
+ # ct = getattr(ar.selected_rows[0], field.type_field)
1842
+ # m = ct.model_class()
1843
+ # # print(f"20250511 {field.remote_field} {repr(field.type_field)}")
1844
+ # t = m.get_default_table()
1845
+ # qs = t.create_request(parent=ar).data_iterator
1846
+ #
1847
+ # def row2dict(obj, d):
1848
+ # d[constants.CHOICES_TEXT_FIELD] = holder.get_choices_text(
1849
+ # obj, ar, field)
1850
+ # d[constants.CHOICES_VALUE_FIELD] = obj.pk
1851
+ # return d
1826
1852
  else:
1827
1853
  raise http.Http404("No choices for %s" % field)
1828
1854
  return (qs, row2dict)
lino/core/kernel.py CHANGED
@@ -44,6 +44,7 @@ from django.db import models
44
44
  # import lino # for is_testing
45
45
  from lino import logger
46
46
  from lino.utils import codetime
47
+ from lino.utils.choosers import check_for_chooser
47
48
  from lino.utils.html import E
48
49
  # from lino.core.utils import format_request
49
50
  # from lino.utils import isiterable
@@ -470,6 +471,11 @@ class Kernel(object):
470
471
  # ~ choosers.discover()
471
472
  actions.discover_choosers()
472
473
 
474
+ for a in actors.actors_list:
475
+ for name, field in a.virtual_fields.items():
476
+ assert name == field.name
477
+ check_for_chooser(a, field)
478
+
473
479
  for a in actors.actors_list:
474
480
  a.on_analyze(site)
475
481
 
lino/core/model.py CHANGED
@@ -726,13 +726,6 @@ class Model(models.Model, fields.TableRow):
726
726
  # return self.preview(ar)
727
727
  # #~ username = kw.pop('username',None)
728
728
 
729
- @classmethod
730
- def get_chooser_for_field(cls, fieldname):
731
- d = getattr(cls, "_choosers_dict", {})
732
- # if fieldname.endswith("__municipality"):
733
- # print("20200425 Model.get_chooser_for_field", cls, fieldname, d)
734
- return d.get(fieldname, None)
735
-
736
729
  def get_printable_target_stem(self):
737
730
  return self._meta.app_label + "." + self.__class__.__name__ + "-" + str(self.pk)
738
731
 
lino/core/store.py CHANGED
@@ -647,13 +647,12 @@ class RowClassStoreField(SpecialStoreField):
647
647
  name = "row_class"
648
648
 
649
649
  def full_value_from_object(self, obj, ar=None):
650
- return " ".join(
651
- [
652
- ar.renderer.row_classes_map.get(s, "")
650
+ lst = [
651
+ ar.renderer.row_classes_map.get(s, s)
653
652
  for s in self.store.actor.get_row_classes(obj, ar)
654
653
  ]
655
- )
656
- # return ar.renderer.row_classes_map.get('x-grid3-row-%s' % s
654
+ # print(f"20250509x {obj} -> {lst}")
655
+ return " ".join(lst)
657
656
 
658
657
 
659
658
  class DisableEditingStoreField(SpecialStoreField):
lino/core/tables.py CHANGED
@@ -566,16 +566,14 @@ method in order to sort the rows of the queryset.
566
566
  For example, if you have two models :class:`Book` and :class:`Author`,
567
567
  and a foreign key :attr:`Book.author`, which points to the author of the
568
568
  book, and a table `BooksByAuthor` having `master_key` set to
569
- ``'author'``, then `get_filter_kw` would return a dict `{'author':
570
- <PK>}` where `<PK>` is the primary key of the action request's
571
- :attr:`master_instance
569
+ ``'author'``, then :meth:`BooksByAuthor.get_filter_kw` would return a
570
+ dict `{'author': <PK>}` where `<PK>` is the primary key of the action
571
+ request's :attr:`master_instance
572
572
  <lino.core.requests.BaseRequest.master_instance>`.
573
573
 
574
- Another example is
575
- :class:`lino_xl.lib.tickets.EntriesBySession`, where blog
576
- entries are not directly linked to a session, but in the
577
- detail of a session we want to display a table of related blog
578
- entries.
574
+ Another example is :class:`lino_xl.lib.tickets.EntriesBySession`, where
575
+ blog entries are not directly linked to a session, but in the detail of
576
+ a session we want to display a table of related blog entries.
579
577
 
580
578
  :class:`lino_xl.lib.households.SiblingsByPerson` Household
581
579
  members are not directly linked to a Person, but usually a
lino/help_texts.py CHANGED
@@ -32,7 +32,7 @@ help_texts = {
32
32
  'lino.mixins.dupable.SimilarObjects' : _("""Shows the other objects that are similar to this one."""),
33
33
  'lino.mixins.duplicable.Duplicate' : _("""Duplicate the selected row."""),
34
34
  'lino.mixins.duplicable.Duplicate.run_from_ui' : _("""This actually runs the action."""),
35
- 'lino.mixins.duplicable.Duplicable' : _("""Adds a row action “Duplicate which duplicates (creates a clone of) the object it was called on."""),
35
+ 'lino.mixins.duplicable.Duplicable' : _("""Adds a row action “Duplicate”, which duplicates (creates a clone of) the object it was called on."""),
36
36
  'lino.mixins.human.Human' : _("""Base class for models that represent a human."""),
37
37
  'lino.mixins.human.Human.title' : _("""Used to specify a professional position or academic qualification like “Dr.” or “PhD”."""),
38
38
  'lino.mixins.human.Human.first_name' : _("""The first name, also known as given name."""),
@@ -70,6 +70,7 @@ help_texts = {
70
70
  'lino.mixins.ref.Referrable.ref' : _("""The reference. This must be either empty or unique."""),
71
71
  'lino.mixins.ref.Referrable.ref_max_length' : _("""The preferred width of the ref field."""),
72
72
  'lino.mixins.ref.Referrable.on_duplicate' : _("""Before saving a duplicated object for the first time, we must change the ref in order to avoid an IntegrityError."""),
73
+ 'lino.mixins.ref.Referrable.get_next_row' : _("""Return the next database row, or None if this is the last one."""),
73
74
  'lino.mixins.ref.Referrable.get_by_ref' : _("""Return the object identified by the given reference."""),
74
75
  'lino.mixins.ref.Referrable.quick_search_filter' : _("""Overrides the default behaviour defined in lino.core.model.Model.quick_search_filter(). For Referrable objects, when quick-searching for a text containing only digits, the user usually means the ref and not the primary key."""),
75
76
  'lino.mixins.ref.StructuredReferrable' : _("""A referrable whose ref field is used to define a hierarchical structure."""),
@@ -97,6 +98,7 @@ help_texts = {
97
98
  'lino.mixins.sequenced.Sequenced.get_siblings' : _("""Return a Django Queryset with all siblings of this, or None if this is a root element which cannot have any siblings."""),
98
99
  'lino.mixins.sequenced.Sequenced.set_seqno' : _("""Initialize seqno to the seqno of eldest sibling + 1."""),
99
100
  'lino.mixins.sequenced.Sequenced.seqno_changed' : _("""If the user manually assigns a seqno."""),
101
+ 'lino.mixins.sequenced.Sequenced.dndreorder' : _("""A place holder column for drag and drop row reorder on React front end"""),
100
102
  'lino.mixins.sequenced.Hierarchical' : _("""Model mixin for things that have a “parent” and “siblings”."""),
101
103
  'lino.mixins.sequenced.Hierarchical.children_summary' : _("""A comma-separated list of the children."""),
102
104
  'lino.mixins.sequenced.Hierarchical.get_parental_line' : _("""Return an ordered list of all ancestors of this instance."""),
lino/mixins/duplicable.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2012-2020 Rumma & Ko Ltd
2
+ # Copyright 2012-2025 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
  """Defines the model mixin :class:`Duplicable`. "duplicable"
5
5
  [du'plikəblə] means "able to produce a duplicate
@@ -9,10 +9,11 @@
9
9
 
10
10
  from django.utils.translation import gettext_lazy as _
11
11
 
12
+ from lino import logger
12
13
  from lino.core import actions
13
14
  from lino.core import model
14
15
  from lino.core.diff import ChangeWatcher
15
- from lino.core.roles import Expert
16
+ # from lino.core.roles import Expert
16
17
 
17
18
 
18
19
  class Duplicate(actions.Action):
@@ -73,6 +74,7 @@ class Duplicate(actions.Action):
73
74
  new.full_clean()
74
75
  new.save(force_insert=True)
75
76
  cw = ChangeWatcher(new)
77
+ relcount = 0
76
78
 
77
79
  for fk, qs in related:
78
80
  for relobj in qs:
@@ -80,6 +82,7 @@ class Duplicate(actions.Action):
80
82
  setattr(relobj, fk.name, new)
81
83
  relobj.on_duplicate(ar, new)
82
84
  relobj.save(force_insert=True)
85
+ relcount += 1
83
86
 
84
87
  new.after_duplicate(ar, obj)
85
88
 
@@ -87,6 +90,9 @@ class Duplicate(actions.Action):
87
90
  new.full_clean()
88
91
  new.save()
89
92
 
93
+ logger.info("%s has been duplicated to %s (%d related rows)",
94
+ obj, new, relcount)
95
+
90
96
  return new
91
97
 
92
98
  def run_from_ui(self, ar, **kw):
@@ -115,7 +121,7 @@ class Duplicate(actions.Action):
115
121
 
116
122
 
117
123
  class Duplicable(model.Model):
118
- """Adds a row action "Duplicate" which duplicates (creates a clone
124
+ """Adds a row action "Duplicate", which duplicates (creates a clone
119
125
  of) the object it was called on.
120
126
 
121
127
  Subclasses may override :meth:`lino.core.model.Model.on_duplicate`
lino/mixins/ref.py CHANGED
@@ -10,6 +10,7 @@ from django.utils.translation import gettext_lazy as _
10
10
  from django.db.models.functions import Length
11
11
 
12
12
  from lino.utils.html import E
13
+ from lino.utils import nextref
13
14
  from lino.core import model
14
15
  from lino.core.fields import displayfield
15
16
 
@@ -61,6 +62,14 @@ class Referrable(model.Model):
61
62
  def __str__(self):
62
63
  return self.ref or super().__str__()
63
64
 
65
+ def get_next_row(self):
66
+ """Return the next database row, or None if this is the last one.
67
+ """
68
+ ref = nextref(self.ref)
69
+ if ref is None:
70
+ return None
71
+ return self.__class__.get_by_ref(ref, None)
72
+
64
73
  @staticmethod
65
74
  def ref_prefix(obj, ar=None):
66
75
  return obj.as_ref_prefix(ar)
@@ -37,7 +37,7 @@ class Registrable(model.Model):
37
37
  Base class to anything that may be "registered" and "deregistered", where
38
38
  "registered" means "this object has been taken account of".
39
39
 
40
- For example, when a :term:`ledger voucher` is registered, its associated
40
+ For example, when a :term:`numbered voucher` is registered, its associated
41
41
  :term:`ledger movements <ledger movement>` have been generated.
42
42
  Deregistering a voucher will first delete these movements.
43
43
 
@@ -45,7 +45,7 @@ class Registrable(model.Model):
45
45
  usually limited to certain fields.
46
46
 
47
47
  :class:`lino_xl.lib.cal.Reservation` is an example of a registrable that is
48
- not a ledger voucher.
48
+ not a numbered voucher.
49
49
 
50
50
  Subclasses must themselves define a field :attr:`state`.
51
51
 
lino/mixins/sequenced.py CHANGED
@@ -279,15 +279,11 @@ class Sequenced(Duplicable):
279
279
  Initialize `seqno` to the `seqno` of eldest sibling + 1.
280
280
  """
281
281
  qs = self.get_siblings().order_by("seqno")
282
- if qs is None: # TODO: remove this as it is no longer used (?)
283
- self.seqno = 0
282
+ if (count := qs.count()) == 0:
283
+ self.seqno = 1
284
284
  else:
285
- n = qs.count()
286
- if n == 0:
287
- self.seqno = 1
288
- else:
289
- last = qs[n - 1]
290
- self.seqno = last.seqno + 1
285
+ last = qs[count - 1]
286
+ self.seqno = last.seqno + 1
291
287
 
292
288
  def full_clean(self, *args, **kw):
293
289
  if not self.seqno:
@@ -329,6 +325,14 @@ class Sequenced(Duplicable):
329
325
  message=_("Renumbered {} of {} siblings.").format(n, qs.count()))
330
326
  ar.set_response(refresh_all=True)
331
327
 
328
+ @fields.displayfield("⇵")
329
+ def dndreorder(self, ar):
330
+ """A place holder column for drag and drop row reorder on :term:`React front end`
331
+
332
+ CAUTION: Do NOT rename this field, for react works on checking the name as dndreorder.
333
+ """
334
+ return None
335
+
332
336
  @fields.displayfield(_("Move"))
333
337
  def move_buttons(obj, ar):
334
338
  if ar is None:
@@ -96,7 +96,7 @@ class ExtRenderer(JsCacheRenderer):
96
96
  super().__init__(plugin)
97
97
  jsgen.register_converter(self.py2js_converter)
98
98
 
99
- for s in "green blue red yellow".split():
99
+ for s in ('green', 'blue', 'red', 'yellow', 'lightgrey'):
100
100
  self.row_classes_map[s] = "x-grid3-row-%s" % s
101
101
 
102
102
  self.prepare_layouts()
@@ -5,7 +5,6 @@
5
5
  from django.db import models
6
6
  from django.conf import settings
7
7
 
8
- from lino.utils.choosers import chooser
9
8
  from lino.core.utils import full_model_name
10
9
 
11
10
  if settings.SITE.is_installed("contenttypes"):
@@ -31,6 +30,7 @@ if settings.SITE.is_installed("contenttypes"):
31
30
  def contribute_to_class(self, cls, name, **kwargs):
32
31
  # Automatically setup chooser and display field for ID field of
33
32
  # generic foreign key.
33
+ from lino.utils.choosers import chooser
34
34
 
35
35
  super().contribute_to_class(cls, name, **kwargs)
36
36
 
@@ -71,6 +71,7 @@ class JinjaRenderer(HtmlRenderer):
71
71
  # ~ print 20130109, loaders
72
72
  self.jinja_env = jinja2.Environment(
73
73
  # ~ extensions=['jinja2.ext.i18n'],
74
+ extensions=['jinja2.ext.do'],
74
75
  loader=jinja2.ChoiceLoader(loaders)
75
76
  )
76
77
 
@@ -32,7 +32,7 @@ class SystemTasks(dd.Table):
32
32
  model = "linod.SystemTask"
33
33
  order_by = ['seqno']
34
34
  required_roles = dd.login_required(SiteStaff)
35
- column_names = "seqno name log_level disabled status procedure *"
35
+ column_names = "seqno name log_level disabled status procedure dndreorder *"
36
36
  detail_layout = """
37
37
  seqno procedure
38
38
  name
@@ -8,16 +8,14 @@ from lino.api import dd, rt, _
8
8
 
9
9
  start_year = dd.get_plugin_setting("periods", "start_year", None)
10
10
 
11
+
11
12
  def objects():
12
13
  StoredYear = rt.models.periods.StoredYear
13
14
 
14
- cfg = dd.plugins.periods
15
15
  site = settings.SITE
16
16
  if site.the_demo_date is not None:
17
17
  if start_year > site.the_demo_date.year:
18
18
  raise Exception("plugins.periods.start_year is after the_demo_date")
19
19
  today = site.the_demo_date or datetime.date.today()
20
20
  for y in range(start_year, today.year + 6):
21
- # yield StoredYear.create_from_year(y)
22
21
  yield StoredYear.get_or_create_from_date(datetime.date(y, today.month, today.day))
23
- # StoredYears.add_item(StoredYear.year2value(y), str(y))
@@ -19,7 +19,6 @@ NEXT_YEAR_SEP = "/"
19
19
  YEAR_PERIOD_SEP = "-"
20
20
 
21
21
 
22
-
23
22
  class StoredYear(DateRange, Referrable):
24
23
 
25
24
  class Meta:
@@ -30,7 +29,7 @@ class StoredYear(DateRange, Referrable):
30
29
 
31
30
  preferred_foreignkey_width = 10
32
31
 
33
- state = PeriodStates.field(default='open')
32
+ state = PeriodStates.field(blank=True)
34
33
 
35
34
  @classmethod
36
35
  def get_simple_parameters(cls):
@@ -80,9 +79,23 @@ class StoredYear(DateRange, Referrable):
80
79
  obj.save()
81
80
  return obj
82
81
 
82
+ def full_clean(self, *args, **kwargs):
83
+ if not self.state:
84
+ if self.start_date.year + 1 < dd.today().year:
85
+ self.state = PeriodStates.closed
86
+ else:
87
+ self.state = PeriodStates.open
88
+ super().full_clean(*args, **kwargs)
89
+
83
90
  def __str__(self):
84
91
  return self.ref
85
92
 
93
+ def get_next_row(self):
94
+ nextyear = self.start_date.replace(year=self.start_date.year+1)
95
+ ref = self.__class__.get_ref_for_date(nextyear)
96
+ return self.__class__.get_by_ref(ref, None)
97
+ # return self.__class__.get_or_create_from_date(nextyear)
98
+
86
99
 
87
100
  class StoredPeriod(DateRange, Referrable):
88
101
 
@@ -94,8 +107,9 @@ class StoredPeriod(DateRange, Referrable):
94
107
 
95
108
  preferred_foreignkey_width = 10
96
109
 
97
- state = PeriodStates.field(default='open')
98
- year = dd.ForeignKey('periods.StoredYear', blank=True, null=True, related_name="periods")
110
+ state = PeriodStates.field(blank=True)
111
+ year = dd.ForeignKey('periods.StoredYear', blank=True,
112
+ null=True, related_name="periods")
99
113
  remark = models.CharField(_("Remark"), max_length=250, blank=True)
100
114
 
101
115
  @classmethod
@@ -107,7 +121,8 @@ class StoredPeriod(DateRange, Referrable):
107
121
  @classmethod
108
122
  def get_request_queryset(cls, ar):
109
123
  qs = super().get_request_queryset(ar)
110
- if (pv := ar.param_values) is None: return qs
124
+ if (pv := ar.param_values) is None:
125
+ return qs
111
126
 
112
127
  # if pv.start_date is None or pv.end_date is None:
113
128
  # period = None
@@ -206,6 +221,8 @@ class StoredPeriod(DateRange, Referrable):
206
221
  self.start_date = dd.today().replace(day=1)
207
222
  if not self.year_id:
208
223
  self.year = StoredYear.get_or_create_from_date(self.start_date)
224
+ if not self.state:
225
+ self.state = self.year.state
209
226
  super().full_clean(*args, **kwargs)
210
227
 
211
228
  def __str__(self):
@@ -230,11 +247,14 @@ class StoredPeriod(DateRange, Referrable):
230
247
  return parts[1]
231
248
  return self.ref
232
249
 
250
+
233
251
  StoredPeriod.set_widget_options('ref', width=6)
234
252
 
253
+
235
254
  def date2ref(d):
236
255
  return StoredYear.get_ref_for_date(d) + YEAR_PERIOD_SEP + StoredPeriod.get_ref_for_date(d)
237
256
 
257
+
238
258
  class StoredYears(dd.Table):
239
259
  model = 'periods.StoredYear'
240
260
  required_roles = dd.login_required(OfficeStaff)
@@ -244,6 +264,7 @@ class StoredYears(dd.Table):
244
264
  # start_date end_date
245
265
  # """
246
266
 
267
+
247
268
  class StoredPeriods(dd.Table):
248
269
  required_roles = dd.login_required(OfficeStaff)
249
270
  model = 'periods.StoredPeriod'
@@ -38,6 +38,7 @@ class Plugin(ad.Plugin):
38
38
  margin = 10
39
39
  margin_left = 17
40
40
  margin_right = 10
41
+ space_before_recipient = 15
41
42
 
42
43
  def get_requirements(self, site):
43
44
  yield "imagesize"
@@ -2,9 +2,9 @@
2
2
  # Copyright 2016-2024 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
 
5
- import os
6
5
  from pathlib import Path
7
- from copy import copy
6
+ from lino.modlib.jinja.choicelists import JinjaBuildMethod
7
+ from lino.modlib.printing.choicelists import BuildMethods
8
8
 
9
9
  try:
10
10
  from weasyprint import HTML
@@ -20,15 +20,6 @@ except ImportError:
20
20
  BULMA_CSS = None
21
21
 
22
22
 
23
-
24
- from django.conf import settings
25
- from django.utils import translation
26
-
27
- from lino.api import dd
28
- from lino.modlib.jinja.choicelists import JinjaBuildMethod
29
- from lino.modlib.printing.choicelists import BuildMethods
30
-
31
-
32
23
  class WeasyBuildMethod(JinjaBuildMethod):
33
24
  template_ext = ".weasy.html"
34
25
  templates_name = "weasy"
@@ -77,7 +77,7 @@ div.recipient {
77
77
  size: {%- block pagesize %}landscape{%- endblock %};
78
78
  margin: {{dd.plugins.weasyprint.margin}}mm;
79
79
  margin-bottom: {{dd.plugins.weasyprint.margin+dd.plugins.weasyprint.footer_height}}mm;
80
- {%- if dd.plugins.weasyprint.top_right_image -%}
80
+ {%- if True or dd.plugins.weasyprint.top_right_image -%}
81
81
  margin-top: {{dd.plugins.weasyprint.margin+dd.plugins.weasyprint.header_height}}mm;
82
82
  {%- endif -%}
83
83
  margin-left: {{dd.plugins.weasyprint.margin_left}}mm;
lino/utils/__init__.py CHANGED
@@ -655,3 +655,23 @@ def logging_disabled(level):
655
655
  yield
656
656
  finally:
657
657
  logging.disable(logging.NOTSET)
658
+
659
+
660
+ def nextref(ref):
661
+ """
662
+ Increment the first number found in the string, preserving everything
663
+ thereafter.
664
+
665
+ Tested examples in :doc:`/topics/utils`.
666
+ """
667
+ num = ""
668
+ suffix = ""
669
+ for i, c in enumerate(ref):
670
+ if c.isdigit():
671
+ num += c
672
+ else:
673
+ suffix = ref[i:]
674
+ break
675
+ if not num:
676
+ return None
677
+ return str(int(num)+1) + suffix
lino/utils/choosers.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2009-2021 Rumma & Ko Ltd
1
+ # Copyright 2009-2025 Rumma & Ko Ltd
2
2
  # License: GNU Affero General Public License v3 (see file COPYING for details)
3
3
  """Extends the possibilities for defining choices for fields of a
4
4
  Django model.
@@ -13,19 +13,18 @@ Example values in :doc:`/topics/utils`.
13
13
 
14
14
  """
15
15
 
16
+ import re
16
17
  import decimal
17
18
  import datetime
18
- from dateutil import parser as dateparser
19
-
20
- import re
21
19
 
22
- GFK_HACK = re.compile(
23
- r'^<a href="javascript:Lino\.(\w+\.\w+)\.detail\.run\(.*,\{ &quot;record_id&quot;: (\w+) \}\)">.*</a>$'
24
- )
25
-
26
- from django.db import models
27
- from django.conf import settings
20
+ from dateutil import parser as dateparser
28
21
  from django.core.exceptions import BadRequest
22
+ from django.conf import settings
23
+ from django.db import models
24
+
25
+ from lino.core.utils import getrqdata
26
+ from lino.core import fields
27
+ from lino.core import constants
29
28
 
30
29
  try:
31
30
  from django.contrib.contenttypes.models import ContentType
@@ -33,10 +32,9 @@ except RuntimeError:
33
32
  pass
34
33
  # Happens when the site doesn't use contenttypes. Ignore silently.
35
34
 
36
- # from lino.api import rt
37
- from lino.core import constants
38
- from lino.core import fields
39
- from lino.core.utils import getrqdata
35
+ GFK_HACK = re.compile(
36
+ r'^<a href="javascript:Lino\.(\w+\.\w+)\.detail\.run\(.*,\{ &quot;record_id&quot;: (\w+) \}\)">.*</a>$'
37
+ )
40
38
 
41
39
  # class DataError(Exception):
42
40
  # pass
@@ -257,6 +255,11 @@ class Chooser(FieldChooser):
257
255
  if isinstance(field, fields.VirtualField):
258
256
  selector = field.return_type
259
257
 
258
+ # TODO: move this to top and fix ImportError cannot import name
259
+ # 'chooser' from partially initialized module 'lino.utils.choosers'
260
+ # (most likely due to a circular import)
261
+ from lino.modlib.gfks.fields import GenericForeignKeyIdField
262
+
260
263
  if isinstance(selector, ChoiceListField):
261
264
  self.simple_values = getattr(meth, "simple_values", False)
262
265
  self.instance_values = getattr(meth, "instance_values", True)
@@ -265,6 +268,8 @@ class Chooser(FieldChooser):
265
268
  )
266
269
  elif is_foreignkey(selector):
267
270
  pass
271
+ elif isinstance(selector, GenericForeignKeyIdField):
272
+ pass
268
273
  # elif isinstance(field, fields.VirtualField) and isinstance(
269
274
  # field.return_type, models.ForeignKey
270
275
  # ):
@@ -471,7 +476,7 @@ def _chooser(make, **options):
471
476
  return fn(*args)
472
477
 
473
478
  cp = options.pop(
474
- "context_params", fn.__code__.co_varnames[1 : fn.__code__.co_argcount]
479
+ "context_params", fn.__code__.co_varnames[1: fn.__code__.co_argcount]
475
480
  )
476
481
  wrapped.context_params = cp
477
482
  for k, v in options.items():
@@ -518,6 +523,7 @@ def check_for_chooser(holder, field):
518
523
  methname = field.name + "_choices"
519
524
  m = getattr(holder, methname, None)
520
525
  if m is not None:
526
+ # print(f"20250511 chooser for {field.name} in {holder} is {m}")
521
527
  # if field.name.endswith('municipality'):
522
528
  # print("20200524 check_for_chooser() found {} on {}", field.name, holder)
523
529
  # 20200425 fix theoretical bug
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lino
3
- Version: 25.5.0
3
+ Version: 25.5.1
4
4
  Summary: A framework for writing desktop-like web applications using Django and ExtJS or React
5
5
  Project-URL: Homepage, https://www.lino-framework.org
6
6
  Project-URL: Repository, https://gitlab.com/lino-framework/lino
@@ -1,10 +1,10 @@
1
1
  lino/.cvsignore,sha256=1vrrWoP-WD8hPfCszHHIiJEi8KUMRCt5WvoKB9TSB1k,28
2
2
  lino/SciTEDirectory.properties,sha256=rCYi_e-6h8Yx5DwXhAa6MBPlVINcl6Vv9BQDYZV2_go,28
3
- lino/__init__.py,sha256=AwfkNuetCwDhmYUEAk9zyjyrSX4Ia362yUdo_uuw1ps,6176
3
+ lino/__init__.py,sha256=uIdvk33nJCfbsJGNg2B_VrDVh7GSL-ASrQK6Lh7rbLc,6176
4
4
  lino/ad.py,sha256=AQ-vJ4scac1mx3xegXezxnxyOQpV-a0q3VFMJSDbj2s,142
5
5
  lino/apps.py,sha256=ECq-dPARDkuhngwNrcipse3b4Irj70HxJs44uWEZFc4,27
6
6
  lino/hello.py,sha256=7-PJg7PnEiznyETqGjOwXcKh8rda0qLetpbS2gvRYy0,532
7
- lino/help_texts.py,sha256=Crr3VSPlkbhO-2pZssbMxcZnwKmcKhvr82H_Rl8ziVI,92012
7
+ lino/help_texts.py,sha256=-PiZ-hMm_JKjoD6591y5A4_hyp2NuVFBFoyn_5mWsbU,92266
8
8
  lino/api/__init__.py,sha256=WmzHU-rHdZ68se_nI0SmepQTGE8-cd9tPpovHRH9aag,512
9
9
  lino/api/ad.py,sha256=F6SrcKPRRalHKOZ7QLwsRWGq9hhykQIeo0b85cEk9NQ,314
10
10
  lino/api/dd.py,sha256=-mxiSJS27LGCWyOgboA8qDudTkE9vg9vmUGOT08Hq6A,7410
@@ -28,31 +28,31 @@ lino/config/unused/403.html,sha256=ePwDIUXhz1iD8xXWWyt5xEvpcGIHU4LMnXma8x0ik1c,2
28
28
  lino/config/unused/404.html,sha256=GOJrAyF6NcM69ETdSHgjff_-lvYs_-bOYhyZBem7x3I,220
29
29
  lino/config/unused/500.html,sha256=aWmP37uPoMS-PJgPuBloxdx0nEreU7AvkXxsex3yVYs,544
30
30
  lino/core/__init__.py,sha256=T7106QxQpa3jXAouGTIer6AXEwpkJ0NAQ9B7Q3-K6qE,686
31
- lino/core/actions.py,sha256=S3vtuXmn3VxqcL_P5BmmZnaqucwIbdLm0zVX5QKoIxU,46740
32
- lino/core/actors.py,sha256=5kIPlEf2fTXneCHkOmhR7Jo-0xdggowPPseypJfH-tA,74929
31
+ lino/core/actions.py,sha256=GIBiuu6bIMl2g0Y9-LWLhvTe2qHOcRGfHjBvl0vyrzU,46542
32
+ lino/core/actors.py,sha256=_Cl25nw65NK7me-wIHTJRh6iqcb6m12nUbvvQrLcjDk,74992
33
33
  lino/core/boundaction.py,sha256=sPRoBNjKApMUSBASu86aVVFhs7egNd6kYyLOBwGBDSs,7048
34
34
  lino/core/callbacks.py,sha256=uu1-znzxVDD-JETUebw-hYsNg_9ExQb1vfwbc7Psjro,7549
35
- lino/core/choicelists.py,sha256=RkgDhPjjC5lS0Zew1F5yNL2y3wYTIJOmqYEAnz5prxA,36530
35
+ lino/core/choicelists.py,sha256=5b_FpURG1ZNwEGfuvCd9FtI3agCHJnhj31ZxaXcur3A,36504
36
36
  lino/core/classproperty.py,sha256=_E95WPAs7BWbAuFpPvoYM2ZwW_mbq3rvF7o43WsMq_8,4316
37
37
  lino/core/constants.py,sha256=chvG1TrwD2gVMmL4nTOZtO8NcffcclcUv3zBE8mMoiQ,4503
38
38
  lino/core/dashboard.py,sha256=1ASWZ9I1LQBUJZ0JKiwW_g_1Pix3jb4SWUFC2cUM23g,6537
39
- lino/core/dbtables.py,sha256=bV1hcr9MXCT3doauxcOrnTZeBmHtb1tAQrLlnfvQj_o,29113
39
+ lino/core/dbtables.py,sha256=6vTjLtwYXtFMSe_-G-5OVcb7kOcRrYFh3nyjJUHdpVw,29127
40
40
  lino/core/dbutils.py,sha256=_QHcWd-ajLUwt5G8uOp8d47lZQKD3VseHnqKJke18rA,263
41
41
  lino/core/ddh.py,sha256=dYScxWKTOCDEgow7wJNJe812ESasmmITPK2ovraBQno,3172
42
42
  lino/core/diff.py,sha256=XQ-oQQDS_v3kXd4eRP9Hwr5UCgp-TPZIPVav9ZblUno,5882
43
- lino/core/elems.py,sha256=BjqG1yVT6yB2aPoRZGZ_M5fOjuHHeA1LL27QjJ3xF3U,108795
43
+ lino/core/elems.py,sha256=LeNPC8SBFobRbwy6LhmfkDcZhSbvcnsC3oVNprhak2Q,109013
44
44
  lino/core/exceptions.py,sha256=QDxDo5cllSyXQ8VWet9hGXzNadxCOmwMVrFXc6V-vpE,665
45
- lino/core/fields.py,sha256=NHnRDIM8AqyOyIcQzW3I1Skr2N4ZecbvhpR48Kp6oq8,59022
45
+ lino/core/fields.py,sha256=rgpuK51TTJHeIq0KE97F6FAWRCYx-nLFTerQTXKquQ4,60130
46
46
  lino/core/frames.py,sha256=ISxgq9zyZfqW3tDZMWdKi9Ij455lT_81qBH0xex0bfE,1161
47
47
  lino/core/gfks.py,sha256=6VXn2FSIXOrwVq0stfbPevT37EWg1tg4Fn-HMNVnbmk,1970
48
48
  lino/core/help.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  lino/core/inject.py,sha256=Qd_PGEn0yMXNYVPI0wCv1vvo2CNdlPkyoBmKZELOtGM,13422
50
- lino/core/kernel.py,sha256=kKgKmgUZqh7rFqpKjuM_T6pUvaw7K_vl3PEfRqvrg8Q,47794
50
+ lino/core/kernel.py,sha256=jJ2bZw77kFX_CVI0M0rdoj0bsTK2GIwnSn2RTt7-Tec,48025
51
51
  lino/core/keyboard.py,sha256=W3jA6qtB5HMppoNnd_6PgIM7ZlyHilJEhBvdeZTY7Xk,1283
52
52
  lino/core/layouts.py,sha256=VT0xcDB7JKSsNm2UOybAwJw3zOie57sRagRYlyA7nkg,28697
53
53
  lino/core/menus.py,sha256=W0Co9K-ayvfX5Zt5rgSxJrFRejtiYrwIR5EecdYXPNc,10857
54
54
  lino/core/merge.py,sha256=sKtTeZtHdoDKerdHj4NXuXXNzpKDsfdPaiq-CY0NqQc,9094
55
- lino/core/model.py,sha256=h3pjAMa61JF-L2hHg74OGFohs3jK5XkLTHJezqdIkhY,37200
55
+ lino/core/model.py,sha256=XSYjml_8WPjUU_LgGIH1qmPIwlIlQ0cjNFdN5A_ULko,36920
56
56
  lino/core/permissions.py,sha256=Fnemz3NwWz21X0YATI9Q7ba2FcAdg-EMLHjIcbt_AVU,6840
57
57
  lino/core/plugin.py,sha256=n_f5dSy4vqamnyzpNnaNcTlUp2EAgaAVxIJ5EHG7aHc,6837
58
58
  lino/core/renderer.py,sha256=C00J0L41hLv9b2sAzqSSrT0Lz5Fc78JnwHiq9LqF81c,47310
@@ -60,8 +60,8 @@ lino/core/requests.py,sha256=aIT8DsWVWwYQWHf7xX7SbbUf0s_8dWfayR3104VDrnQ,95833
60
60
  lino/core/roles.py,sha256=PXwk436xUupxdbJcygRSYFu7ixfKjAJPQRUQ8sy0lB0,4425
61
61
  lino/core/signals.py,sha256=0JT89mkjSbRm57QZcSI9DoThoKUGkyi-egNhuLUKEds,948
62
62
  lino/core/site.py,sha256=8qV5nEPuUaA7ViEcFOeOGPpzILrmWKOFS9MGPjqFcAs,82713
63
- lino/core/store.py,sha256=oIQMOTr5lSBYKZxkPMWjqfLFod8DJnBnF728brFldWg,51474
64
- lino/core/tables.py,sha256=68j42gXfUl5vIpqkRdgNBATi6blWLdjjh6Iznz3Gb8M,24472
63
+ lino/core/store.py,sha256=ibshCPBKd0zNSoAfMYx1MaqHXggE-O4zqiSUSlY-x68,51444
64
+ lino/core/tables.py,sha256=3IxOHtnN_5fkkLp5-TCBM_Z61aBGzynwIhvAEY71UcM,24476
65
65
  lino/core/urls.py,sha256=06QlmN1vpxjmb5snO3SPpP6lX1pMdE60bTiBiC77_vQ,2677
66
66
  lino/core/user_types.py,sha256=0iSYmzr2M9v2Mn2y6hzAZeqareUT-gD7l3MfIPyG9ZI,867
67
67
  lino/core/userprefs.py,sha256=cmufIS9xJErKDrw05uoWtQTUR6WRWIGkU1KdAqzNr5M,2850
@@ -117,14 +117,14 @@ lino/management/commands/syncscreenshots.py,sha256=XYZhqfm5_RwJzVFNGhHJKucl4j6T6
117
117
  lino/management/commands/update_conf.py,sha256=saAaQdPhn3mNOoHcBxOFSf_vBEgM-aopTHq1sJN20Bo,1026
118
118
  lino/mixins/__init__.py,sha256=7_gW-TzHryL9hvcOx90Op-uBGvivtEj4e6bfNp6j_OE,8854
119
119
  lino/mixins/dupable.py,sha256=9DNZ9xsgSFsxchqzlo86jQVecXABuDeEZ62tamj-X9A,10235
120
- lino/mixins/duplicable.py,sha256=a5-1jZZWX-kgKg5GNhV6yaSGOgfTyd2lWHtY35bVmes,4247
120
+ lino/mixins/duplicable.py,sha256=kraLul9nZb2oWi_MyTdwoSU2IQnHgTb_snLaMmGqgyQ,4436
121
121
  lino/mixins/human.py,sha256=YDIfIHHAaVmzd3uGsJp_vDvkaBWOp09I7i4AGNy9YsQ,13255
122
122
  lino/mixins/periods.py,sha256=iFqUCUZlgUW2GmEHRl9suwJedztrXLluQhgFpu7Uj54,11479
123
123
  lino/mixins/polymorphic.py,sha256=MLbfOeIYRoDZO4048X2oWhG5cxds2pLkwciXcw1xjVQ,9393
124
124
  lino/mixins/printable.py,sha256=4U8M1lrTjUeuaPwrcWoanCBo53iAxiNpSTsVctI-gI0,199
125
- lino/mixins/ref.py,sha256=ZnyTcpHgcbYwDwgaBwLc5cGLQqz8MmjAypSEgPB7Ql4,5272
126
- lino/mixins/registrable.py,sha256=EqszzywB9LN37zl7NRBHZxsHzFRoBQb-f9HBkSHo0vw,7206
127
- lino/mixins/sequenced.py,sha256=6FiHJX2ZIraqUofdnb082b978PISNbpd2Vr87YA_1pg,16460
125
+ lino/mixins/ref.py,sha256=qV8CprGCvwzzWTl6LQc9I1RwD8pziD0F7Ykoaznm8wM,5550
126
+ lino/mixins/registrable.py,sha256=37usXcHtz_F8rJtZ0fCS4_KZFQ87fSDXO-GPYEZ_IzY,7210
127
+ lino/mixins/sequenced.py,sha256=GRw8KmCQyEfrXvkDf-moND_tHKcsFLhzLzQ6_s94tHM,16605
128
128
  lino/modlib/__init__.py,sha256=cO31gNu2oRkp7o2v3D9gK2H7j4jF9rbVyPxPZhZQwrQ,940
129
129
  lino/modlib/about/__init__.py,sha256=jhqGQIXU1o7KkmmQjfwPKJc3buibB09Fy55carAmi7U,342
130
130
  lino/modlib/about/choicelists.py,sha256=2bxDb2u7cFacBOgLoEWrMmzQ4BJ3x1pmhdgqxzUp-Rw,1424
@@ -187,7 +187,7 @@ lino/modlib/dupable/models.py,sha256=0watviKwTiVwlArC54V3IxVVfcB1Yg5kO6ed2xCM9a0
187
187
  lino/modlib/export_excel/__init__.py,sha256=HrsrhXjIMvMHRGu8whH3A_WijZWrH35p2cQsFXK60DY,356
188
188
  lino/modlib/export_excel/models.py,sha256=A-lFS-xFDSPHcVbY0OI7VsuGr82jJyLDhXH2Dcv80Os,5039
189
189
  lino/modlib/extjs/__init__.py,sha256=6UBWAWSROwy3DfTXQmVUVJTF6eZ_e2k3BEfE4wtqVhU,10172
190
- lino/modlib/extjs/ext_renderer.py,sha256=KkzvGe7pyOB-_tMiiifqeDI2KuKZd-nGNic9XbV3uu0,60616
190
+ lino/modlib/extjs/ext_renderer.py,sha256=1qEQ32fNDz6cXWgKTUc2qpfAjwrLjSRcWYk94A2odAo,60632
191
191
  lino/modlib/extjs/views.py,sha256=CrX_tPshJtSkHT7raDDqn4B_XYamujRs-CgLzY4CBxg,26299
192
192
  lino/modlib/extjs/config/extjs/index.html,sha256=jO5hdNpFSkm9t0xhHD5hc8Hw1fSr6xb3zYq9aMyOI7Q,8603
193
193
  lino/modlib/extjs/config/extjs/linoweb.js,sha256=I4VYGmkK4htqZvHM9g-6psJF3pp7SvgHEI0I02Sxpvo,175127
@@ -3490,7 +3490,7 @@ lino/modlib/forms/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3490
3490
  lino/modlib/forms/renderer.py,sha256=KLMAyDPLpWlRCzzQ4F4aS2bs5birR_JaFQu6lQX-ZNk,2406
3491
3491
  lino/modlib/forms/views.py,sha256=L8U8LKFB1bP1q2NiAcRkZvynT_uw7yb93HIwA3TaXTs,10313
3492
3492
  lino/modlib/gfks/__init__.py,sha256=2f34DqbWXWUxE_y_0LqLfkpoyZ-cqQucg4xZkI2H3VY,1247
3493
- lino/modlib/gfks/fields.py,sha256=43yIsudT07bXuKD1Gsqvn15_F3UaeZQqZzuzK9dDdLc,3239
3493
+ lino/modlib/gfks/fields.py,sha256=csvdt8U1GbyJ9iM6gbCWq5gFanbLJipM5Kw80wwp-Kw,3251
3494
3494
  lino/modlib/gfks/mixins.py,sha256=BcITVETBR7zVj684diZGCii3rzm7lgrgzr2euy-D-iw,2626
3495
3495
  lino/modlib/gfks/models.py,sha256=bDbuvtpSW-wxNWLC3pKWsDQPyN4g6MFMTS-66aQ2DX8,6723
3496
3496
  lino/modlib/help/__init__.py,sha256=PlThatoYB6lThNahWkVL2ZatimW0d_9LAPTZIE7hiuE,4434
@@ -3519,7 +3519,7 @@ lino/modlib/jinja/choicelists.py,sha256=QHjWQWLnJCKSGnLIKeGqnCw41JYvcbTkCeXjBpWh
3519
3519
  lino/modlib/jinja/loader.py,sha256=MX027X_UuQPqq0wOUr06QnOkdTzGpksNv0Om1CGp61I,1765
3520
3520
  lino/modlib/jinja/mixins.py,sha256=YqvNt1JB7as6IohBoyP8DqHldxmg9yOfrN27V5cn7Ho,2913
3521
3521
  lino/modlib/jinja/models.py,sha256=vXNFS78qP-qIzeSmnXJNOpvW_IfH1h6hhPgYjfkvvD0,207
3522
- lino/modlib/jinja/renderer.py,sha256=PtjhJdidlOOGzRdyUQlaEIj2FhFFuthFB80-bkSfsoM,6156
3522
+ lino/modlib/jinja/renderer.py,sha256=M7qrPwf_n730YoGXNQAnaJSBvD2lw-hez2_1YPIqSHo,6198
3523
3523
  lino/modlib/jinja/config/jinja/status.jinja.rst,sha256=XEsfXd82B0fLsBfds5-pXdeDWVr4ccTv7WyGa9ayigk,312
3524
3524
  lino/modlib/jinja/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3525
3525
  lino/modlib/jinja/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -3535,7 +3535,7 @@ lino/modlib/linod/__init__.py,sha256=efmj_Kz3OO2zF1lvs7P459iufYGimH1-6Ge6Cbq85tQ
3535
3535
  lino/modlib/linod/choicelists.py,sha256=Cu82s1QpcGFmKUXJsg-7TSqpaESBCZKOEfxzFlJP06I,2626
3536
3536
  lino/modlib/linod/consumers.py,sha256=XBjA1fflJ-e9yWRMKXyQAhrOklYzs5JRhEeGMOKWFqM,6730
3537
3537
  lino/modlib/linod/mixins.py,sha256=Dff7rX5Ih0uJul0jID6tdY5CIZa6z6iq9feXIjNohkQ,11691
3538
- lino/modlib/linod/models.py,sha256=yqGytDq7KxkHBCZpwF-7GQdR3nGh6-rBB-gLkvqoouQ,2361
3538
+ lino/modlib/linod/models.py,sha256=Jj9N84793qsx5QmBrJ8LV2hiuqnaAKwQuAGiMF-t104,2372
3539
3539
  lino/modlib/linod/routing.py,sha256=3pYJT6FbaVMj6rKkDVv3E5VTFFc9nglIDeo6dgAKu8Q,2399
3540
3540
  lino/modlib/linod/utils.py,sha256=dE973Xib6Be1DvNsZ0M5wzY_jpkk35R21WKs-jQPorM,339
3541
3541
  lino/modlib/linod/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -3577,8 +3577,8 @@ lino/modlib/office/roles.py,sha256=oQWTZ-_9_vssc9QW5A0YspMjG_V5gCiHSU0eE0AIH30,3
3577
3577
  lino/modlib/periods/__init__.py,sha256=MFTI4YTYw3zTxYVlD568rBxn02e_J9Yn10uZ6U5W-uQ,1069
3578
3578
  lino/modlib/periods/choicelists.py,sha256=_t_l40FlXYAyRnlOZzh4DR5wMCM1nIWiIfS7QKbXkmw,1304
3579
3579
  lino/modlib/periods/mixins.py,sha256=QQXtu1Z6HjqDlL_xMM98FR7PQGKhYgjslhusOiWwPDA,4271
3580
- lino/modlib/periods/models.py,sha256=hu1myxDqrBMIiORNYWBpAIDz-WjDe3LmF2JhrvG6DeI,8444
3581
- lino/modlib/periods/fixtures/std.py,sha256=iV6a2DNAhfYvO1VspGkcncEmygHK94zWdDZlHUY8JSw,881
3580
+ lino/modlib/periods/models.py,sha256=ICjFPH1To5UwH8Bd_EWm2A5q-R_q0OGvilGKZzLr6uY,9107
3581
+ lino/modlib/periods/fixtures/std.py,sha256=aWzt-frGjzPDwQ2pCKU1nT3oE4xzm7AQ8uLTJqSnS54,741
3582
3582
  lino/modlib/printing/__init__.py,sha256=u1fq44d073-IDH_t8hWs1sQdlAHdsCP85sfEOMSW5L4,689
3583
3583
  lino/modlib/printing/actions.py,sha256=2-JORKcjJYuzK2kYmhqZl0gRR-4CrG7ul8mcFNfYYAI,11766
3584
3584
  lino/modlib/printing/choicelists.py,sha256=DM929pej7NUi1gm38nUr0t3-VANV95eqCLeW5ubh8y8,9712
@@ -4422,10 +4422,10 @@ lino/modlib/users/fixtures/demo.py,sha256=YHPhvjqAh-V9WHgFct2GQlQATZmS-W3Nry-X6m
4422
4422
  lino/modlib/users/fixtures/demo2.py,sha256=j2ke91wvpHs3kHpeztzV3nOG4rJvavkHv2YJo0dISdI,495
4423
4423
  lino/modlib/users/fixtures/demo_users.py,sha256=FJz5gW95PbgxrPD8BMSVnpA8SJJSPWtjyi3LMdytd5o,1805
4424
4424
  lino/modlib/users/fixtures/std.py,sha256=Eo_TdqFC7NPryLeGRfp-nbOXw3hDqxTUpddFTxUuZ74,712
4425
- lino/modlib/weasyprint/__init__.py,sha256=81eULMBEUKB_2eKtWloEEh7ijOiSoxEgGigK6LJFrGo,2253
4426
- lino/modlib/weasyprint/choicelists.py,sha256=SaqDFfJLx9IFjhtOUtnBGZNpihUAzLvj0W49YHCZt0I,1396
4425
+ lino/modlib/weasyprint/__init__.py,sha256=i0xDsTTIAHGtoStybEv_YNCFJmxGeCy-e5qAyDWWygg,2285
4426
+ lino/modlib/weasyprint/choicelists.py,sha256=A3zXUGoiU06DuC8PIM2zS3lnKQk_pahsfXPRAQfMLog,1266
4427
4427
  lino/modlib/weasyprint/models.py,sha256=op6CRRBC8NatqGgGdQHU4zcVG4fu4mRS9Mz8STG3B3g,168
4428
- lino/modlib/weasyprint/config/weasyprint/base.weasy.html,sha256=SSDxOOKj_0o5JmUUDEoXHDb_eC5tT0dyjRa9l4ta3-o,4609
4428
+ lino/modlib/weasyprint/config/weasyprint/base.weasy.html,sha256=zs2QpK7DX-uGBYs30eJAFlPyyI2UySyt_Ee17twNcgY,4617
4429
4429
  lino/modlib/wkhtmltopdf/__init__.py,sha256=1nqwVpOQH4YMhegnzrcfvXW_Xy9CIdHBHzrNvDGyVxg,773
4430
4430
  lino/modlib/wkhtmltopdf/choicelists.py,sha256=Mq7LySs-HJIXnyUHN5PR55nQyg2cgPjEQuN9JUhBmUY,1818
4431
4431
  lino/modlib/wkhtmltopdf/models.py,sha256=T7lHSSQKNcUWceNnNzq_bKEguXQ1THK5qyCDgeV6xfM,294
@@ -4579,10 +4579,10 @@ lino/templates_old/404.html,sha256=9O5EJgULwLws0X1LjNig3xT4l9Hw04MBIGswD-EAlnw,2
4579
4579
  lino/templates_old/500.html,sha256=inOR744w4FGJM_fCSQcnlk-OIYQpaBTxUQWgXQozon4,496
4580
4580
  lino/templates_old/base.html,sha256=qYqj5-u1UPtpatrFkqBr3MnwS0oFUXCyQRcuNZczDfk,1641
4581
4581
  lino/templates_old/base_site.html,sha256=NcLEk0kBT1b-SrhoGpDPUej7NExqJ9-dP1kbrvwBzrs,255
4582
- lino/utils/__init__.py,sha256=6zmXTodRdV_hmdCcWsn_vBJUFlMZupc8O55vAOvWS9w,16838
4582
+ lino/utils/__init__.py,sha256=28o4xeRyrgkRVsUoAd0C3RJhhpIUSMNRU_-d44PulXs,17249
4583
4583
  lino/utils/addressable.py,sha256=o7bmLbyvrmOoAT478s7XqjWKvnZ7zSXj4k7Xf0ccqf8,2213
4584
4584
  lino/utils/ajax.py,sha256=npCS0WumhTQlBzXxQPKnp2sYCRcPsYcbFqzE2ykVc4Q,3254
4585
- lino/utils/choosers.py,sha256=9jjeLz-QcWVBfR8_GdY4PNYoqIYM63OI3OvOL2l1ZaU,17604
4585
+ lino/utils/choosers.py,sha256=8-D4ccUqxveu5r7lHJdunywEkwMT7E5tYsPKPk6Neto,17994
4586
4586
  lino/utils/code.py,sha256=-2vc8npHQ5jsrRqzxmjRZZ8FWY7QJ7-sRg7-ZhOsUEU,7053
4587
4587
  lino/utils/config.py,sha256=gtaJURPOwHyV_AW7i7_vin3KTj3UwZa4iviDbldwXGE,8568
4588
4588
  lino/utils/cycler.py,sha256=2LGMhMJX5At5ln4yZvmNleWROs-CA_YE_UCzxzvxK4U,1548
@@ -4635,8 +4635,8 @@ lino/utils/xml.py,sha256=EGDnO1UaREst9fS7KTESdbHnrrVCwKbRQdvut6B6GmQ,1612
4635
4635
  lino/utils/mldbc/__init__.py,sha256=QqWRlzeXaOmFfbCk-vTY3SZMn1-FCf67XnpZdd_Nim0,1134
4636
4636
  lino/utils/mldbc/fields.py,sha256=tAX8G5UKigr9c6g0F3ARIjZZtg406mdaZ--PWSbiH9E,2873
4637
4637
  lino/utils/mldbc/mixins.py,sha256=CkYe5jDa7xp9fJq_V8zcZf8ocxgIjUgHc9KZccvA_Yw,1945
4638
- lino-25.5.0.dist-info/METADATA,sha256=5TiawKicYEoAhL81Eg0HOAp-GZ6oXqSXl_m6o-yhO3A,42534
4639
- lino-25.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4640
- lino-25.5.0.dist-info/licenses/AUTHORS.rst,sha256=8VEm_G4HOmYEa4oi1nVoKKsdo4JanekEJCefWd2E8vk,981
4641
- lino-25.5.0.dist-info/licenses/COPYING,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
4642
- lino-25.5.0.dist-info/RECORD,,
4638
+ lino-25.5.1.dist-info/METADATA,sha256=zZMb_Y5CgBuAGaAwtzKtOzuEE1Fp2o8KrE-VAZoKD78,42534
4639
+ lino-25.5.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4640
+ lino-25.5.1.dist-info/licenses/AUTHORS.rst,sha256=8VEm_G4HOmYEa4oi1nVoKKsdo4JanekEJCefWd2E8vk,981
4641
+ lino-25.5.1.dist-info/licenses/COPYING,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
4642
+ lino-25.5.1.dist-info/RECORD,,
File without changes