lino 24.10.3__py3-none-any.whl → 24.11.0__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 -10
- lino/api/rt.py +2 -3
- lino/config/admin_main_base.html +2 -2
- lino/core/actors.py +68 -33
- lino/core/choicelists.py +2 -2
- lino/core/dashboard.py +2 -1
- lino/core/dbtables.py +3 -3
- lino/core/elems.py +8 -4
- lino/core/fields.py +11 -3
- lino/core/kernel.py +4 -5
- lino/core/layouts.py +1 -1
- lino/core/model.py +1 -9
- lino/core/plugin.py +1 -0
- lino/core/renderer.py +19 -19
- lino/core/requests.py +73 -38
- lino/core/site.py +5 -45
- lino/core/store.py +12 -15
- lino/core/tables.py +0 -17
- lino/core/utils.py +28 -1
- lino/core/views.py +2 -1
- lino/help_texts.py +6 -2
- lino/management/commands/show.py +2 -4
- lino/mixins/periods.py +9 -3
- lino/mixins/polymorphic.py +3 -3
- lino/mixins/ref.py +6 -3
- lino/modlib/checkdata/__init__.py +3 -3
- lino/modlib/extjs/ext_renderer.py +1 -1
- lino/modlib/linod/consumers.py +2 -3
- lino/modlib/memo/mixins.py +1 -1
- lino/modlib/periods/__init__.py +12 -1
- lino/modlib/periods/fixtures/std.py +2 -1
- lino/modlib/periods/models.py +79 -75
- lino/modlib/printing/actions.py +2 -0
- lino/modlib/publisher/ui.py +2 -2
- lino/modlib/system/__init__.py +0 -2
- lino/modlib/system/choicelists.py +55 -1
- lino/modlib/system/models.py +1 -0
- lino/modlib/users/models.py +2 -2
- lino/modlib/weasyprint/__init__.py +2 -0
- lino/utils/__init__.py +8 -9
- lino/utils/djangotest.py +2 -1
- lino/utils/html.py +31 -0
- {lino-24.10.3.dist-info → lino-24.11.0.dist-info}/METADATA +1 -1
- {lino-24.10.3.dist-info → lino-24.11.0.dist-info}/RECORD +48 -48
- {lino-24.10.3.dist-info → lino-24.11.0.dist-info}/WHEEL +0 -0
- {lino-24.10.3.dist-info → lino-24.11.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-24.10.3.dist-info → lino-24.11.0.dist-info}/licenses/COPYING +0 -0
lino/core/requests.py
CHANGED
@@ -35,6 +35,7 @@ from asgiref.sync import sync_to_async
|
|
35
35
|
|
36
36
|
from lino.utils.html import E, tostring, iselement
|
37
37
|
from lino.utils import AttrDict
|
38
|
+
from lino.utils import capture_output
|
38
39
|
from lino.utils import MissingRow
|
39
40
|
from lino.utils.html import html2text
|
40
41
|
from lino.core import constants
|
@@ -237,8 +238,8 @@ class ValidActionResponses(object):
|
|
237
238
|
|
238
239
|
|
239
240
|
inheritable_attrs = frozenset(
|
240
|
-
"user subst_user renderer requesting_panel master_instance
|
241
|
-
)
|
241
|
+
["user", "subst_user", "renderer", "requesting_panel", "master_instance",
|
242
|
+
"logger", "show_urls"] )
|
242
243
|
|
243
244
|
|
244
245
|
def bool2text(x):
|
@@ -311,6 +312,8 @@ class BaseRequest:
|
|
311
312
|
no_data_text = _("No data to display")
|
312
313
|
is_on_main_actor = True
|
313
314
|
permalink_uris = False
|
315
|
+
show_urls = True
|
316
|
+
master = None
|
314
317
|
master_instance = None
|
315
318
|
request = None
|
316
319
|
selected_rows = []
|
@@ -326,12 +329,13 @@ class BaseRequest:
|
|
326
329
|
self,
|
327
330
|
request=None,
|
328
331
|
parent=None,
|
329
|
-
hash_router=None,
|
330
332
|
is_on_main_actor=True,
|
331
333
|
permalink_uris=None,
|
332
334
|
**kw
|
333
335
|
):
|
334
336
|
self.response = dict()
|
337
|
+
# if str(kw.get('actor', None)) == "cal.EntriesByController":
|
338
|
+
# if kw.get('actor', None):
|
335
339
|
if request is not None:
|
336
340
|
assert parent is None
|
337
341
|
self.request = request
|
@@ -339,8 +343,6 @@ class BaseRequest:
|
|
339
343
|
kw = self.parse_req(request, self.rqdata, **kw)
|
340
344
|
if permalink_uris is None:
|
341
345
|
permalink_uris = False # todo: which default value?
|
342
|
-
# if hash_router is None:
|
343
|
-
# hash_router = False
|
344
346
|
elif parent is not None:
|
345
347
|
# if parent.actor is None:
|
346
348
|
# 20190926 we want to have javascript extjs links in dasboard
|
@@ -369,15 +371,16 @@ class BaseRequest:
|
|
369
371
|
# is_on_main_actor = False
|
370
372
|
if permalink_uris is None:
|
371
373
|
permalink_uris = parent.permalink_uris
|
372
|
-
|
373
|
-
|
374
|
+
# else:
|
375
|
+
# kw.setdefault("show_urls", False)
|
374
376
|
|
375
377
|
self.is_on_main_actor = is_on_main_actor
|
376
378
|
self.permalink_uris = permalink_uris
|
377
|
-
# self.hash_router = hash_router
|
378
379
|
|
379
380
|
self.setup(**kw)
|
380
381
|
|
382
|
+
# print("20241101", self.__class__.__name__, self.show_urls)
|
383
|
+
|
381
384
|
if self.master is not None and settings.SITE.strict_master_check:
|
382
385
|
if self.master_instance is None:
|
383
386
|
raise exceptions.BadRequest(
|
@@ -407,6 +410,7 @@ class BaseRequest:
|
|
407
410
|
renderer=None,
|
408
411
|
xcallback_answers=None,
|
409
412
|
known_values={},
|
413
|
+
show_urls=None,
|
410
414
|
):
|
411
415
|
if logger is not None:
|
412
416
|
self.logger = logger
|
@@ -418,27 +422,31 @@ class BaseRequest:
|
|
418
422
|
self.current_project = current_project
|
419
423
|
if renderer is None:
|
420
424
|
renderer = settings.SITE.kernel.text_renderer
|
425
|
+
# renderer = settings.SITE.kernel.default_renderer
|
421
426
|
self.renderer = renderer
|
427
|
+
if show_urls is not None:
|
428
|
+
self.show_urls = show_urls
|
422
429
|
self.subst_user = subst_user
|
423
430
|
if xcallback_answers is not None:
|
424
431
|
self.xcallback_answers = xcallback_answers
|
425
432
|
|
426
|
-
if
|
427
|
-
master
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
master_instance
|
439
|
-
self.
|
440
|
-
|
441
|
-
|
433
|
+
if self.actor is not None:
|
434
|
+
if master is None:
|
435
|
+
master = self.actor.master
|
436
|
+
if master_type is not None and ContentType is not None:
|
437
|
+
try:
|
438
|
+
master = ContentType.objects.get(pk=master_type).model_class()
|
439
|
+
except ContentType.DoesNotExist:
|
440
|
+
raise exceptions.BadRequest("Invalid master_type {}".format(master_type)) from None
|
441
|
+
self.master = master
|
442
|
+
|
443
|
+
if self.master is not None and self.master_instance is None:
|
444
|
+
# master_instance might have been inherited from parent
|
445
|
+
if master_instance is None:
|
446
|
+
master_instance = self.get_master_instance(
|
447
|
+
self.master, master_key, master_type
|
448
|
+
)
|
449
|
+
self.master_instance = self.actor.cast_master_instance(master_instance)
|
442
450
|
|
443
451
|
self.row_meta = dict(meta=True)
|
444
452
|
|
@@ -1166,6 +1174,9 @@ class BaseRequest:
|
|
1166
1174
|
def story2rst(self, story, *args, **kwargs):
|
1167
1175
|
return self.renderer.show_story(self, story, *args, **kwargs)
|
1168
1176
|
|
1177
|
+
def shows(self, *args, **kwargs):
|
1178
|
+
return capture_output(self.show, *args, **kwargs)
|
1179
|
+
|
1169
1180
|
def show(
|
1170
1181
|
self,
|
1171
1182
|
spec=None,
|
@@ -1175,6 +1186,7 @@ class BaseRequest:
|
|
1175
1186
|
language=None,
|
1176
1187
|
nosummary=False,
|
1177
1188
|
stripped=True,
|
1189
|
+
show_links=False,
|
1178
1190
|
show_urls=False,
|
1179
1191
|
max_width=None,
|
1180
1192
|
header_links=False,
|
@@ -1194,10 +1206,14 @@ class BaseRequest:
|
|
1194
1206
|
|
1195
1207
|
:column_names: overrides default list of columns
|
1196
1208
|
|
1197
|
-
:
|
1209
|
+
:show_links: show links and other html formatting. Used
|
1198
1210
|
.e.g. in :ref:`avanti.specs.roles` where we want
|
1199
1211
|
to show whether cells are clickable or not.
|
1200
1212
|
|
1213
|
+
:show_urls: show the real URLs. URLs in doctests are usually replaced by
|
1214
|
+
"…" to increase readability. If this is explicitly set to True, Lino
|
1215
|
+
prints the full URLs.
|
1216
|
+
|
1201
1217
|
:nosummary: if it is a table with :attr:`display_mode
|
1202
1218
|
<lino.core.tables.AbstractTable.display_mode>`
|
1203
1219
|
set to ``((None, DISPLAY_MODE_SUMMARY), )``,
|
@@ -1253,6 +1269,8 @@ class BaseRequest:
|
|
1253
1269
|
ar = self.spawn(spec, **kwargs)
|
1254
1270
|
# return self.renderer.show_story(spec, **kwargs)
|
1255
1271
|
|
1272
|
+
ar.show_urls = show_urls
|
1273
|
+
|
1256
1274
|
def doit():
|
1257
1275
|
# print 20160530, ar.renderer
|
1258
1276
|
if issubclass(ar.actor, Report):
|
@@ -1275,7 +1293,7 @@ class BaseRequest:
|
|
1275
1293
|
nosummary=nosummary,
|
1276
1294
|
stripped=stripped,
|
1277
1295
|
max_width=max_width,
|
1278
|
-
|
1296
|
+
show_links=show_links,
|
1279
1297
|
display_mode=display_mode,
|
1280
1298
|
)
|
1281
1299
|
elif isinstance(ar.bound_action.action, actions.ShowDetail):
|
@@ -1630,9 +1648,9 @@ class ActionRequest(BaseRequest):
|
|
1630
1648
|
Holds information about an individual web request and provides
|
1631
1649
|
methods like
|
1632
1650
|
|
1633
|
-
- :meth:`get_user <lino.core.
|
1634
|
-
- :meth:`confirm <lino.core.
|
1635
|
-
- :meth:`spawn <lino.core.
|
1651
|
+
- :meth:`get_user <lino.core.requests.BaseRequest.get_user>`
|
1652
|
+
- :meth:`confirm <lino.core.requests.BaseRequest.confirm>`
|
1653
|
+
- :meth:`spawn <lino.core.requests.BaseRequest.spawn>`
|
1636
1654
|
|
1637
1655
|
An `ActionRequest` is also a :class:`BaseRequest` and inherits its
|
1638
1656
|
methods.
|
@@ -1845,7 +1863,7 @@ class ActionRequest(BaseRequest):
|
|
1845
1863
|
return "{0} {1}".format(self.__class__.__name__, self.bound_action)
|
1846
1864
|
|
1847
1865
|
def gen_insert_button(
|
1848
|
-
self, target, button_attrs=dict(style="float: right;"), **values
|
1866
|
+
self, target=None, button_attrs=dict(style="float: right;"), **values
|
1849
1867
|
):
|
1850
1868
|
"""
|
1851
1869
|
Generate an insert button using a cached insertable object.
|
@@ -1860,13 +1878,26 @@ class ActionRequest(BaseRequest):
|
|
1860
1878
|
The difference is that gen_insert_button is more efficient when you do
|
1861
1879
|
this more than once during a single request.
|
1862
1880
|
|
1863
|
-
`target` is the actor into which we want to insert an object.
|
1864
|
-
`
|
1881
|
+
`target` is the actor into which we want to insert an object. When this is `None`, Lino uses :attr:`self.actor`.
|
1882
|
+
`button_attrs` if given, are forwarded to :meth:`ar2button`.
|
1865
1883
|
`values` is a dict of extra default values to apply to the insertable object.
|
1866
1884
|
|
1885
|
+
The `values` must be atomized by the caller, which is especially
|
1886
|
+
important when you want to set a :term:`foreign key` field. So instead
|
1887
|
+
of saying::
|
1888
|
+
|
1889
|
+
gen_insert_button(None, user=u)
|
1890
|
+
|
1891
|
+
you must say::
|
1892
|
+
|
1893
|
+
gen_insert_button(None, user=u.pk, userHidden=str(u))
|
1894
|
+
|
1867
1895
|
First usage example is in :mod:`lino_xl.lib.calview`.
|
1896
|
+
Second usage example is :class:`lino_prima.lib.prima.PupilsAndProjects`.
|
1868
1897
|
|
1869
1898
|
"""
|
1899
|
+
if target is None:
|
1900
|
+
target = self.actor
|
1870
1901
|
if self._insert_sar is None:
|
1871
1902
|
self._insert_sar = target.insert_action.request_from(self)
|
1872
1903
|
else:
|
@@ -1877,7 +1908,7 @@ class ActionRequest(BaseRequest):
|
|
1877
1908
|
# obj = st['data_record']
|
1878
1909
|
# for k, v in values.items():
|
1879
1910
|
# setattr(obj, k, v)
|
1880
|
-
# print(
|
1911
|
+
# print(20241018, st['data_record'])
|
1881
1912
|
return self._insert_sar.ar2button(**button_attrs)
|
1882
1913
|
|
1883
1914
|
def run(self, *args, **kw):
|
@@ -2030,7 +2061,10 @@ class ActionRequest(BaseRequest):
|
|
2030
2061
|
if self.create_kw:
|
2031
2062
|
kw.update(self.create_kw)
|
2032
2063
|
if self.known_values:
|
2033
|
-
kw.update(self.known_values)
|
2064
|
+
# kw.update(self.known_values)
|
2065
|
+
for k, v in self.known_values.items():
|
2066
|
+
if not "__" in k:
|
2067
|
+
kw[k] = v
|
2034
2068
|
obj = self.actor.create_instance(self, **kw)
|
2035
2069
|
return obj
|
2036
2070
|
|
@@ -2143,8 +2177,8 @@ class ActionRequest(BaseRequest):
|
|
2143
2177
|
# actor = self.actor
|
2144
2178
|
# return super(ActorRequest, self).spawn(actor, **kw)
|
2145
2179
|
|
2146
|
-
def row_as_summary(self, obj,
|
2147
|
-
return self.actor.row_as_summary(self, obj,
|
2180
|
+
def row_as_summary(self, obj, text=None, **kwargs):
|
2181
|
+
return self.actor.row_as_summary(self, obj, text, **kwargs)
|
2148
2182
|
|
2149
2183
|
def row_as_page(self, row, **kwargs):
|
2150
2184
|
return self.actor.row_as_page(self, row, **kwargs)
|
@@ -2242,6 +2276,7 @@ class ActionRequest(BaseRequest):
|
|
2242
2276
|
column_names=None,
|
2243
2277
|
header_links=False,
|
2244
2278
|
max_width=None, # ignored
|
2279
|
+
show_links=None, # ignored
|
2245
2280
|
hide_sums=None,
|
2246
2281
|
):
|
2247
2282
|
"""
|
@@ -2431,12 +2466,12 @@ class ActionRequest(BaseRequest):
|
|
2431
2466
|
if v is None:
|
2432
2467
|
td = E.td(**cellattrs)
|
2433
2468
|
else:
|
2469
|
+
td = col.value2html(self, v, **cellattrs)
|
2470
|
+
# print("20240506 {} {}".format(col.__class__, tostring(td)))
|
2434
2471
|
nv = col.value2num(v)
|
2435
2472
|
if nv != 0:
|
2436
2473
|
sums[i] += nv
|
2437
2474
|
has_numeric_value = True
|
2438
|
-
td = col.value2html(self, v, **cellattrs)
|
2439
|
-
# print("20240506 {} {}".format(col.__class__, tostring(td)))
|
2440
2475
|
col.apply_cell_format(td)
|
2441
2476
|
self.actor.apply_cell_format(self, row, col, recno, td)
|
2442
2477
|
cells.append(td)
|
lino/core/site.py
CHANGED
@@ -621,7 +621,6 @@ class Site(object):
|
|
621
621
|
db = self.get_database_settings()
|
622
622
|
if db is not None:
|
623
623
|
self.django_settings.update(DATABASES=db)
|
624
|
-
|
625
624
|
else:
|
626
625
|
self.site_dir = self.master_site.site_dir
|
627
626
|
self._history_aware_logging = self.master_site._history_aware_logging
|
@@ -868,16 +867,13 @@ class Site(object):
|
|
868
867
|
for x in self.local_apps:
|
869
868
|
add(x)
|
870
869
|
|
871
|
-
# actual_apps = []
|
872
870
|
plugins = []
|
873
871
|
|
874
|
-
# disabled_plugins = set()
|
875
|
-
|
876
872
|
def install_plugin(app_name, needed_by=None):
|
877
873
|
# print("20210305 install_plugin({})".format(app_name))
|
878
874
|
# Django does not accept newstr, and we don't want to see
|
879
875
|
# ``u'applabel'`` in doctests.
|
880
|
-
app_name = str(app_name)
|
876
|
+
# app_name = str(app_name)
|
881
877
|
# print("20160524 install_plugin(%r)" % app_name)
|
882
878
|
app_mod = import_module(app_name)
|
883
879
|
|
@@ -907,14 +903,11 @@ class Site(object):
|
|
907
903
|
|
908
904
|
# Can an `__init__.py` file explicitly set ``Plugin =
|
909
905
|
# None``? Is that feature being used?
|
910
|
-
app_class = getattr(app_mod, "Plugin",
|
911
|
-
if app_class is None:
|
912
|
-
|
906
|
+
app_class = getattr(app_mod, "Plugin", Plugin)
|
907
|
+
# if app_class is None:
|
908
|
+
# app_class = Plugin
|
913
909
|
cfg = PLUGIN_CONFIGS.pop(k, None)
|
914
910
|
ip = app_class(self, k, app_name, app_mod, needed_by, cfg or dict())
|
915
|
-
# cfg = PLUGIN_CONFIGS.pop(k, None)
|
916
|
-
# if cfg:
|
917
|
-
# ip.configure(**cfg)
|
918
911
|
|
919
912
|
self.plugins.define(k, ip)
|
920
913
|
|
@@ -928,8 +921,6 @@ class Site(object):
|
|
928
921
|
# plugins.append(dep)
|
929
922
|
|
930
923
|
plugins.append(ip)
|
931
|
-
# for dp in ip.disables_plugins:
|
932
|
-
# disabled_plugins.add(dp)
|
933
924
|
|
934
925
|
# lino is always the first plugin:
|
935
926
|
install_plugin("lino")
|
@@ -951,17 +942,10 @@ class Site(object):
|
|
951
942
|
# afterwards.
|
952
943
|
# if self.get_auth_method() == 'session':
|
953
944
|
if self.user_model:
|
954
|
-
k =
|
945
|
+
k = "django.contrib.sessions"
|
955
946
|
if k not in self.plugins:
|
956
947
|
install_plugin(k)
|
957
948
|
|
958
|
-
# for p in plugins:
|
959
|
-
# if p.app_label in disabled_plugins \
|
960
|
-
# or p.app_name in disabled_plugins:
|
961
|
-
# plugins.remove(p)
|
962
|
-
# del self.plugins[p.app_label]
|
963
|
-
|
964
|
-
# self.update_settings(INSTALLED_APPS=tuple(actual_apps))
|
965
949
|
self.update_settings(INSTALLED_APPS=tuple([p.app_name for p in plugins]))
|
966
950
|
self.installed_plugins = tuple(plugins)
|
967
951
|
|
@@ -1009,13 +993,6 @@ class Site(object):
|
|
1009
993
|
|
1010
994
|
for p in self.installed_plugins:
|
1011
995
|
reg(p.__class__)
|
1012
|
-
# for pp in plugin_parents(p.__class__):
|
1013
|
-
# if p.app_label == 'contacts':
|
1014
|
-
# print("20160524c %s" % pp)
|
1015
|
-
# reg(p.__class__)
|
1016
|
-
|
1017
|
-
# for m, p in self.override_modlib_models.items():
|
1018
|
-
# print("20160524 %s : %s" % (m, p))
|
1019
996
|
|
1020
997
|
self.installed_plugin_modules = set()
|
1021
998
|
for p in self.installed_plugins:
|
@@ -2304,23 +2281,6 @@ class Site(object):
|
|
2304
2281
|
|
2305
2282
|
yield E.span(*p)
|
2306
2283
|
|
2307
|
-
def login(self, username=None, **kw):
|
2308
|
-
from lino.core import requests
|
2309
|
-
|
2310
|
-
self.startup()
|
2311
|
-
User = self.user_model
|
2312
|
-
if User and username:
|
2313
|
-
try:
|
2314
|
-
kw.update(user=User.objects.get(username=username))
|
2315
|
-
except User.DoesNotExist:
|
2316
|
-
raise User.DoesNotExist("'{0}' : no such user".format(username))
|
2317
|
-
|
2318
|
-
# if not 'renderer' in kw:
|
2319
|
-
# kw.update(renderer=self.ui.text_renderer)
|
2320
|
-
|
2321
|
-
# import lino.core.urls # hack: trigger ui instantiation
|
2322
|
-
return requests.BaseRequest(**kw)
|
2323
|
-
|
2324
2284
|
def get_letter_date_text(self, today=None):
|
2325
2285
|
sc = self.site_config.site_company
|
2326
2286
|
if today is None:
|
lino/core/store.py
CHANGED
@@ -53,6 +53,8 @@ from lino.utils.format_date import fds
|
|
53
53
|
from lino.utils import IncompleteDate
|
54
54
|
from lino.core.utils import DelayedValue
|
55
55
|
|
56
|
+
FIELD_TYPES = {}
|
57
|
+
|
56
58
|
|
57
59
|
class StoreField(object):
|
58
60
|
"""
|
@@ -207,6 +209,14 @@ class StoreField(object):
|
|
207
209
|
setattr(instance, self.name, v)
|
208
210
|
return True
|
209
211
|
|
212
|
+
@classmethod
|
213
|
+
def register_for_field(cls, dftype, elemtype):
|
214
|
+
if dftype in FIELD_TYPES:
|
215
|
+
raise Exception(f"Duplicate field type {dftype}")
|
216
|
+
FIELD_TYPES[dftype] = cls
|
217
|
+
from lino.core.elems import _FIELD2ELEM
|
218
|
+
_FIELD2ELEM.append((dftype, elemtype))
|
219
|
+
|
210
220
|
def format_value(self, ar, v):
|
211
221
|
"""
|
212
222
|
Return a plain textual representation as a unicode string
|
@@ -424,19 +434,6 @@ class PreviewTextStoreField(StoreField):
|
|
424
434
|
for name in self.column_names():
|
425
435
|
d[name] = getattr(row, name)
|
426
436
|
|
427
|
-
|
428
|
-
class MeasurementStoreField(StoreField):
|
429
|
-
def extract_form_data(self, obj, post_data, ar=None):
|
430
|
-
unit = self.field.measurement.STANDARD_UNIT
|
431
|
-
if hasattr(obj, "unit"):
|
432
|
-
new_unit = post_data.get("unitHidden", None)
|
433
|
-
if new_unit is not None:
|
434
|
-
unit = new_unit
|
435
|
-
elif obj.unit is not None:
|
436
|
-
unit = obj.unit.name
|
437
|
-
return self.field.measurement(**{unit: float(post_data.get(self.name, "0.0"))})
|
438
|
-
|
439
|
-
|
440
437
|
# class LinkedForeignKeyField(ForeignKeyStoreField):
|
441
438
|
|
442
439
|
# def get_rel_to(self,obj):
|
@@ -1062,13 +1059,13 @@ def create_atomizer(holder, fld, name):
|
|
1062
1059
|
return DisplayStoreField(fld, name)
|
1063
1060
|
if isinstance(fld, models.IntegerField):
|
1064
1061
|
return IntegerStoreField(fld, name)
|
1065
|
-
if isinstance(fld, fields.MeasurementField):
|
1066
|
-
return MeasurementStoreField(fld, name)
|
1067
1062
|
if isinstance(fld, fields.PreviewTextField):
|
1068
1063
|
return PreviewTextStoreField(fld, name)
|
1069
1064
|
if isinstance(fld, models.ManyToOneRel):
|
1070
1065
|
# raise Exception("20190625 {} {} {}".format(holder, fld, name))
|
1071
1066
|
return
|
1067
|
+
if (sft := FIELD_TYPES.get(fld.__class__, None)) is not None:
|
1068
|
+
return sft(fld, name)
|
1072
1069
|
kw = {}
|
1073
1070
|
if choosers.uses_simple_values(holder, fld):
|
1074
1071
|
return StoreField(fld, name, **kw)
|
lino/core/tables.py
CHANGED
@@ -639,23 +639,6 @@ method in order to sort the rows of the queryset.
|
|
639
639
|
# # print("20181230 detail_pointer() {}".format(cls))
|
640
640
|
# return obj.as_summary_item(ar)
|
641
641
|
|
642
|
-
@classmethod
|
643
|
-
def request(self, master_instance=None, **kw):
|
644
|
-
"""Return a new :class:`ActionRequest
|
645
|
-
<lino.core.requests.ActionRequest>` on this table.
|
646
|
-
|
647
|
-
The :attr:`master_instance
|
648
|
-
<lino.core.requests.ActionRequest.master_instance>` can be
|
649
|
-
specified as optional first positional argument.
|
650
|
-
|
651
|
-
"""
|
652
|
-
from lino.core.requests import ActionRequest
|
653
|
-
kw.update(actor=self)
|
654
|
-
if master_instance is not None:
|
655
|
-
kw.update(master_instance=master_instance)
|
656
|
-
kw.setdefault("action", self.default_action)
|
657
|
-
return ActionRequest(**kw)
|
658
|
-
|
659
642
|
@classmethod
|
660
643
|
def run_action_from_console(self, pk=None, an=None):
|
661
644
|
"""
|
lino/core/utils.py
CHANGED
@@ -23,6 +23,7 @@ from django.core import exceptions
|
|
23
23
|
from django.http import QueryDict
|
24
24
|
|
25
25
|
from lino.utils.html import E, assert_safe, tostring
|
26
|
+
from lino.utils import capture_output
|
26
27
|
from lino.utils.ranges import isrange
|
27
28
|
|
28
29
|
from django.core.validators import validate_email, ValidationError, URLValidator
|
@@ -1063,7 +1064,7 @@ class InstanceAction:
|
|
1063
1064
|
"""
|
1064
1065
|
if len(args) and isinstance(args[0], BaseRequest):
|
1065
1066
|
raise ChangedAPI("20181004")
|
1066
|
-
ar = self.bound_action.request()
|
1067
|
+
ar = self.bound_action.request(renderer=settings.SITE.kernel.text_renderer)
|
1067
1068
|
self.run_from_code(ar, *args, **kwargs)
|
1068
1069
|
return ar.response
|
1069
1070
|
|
@@ -1108,3 +1109,29 @@ class PhantomRow(VirtualRow):
|
|
1108
1109
|
|
1109
1110
|
def __str__(self):
|
1110
1111
|
return str(self._ar.get_action_title())
|
1112
|
+
|
1113
|
+
|
1114
|
+
|
1115
|
+
def login(username=None, **kwargs):
|
1116
|
+
"""Return a basic :term:`action request` with the specified user signed in.
|
1117
|
+
"""
|
1118
|
+
from lino.core.requests import BaseRequest # avoid circular import
|
1119
|
+
# settings.SITE.startup()
|
1120
|
+
User = settings.SITE.user_model
|
1121
|
+
if User and username:
|
1122
|
+
try:
|
1123
|
+
kwargs.update(user=User.objects.get(username=username))
|
1124
|
+
except User.DoesNotExist:
|
1125
|
+
raise User.DoesNotExist(f"'{username}' : no such user")
|
1126
|
+
|
1127
|
+
kwargs.setdefault("show_urls", False)
|
1128
|
+
# import lino.core.urls # hack: trigger ui instantiation
|
1129
|
+
return BaseRequest(**kwargs)
|
1130
|
+
|
1131
|
+
def show(*args, **kwargs):
|
1132
|
+
"""Print the specified data table to stdout."""
|
1133
|
+
return login().show(*args, **kwargs)
|
1134
|
+
|
1135
|
+
def shows(*args, **kwargs):
|
1136
|
+
"""Return the output of :func:`show`."""
|
1137
|
+
return capture_output(show, *args, **kwargs)
|
lino/core/views.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2010-
|
1
|
+
# Copyright 2010-2024 Rumma & Ko Ltd
|
2
2
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
3
3
|
"""Utility functions used by :mod:`lino.modlib.extjs.views`.
|
4
4
|
|
@@ -99,6 +99,7 @@ def action_request(app_label, actor, request, rqdata, is_list, **kw):
|
|
99
99
|
# The text of an Exception may not be
|
100
100
|
# internationalized because some error handling code
|
101
101
|
# may want to write it to a plain ascii stream.
|
102
|
+
kw.update(renderer=settings.SITE.kernel.default_renderer)
|
102
103
|
ar = rpt.request(request=request, action=a, rqdata=rqdata, **kw)
|
103
104
|
# print("20210403b", a.action.__class__, rqdata)
|
104
105
|
return ar
|
lino/help_texts.py
CHANGED
@@ -376,8 +376,11 @@ help_texts = {
|
|
376
376
|
'lino.modlib.linod.SystemTask.log_level' : _("""The logging level to apply when running this task."""),
|
377
377
|
'lino.modlib.linod.SystemTask.run' : _("""Performs a routine job."""),
|
378
378
|
'lino.modlib.linod.SystemTasks' : _("""The default actor for the SystemTask model."""),
|
379
|
-
'lino.modlib.periods.
|
380
|
-
'lino.modlib.periods.
|
379
|
+
'lino.modlib.periods.StoredYear' : _("""The Django model used to store an fiscal year."""),
|
380
|
+
'lino.modlib.periods.StoredPeriod' : _("""The Django model used to store an accounting period."""),
|
381
|
+
'lino.modlib.periods.StoredYears' : _("""The fiscal years defined in this database."""),
|
382
|
+
'lino.modlib.periods.StoredPeriods' : _("""The accounting periods defined in this database."""),
|
383
|
+
'lino.modlib.periods.PeriodTypes' : _("""A list of choices for the values allowed as periods.period_type."""),
|
381
384
|
'lino.modlib.periods.PeriodRange' : _("""Model mixin for objects that cover a range of accounting periods."""),
|
382
385
|
'lino.modlib.periods.PeriodRange.start_period' : _("""The first period of the range to cover."""),
|
383
386
|
'lino.modlib.periods.PeriodRange.end_period' : _("""The last period of the range to cover."""),
|
@@ -654,6 +657,7 @@ help_texts = {
|
|
654
657
|
'lino.modlib.system.DurationUnits' : _("""The list of possible duration units defined by this application."""),
|
655
658
|
'lino.modlib.system.DurationUnit' : _("""Base class for the choices in the DurationUnits choicelist."""),
|
656
659
|
'lino.modlib.system.DurationUnit.add_duration' : _("""Return a date or datetime obtained by adding value times this unit to the specified value orig. Returns None is orig is empty."""),
|
660
|
+
'lino.modlib.system.DisplayColors' : _("""A list of colors to be specified for displaying."""),
|
657
661
|
'lino.modlib.system.BleachChecker' : _("""A data checker used to find unbleached html content."""),
|
658
662
|
'lino.modlib.system.Genders' : _("""Defines the possible choices for the gender of a person (“male”, “female” and “nonbinary”)."""),
|
659
663
|
'lino.modlib.system.YesNo' : _("""A choicelist with two values “Yes” and “No”."""),
|
lino/management/commands/show.py
CHANGED
@@ -3,11 +3,9 @@
|
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
5
|
import argparse
|
6
|
-
|
7
|
-
from django.conf import settings
|
6
|
+
from lino.api import rt
|
8
7
|
from django.core.management.base import BaseCommand, CommandError
|
9
8
|
|
10
|
-
|
11
9
|
class Command(BaseCommand):
|
12
10
|
help = "Show the content of a specified table to standard output."
|
13
11
|
|
@@ -43,6 +41,6 @@ class Command(BaseCommand):
|
|
43
41
|
spec = args[0]
|
44
42
|
|
45
43
|
username = options["username"]
|
46
|
-
ses =
|
44
|
+
ses = rt.login(username)
|
47
45
|
|
48
46
|
ses.show(spec, language=options["language"])
|
lino/mixins/periods.py
CHANGED
@@ -214,11 +214,11 @@ class DateRangeObservable(Model):
|
|
214
214
|
def setup_parameters(cls, fields):
|
215
215
|
fields.update(
|
216
216
|
start_date=models.DateField(
|
217
|
-
_("
|
217
|
+
_("Date range from"),
|
218
218
|
blank=True,
|
219
219
|
null=True,
|
220
220
|
default=cls.get_default_start_date,
|
221
|
-
help_text=_("Start date of observed
|
221
|
+
help_text=_("Start date of observed date range."),
|
222
222
|
)
|
223
223
|
)
|
224
224
|
fields.update(
|
@@ -227,7 +227,7 @@ class DateRangeObservable(Model):
|
|
227
227
|
blank=True,
|
228
228
|
null=True,
|
229
229
|
default=cls.get_default_end_date,
|
230
|
-
help_text=_("End date of observed
|
230
|
+
help_text=_("End date of observed date range."),
|
231
231
|
)
|
232
232
|
)
|
233
233
|
super().setup_parameters(fields)
|
@@ -267,6 +267,12 @@ class DateRange(DateRangeObservable):
|
|
267
267
|
def get_period_text(self):
|
268
268
|
return rangetext(self, fdl, self.empty_period_text)
|
269
269
|
|
270
|
+
def covers_date(self, d):
|
271
|
+
if self.start_date and self.start_date > d:
|
272
|
+
return False
|
273
|
+
if self.end_date and self.end_date < d:
|
274
|
+
return False
|
275
|
+
return True
|
270
276
|
|
271
277
|
DateRange.set_widget_options("start_date", width=10)
|
272
278
|
DateRange.set_widget_options("end_date", width=10)
|
lino/mixins/polymorphic.py
CHANGED
@@ -247,16 +247,16 @@ class Polymorphic(model.Model):
|
|
247
247
|
buttons = self.get_mti_buttons(ar)
|
248
248
|
return E.span(*buttons)
|
249
249
|
|
250
|
-
def as_summary_item(self, ar,
|
250
|
+
def as_summary_item(self, ar, text=None, **kwargs):
|
251
251
|
# a = super(Polymorphic, self).get_detail_action(ar)
|
252
252
|
a = self.get_detail_action(ar)
|
253
253
|
if a is not None:
|
254
|
-
return super().as_summary_item(ar,
|
254
|
+
return super().as_summary_item(ar, text, **kwargs)
|
255
255
|
for m in self._mtinav_models:
|
256
256
|
if m is not self.__class__:
|
257
257
|
obj = mti.get_child(self, m)
|
258
258
|
if obj is not None:
|
259
259
|
if obj.get_detail_action(ar) is not None:
|
260
|
-
return obj.as_summary_item(ar,
|
260
|
+
return obj.as_summary_item(ar, text, **kwargs)
|
261
261
|
return str(self)
|
262
262
|
# return "?!? {} has no detail_action for {}".format(self, ar)
|
lino/mixins/ref.py
CHANGED
@@ -47,7 +47,7 @@ class Referrable(model.Model):
|
|
47
47
|
@classmethod
|
48
48
|
def on_analyze(cls, site):
|
49
49
|
cls.set_widget_options("ref", width=cls.ref_max_length)
|
50
|
-
super(
|
50
|
+
super().on_analyze(site)
|
51
51
|
|
52
52
|
def on_duplicate(self, ar, master):
|
53
53
|
"""
|
@@ -56,7 +56,10 @@ class Referrable(model.Model):
|
|
56
56
|
"""
|
57
57
|
if self.ref:
|
58
58
|
self.ref += " (DUP)"
|
59
|
-
super(
|
59
|
+
super().on_duplicate(ar, master)
|
60
|
+
|
61
|
+
def __str__(self):
|
62
|
+
return self.ref or super().__str__()
|
60
63
|
|
61
64
|
@staticmethod
|
62
65
|
def ref_prefix(obj, ar=None):
|
@@ -96,7 +99,7 @@ class Referrable(model.Model):
|
|
96
99
|
# if search_text.isdigit():
|
97
100
|
if search_text.startswith("*"):
|
98
101
|
return models.Q(**{prefix + "ref__icontains": search_text[1:]})
|
99
|
-
return super(
|
102
|
+
return super().quick_search_filter(search_text, prefix)
|
100
103
|
|
101
104
|
|
102
105
|
class StructuredReferrable(Referrable):
|