lino 24.10.0__py3-none-any.whl → 24.10.2__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
@@ -26,7 +26,7 @@ defines no models, some template files, a series of :term:`django-admin commands
26
26
 
27
27
  """
28
28
 
29
- __version__ = '24.10.0'
29
+ __version__ = '24.10.2'
30
30
 
31
31
  # import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
32
32
 
lino/api/doctest.py CHANGED
@@ -7,9 +7,9 @@ A selection of names to be used in tested documents.
7
7
 
8
8
  import six # TODO: remove here and then run all doctests
9
9
  import logging
10
- import django
11
10
  import sqlparse
12
-
11
+ from urllib.parse import urlencode
12
+ import django
13
13
  django.setup()
14
14
  from lino.core.constants import *
15
15
  from lino.api.shell import *
@@ -63,12 +63,18 @@ HttpQuery = collections.namedtuple(
63
63
 
64
64
  def get_json_dict(username, uri, an="detail", **kwargs):
65
65
  url = "/api/{0}?fmt=json&an={1}".format(uri, an)
66
- for k, v in kwargs.items():
67
- url += "&{}={}".format(k, v)
66
+ if kwargs:
67
+ url += "&" + urlencode(kwargs, True)
68
+ # for k, v in kwargs.items():
69
+ # url += "&{}={}".format(k, v)
68
70
  test_client.force_login(rt.login(username).user)
69
71
  res = test_client.get(url, REMOTE_USER=username)
70
72
  assert res.status_code == 200
71
- return json.loads(res.content.decode())
73
+ s = res.content.decode()
74
+ try:
75
+ return json.loads(s)
76
+ except Exception as e:
77
+ raise Exception(f"GET {url} got non-JSON response:\n{s}") from None
72
78
 
73
79
 
74
80
  def get_json_soup(username, uri, fieldname, **kwargs):
lino/core/__init__.py CHANGED
@@ -39,7 +39,6 @@ For some modules the documentation has already been migrated to prosa:
39
39
  signals
40
40
  store
41
41
  tables
42
- tablerequest
43
42
  urls
44
43
  userprefs
45
44
  user_types
lino/core/actions.py CHANGED
@@ -33,7 +33,7 @@ from .utils import resolve_model
33
33
  from .utils import navinfo
34
34
  from .utils import Parametrizable
35
35
  from .utils import traverse_ddh_fklist
36
- from .requests import InstanceAction
36
+ from .utils import InstanceAction
37
37
 
38
38
 
39
39
  def discover_choosers():
lino/core/actors.py CHANGED
@@ -28,7 +28,6 @@ from lino.core import fields
28
28
  from lino.core import actions
29
29
  from lino.core import layouts
30
30
  from lino.core import constants
31
- from lino.core.requests import ActionRequest
32
31
  from lino.core.boundaction import BoundAction
33
32
  from lino.core.exceptions import ChangedAPI
34
33
  from lino.core.constants import _handle_attr_name
@@ -163,22 +162,12 @@ class ActorMetaClass(type):
163
162
 
164
163
  cls = super().__new__(meta, classname, bases, classDict)
165
164
 
166
- # ~ if not classDict.has_key('label'):
167
- # ~ cls.label = ClassProperty(cls.get_actor_label)
168
- # ~ meta.label = property(cls.get_actor_label.im_func) # 20130906
169
- # ~ if not classDict.has_key('known_values'):
170
- # ~ cls.known_values = ClassProperty(cls.get_known_values)
171
- # ~ meta.known_values = property(cls.get_known_values.im_func) # 20130906
172
- """
173
- On 20110822 I thought "A Table always gets the app_label of its model,
174
- you cannot set this yourself in a subclass
175
- because otherwise it gets complex when inheriting reports from other
176
- app_labels."
177
- On 20110912 I cancelled change 20110822 because PersonsByOffer
178
- should clearly get app_label 'jobs' and not 'contacts'.
179
-
180
- """
181
-
165
+ # On 20110822 I thought "A Table always gets the app_label of its model,
166
+ # you cannot set this yourself in a subclass
167
+ # because otherwise it gets complex when inheriting reports from other
168
+ # app_labels."
169
+ # On 20110912 I cancelled change 20110822 because PersonsByOffer
170
+ # should clearly get app_label 'jobs' and not 'contacts'.
182
171
  if classDict.get("app_label", None) is None:
183
172
  # Figure out the app_label by looking one level up.
184
173
  # For 'django.contrib.sites.models', this would be 'sites'.
@@ -1794,6 +1783,7 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
1794
1783
  Return an action request on this actor.
1795
1784
  """
1796
1785
  kw.update(actor=self)
1786
+ from lino.core.requests import ActionRequest
1797
1787
  return ActionRequest(*args, **kw)
1798
1788
 
1799
1789
  @classmethod
@@ -1843,11 +1833,16 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
1843
1833
  # welfare/docs/specs/weleup/weleup1r.rst
1844
1834
  # kwargs.update(request=ar.request)
1845
1835
  kwargs.update(parent=ar)
1846
-
1847
- ar = cls.request(master, **kwargs)
1848
- ar.renderer = settings.SITE.kernel.default_renderer
1849
- el = ar.table2xhtml()
1850
- toolbar = ar.plain_toolbar_buttons()
1836
+ kwargs.update(renderer=settings.SITE.kernel.default_renderer)
1837
+ try:
1838
+ ar = cls.request(master_instance=master, **kwargs)
1839
+ el = ar.table2xhtml()
1840
+ toolbar = ar.plain_toolbar_buttons()
1841
+ except Exception as e:
1842
+ msg = f"20241004 Error in {repr(ar)}: {e}"
1843
+ raise Exception(msg) from e
1844
+ logger.warning(msg)
1845
+ return msg
1851
1846
  if len(toolbar):
1852
1847
  el = E.div(el, E.p(*toolbar))
1853
1848
  return el
lino/core/choicelists.py CHANGED
@@ -368,28 +368,43 @@ class ChoiceList(with_metaclass(ChoiceListMeta, tables.AbstractTable)):
368
368
  """A string with the name of a choice to be used as default value for
369
369
  fields using this list.
370
370
 
371
+ Usage example in :mod:`lino_xl.lib.clients.models`::
372
+
373
+ class ClientStates(dd.Workflow):
374
+ required_roles = dd.login_required(ContactsStaff)
375
+ verbose_name_plural = _("Client states")
376
+ default_value = 'newcomer'
377
+
378
+ add = ClientStates.add_item
379
+ add('10', _("Newcomer"), 'newcomer')
380
+ add('20', _("Refused"), 'refused')
381
+ add('30', _("Coached"), 'coached')
382
+ add('50', _("Former"), 'former')
383
+
384
+ And :mod:`lino_avant.lib.avanti.models` overrides it::
385
+
386
+ from lino_xl.lib.clients.choicelists import ClientStates
387
+ ClientStates.default_value = 'coached'
388
+
371
389
  Note that this specifies the *default* default value for *all*
372
390
  :class:`ChoiceListField <lino.core.choicelists.ChoiceListField>`
373
391
  of this choicelist, including parameter fields.
374
392
 
393
+ The disadvantage is that when somebody *does not* want your default value,
394
+ then they must explicitly specify `default=''` when defining a field on your
395
+ choicelist.
396
+
375
397
  You can remove that "default default value" for all tables by
376
398
  specifying `default=''`. There are two places where you can
377
399
  specify this: (a) on the parameter field itself (which then
378
400
  applies to all subtables) or (b) just on one table. For example
379
401
  (excerpt from :mod:`lino_avanti.lib.avanti`)::
380
402
 
381
- from lino_xl.lib.clients.choicelists import ClientStates
382
- ClientStates.default_value = 'coached'
383
-
384
403
  parameters = ObservedDateRange(
385
404
  ...
386
405
  client_state=ClientStates.field(blank=True, default=''))
387
406
 
388
- Note that the default values of parameter fields of a table that
389
- is used as the *models default table* will apply for the choices
390
- of pointers to that model. Concrete use case is the choicelist of
391
- `cal.Guest.partner` in :ref:`avanti`, which should show only
392
- coached clients. So instead of above code we actually now do::
407
+ Example for (b)::
393
408
 
394
409
  class MyClients(My, Clients):
395
410
  @classmethod
@@ -398,9 +413,11 @@ class ChoiceList(with_metaclass(ChoiceListMeta, tables.AbstractTable)):
398
413
  kw.update(client_state='')
399
414
  return kw
400
415
 
401
- The disadvantage is that when somebody *does not* want your default value,
402
- then they must explicitly specify `default=''` when defining a field on your
403
- choicelist.
416
+ Note that the default values of parameter fields of a table that
417
+ is used as the *model's default table* will apply for the choices
418
+ of pointers to that model. Concrete use case is the choicelist of
419
+ `cal.Guest.partner` in :ref:`avanti`, which we want to show only
420
+ coached clients.
404
421
 
405
422
  """
406
423
 
lino/core/dashboard.py CHANGED
@@ -7,9 +7,13 @@
7
7
 
8
8
  from lino.api import _
9
9
  from lino.core.permissions import Permittable
10
+ from lino.core.requests import ActionRequest
11
+ from lino.core.tables import AbstractTable
12
+ # from lino.core.actions import ShowTable
10
13
  from lino.utils.html import E, tostring, assert_safe
11
14
  from django.utils.html import mark_safe
12
15
 
16
+
13
17
  class DashboardItem(Permittable):
14
18
  """Base class for all dashboard items.
15
19
 
@@ -51,9 +55,6 @@ class DashboardItem(Permittable):
51
55
  This is a helper function for shared use by :class:`ActorItem`
52
56
  and :class:`RequestItem`.
53
57
  """
54
- from lino.core.tables import TableRequest
55
- from lino.core.requests import ActionRequest
56
-
57
58
  assert ar.user is sar.user
58
59
  # T = sar.actor
59
60
  # print("20210112 render_request()", sar.actor, sar)
@@ -72,20 +73,22 @@ class DashboardItem(Permittable):
72
73
  yield tostring(E.h2(str(sar.actor.get_title_base(sar)), " ", *elems))
73
74
 
74
75
  assert sar.renderer is not None
75
- if isinstance(sar, TableRequest):
76
+ if isinstance(sar, ActionRequest):
76
77
  # TODO 20220930 until now, dashboard was always acting as if
77
78
  # display_mode was DISPLAY_MODE_GRID
78
- for e in sar.renderer.table2story(sar, **kwargs):
79
- # assert_safe(tostring(e))
80
- yield tostring(e)
81
- elif isinstance(sar, ActionRequest):
82
- # example : courses.StatusReport in dashboard
83
- yield sar.renderer.show_story(
84
- ar, sar.actor.get_story(None, ar), **kwargs)
85
- # for e in sar.renderer.show_story(
86
- # ar, sar.actor.get_story(None, ar), **kwargs):
87
- # # assert_safe(tostring(e))
88
- # yield tostring(e)
79
+ if issubclass(sar.actor, AbstractTable):
80
+ # if isinstance(sar.bound_action.action, ShowTable):
81
+ for e in sar.renderer.table2story(sar, **kwargs):
82
+ # assert_safe(tostring(e))
83
+ yield tostring(e)
84
+ else:
85
+ # example : courses.StatusReport in dashboard
86
+ yield sar.renderer.show_story(
87
+ ar, sar.actor.get_story(None, ar), **kwargs)
88
+ # for e in sar.renderer.show_story(
89
+ # ar, sar.actor.get_story(None, ar), **kwargs):
90
+ # # assert_safe(tostring(e))
91
+ # yield tostring(e)
89
92
  else:
90
93
  raise Exception("20240908 Cannot render {}".format(sar))
91
94
  yield "Cannot render {}".format(sar)
lino/core/dbtables.py CHANGED
@@ -28,14 +28,13 @@ from lino.core import actions
28
28
  from lino.core.model import Model
29
29
  from lino.core import actors
30
30
  from lino.core import constants
31
- from lino.core.tablerequest import TableRequest
32
31
 
33
32
  from lino.core.choicelists import ChoiceListField
34
33
  from .utils import models_by_base
35
34
  # from .fields import get_data_elem_from_model
36
35
 
37
36
  from lino.core.utils import resolve_model, get_field, UnresolvedModel
38
- from lino.core.tables import AbstractTable, TableRequest
37
+ from lino.core.tables import AbstractTable
39
38
  from lino.core.gfks import ContentType, GenericForeignKey
40
39
 
41
40
  INVALID_MK = "Invalid master_key '{0}' in {1} : {2}"
@@ -129,9 +128,9 @@ def rc_name(rptclass):
129
128
  return rptclass.app_label + "." + rptclass.__name__
130
129
 
131
130
 
132
- def has_fk(rr, name):
133
- if isinstance(rr, TableRequest):
134
- return rr.actor.master_key == name
131
+ def has_fk(ar, name):
132
+ if isinstance(ar, ActionRequest) and ar.actor is not None:
133
+ return ar.actor.master_key == name
135
134
  return False
136
135
 
137
136
 
@@ -318,19 +317,24 @@ class Table(AbstractTable):
318
317
  # raise Exception("20240929")
319
318
  qs = self.get_request_queryset(ar)
320
319
  # qs = self.model.get_request_queryset(ar)
320
+ # str(qs.query)
321
321
 
322
- return qs.get(pk=pk)
323
- # try:
324
- # return qs.get(pk=pk)
325
- # # return self.get_queryset(ar).get(pk=pk)
322
+ # return qs.get(pk=pk)
323
+ try:
324
+ return qs.get(pk=pk)
326
325
  # except ValueError:
327
326
  # return None
328
- # except self.model.DoesNotExist:
329
- # import sqlparse
330
- # sql = str(qs.query).replace('"', '')
331
- # sql = sqlparse.format(sql, reindent=True, keyword_case='upper')
332
- # raise Exception("20240324 {}, {}".format(ar.param_values, sql))
333
- # return None
327
+ except self.model.DoesNotExist:
328
+ # sql = qs.query
329
+ # import sqlparse
330
+ # sql = str(sql).replace('"', '')
331
+ # sql = sqlparse.format(sql, reindent=True, keyword_case='upper')
332
+ # raise self.model.DoesNotExist(f"No row {pk} in {ar} ({sql})")
333
+ raise self.model.DoesNotExist(f"No row {pk} in {ar}") from None
334
+ # from django.core.exceptions import ObjectDoesNotExist
335
+ # assert isinstance(exc, ObjectDoesNotExist)
336
+ # raise exc
337
+ # return None
334
338
 
335
339
  # @classmethod
336
340
  # def disabled_actions(self, ar, obj): # no longer used since 20170909
@@ -630,6 +634,8 @@ class Table(AbstractTable):
630
634
  elif v is not None:
631
635
  spv[k] = v
632
636
  # print("20240506 {} = {}".format(k, v))
637
+ # e.g. in MyTickets 'user' should be an obvious field
638
+ # 20241004 caused #5768:
633
639
  ar.known_values[k] = v # make it an obvious field
634
640
 
635
641
  qs = self.model.add_param_filter(qs, **spv)
lino/core/fields.py CHANGED
@@ -1738,7 +1738,8 @@ def choices_for_field(ar, holder, field):
1738
1738
  if isinstance(field, models.ForeignKey):
1739
1739
  m = field.remote_field.model
1740
1740
  t = m.get_default_table()
1741
- qs = t.request(request=ar.request).data_iterator
1741
+ # qs = t.request(request=ar.request).data_iterator
1742
+ qs = t.request(parent=ar).data_iterator
1742
1743
 
1743
1744
  # logger.info('20120710 choices_view(FK) %s --> %s', t, qs.query)
1744
1745
 
lino/core/model.py CHANGED
@@ -33,7 +33,8 @@ from .utils import obj2unicode
33
33
  from .utils import class_dict_items
34
34
  from .signals import receiver, on_ui_created, pre_ui_delete, pre_ui_save, post_ui_save
35
35
  from .workflows import ChangeStateAction
36
- from .tablerequest import TableRequest, sliced_data_iterator
36
+ from .requests import ActionRequest, sliced_data_iterator
37
+ from .tables import AbstractTable
37
38
 
38
39
  try:
39
40
  import bleach
@@ -633,7 +634,8 @@ class Model(models.Model, fields.TableRow):
633
634
 
634
635
  @fields.htmlbox()
635
636
  def navigation_panel(self, ar):
636
- if not isinstance(ar, TableRequest):
637
+ # if not isinstance(ar, ActionRequest):
638
+ if ar is None or ar.actor is None or not issubclass(ar.actor, AbstractTable):
637
639
  return None
638
640
  items = []
639
641
  navinfo = ar.actor.get_navinfo(ar, self)
lino/core/renderer.py CHANGED
@@ -442,7 +442,7 @@ class HtmlRenderer(Renderer):
442
442
  def quick_add_buttons(self, ar):
443
443
  """Returns a HTML chunk that displays "quick add buttons" for the
444
444
  given :class:`action request
445
- <lino.core.dbtables.TableRequest>`: a button :guilabel:`[New]`
445
+ <lino.core.request.ActionRequest>`: a button :guilabel:`[New]`
446
446
  followed possibly (if the request has rows) by a
447
447
  :guilabel:`[Show last]` and a :guilabel:`[Show all]` button.
448
448
 
@@ -629,7 +629,7 @@ class HtmlRenderer(Renderer):
629
629
 
630
630
  """
631
631
  from lino.core.actors import Actor
632
- from lino.core.tables import TableRequest
632
+ from lino.core.tables import AbstractTable
633
633
  from lino.core.requests import ActionRequest
634
634
  # print(f"20240908 show_story {ar}")
635
635
 
@@ -646,19 +646,18 @@ class HtmlRenderer(Renderer):
646
646
  ar = item.default_action.request(parent=ar)
647
647
  # 20200501 elems.extend(self.table2story(ar, **kwargs))
648
648
  elems += [tostring(e) for e in self.table2story(ar, **kwargs)]
649
- elif isinstance(item, TableRequest):
650
- # print(f"20240908 TableRequest {item}")
651
- assert item.renderer is not None
652
- # 20200501 elems.extend(self.table2story(item, **kwargs))
653
- elems += [tostring(e)
654
- for e in self.table2story(item, **kwargs)]
655
649
  elif isinstance(item, ActionRequest):
656
- # example : courses.StatusReport in dashboard
657
650
  assert item.renderer is not None
658
- # 20200501 elems.append(self.show_story(ar, item.actor.get_story(None, ar), **kwargs))
659
- elems += [tostring(e)
660
- for e in self.show_story(
661
- ar, item.actor.get_story(None, ar), **kwargs)]
651
+ if issubclass(item.actor, AbstractTable):
652
+ # 20200501 elems.extend(self.table2story(item, **kwargs))
653
+ elems += [tostring(e)
654
+ for e in self.table2story(item, **kwargs)]
655
+ else:
656
+ # example : courses.StatusReport in dashboard
657
+ # 20200501 elems.append(self.show_story(ar, item.actor.get_story(None, ar), **kwargs))
658
+ elems += [tostring(e)
659
+ for e in self.show_story(
660
+ ar, item.actor.get_story(None, ar), **kwargs)]
662
661
  elif isinstance(item, DashboardItem):
663
662
  elems.extend(item.render(ar, **kwargs))
664
663
  # elems += [tostring(e) for e in item.render(ar, **kwargs)]
@@ -932,7 +931,7 @@ class TextRenderer(HtmlRenderer):
932
931
  def show_story(self, ar, story, stripped=True, **kwargs):
933
932
  """Render the given story as reStructuredText to stdout."""
934
933
  from lino.core.actors import Actor
935
- from lino.core.tables import TableRequest
934
+ from lino.core.tables import AbstractTable
936
935
  from lino.core.requests import ActionRequest
937
936
 
938
937
  try:
@@ -944,13 +943,14 @@ class TextRenderer(HtmlRenderer):
944
943
  self.show_table(ar, stripped=stripped, **kwargs)
945
944
  elif isinstance(item, DashboardItem):
946
945
  self.show_story(ar, item.get_story(ar), stripped, **kwargs)
947
- elif isinstance(item, TableRequest):
948
- self.show_table(item, stripped=stripped, **kwargs)
949
- # print(item.table2rst(*args, **kwargs))
950
946
  elif isinstance(item, ActionRequest):
951
- # example : courses.StatusReport in dashboard
952
947
  assert item.renderer is not None
953
- self.show_story(ar, item.actor.get_story(None, ar), **kwargs)
948
+ if issubclass(item.actor, AbstractTable):
949
+ self.show_table(item, stripped=stripped, **kwargs)
950
+ # print(item.table2rst(*args, **kwargs))
951
+ else:
952
+ # example : courses.StatusReport in dashboard
953
+ self.show_story(ar, item.actor.get_story(None, ar), **kwargs)
954
954
  elif isiterable(item):
955
955
  self.show_story(ar, item, stripped, **kwargs)
956
956
  # for i in self.show_story(ar, item, *args, **kwargs):