lino 25.3.4__py3-none-any.whl → 25.4.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.3.4'
34
+ __version__ = '25.4.1'
35
35
 
36
36
  # import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
37
37
 
@@ -96,6 +96,12 @@ warnings.filterwarnings(
96
96
  # TODO: get everything to work even when ResourceWarning gives an error
97
97
  # warnings.filterwarnings("error", category=ResourceWarning)
98
98
 
99
+ warnings.filterwarnings("error", category=SyntaxWarning)
100
+
101
+ # 20250401 Activating the following line caused `doctest docs/plugins/linod.rst`
102
+ # to fail with "Fatal Python error: Segmentation fault":
103
+ # warnings.filterwarnings("error", category=DeprecationWarning)
104
+
99
105
 
100
106
  # def setup_project(settings_module):
101
107
  # os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
lino/api/dd.py CHANGED
@@ -155,7 +155,7 @@ from lino.utils import IncompleteDate, read_exception
155
155
 
156
156
  from lino.utils.format_date import fdm, fdl, fdf, fdmy
157
157
  from lino.utils.format_date import fds as fds_
158
- from lino.utils.format_date import ftl
158
+ from lino.utils.format_date import ftl, ftf
159
159
 
160
160
 
161
161
  def fds(d):
@@ -77,20 +77,21 @@
77
77
  {{_("Or sign in using ")}}
78
78
  {% for e in site.get_social_auth_links() %}
79
79
  {{tostring(e)}}
80
- {% endfor %}
80
+ {% endfor %}.
81
81
  {% endif %}
82
82
  </p>
83
83
  {% if site.is_demo_site %}
84
84
  <p>
85
85
  {{_("This demo site has %d users:") %
86
- site.models.users.UsersOverview.request().get_total_count()}}
86
+ site.models.users.UsersOverview.create_request().get_total_count()}}
87
87
  </p>
88
88
  {% if False %}
89
89
  <p>{{ar.show(site.models.users.UsersOverview, display_mode="ul")}}</p>
90
90
  {% else %}
91
91
  {{as_ul('users.UsersOverview')}}
92
92
  {% endif %}
93
- <p>{{_("The password is the same for all of them: \"1234\".")}}</p>
93
+ <p>{{_("The password is the same for all of them:")}}
94
+ "{{site.plugins.users.demo_password}}".</p>
94
95
  {% endif %}
95
96
  {% endif %}
96
97
  {% endblock %}
lino/core/kernel.py CHANGED
@@ -41,7 +41,7 @@ from django.db import models
41
41
 
42
42
  # import lino # for is_testing
43
43
  from lino import logger
44
- from lino.utils import codetime
44
+ # from lino.utils import codetime
45
45
  from lino.utils.html import E
46
46
  # from lino.core.utils import format_request
47
47
  # from lino.utils import isiterable
@@ -111,22 +111,21 @@ class Kernel(object):
111
111
  self.GFK_LIST = []
112
112
  # logger.info("20140227 Kernel.__init__() done")
113
113
 
114
- _code_mtime = None
115
- _lino_version = None
116
-
117
- @property
118
- def code_mtime(self):
119
- if self._code_mtime is None:
120
- # packages = [os.environ['DJANGO_SETTINGS_MODULE'], 'lino']
121
- # self._code_mtime = codetime(*packages)
122
- if self.site.developer_site_cache:
123
- self._code_mtime = codetime()
124
- else:
125
- # On a production server we test only the timestamp of
126
- # settings.py file because otherwise the result can differ
127
- # depending on which modules have already been imported.
128
- self._code_mtime = codetime(settings.SETTINGS_MODULE)
129
- return self._code_mtime
114
+ # _code_mtime = None
115
+ #
116
+ # @property
117
+ # def code_mtime(self):
118
+ # if self._code_mtime is None:
119
+ # # packages = [os.environ['DJANGO_SETTINGS_MODULE'], 'lino']
120
+ # # self._code_mtime = codetime(*packages)
121
+ # if self.site.developer_site_cache:
122
+ # self._code_mtime = codetime()
123
+ # else:
124
+ # # On a production server we test only the timestamp of
125
+ # # settings.py file because otherwise the result can differ
126
+ # # depending on which modules have already been imported.
127
+ # self._code_mtime = codetime(settings.SETTINGS_MODULE)
128
+ # return self._code_mtime
130
129
 
131
130
  def kernel_startup(self, site):
132
131
  """This is a part of a Lino site startup. The Django Model
@@ -908,7 +907,7 @@ class Kernel(object):
908
907
  self._must_build = True
909
908
 
910
909
  def make_cache_file(self, fn, write, force=False, verbosity=1):
911
- """Make the specified cache file. This is used internally at server
910
+ """Make the specified cache file. This is used internally at site
912
911
  startup.
913
912
 
914
913
  """
@@ -919,7 +918,8 @@ class Kernel(object):
919
918
  fn = self.site.media_root / fn
920
919
  if not force and not self._must_build and fn.exists():
921
920
  mtime = os.stat(fn).st_mtime
922
- if mtime > self.code_mtime:
921
+ # if mtime > self.code_mtime:
922
+ if mtime > self.site.lino_version:
923
923
  # logger.debug("%s (%s) is up to date.", fn, time.ctime(mtime))
924
924
  return 0
925
925
 
lino/core/site.py CHANGED
@@ -558,17 +558,18 @@ class Site(object):
558
558
  break
559
559
 
560
560
  if self.master_site is None:
561
- cache_root = os.environ.get("LINO_CACHE_ROOT", None)
562
- if cache_root:
563
- # TODO: deprecate
564
- cr = Path(cache_root).absolute()
565
- if not cr.exists():
566
- msg = "LINO_CACHE_ROOT ({0}) does not exist!".format(cr)
567
- raise Exception(msg)
568
- self.site_dir = (cr / self.project_name).resolve()
569
- self.setup_cache_directory()
570
- else:
571
- self.site_dir = self.project_dir
561
+ # cache_root = os.environ.get("LINO_CACHE_ROOT", None)
562
+ # if cache_root:
563
+ # # TODO: deprecate
564
+ # cr = Path(cache_root).absolute()
565
+ # if not cr.exists():
566
+ # msg = "LINO_CACHE_ROOT ({0}) does not exist!".format(cr)
567
+ # raise Exception(msg)
568
+ # self.site_dir = (cr / self.project_name).resolve()
569
+ # self.setup_cache_directory()
570
+ # else:
571
+ # self.site_dir = self.project_dir
572
+ self.site_dir = self.project_dir
572
573
  db = self.get_database_settings()
573
574
  if db is not None:
574
575
  self.django_settings.update(DATABASES=db)
@@ -987,11 +988,12 @@ class Site(object):
987
988
  self.update_settings(MEDIA_URL="/media/")
988
989
 
989
990
  if not "STATIC_ROOT" in self.django_settings:
990
- cache_root = os.environ.get("LINO_CACHE_ROOT", None)
991
- if cache_root:
992
- p = Path(cache_root)
993
- else:
994
- p = self.site_dir
991
+ # cache_root = os.environ.get("LINO_CACHE_ROOT", None)
992
+ # if cache_root:
993
+ # p = Path(cache_root)
994
+ # else:
995
+ # p = self.site_dir
996
+ p = self.site_dir
995
997
  self.update_settings(STATIC_ROOT=str(p / "static_root"))
996
998
  if not "STATIC_URL" in self.django_settings:
997
999
  self.update_settings(STATIC_URL="/static/")
@@ -1142,40 +1144,40 @@ class Site(object):
1142
1144
 
1143
1145
  # print(20150331, self.django_settings['FIXTURE_DIRS'])
1144
1146
 
1145
- def setup_cache_directory(self):
1146
- stamp = self.site_dir / "lino_cache.txt"
1147
- this = class2str(self.__class__)
1148
- if stamp.exists():
1149
- other = stamp.read_file()
1150
- if other == this:
1151
- ok = True
1152
- else:
1153
- ok = False
1154
- for parent in self.__class__.__mro__:
1155
- if other == class2str(parent):
1156
- ok = True
1157
- break
1158
- if not ok:
1159
- # Can happen e.g. when `python -m lino.hello` is
1160
- # called. in certain conditions.
1161
- msg = (
1162
- "Cannot use {site_dir} for {this} "
1163
- "because it is used for {other}. (Settings {settings})"
1164
- )
1165
- msg = msg.format(
1166
- site_dir=self.site_dir,
1167
- this=this,
1168
- settings=self.django_settings.get("SETTINGS_MODULE"),
1169
- other=other,
1170
- )
1171
- if True:
1172
- raise Exception(msg)
1173
- else:
1174
- # print(msg)
1175
- self.site_dir = None
1176
- else:
1177
- self.makedirs_if_missing(self.site_dir)
1178
- stamp.write_file(this)
1147
+ # def setup_cache_directory(self):
1148
+ # stamp = self.site_dir / "lino_cache.txt"
1149
+ # this = class2str(self.__class__)
1150
+ # if stamp.exists():
1151
+ # other = stamp.read_file()
1152
+ # if other == this:
1153
+ # ok = True
1154
+ # else:
1155
+ # ok = False
1156
+ # for parent in self.__class__.__mro__:
1157
+ # if other == class2str(parent):
1158
+ # ok = True
1159
+ # break
1160
+ # if not ok:
1161
+ # # Can happen e.g. when `python -m lino.hello` is
1162
+ # # called. in certain conditions.
1163
+ # msg = (
1164
+ # "Cannot use {site_dir} for {this} "
1165
+ # "because it is used for {other}. (Settings {settings})"
1166
+ # )
1167
+ # msg = msg.format(
1168
+ # site_dir=self.site_dir,
1169
+ # this=this,
1170
+ # settings=self.django_settings.get("SETTINGS_MODULE"),
1171
+ # other=other,
1172
+ # )
1173
+ # if True:
1174
+ # raise Exception(msg)
1175
+ # else:
1176
+ # # print(msg)
1177
+ # self.site_dir = None
1178
+ # else:
1179
+ # self.makedirs_if_missing(self.site_dir)
1180
+ # stamp.write_file(this)
1179
1181
 
1180
1182
  def set_user_model(self, spec):
1181
1183
  # if self.user_model is not None:
@@ -2004,6 +2006,17 @@ class Site(object):
2004
2006
  s = self.plugins.jinja.render_from_ar(ar, "admin_main.html", **context)
2005
2007
  return mark_safe(s)
2006
2008
 
2009
+ _lino_version = None
2010
+
2011
+ @property
2012
+ def lino_version(self):
2013
+ p = self.site_dir / "lino_version.txt"
2014
+ if self._lino_version is None:
2015
+ if not p.exists():
2016
+ p.touch()
2017
+ self._lino_version = p.stat().st_mtime
2018
+ return self._lino_version
2019
+
2007
2020
  def build_site_cache(self, force=False, later=False, verbosity=1):
2008
2021
  from lino.modlib.users.utils import with_user_profile
2009
2022
  from lino.modlib.users.choicelists import UserTypes
@@ -2011,10 +2024,12 @@ class Site(object):
2011
2024
  # if not self.is_prepared:
2012
2025
  # self.prepare_layouts()
2013
2026
  # self.is_prepared = True
2027
+ # settings_file = self.django_settings.get("__file__")
2028
+ # Path(settings_file).touch()
2029
+ p = self.site_dir / "lino_version.txt"
2030
+ p.touch()
2014
2031
 
2015
2032
  if later:
2016
- settings_file = self.django_settings.get("__file__")
2017
- Path(settings_file).touch()
2018
2033
  # print("20230823 later")
2019
2034
  return
2020
2035
 
lino/help_texts.py CHANGED
@@ -139,7 +139,7 @@ help_texts = {
139
139
  'lino.modlib.extjs.ext_renderer.ExtRenderer.goto_instance' : _("""See JsRenderer.goto_instance(), but when this is called while the detail window is already open (only on another record), then we don’t want to redirect to another page because that would take more time."""),
140
140
  'lino.modlib.extjs.views.AdminIndex' : _("""Similar to PlainIndex"""),
141
141
  'lino.modlib.extjs.views.Restful' : _("""Used to collaborate with a restful Ext.data.Store."""),
142
- 'lino.modlib.extjs.views.ApiElement' : _("""The view that responds to r'api/(?P<app_label>\w+)/(?P<actor>\w+)/(?P<pk>[^/]+)$'."""),
142
+ 'lino.modlib.extjs.views.ApiElement' : _("""The view that responds to api/app_label/actor/pk."""),
143
143
  'lino.modlib.gfks.Plugin' : _("""Base class for this plugin."""),
144
144
  'lino.modlib.importfilters.Plugin' : _("""See /dev/plugins."""),
145
145
  'lino.modlib.jinja.Plugin' : _("""See /dev/plugins."""),
@@ -172,13 +172,6 @@ help_texts = {
172
172
  'lino.modlib.tinymce.Plugin.window_buttons2' : _("""The second row of toolbar buttons when editing in own window."""),
173
173
  'lino.modlib.tinymce.Plugin.window_buttons3' : _("""The third row of toolbar buttons when editing in own window."""),
174
174
  'lino.modlib.tinymce.Plugin.media_name' : _("""Lino currently includes three versions of TinyMCE, but for production sites we still use the eldest version 3.4.8."""),
175
- 'lino.modlib.weasyprint.Plugin' : _("""See /dev/plugins."""),
176
- 'lino.modlib.weasyprint.Plugin.header_height' : _("""Height of header in mm. Set to None if you want no header."""),
177
- 'lino.modlib.weasyprint.Plugin.footer_height' : _("""Height of footer in mm. Set to None if you want no header."""),
178
- 'lino.modlib.weasyprint.Plugin.top_right_width' : _("""Width of top-right.jpg in mm. If not given, Lino computes it based on height."""),
179
- 'lino.modlib.weasyprint.Plugin.top_right_image' : _("""The first image file found in config named either top-right.jpg or top-right.png."""),
180
- 'lino.modlib.weasyprint.Plugin.header_image' : _("""The first image file found in config named either header.jpg or header.png."""),
181
- 'lino.modlib.weasyprint.Plugin.margin' : _("""Top and bottom page margin in mm."""),
182
175
  'lino.modlib.wkhtmltopdf.Plugin' : _("""See /dev/plugins."""),
183
176
  'lino.sphinxcontrib.actordoc.CurrentLanguage' : _("""Tell Sphinx to switch to the specified language until the end of this document."""),
184
177
  'lino.sphinxcontrib.actordoc.CurrentProject' : _("""Tell Sphinx to switch to the specified project until the end of this document."""),
@@ -371,13 +364,18 @@ help_texts = {
371
364
  'lino.modlib.linod.LogLevels' : _("""A choicelist of logging levels available in this application."""),
372
365
  'lino.modlib.linod.SystemTask' : _("""Django model used to represent a background task."""),
373
366
  'lino.modlib.linod.SystemTask.start_datetime' : _("""Tells at what time exactly this job started."""),
374
- 'lino.modlib.linod.SystemTask.message' : _("""Stores information about the job, mostly logs."""),
375
- 'lino.modlib.linod.SystemTask.disabled' : _("""Tells whether the task should be ignored."""),
376
- 'lino.modlib.linod.SystemTask.log_level' : _("""The logging level to apply when running this task."""),
377
367
  'lino.modlib.linod.SystemTask.run' : _("""Performs a routine job."""),
378
368
  'lino.modlib.linod.SystemTasks' : _("""The default table for the SystemTask model."""),
379
369
  'lino.modlib.linod.Runnable' : _("""Model mixin used by SystemTask and other models."""),
370
+ 'lino.modlib.linod.Runnable.run_now' : _("""Explicitly request to tun this task as soon as possible."""),
371
+ 'lino.modlib.linod.Runnable.cancel_run' : _("""Cancel the explicit request to tun this task as soon as possible."""),
380
372
  'lino.modlib.linod.Runnable.procedure' : _("""The background procedure to run in this task."""),
373
+ 'lino.modlib.linod.Runnable.requested_at' : _("""The timestamp when a user has explicitly requested to run this task."""),
374
+ 'lino.modlib.linod.Runnable.last_start_time' : _("""The timestamp when this task has started running in the task runner."""),
375
+ 'lino.modlib.linod.Runnable.last_end_time' : _("""The timestamp when this task has finished running in the task runner."""),
376
+ 'lino.modlib.linod.Runnable.message' : _("""Stores information about the job, mostly logs."""),
377
+ 'lino.modlib.linod.Runnable.disabled' : _("""Tells whether the task should be ignored."""),
378
+ 'lino.modlib.linod.Runnable.log_level' : _("""The logging level to apply when running this task."""),
381
379
  'lino.modlib.periods.StoredYear' : _("""The Django model used to store a fiscal year."""),
382
380
  'lino.modlib.periods.StoredPeriod' : _("""The Django model used to store an accounting period."""),
383
381
  'lino.modlib.periods.StoredYears' : _("""The fiscal years defined in this database."""),
@@ -37,7 +37,7 @@ class Command(BaseCommand):
37
37
 
38
38
  # the following log message was useful on Travis 20150104
39
39
  if verbosity > 0:
40
- logger.info("`buildsite` started on %s.", project_dir)
40
+ logger.info("`buildcache` started on %s.", project_dir)
41
41
 
42
42
  # pth = project_dir / "settings.py"
43
43
  # if pth.exists():
@@ -55,4 +55,4 @@ class Command(BaseCommand):
55
55
 
56
56
  # settings.SITE.clear_site_config()
57
57
 
58
- logger.info("`buildsite` finished on %s.", project_dir)
58
+ logger.info("`buildcache` finished on %s.", project_dir)
lino/modlib/__init__.py CHANGED
@@ -55,7 +55,6 @@ Communication
55
55
 
56
56
  comments
57
57
  notify
58
- linod
59
58
 
60
59
  Enterprise Resources
61
60
  ====================
@@ -119,7 +119,7 @@ class About(EmptyTable):
119
119
  packages = set(["django"])
120
120
 
121
121
  items.append(
122
- E.li(gettext("Server timestamp"), " : ", E.b(dtfmt(site.kernel.code_mtime)))
122
+ E.li(gettext("Server timestamp"), " : ", E.b(dtfmt(site.lino_version)))
123
123
  )
124
124
 
125
125
  for p in site.installed_plugins:
@@ -1481,7 +1481,7 @@ class ExtRenderer(JsCacheRenderer):
1481
1481
  # if settings.SITE.never_build_site_cache:
1482
1482
  # yield "GEN_TIMESTAMP = '%s';" % settings.SITE.kernel.lino_version
1483
1483
  # else:
1484
- yield "GEN_TIMESTAMP = %s;" % py2js(settings.SITE.kernel.code_mtime)
1484
+ yield "GEN_TIMESTAMP = %s;" % py2js(settings.SITE.lino_version)
1485
1485
 
1486
1486
  return "\n".join(fn())
1487
1487
 
@@ -138,7 +138,7 @@ def test_version_mismatch(request):
138
138
  if os.environ.get("PYCHARM_HOSTED", False):
139
139
  return {}
140
140
  lv = request.GET.get(constants.URL_PARAM_LINO_VERSION)
141
- if lv is None or float(lv) == settings.SITE.kernel.code_mtime:
141
+ if lv is None or float(lv) == settings.SITE.lino_version:
142
142
  return {}
143
143
  # print("20201217", lv, settings.SITE.kernel.code_mtime)
144
144
  cache.clear()
@@ -321,8 +321,7 @@ NOT_FOUND = "%s has no row with primary key %r"
321
321
 
322
322
  class ApiElement(View):
323
323
  """
324
- The view that responds to
325
- ``r'api/(?P<app_label>\w+)/(?P<actor>\w+)/(?P<pk>[^/]+)$'``.
324
+ The view that responds to ``api/app_label/actor/pk``.
326
325
  """
327
326
 
328
327
  @method_decorator(ensure_csrf_cookie)
@@ -38,6 +38,11 @@ class RunNow(dd.Action):
38
38
  # icon_name = 'bell'
39
39
  # icon_name = 'lightning'
40
40
 
41
+ # def get_action_permission(self, ar, obj, state):
42
+ # if obj.requested_at or obj.is_running():
43
+ # return False
44
+ # return super().get_action_permission(ar, obj, state)
45
+
41
46
  def run_from_ui(self, ar, **kwargs):
42
47
  # print("20231102 RunNow", ar.selected_rows)
43
48
  for obj in ar.selected_rows:
@@ -45,10 +50,11 @@ class RunNow(dd.Action):
45
50
  if True: # dd.plugins.linod.use_channels:
46
51
  obj.last_start_time = None
47
52
  obj.last_end_time = None
53
+ obj.requested_at = timezone.now()
48
54
  obj.message = "{} requested to run this task at {}.".format(
49
55
  ar.get_user(), dd.ftl(timezone.now())
50
56
  )
51
- obj.disabled = False
57
+ # obj.disabled = False
52
58
  obj.full_clean()
53
59
  obj.save()
54
60
  else:
@@ -57,6 +63,30 @@ class RunNow(dd.Action):
57
63
  ar.set_response(refresh=True)
58
64
 
59
65
 
66
+ class CancelRun(dd.Action):
67
+ label = _("Cancel request")
68
+ help_text = _("Cancel the request to run this task asap.")
69
+ select_rows = True
70
+ button_text = "🗙" # ⛒
71
+ # icon_name = 'bell'
72
+ # icon_name = 'lightning'
73
+
74
+ # def get_action_permission(self, ar, obj, state):
75
+ # if obj.requested_at is None:
76
+ # return False
77
+ # return super().get_action_permission(ar, obj, state)
78
+
79
+ def run_from_ui(self, ar, **kwargs):
80
+ for obj in ar.selected_rows:
81
+ assert issubclass(obj.__class__, Runnable)
82
+ obj.requested_at = None
83
+ obj.message = "{} cancelled the request to run this task.".format(
84
+ ar.get_user())
85
+ obj.full_clean()
86
+ obj.save()
87
+ ar.set_response(refresh=True)
88
+
89
+
60
90
  class Runnable(Sequenced, RecurrenceSet):
61
91
  class Meta:
62
92
  abstract = True
@@ -66,6 +96,7 @@ class Runnable(Sequenced, RecurrenceSet):
66
96
  last_start_time = dd.DateTimeField(
67
97
  _("Started at"), null=True, editable=False)
68
98
  last_end_time = dd.DateTimeField(_("Ended at"), null=True, editable=False)
99
+ requested_at = dd.DateTimeField(_("Requested at"), null=True, editable=False)
69
100
  message = dd.RichTextField(
70
101
  _("Logged messages"), format="plain", editable=False)
71
102
 
@@ -74,12 +105,23 @@ class Runnable(Sequenced, RecurrenceSet):
74
105
  name = models.CharField(_("Name"), max_length=200, blank=True)
75
106
 
76
107
  run_now = RunNow()
108
+ cancel_run = CancelRun()
77
109
 
78
110
  def __str__(self):
111
+ # r = "{} ({} #{})".format(
112
+ # self.name, self._meta.verbose_name, self.seqno)
79
113
  r = "{} #{} ({})".format(
80
114
  self._meta.verbose_name, self.seqno, self.name)
81
115
  return r
82
116
 
117
+ def disabled_fields(self, ar):
118
+ rv = super().disabled_fields(ar)
119
+ if self.requested_at is None:
120
+ rv.add('cancel_run')
121
+ else:
122
+ rv.add('run_now')
123
+ return rv
124
+
83
125
  def full_clean(self, *args, **kwargs):
84
126
  super().full_clean(*args, **kwargs)
85
127
  # 20250213 The following caused 'Invalid procedure invoicing.Task for
@@ -114,6 +156,7 @@ class Runnable(Sequenced, RecurrenceSet):
114
156
  # ar.info("Start %s with logging level %s", astr(self), self.log_level)
115
157
  # forget about any previous run:
116
158
  self.last_start_time = timezone.now()
159
+ self.requested_at = None
117
160
  self.last_end_time = None
118
161
  self.message = ""
119
162
  # print("20231102 full_clean")
@@ -150,17 +193,20 @@ class Runnable(Sequenced, RecurrenceSet):
150
193
  @dd.displayfield("Status")
151
194
  def status(self, ar=None):
152
195
  if self.is_running():
153
- return _("Running since {}").format(dd.ftl(self.last_start_time))
196
+ return _("Running since {}").format(dd.ftf(self.last_start_time))
197
+ if self.requested_at is not None:
198
+ return _("Requested to run asap (since {})").format(
199
+ dd.ftf(self.requested_at))
154
200
  if self.disabled:
155
201
  return _("Disabled")
156
202
  if self.last_start_time is None or self.last_end_time is None:
157
- if self.every_unit in (Recurrences.never, None):
203
+ if self.every_unit in {Recurrences.never, None}:
158
204
  return _("Not scheduled")
159
205
  return _("Scheduled to run asap")
160
206
  next_time = self.get_next_suggested_date(self.last_end_time)
161
207
  if next_time is None:
162
208
  return _("Not scheduled")
163
- return _("Scheduled to run at {}").format(dd.ftl(next_time))
209
+ return _("Scheduled to run at {}").format(dd.ftf(next_time))
164
210
 
165
211
 
166
212
  async def start_task_runner(ar=None, max_count=None):
@@ -177,53 +223,60 @@ async def start_task_runner(ar=None, max_count=None):
177
223
  next_time = now + \
178
224
  timedelta(seconds=dd.plugins.linod.background_sleep_time)
179
225
 
226
+ tasks = []
180
227
  for cls in Procedures.task_classes():
181
228
  # asyncio.ensure_future(m.start_task_runner(ar.spawn_request()))
182
229
  # print("20240424b")
183
- tasks = cls.objects.filter(disabled=False).order_by("seqno")
230
+ async for obj in cls.objects.filter(
231
+ requested_at__isnull=False).order_by("requested_at"):
232
+ tasks.append(obj)
233
+ for cls in Procedures.task_classes():
234
+ async for obj in cls.objects.filter(
235
+ requested_at__isnull=True, disabled=False).order_by("seqno"):
236
+ tasks.append(obj)
184
237
  # print("20240424c")
185
238
  # async for self in tasks:
186
- async for self in tasks:
187
- # print("20240424d")
188
- # raise Warning("20231230")
189
- if self.last_end_time is None and self.last_start_time is not None:
190
- run_duration = now - self.last_start_time
191
- if run_duration > timedelta(hours=2):
192
- msg = "Kill {} because it has been running more than 2 hours".format(
193
- astr(self)
194
- )
195
- await ar.adebug(msg)
196
- self.last_end_time = now
197
- self.message = msg
198
- await sync_to_async(self.full_clean)()
199
- # self.full_clean()
200
- await self.asave()
201
- # self.disabled = True
202
- else:
203
- await ar.adebug("Skip running task %s", astr(self))
204
- continue
205
-
206
- if self.last_end_time is not None:
207
- nst = await sync_to_async(self.get_next_suggested_date)(
208
- self.last_end_time, ar.logger
239
+ for self in tasks:
240
+ # print("20240424d")
241
+ # raise Warning("20231230")
242
+ if self.last_end_time is None and self.last_start_time is not None:
243
+ run_duration = now - self.last_start_time
244
+ if run_duration > timedelta(hours=2):
245
+ msg = "Killed {} because running more than 2 hours".format(
246
+ astr(self)
209
247
  )
210
- if nst is None:
211
- await ar.adebug("No time suggested to start %s", astr(self))
212
- continue
213
- if nst > now:
214
- await ar.adebug("Too early to start %s", astr(self))
215
- next_time = min(next_time, nst)
216
- continue
217
-
218
- # await ar.adebug("Start %s", self)
219
- # print("20231021 1 gonna start", self)
220
- await self.start_task(ar)
221
- assert self.last_end_time is not None
248
+ await ar.adebug(msg)
249
+ self.last_end_time = now
250
+ self.message = msg
251
+ await sync_to_async(self.full_clean)()
252
+ # self.full_clean()
253
+ await self.asave()
254
+ # self.disabled = True
255
+ else:
256
+ await ar.adebug("Skip running task %s", astr(self))
257
+ continue
258
+
259
+ if self.requested_at is None and self.last_end_time is not None:
222
260
  nst = await sync_to_async(self.get_next_suggested_date)(
223
261
  self.last_end_time, ar.logger
224
262
  )
225
- if nst is not None:
263
+ if nst is None:
264
+ await ar.adebug("No time suggested to start %s", astr(self))
265
+ continue
266
+ if nst > now:
267
+ await ar.adebug("Too early to start %s", astr(self))
226
268
  next_time = min(next_time, nst)
269
+ continue
270
+
271
+ # await ar.adebug("Start %s", self)
272
+ # print("20231021 1 gonna start", self)
273
+ await self.start_task(ar)
274
+ assert self.last_end_time is not None
275
+ nst = await sync_to_async(self.get_next_suggested_date)(
276
+ self.last_end_time, ar.logger
277
+ )
278
+ if nst is not None:
279
+ next_time = min(next_time, nst)
227
280
 
228
281
  count += 1
229
282
  if max_count is not None and count >= max_count:
@@ -38,7 +38,7 @@ class SystemTasks(dd.Table):
38
38
  name
39
39
  every every_unit
40
40
  log_level disabled status
41
- last_start_time last_end_time
41
+ requested_at last_start_time last_end_time
42
42
  message
43
43
  """
44
44
  insert_layout = """
@@ -158,3 +158,7 @@ class Plugin(ad.Plugin):
158
158
  mg = site.plugins.office
159
159
  m = m.add_menu(mg.app_label, mg.verbose_name)
160
160
  m.add_action("memo.Mentions")
161
+ # m.add_action("about.About.insert_reference")
162
+
163
+ # def get_quicklinks(self):
164
+ # yield "about.About.insert_reference"
@@ -14,8 +14,9 @@ from lino.core.roles import SiteStaff
14
14
  from lino.core.gfks import gfk2lookup
15
15
  from lino.modlib.gfks.mixins import Controllable
16
16
  from lino.modlib.gfks.fields import GenericForeignKey, GenericForeignKeyIdField
17
+ from lino.modlib.about.models import About
17
18
  from .parser import split_name_rest
18
- # from .mixins import *
19
+ from .mixins import MemoReferrable
19
20
 
20
21
  # Translators: will also be concatenated with '(type)' and '(object)'
21
22
  target_label = _("Target")
@@ -71,8 +72,10 @@ class Mention(Controllable):
71
72
  obj = super()
72
73
  return obj.as_summary_item(ar, text, **kwargs)
73
74
 
75
+
74
76
  dd.update_field(Mention, 'owner', verbose_name=_("Referrer"))
75
77
 
78
+
76
79
  class Mentions(dd.Table):
77
80
  required_roles = dd.login_required(SiteStaff)
78
81
  editable = False
@@ -89,8 +92,51 @@ class Mentions(dd.Table):
89
92
  # column_names = "target *"
90
93
  # default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
91
94
 
95
+
92
96
  class MentionsByTarget(Mentions):
93
97
  label = _("Mentioned by")
94
98
  master_key = "target"
95
99
  column_names = "owner *"
96
100
  default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
101
+
102
+
103
+ CONTENT_TYPE_FIELD = dd.ForeignKey(ContentType, editable=True)
104
+
105
+
106
+ class InsertReference(dd.Action):
107
+
108
+ label = _("Insert reference")
109
+ select_rows = False
110
+ parameters = dict(
111
+ content_type=CONTENT_TYPE_FIELD,
112
+ primary_key=GenericForeignKeyIdField(CONTENT_TYPE_FIELD, editable=True)
113
+ )
114
+ params_layout = """
115
+ content_type
116
+ primary_key
117
+ """
118
+
119
+ @dd.chooser()
120
+ def content_type_choices(self):
121
+ for obj in rt.models.gfks.ContentType.objects.all():
122
+ if issubclass(obj.model_class(), MemoReferrable):
123
+ yield obj
124
+
125
+ @dd.chooser(instance_values=True)
126
+ def primary_key_choices(cls, content_type):
127
+ if content_type is None:
128
+ # print("You must select a content type")
129
+ # return []
130
+ return [("", _("You must select a content type"))]
131
+ m = content_type.model_class()
132
+ return m.objects.all()
133
+
134
+ def run_from_ui(self, ar, **kwargs):
135
+ pv = ar.action_param_values
136
+ ct = pv.content_type.model_class()
137
+ obj = ct.objects.get(pk=pv.primary_key)
138
+ txt = obj.obj2memo()
139
+ ar.success(message=txt)
140
+
141
+
142
+ About.insert_reference = InsertReference()
@@ -13,6 +13,9 @@ TODO:
13
13
  which would be empty for # and @ but "]" for memo commands.
14
14
 
15
15
  """
16
+ from etgen import etree
17
+ from django.db.models import Model
18
+ from django.conf import settings
16
19
  from lino import logger
17
20
 
18
21
  import re
@@ -24,9 +27,6 @@ import warnings
24
27
 
25
28
  warnings.filterwarnings("ignore", category=MarkupResemblesLocatorWarning)
26
29
 
27
- from django.conf import settings
28
- from django.db.models import Model
29
- from etgen import etree
30
30
 
31
31
  # COMMAND_REGEX = re.compile(r"\[(\w+)\s*((?:[^[\]]|\[.*?\])*?)\]")
32
32
  # ===...... .......=
@@ -139,7 +139,7 @@ class Parser:
139
139
  for key in self.suggesters.keys()
140
140
  ]
141
141
  )
142
- return re.compile(r"([^\w])?([" + triggers + "])(\w+)")
142
+ return re.compile(r"([^\w])?([" + triggers + r"])(\w+)")
143
143
 
144
144
  def register_command(self, cmdname, func: Callable[[Any, str, str, dict], None]):
145
145
  """Register a memo command identified by the given text `cmd`.
@@ -56,7 +56,9 @@ div.recipient {
56
56
  position:relative; left:{{100-dd.plugins.weasyprint.margin_left}}mm;
57
57
  height:30mm;
58
58
  width:80mm;
59
- border: 1px solid grey;
59
+ border: 1px solid lightgray;
60
+ border-radius: 2mm;
61
+ background-color: lightgray;
60
62
  padding: 1em;
61
63
  margin: 1em;
62
64
  }
@@ -44,7 +44,8 @@ def config_inited(app, config):
44
44
 
45
45
  def copy_custom_files(app, env, docnames):
46
46
  if app.builder.format == "html":
47
- staticdir = Path(app.builder.outdir / "_static")
47
+ # In older Sphinx version the builder.outdir was a simple string
48
+ staticdir = Path(app.builder.outdir) / "_static"
48
49
  staticdir.mkdir(exist_ok=True)
49
50
  (staticdir / "favicons").mkdir(exist_ok=True)
50
51
  for fn in ("synodal-logo.png", "favicons/favicon.ico"):
lino/utils/__init__.py CHANGED
@@ -23,6 +23,7 @@ function for general use. It has also many subpackages and submodules.
23
23
  djangotest
24
24
  dpy
25
25
  fieldutils
26
+ format_date
26
27
  html
27
28
  html2odf
28
29
  html2xhtml
lino/utils/dbhash.py CHANGED
@@ -91,15 +91,17 @@ def check_virgin(restore=True, verbose=True):
91
91
  if not can_restore:
92
92
  raise Exception(
93
93
  "Cannot restore database because some rows have been deleted")
94
- # print(f"Tidy up {len(must_delete)} rows from database")
95
- # It can happen that some rows refer to each other with a protected fk
96
- # We call bulk delete() to avoid Lino deleting the items of an invoice
97
94
  must_delete = list(must_delete.items())
95
+ if verbose:
96
+ print(f"Tidy up {len(must_delete)} rows from database: {must_delete}.")
98
97
  while len(must_delete):
99
98
  todo = []
100
99
  hope = False
101
100
  for m, added in must_delete:
102
101
  try:
102
+ # It can happen that some rows refer to each other with a
103
+ # protected fk, so we call bulk delete() to avoid Lino deleting
104
+ # the items of an invoice.
103
105
  m.objects.filter(pk__in=added).delete()
104
106
  # obj.delete()
105
107
  hope = True
lino/utils/format_date.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2009-2023 Rumma & Ko Ltd
2
+ # Copyright 2009-2025 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
  """See :doc:`/topics/datetime`.
5
5
  """
@@ -111,6 +111,11 @@ def day_and_weekday(d):
111
111
 
112
112
  def ftl(t):
113
113
  # "format time long"
114
- return "{} ({})".format(
115
- t.strftime(settings.SITE.datetime_format_strftime), naturaltime(t)
116
- )
114
+ return "{} {}".format(
115
+ t.strftime(settings.SITE.date_format_strftime),
116
+ t.strftime(settings.SITE.time_format_strftime))
117
+
118
+
119
+ def ftf(t):
120
+ # "format time full"
121
+ return "{} ({})".format(ftl(t), naturaltime(t))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lino
3
- Version: 25.3.4
3
+ Version: 25.4.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,20 +1,20 @@
1
1
  lino/.cvsignore,sha256=1vrrWoP-WD8hPfCszHHIiJEi8KUMRCt5WvoKB9TSB1k,28
2
2
  lino/SciTEDirectory.properties,sha256=rCYi_e-6h8Yx5DwXhAa6MBPlVINcl6Vv9BQDYZV2_go,28
3
- lino/__init__.py,sha256=MIAjy5Wjt2meRGS0IgNyaSajX0Pr84XYb4Ysh1YIRzQ,5915
3
+ lino/__init__.py,sha256=CcmiXMf7kjvfR3armsMJoL2tdYjk5eeMI5a5XcwanPk,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=RMFC74eOV6v5TO7dv-1jM9FFham2AyuuuzDTJgOIKfU,91231
7
+ lino/help_texts.py,sha256=MGIQvx-xvYSyfgtSHkolpJ2aQlvChEFyR9_Xw1qt49w,90988
8
8
  lino/api/__init__.py,sha256=WmzHU-rHdZ68se_nI0SmepQTGE8-cd9tPpovHRH9aag,512
9
9
  lino/api/ad.py,sha256=F6SrcKPRRalHKOZ7QLwsRWGq9hhykQIeo0b85cEk9NQ,314
10
- lino/api/dd.py,sha256=R0Hx5McdTz86Veba9SttWSOIn2lTDXdpTQEEK10XEsw,7426
10
+ lino/api/dd.py,sha256=FSv9gDstKcKM1qFgSSJpJOGhvT0bDTIqwjcPA9pbMm8,7431
11
11
  lino/api/doctest.py,sha256=87j1_xGpomQlEmUUh8CprBFbbqKuQe1OWr5iIXABWag,24028
12
12
  lino/api/rt.py,sha256=OCYWhrWnMcL988MdvBLBEP8qKQJEGXQhVoam_X0sotU,1376
13
13
  lino/api/selenium.py,sha256=bOu8UaNz3Q7lGVvxjmvrtYtSWn1xfI1f5MN5sVcdYr8,9383
14
14
  lino/api/shell.py,sha256=epyjwEZ396TiJ0AHqhVIvzX8TBIXU8xR4UHJlYOrRhc,536
15
15
  lino/config/about.html.tmpl,sha256=Vt3drpxP5f26wb1meXq_Kc8ZUAl_TGfr1bcuWuz-7L4,705
16
16
  lino/config/admin_main.html,sha256=uq179bSDST-ACy0iuOFqMfs501mdwnR7Zw9ICDFZfsc,37
17
- lino/config/admin_main_base.html,sha256=J_686VOAe9MBpDEwVovwhCSvr7zhK877bME9BhvBkSk,3969
17
+ lino/config/admin_main_base.html,sha256=oTcu3ZNQi1R45WuwzY1OTP3X800m9LeUQCUcLxutV8s,4021
18
18
  lino/config/apps.html.tmpl,sha256=UxRVdZq1BEVJWD77_mhxIB4ILenVO_Ml-w3LgumG22Y,696
19
19
  lino/config/letter_margin_bottom.html,sha256=BzASupSu5l49EGrPFpUcrK4DDm_sijoOd28HC8kmpBg,66
20
20
  lino/config/letter_margin_top.html,sha256=mTKb62bWXX0rH9FyIrwJUmptJmoC8_ZATxGUm0YW4RI,216
@@ -47,7 +47,7 @@ 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=EVufMGdX_80mZrLbDAxUJj1kjISXinM15OfojmLVuGg,46882
50
+ lino/core/kernel.py,sha256=BY86dmUaU95zw22RlhZXbbBGpuOvsxfanMqzFr6Us0w,46939
51
51
  lino/core/keyboard.py,sha256=W3jA6qtB5HMppoNnd_6PgIM7ZlyHilJEhBvdeZTY7Xk,1283
52
52
  lino/core/layouts.py,sha256=Wojx5UUyhy7PsZE8FWmiXcmZDj4wz2y_1_wlz1G560Q,28663
53
53
  lino/core/menus.py,sha256=W0Co9K-ayvfX5Zt5rgSxJrFRejtiYrwIR5EecdYXPNc,10857
@@ -59,7 +59,7 @@ lino/core/renderer.py,sha256=auQn2v9bBp6jLIH3hXRQXbrjFrTmsO1o0DwaoXBn1eE,47310
59
59
  lino/core/requests.py,sha256=67335dTFqcwfF1PvWFmWwDKmVyJvDW9dMRQJF_X_Q9I,95093
60
60
  lino/core/roles.py,sha256=PXwk436xUupxdbJcygRSYFu7ixfKjAJPQRUQ8sy0lB0,4425
61
61
  lino/core/signals.py,sha256=0JT89mkjSbRm57QZcSI9DoThoKUGkyi-egNhuLUKEds,948
62
- lino/core/site.py,sha256=SOwcz3MZNRq0HFpRUwEMhnrqX4bGAWMPBYi0sS9eIaw,82631
62
+ lino/core/site.py,sha256=RsvMMSaCHzGcVpvUWKhkW4ugP6Fj7EfQXx5NegzJ9PQ,83164
63
63
  lino/core/store.py,sha256=6pd4J5Y-U7Muz4mKFSL6K9KEZpJUbpM-I7RQTWyCo-8,51483
64
64
  lino/core/tables.py,sha256=rMMDmDKMcWJ39fD9Q_EFV7qdcXMktwO40G4ATyKuRGg,24427
65
65
  lino/core/urls.py,sha256=06QlmN1vpxjmb5snO3SPpP6lX1pMdE60bTiBiC77_vQ,2677
@@ -96,7 +96,7 @@ lino/locale/pt_BR/LC_MESSAGES/django.po,sha256=nDmkf5MQtIj9trPVTZcNNMghM4rH8nZet
96
96
  lino/locale/zh_Hant/LC_MESSAGES/django.po,sha256=J9IELaIaZmBI2Vfu9HPIVA-g45QTYIxFzftWAB1U-s0,158557
97
97
  lino/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
98
  lino/management/commands/__init__.py,sha256=raVDRiXns3SegyXEhaLZMcxfEDs7ggy2nFUN5D0f5F0,47
99
- lino/management/commands/buildcache.py,sha256=xScnNBBgD3pDmSlEjGVhR9IvHyXE2u6tx-6HO7CKlDw,1947
99
+ lino/management/commands/buildcache.py,sha256=MrOio6uBtQNW5gnbRrPIjarwUe18R-irxC7dLBBCpR4,1949
100
100
  lino/management/commands/demotest.py,sha256=yvxpl1G0Clt-iu7lY0DK9HVhaWyl_tQhYx0YUpddTWM,5041
101
101
  lino/management/commands/diag.py,sha256=vt-NlZUx5gf7T4EpbM-gle3tAwMuwfPQY8lUgxjFaUw,478
102
102
  lino/management/commands/dump2py.py,sha256=X2u6OVkSb4MVQucF6jOKDDwRc8TCBNakW3UX_9S-n-U,20344
@@ -126,10 +126,10 @@ lino/mixins/printable.py,sha256=4U8M1lrTjUeuaPwrcWoanCBo53iAxiNpSTsVctI-gI0,199
126
126
  lino/mixins/ref.py,sha256=ZnyTcpHgcbYwDwgaBwLc5cGLQqz8MmjAypSEgPB7Ql4,5272
127
127
  lino/mixins/registrable.py,sha256=0m68YGlipRhE83IsEvXATZtvyxtMsH-Dpd5TGZh_GzE,6936
128
128
  lino/mixins/sequenced.py,sha256=6FiHJX2ZIraqUofdnb082b978PISNbpd2Vr87YA_1pg,16460
129
- lino/modlib/__init__.py,sha256=TRu6zfFj6MhGJ_90D0zNzw2T0dzVWyld3l1tsgrLF3k,950
129
+ lino/modlib/__init__.py,sha256=cO31gNu2oRkp7o2v3D9gK2H7j4jF9rbVyPxPZhZQwrQ,940
130
130
  lino/modlib/about/__init__.py,sha256=jhqGQIXU1o7KkmmQjfwPKJc3buibB09Fy55carAmi7U,342
131
131
  lino/modlib/about/choicelists.py,sha256=2bxDb2u7cFacBOgLoEWrMmzQ4BJ3x1pmhdgqxzUp-Rw,1424
132
- lino/modlib/about/models.py,sha256=X5yJJlyoQ58GN9RsdlUA1KsumvRtkureWnxLPPAXWlE,5131
132
+ lino/modlib/about/models.py,sha256=7dbWnw5DXy0BWsvj9bW9ugRQIxD8B_My3yA_ReLUP5o,5126
133
133
  lino/modlib/awesomeuploader/__init__.py,sha256=pUUnpdglJ0IDMBgW-ZQwnTOPHDRjItEiIWbSwv4qvBM,1825
134
134
  lino/modlib/awesomeuploader/models.py,sha256=LaK6IzLoCX4qMkVhsgdPfgawyFPe5rcHUgBFY0PbzWg,801
135
135
  lino/modlib/awesomeuploader/config/awesomeuploader/snippet.js,sha256=cZCGi0e9lDW96vCg3KVJ5DUtg2KHLNdnPo5KPRrQxv8,1792
@@ -188,8 +188,8 @@ lino/modlib/dupable/models.py,sha256=0watviKwTiVwlArC54V3IxVVfcB1Yg5kO6ed2xCM9a0
188
188
  lino/modlib/export_excel/__init__.py,sha256=k11dEbh1VgA7cMaUdMhiJvHNboX4BqN0Z5Wj9fV7rWw,469
189
189
  lino/modlib/export_excel/models.py,sha256=MoGj3RyAj8PBy75HFGBv9Y1QnsG-H_ajRK27UDpZPKo,5094
190
190
  lino/modlib/extjs/__init__.py,sha256=6UBWAWSROwy3DfTXQmVUVJTF6eZ_e2k3BEfE4wtqVhU,10172
191
- lino/modlib/extjs/ext_renderer.py,sha256=FAuqvEpmX8skaGXN6mXZuB7cq2pC38lxACxntiJ7RvU,60614
192
- lino/modlib/extjs/views.py,sha256=Ghyv1XeJsVrRvGcvxg9Hgq5P9xBuUNwwaF4SCcLBim8,26334
191
+ lino/modlib/extjs/ext_renderer.py,sha256=DVUoCmy-knDO_i6s8yVGy8xI2qsWBA8GByiv29Lm8ts,60609
192
+ lino/modlib/extjs/views.py,sha256=7IlzseAwtGPZtpsbow103mEV8VuKIH9uhfZmv9StneA,26292
193
193
  lino/modlib/extjs/config/extjs/index.html,sha256=jO5hdNpFSkm9t0xhHD5hc8Hw1fSr6xb3zYq9aMyOI7Q,8603
194
194
  lino/modlib/extjs/config/extjs/linoweb.js,sha256=I4VYGmkK4htqZvHM9g-6psJF3pp7SvgHEI0I02Sxpvo,175127
195
195
  lino/modlib/extjs/config/extjs/service-worker.js,sha256=KEKWeehTlfBHk3r8NbsP9C5av_DukHORybxFOwbjYaQ,1767
@@ -3535,8 +3535,8 @@ lino/modlib/languages/fixtures/iso-639-3_20100707.tab,sha256=u8PwI2s8shy0_Val5-s
3535
3535
  lino/modlib/linod/__init__.py,sha256=efmj_Kz3OO2zF1lvs7P459iufYGimH1-6Ge6Cbq85tQ,2665
3536
3536
  lino/modlib/linod/choicelists.py,sha256=Cu82s1QpcGFmKUXJsg-7TSqpaESBCZKOEfxzFlJP06I,2626
3537
3537
  lino/modlib/linod/consumers.py,sha256=XBjA1fflJ-e9yWRMKXyQAhrOklYzs5JRhEeGMOKWFqM,6730
3538
- lino/modlib/linod/mixins.py,sha256=LL2V2S2Y_s1dsV_xFjQHUX8A_q7wQOfC-ndi-zgBVfc,10926
3539
- lino/modlib/linod/models.py,sha256=_5usJGbYR6EW2hPjyQ2uNLW2bC9xcFMqbZHS1LSpeVc,2344
3538
+ lino/modlib/linod/mixins.py,sha256=yE0J51bRA8u5YekrNnAlWoOBee3z5rqDE8yxHgiqGMM,12739
3539
+ lino/modlib/linod/models.py,sha256=tbPKNk3BLnnAPWvbNrWhz713b3IPM0z8UoctJcwVhhw,2357
3540
3540
  lino/modlib/linod/routing.py,sha256=FiG0JVqp9TWWkNpl9Y_50UCAJ7ZEImDXkQUhlg4aGL4,2094
3541
3541
  lino/modlib/linod/utils.py,sha256=dE973Xib6Be1DvNsZ0M5wzY_jpkk35R21WKs-jQPorM,339
3542
3542
  lino/modlib/linod/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -3544,10 +3544,10 @@ lino/modlib/linod/fixtures/linod.py,sha256=qCFU2IktRVEdt1lChwu6kZLLnd4Ppfq_x9_2q
3544
3544
  lino/modlib/linod/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3545
3545
  lino/modlib/linod/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3546
3546
  lino/modlib/linod/management/commands/linod.py,sha256=RsUgUBz-soF8wkfgUE8KJiLZltrhEeCm1m7AFNthUps,3826
3547
- lino/modlib/memo/__init__.py,sha256=5cyWewD1umtSh5rqsSTwO0uR73ZJR-6mIHR05MClq0Q,5254
3547
+ lino/modlib/memo/__init__.py,sha256=GdzNURo3v_o5PxTBZ_KNkzrjlcDzn4AO95pYBbHmk8Q,5389
3548
3548
  lino/modlib/memo/mixins.py,sha256=Ij_bFcBY_ivo5t2HWArEeRkxIQhg61iP0sTNnhZpRac,11246
3549
- lino/modlib/memo/models.py,sha256=2UbKaixPWCnP1ZkoJx_DDTtltpfYVbU3uJ1hmsZDU8A,3146
3550
- lino/modlib/memo/parser.py,sha256=YbGf1EdJqTI4rqM05ZPTdufN_-aWeV_Z85Nh47HH8Pw,12996
3549
+ lino/modlib/memo/models.py,sha256=zUEvWu0dK5QhkU3DeMqNqsrzSUzOl6DLZaJBNytfgrs,4388
3550
+ lino/modlib/memo/parser.py,sha256=AKMfdbNFl9ehxl8TTFWwQ-ngfx7CD7hUjVA6bUNBY1c,12997
3551
3551
  lino/modlib/memo/views.py,sha256=H3g_nuiMzguptXLSWaWLJClU7BefXPFn9Rh8UIVVBeg,555
3552
3552
  lino/modlib/notify/__init__.py,sha256=suo7EMVdyTPTPFwuU2KZL25PwIEstTvvR4Turgdz7UE,6926
3553
3553
  lino/modlib/notify/actions.py,sha256=ClRKDjmgx3m43IZ5cx0TdxXN_pU6hLIlo_jCwEW2LhY,2468
@@ -4424,7 +4424,7 @@ lino/modlib/users/fixtures/std.py,sha256=Eo_TdqFC7NPryLeGRfp-nbOXw3hDqxTUpddFTxU
4424
4424
  lino/modlib/weasyprint/__init__.py,sha256=81eULMBEUKB_2eKtWloEEh7ijOiSoxEgGigK6LJFrGo,2253
4425
4425
  lino/modlib/weasyprint/choicelists.py,sha256=SaqDFfJLx9IFjhtOUtnBGZNpihUAzLvj0W49YHCZt0I,1396
4426
4426
  lino/modlib/weasyprint/models.py,sha256=op6CRRBC8NatqGgGdQHU4zcVG4fu4mRS9Mz8STG3B3g,168
4427
- lino/modlib/weasyprint/config/weasyprint/base.weasy.html,sha256=2R9rRFr2MeNd8Oma8H-BLQHC1fvQtU-OIzo3wofGhWg,4547
4427
+ lino/modlib/weasyprint/config/weasyprint/base.weasy.html,sha256=SSDxOOKj_0o5JmUUDEoXHDb_eC5tT0dyjRa9l4ta3-o,4609
4428
4428
  lino/modlib/wkhtmltopdf/__init__.py,sha256=1nqwVpOQH4YMhegnzrcfvXW_Xy9CIdHBHzrNvDGyVxg,773
4429
4429
  lino/modlib/wkhtmltopdf/choicelists.py,sha256=Mq7LySs-HJIXnyUHN5PR55nQyg2cgPjEQuN9JUhBmUY,1818
4430
4430
  lino/modlib/wkhtmltopdf/models.py,sha256=T7lHSSQKNcUWceNnNzq_bKEguXQ1THK5qyCDgeV6xfM,294
@@ -4436,7 +4436,7 @@ lino/sphinxcontrib/__init__.py,sha256=cz8sRK--NVr9thlToiuHRfJVelqdiYdRYmK2kyQKHq
4436
4436
  lino/sphinxcontrib/actordoc.py,sha256=CrHbqW15V1w_6Rm1bZzRBZv1CduDRZKpCcJQtOJqOCM,20710
4437
4437
  lino/sphinxcontrib/base.py,sha256=pq5u4bFSxMgPm9OMDo8xPVGhuS6MZAUd9bF6rQCCHJs,1193
4438
4438
  lino/sphinxcontrib/help_texts_extractor.py,sha256=mlHyeiIdzbfzQXkT8j8skdcBQ-FV3zAPW2DXnY8Hs1I,10385
4439
- lino/sphinxcontrib/logo/__init__.py,sha256=z4niIvjyZbxSG-Cr1lixdB6wd_ecxBrHSnVevRSnIcg,2184
4439
+ lino/sphinxcontrib/logo/__init__.py,sha256=dCHNLzhlMdSYVMui3vc2ybzPU6D4GikiRP-G9SCu1TA,2257
4440
4440
  lino/sphinxcontrib/logo/make_favicons.py,sha256=cDFmh937qfYngnxajIOmuxPRyZ9X9iNWCiRlJjq6QKI,224
4441
4441
  lino/sphinxcontrib/logo/src/lino-web-logo.xcf,sha256=AN4PbfN_HpdvW00PxkqbKmYQSOdmIXW5_zAmHxrC2Eo,68662
4442
4442
  lino/sphinxcontrib/logo/src/logo_web.xcf,sha256=Tv9JaH1WXtPkgrx39Q_H_iIr2wlU7uFToNIkVQ2h57w,119207
@@ -4578,7 +4578,7 @@ lino/templates_old/404.html,sha256=9O5EJgULwLws0X1LjNig3xT4l9Hw04MBIGswD-EAlnw,2
4578
4578
  lino/templates_old/500.html,sha256=inOR744w4FGJM_fCSQcnlk-OIYQpaBTxUQWgXQozon4,496
4579
4579
  lino/templates_old/base.html,sha256=qYqj5-u1UPtpatrFkqBr3MnwS0oFUXCyQRcuNZczDfk,1641
4580
4580
  lino/templates_old/base_site.html,sha256=NcLEk0kBT1b-SrhoGpDPUej7NExqJ9-dP1kbrvwBzrs,255
4581
- lino/utils/__init__.py,sha256=MFwWwBqgr6pPomfpfB9DvsXiB16EVBpWSSNanmI7Vqw,18391
4581
+ lino/utils/__init__.py,sha256=r3PhUv44LoVlci5lARpqvq-8re6IwmfXxbTjDU-FVDs,18407
4582
4582
  lino/utils/addressable.py,sha256=o7bmLbyvrmOoAT478s7XqjWKvnZ7zSXj4k7Xf0ccqf8,2213
4583
4583
  lino/utils/ajax.py,sha256=npCS0WumhTQlBzXxQPKnp2sYCRcPsYcbFqzE2ykVc4Q,3254
4584
4584
  lino/utils/choosers.py,sha256=9jjeLz-QcWVBfR8_GdY4PNYoqIYM63OI3OvOL2l1ZaU,17604
@@ -4589,13 +4589,13 @@ lino/utils/daemoncommand.py,sha256=NjGShiz09fddIV0WU0jK2nzO_CwPj1MfdmgwAOYZi4M,1
4589
4589
  lino/utils/dataserializer.py,sha256=-_xHXaGwDSO6-sYEHEa2BtEmKS8bW6gsYx4dV-GbvDs,3779
4590
4590
  lino/utils/dates.py,sha256=eWF5WxA5uJf51Y9PKvDVBWD8yIf6yBF6oO6TeU3ujzw,1030
4591
4591
  lino/utils/dbfreader.py,sha256=4sXOGBKX6DFQCEPkCMfnJAVneHMyDzJQB5tsYAq90vQ,12205
4592
- lino/utils/dbhash.py,sha256=9qZS64tri2A88fX7iKRQVx0Xks7ow2r9oo020paYmSg,3368
4592
+ lino/utils/dbhash.py,sha256=S2jCCYUItovF8Qkc2kVk5WS14U1c3RjijkUGqYs20_c,3450
4593
4593
  lino/utils/dblogger.py,sha256=kr0YxQY6veymvNg5A4tsvkqW8haRWdwqL0C-_9_QTg0,721
4594
4594
  lino/utils/diag.py,sha256=8BGsPrLd1_Fympy3PKiTpX1MdMWGApXr6IBoVOkWRxE,18314
4595
4595
  lino/utils/djangotest.py,sha256=Phz1qNp0wDonZRja5dxbCk0Xl3a73gZNiKK8v9tAgZg,8334
4596
4596
  lino/utils/dpy.py,sha256=Hw4ofFnhRPAE2PsPf9r5RpzcfVLQdIjtOe-XtMMLtuE,20661
4597
4597
  lino/utils/fieldutils.py,sha256=IfwuTpSirKYEk5h1URxQ9CF6i0ZPcsuNQHyk-LQOdRE,2874
4598
- lino/utils/format_date.py,sha256=HZ3wbN-1OjhawYG8-w2_TwgT33yYDuUvU877j-3QX7Q,2888
4598
+ lino/utils/format_date.py,sha256=esVElXGtmc_M5CAoFyomVr1hoi3ITn95e1ToZ09li-U,3008
4599
4599
  lino/utils/html.py,sha256=pcE0UQmdQGxxmb-p0mBb47zNbRMXLP9cxxrXTLs4gbY,3143
4600
4600
  lino/utils/html2odf.py,sha256=Hxw4HiIHY1ZCjb4_JLykVHbr6yAMhhHrnrCnLNDYKAs,4826
4601
4601
  lino/utils/html2xhtml.py,sha256=fvrIoLBFpiXtYO3UYaIgAIDjf6ATvrxolQX4etxS57Y,2119
@@ -4633,8 +4633,8 @@ lino/utils/xml.py,sha256=EGDnO1UaREst9fS7KTESdbHnrrVCwKbRQdvut6B6GmQ,1612
4633
4633
  lino/utils/mldbc/__init__.py,sha256=QqWRlzeXaOmFfbCk-vTY3SZMn1-FCf67XnpZdd_Nim0,1134
4634
4634
  lino/utils/mldbc/fields.py,sha256=tAX8G5UKigr9c6g0F3ARIjZZtg406mdaZ--PWSbiH9E,2873
4635
4635
  lino/utils/mldbc/mixins.py,sha256=CkYe5jDa7xp9fJq_V8zcZf8ocxgIjUgHc9KZccvA_Yw,1945
4636
- lino-25.3.4.dist-info/METADATA,sha256=dfMFEG6mz8R5aCIhOUkM9Mbjot81DHXJSaectmbwZZo,42534
4637
- lino-25.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4638
- lino-25.3.4.dist-info/licenses/AUTHORS.rst,sha256=8VEm_G4HOmYEa4oi1nVoKKsdo4JanekEJCefWd2E8vk,981
4639
- lino-25.3.4.dist-info/licenses/COPYING,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
4640
- lino-25.3.4.dist-info/RECORD,,
4636
+ lino-25.4.1.dist-info/METADATA,sha256=tByKXlu49CnGrkvvWX-WVmYBSYIQyjUr3rhA5AIKys4,42534
4637
+ lino-25.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4638
+ lino-25.4.1.dist-info/licenses/AUTHORS.rst,sha256=8VEm_G4HOmYEa4oi1nVoKKsdo4JanekEJCefWd2E8vk,981
4639
+ lino-25.4.1.dist-info/licenses/COPYING,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
4640
+ lino-25.4.1.dist-info/RECORD,,
File without changes