lino 24.10.0__py3-none-any.whl → 24.10.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 +1 -1
- lino/api/doctest.py +11 -5
- lino/core/__init__.py +0 -1
- lino/core/actions.py +1 -1
- lino/core/actors.py +17 -22
- lino/core/choicelists.py +28 -11
- lino/core/dashboard.py +18 -15
- lino/core/dbtables.py +21 -15
- lino/core/model.py +4 -2
- lino/core/renderer.py +19 -19
- lino/core/requests.py +872 -295
- lino/core/store.py +6 -6
- lino/core/tables.py +7 -7
- lino/core/utils.py +134 -0
- lino/modlib/bootstrap3/views.py +4 -3
- lino/modlib/extjs/views.py +2 -1
- lino/modlib/forms/views.py +3 -3
- lino/modlib/odata/views.py +2 -2
- lino/modlib/publisher/views.py +4 -1
- lino/utils/report.py +3 -10
- {lino-24.10.0.dist-info → lino-24.10.1.dist-info}/METADATA +1 -1
- {lino-24.10.0.dist-info → lino-24.10.1.dist-info}/RECORD +25 -26
- lino/core/tablerequest.py +0 -758
- {lino-24.10.0.dist-info → lino-24.10.1.dist-info}/WHEEL +0 -0
- {lino-24.10.0.dist-info → lino-24.10.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-24.10.0.dist-info → lino-24.10.1.dist-info}/licenses/COPYING +0 -0
lino/__init__.py
CHANGED
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
|
-
|
67
|
-
url += "&
|
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
|
-
|
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
lino/core/actions.py
CHANGED
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
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
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
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
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
|
-
|
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
|
-
|
402
|
-
|
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,
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
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(
|
133
|
-
if isinstance(
|
134
|
-
return
|
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
|
-
|
324
|
-
|
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
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
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/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 .
|
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,
|
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.
|
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
|
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
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
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
|
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
|
-
|
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):
|