lino 25.4.2__py3-none-any.whl → 25.4.4__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.
Files changed (45) hide show
  1. lino/__init__.py +1 -1
  2. lino/core/kernel.py +33 -7
  3. lino/core/renderer.py +3 -3
  4. lino/core/site.py +10 -36
  5. lino/help_texts.py +3 -3
  6. lino/management/commands/demotest.py +16 -22
  7. lino/mixins/__init__.py +5 -5
  8. lino/mixins/dupable.py +2 -4
  9. lino/mixins/registrable.py +5 -2
  10. lino/modlib/about/models.py +2 -2
  11. lino/modlib/checkdata/choicelists.py +4 -4
  12. lino/modlib/checkdata/models.py +11 -3
  13. lino/modlib/comments/fixtures/demo2.py +4 -0
  14. lino/modlib/comments/models.py +1 -1
  15. lino/modlib/dupable/mixins.py +3 -5
  16. lino/modlib/extjs/ext_renderer.py +1 -1
  17. lino/modlib/extjs/views.py +1 -1
  18. lino/modlib/help/fixtures/demo2.py +3 -2
  19. lino/modlib/jinja/mixins.py +18 -5
  20. lino/modlib/linod/models.py +1 -1
  21. lino/modlib/linod/routing.py +49 -46
  22. lino/modlib/memo/mixins.py +3 -2
  23. lino/modlib/notify/api.py +33 -14
  24. lino/modlib/notify/mixins.py +4 -3
  25. lino/modlib/printing/mixins.py +1 -1
  26. lino/modlib/publisher/choicelists.py +41 -20
  27. lino/modlib/publisher/config/publisher/page.pub.html +24 -0
  28. lino/modlib/publisher/fixtures/demo2.py +12 -0
  29. lino/modlib/publisher/fixtures/std.py +2 -1
  30. lino/modlib/publisher/fixtures/synodalworld.py +17 -0
  31. lino/modlib/publisher/mixins.py +7 -0
  32. lino/modlib/publisher/models.py +77 -7
  33. lino/modlib/publisher/ui.py +5 -5
  34. lino/modlib/publisher/views.py +9 -2
  35. lino/modlib/system/models.py +1 -1
  36. lino/modlib/uploads/models.py +2 -2
  37. lino/static/bootstrap.css +1 -1
  38. lino/utils/dbfreader.py +64 -32
  39. lino/utils/dbhash.py +3 -2
  40. {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/METADATA +1 -1
  41. {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/RECORD +44 -43
  42. lino/management/commands/monitor.py +0 -160
  43. {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/WHEEL +0 -0
  44. {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/licenses/AUTHORS.rst +0 -0
  45. {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/licenses/COPYING +0 -0
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.4.2'
34
+ __version__ = '25.4.4'
35
35
 
36
36
  # import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
37
37
 
lino/core/kernel.py CHANGED
@@ -21,6 +21,7 @@ application.
21
21
 
22
22
  import os
23
23
  import sys
24
+ import time
24
25
  import codecs
25
26
  import atexit
26
27
  import signal
@@ -41,7 +42,7 @@ from django.db import models
41
42
 
42
43
  # import lino # for is_testing
43
44
  from lino import logger
44
- # from lino.utils import codetime
45
+ from lino.utils import codetime
45
46
  from lino.utils.html import E
46
47
  # from lino.core.utils import format_request
47
48
  # from lino.utils import isiterable
@@ -111,6 +112,32 @@ class Kernel(object):
111
112
  self.GFK_LIST = []
112
113
  # logger.info("20140227 Kernel.__init__() done")
113
114
 
115
+ # On a production server we test only the timestamp of settings.py file
116
+ # because otherwise the result can differ depending on which modules have
117
+ # already been imported.
118
+
119
+ _lino_version = None
120
+
121
+ def touch_lino_version(self):
122
+ if is_devserver():
123
+ self._lino_version = time.time()
124
+ else:
125
+ p = self.site.site_dir / "lino_version.txt"
126
+ p.touch()
127
+ self._lino_version = p.stat().st_mtime
128
+
129
+ @property
130
+ def lino_version(self):
131
+ if self._lino_version is None:
132
+ if is_devserver():
133
+ self._lino_version = codetime()
134
+ else:
135
+ p = self.site.site_dir / "lino_version.txt"
136
+ if not p.exists():
137
+ p.touch()
138
+ self._lino_version = p.stat().st_mtime
139
+ return self._lino_version
140
+
114
141
  # _code_mtime = None
115
142
  #
116
143
  # @property
@@ -118,13 +145,12 @@ class Kernel(object):
118
145
  # if self._code_mtime is None:
119
146
  # # packages = [os.environ['DJANGO_SETTINGS_MODULE'], 'lino']
120
147
  # # self._code_mtime = codetime(*packages)
121
- # if self.site.developer_site_cache:
148
+ # # if self.site.developer_site_cache:
149
+ # if is_devserver():
122
150
  # self._code_mtime = codetime()
123
151
  # 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)
152
+ # self._code_mtime = self.lino_version
153
+ # # self._code_mtime = codetime(settings.SETTINGS_MODULE)
128
154
  # return self._code_mtime
129
155
 
130
156
  def kernel_startup(self, site):
@@ -919,7 +945,7 @@ class Kernel(object):
919
945
  if not force and not self._must_build and fn.exists():
920
946
  mtime = os.stat(fn).st_mtime
921
947
  # if mtime > self.code_mtime:
922
- if mtime > self.site.lino_version:
948
+ if mtime > self.lino_version:
923
949
  # logger.debug("%s (%s) is up to date.", fn, time.ctime(mtime))
924
950
  return 0
925
951
 
lino/core/renderer.py CHANGED
@@ -124,6 +124,9 @@ class Renderer(object):
124
124
  # if ar is None or a.get_bound_action_permission(ar, obj, None):
125
125
  # return a
126
126
 
127
+ def add_help_text(self, kw, help_text, title, datasource, fieldname):
128
+ pass
129
+
127
130
  def get_detail_url(self, *args, **kwargs):
128
131
  return self.front_end.get_detail_url(*args, **kwargs)
129
132
 
@@ -750,9 +753,6 @@ class HtmlRenderer(Renderer):
750
753
  def goto_instance(self, ar, obj, **kw):
751
754
  pass
752
755
 
753
- def add_help_text(self, kw, help_text, title, datasource, fieldname):
754
- pass
755
-
756
756
 
757
757
  class TextRenderer(HtmlRenderer):
758
758
  """
lino/core/site.py CHANGED
@@ -559,21 +559,13 @@ class Site(object):
559
559
  break
560
560
 
561
561
  if self.master_site is None:
562
- # cache_root = os.environ.get("LINO_CACHE_ROOT", None)
563
- # if cache_root:
564
- # # TODO: deprecate
565
- # cr = Path(cache_root).absolute()
566
- # if not cr.exists():
567
- # msg = "LINO_CACHE_ROOT ({0}) does not exist!".format(cr)
568
- # raise Exception(msg)
569
- # self.site_dir = (cr / self.project_name).resolve()
570
- # self.setup_cache_directory()
571
- # else:
572
- # self.site_dir = self.project_dir
573
562
  self.site_dir = self.project_dir
574
- db = self.get_database_settings()
575
- if db is not None:
576
- self.django_settings.update(DATABASES=db)
563
+ self.django_settings.update(DATABASES={
564
+ "default": {
565
+ "ENGINE": "django.db.backends.sqlite3",
566
+ "NAME": str(self.site_dir / "default.db")
567
+ }
568
+ })
577
569
  else:
578
570
  self.site_dir = self.master_site.site_dir
579
571
  self._history_aware_logging = self.master_site._history_aware_logging
@@ -721,15 +713,6 @@ class Site(object):
721
713
  # import yaml
722
714
  # print("20231019", yaml.dump(d))
723
715
 
724
- def get_database_settings(self):
725
- if self.site_dir is None:
726
- pass # raise Exception("20160516 No site_dir")
727
- else:
728
- dbname = self.site_dir / "default.db"
729
- return {
730
- "default": {"ENGINE": "django.db.backends.sqlite3", "NAME": str(dbname)}
731
- }
732
-
733
716
  def get_anonymous_user(self):
734
717
  # The code below works even when users is not installed
735
718
  from lino.modlib.users.choicelists import UserTypes
@@ -1829,6 +1812,7 @@ class Site(object):
1829
1812
  self.install_settings()
1830
1813
 
1831
1814
  def is_imported_partner(self, obj):
1815
+ # Deprecated.
1832
1816
  # ~ return obj.id is not None and (obj.id < 200000 or obj.id > 299999)
1833
1817
  return False
1834
1818
  # ~ return obj.id is not None and (obj.id > 10 and obj.id < 21)
@@ -2012,17 +1996,6 @@ class Site(object):
2012
1996
  s = self.plugins.jinja.render_from_ar(ar, "admin_main.html", **context)
2013
1997
  return mark_safe(s)
2014
1998
 
2015
- _lino_version = None
2016
-
2017
- @property
2018
- def lino_version(self):
2019
- p = self.site_dir / "lino_version.txt"
2020
- if self._lino_version is None:
2021
- if not p.exists():
2022
- p.touch()
2023
- self._lino_version = p.stat().st_mtime
2024
- return self._lino_version
2025
-
2026
1999
  def build_site_cache(self, force=False, later=False, verbosity=1):
2027
2000
  from lino.modlib.users.utils import with_user_profile
2028
2001
  from lino.modlib.users.choicelists import UserTypes
@@ -2032,8 +2005,9 @@ class Site(object):
2032
2005
  # self.is_prepared = True
2033
2006
  # settings_file = self.django_settings.get("__file__")
2034
2007
  # Path(settings_file).touch()
2035
- p = self.site_dir / "lino_version.txt"
2036
- p.touch()
2008
+ # p = self.site_dir / "lino_version.txt"
2009
+ # p.touch()
2010
+ self.kernel.touch_lino_version()
2037
2011
 
2038
2012
  if later:
2039
2013
  # print("20230823 later")
lino/help_texts.py CHANGED
@@ -11,8 +11,8 @@ help_texts = {
11
11
  'lino.mixins.Modified' : _("""Adds a a timestamp field that holds the last modification time of every individual database object."""),
12
12
  'lino.mixins.Modified.modified' : _("""The time when this database object was last modified."""),
13
13
  'lino.mixins.Modified.auto_touch' : _("""Whether to touch objects automatically when saving them."""),
14
- 'lino.mixins.Created' : _("""Adds a timestamp field which holds the creation time of every individual database object."""),
15
- 'lino.mixins.Created.created' : _("""The time when this object was created."""),
14
+ 'lino.mixins.Created' : _("""Adds a timestamp field that holds the creation time of every individual database row."""),
15
+ 'lino.mixins.Created.created' : _("""The time when this database row was created."""),
16
16
  'lino.mixins.CreatedModified' : _("""Adds two timestamp fields created and modified."""),
17
17
  'lino.mixins.ProjectRelated' : _("""Mixin for models that are related to a “project”, i.e. to an object of the type given by your lino.core.site.Site.project_model."""),
18
18
  'lino.mixins.ProjectRelated.project' : _("""Pointer to the project to which this object is related."""),
@@ -646,7 +646,7 @@ help_texts = {
646
646
  'lino.modlib.system.SiteConfig.simulate_today' : _("""A constant user-defined date to be substituted as current system date."""),
647
647
  'lino.modlib.system.SiteConfig.site_company' : _("""The site operator, i.e. the legal person that operates this Lino site."""),
648
648
  'lino.modlib.system.SiteConfig.hide_events_before' : _("""If this is not empty, any calendar events before that date are being hidden in certain places."""),
649
- 'lino.modlib.system.SiteConfigManager' : _("""Always return the cached instance which holds the one and only database instance."""),
649
+ 'lino.modlib.system.SiteConfigManager' : _("""Returns the cached instance, which holds the one and only database instance."""),
650
650
  'lino.modlib.system.Lockable' : _("""Mixin to add row-level edit locking to any model."""),
651
651
  'lino.modlib.system.BuildSiteCache' : _("""Rebuild the site cache. This action is available on About."""),
652
652
  'lino.modlib.system.SiteConfigs' : _("""The table used to present the SiteConfig row in a Detail form."""),
@@ -68,34 +68,32 @@ class TestCase(DemoTestCase):
68
68
 
69
69
  # For this test we reduce max_blacklist_time because we are going to
70
70
  # simulate a hacker who patiently waits:
71
- ipdict.max_blacklist_time = timedelta(seconds=1)
71
+ ipdict.max_blacklist_time = timedelta(seconds=4)
72
72
 
73
73
  self.assertEqual(ipdict.ip_records, {})
74
74
 
75
- def login(pwd):
75
+ def login(pwd, expected):
76
76
  d = self.login("robin", pwd)
77
- return d.message
77
+ if d.message != expected:
78
+ self.fail(f"Expected {expected} but got {d.message} ({d})")
78
79
 
79
- self.assertEqual(login("bad"), "Failed to sign in as robin.")
80
+ login("bad", "Failed to sign in as robin.")
80
81
  rec = ipdict.ip_records["127.0.0.1"]
81
82
  self.assertEqual(rec.login_failures, 1)
82
- self.assertEqual(login("bad"), "Failed to sign in as robin.")
83
+ login("bad", "Failed to sign in as robin.")
83
84
  self.assertEqual(rec.login_failures, 2)
84
- self.assertEqual(login("bad"), "Failed to sign in as robin.")
85
+ login("bad", "Failed to sign in as robin.")
85
86
  self.assertEqual(rec.login_failures, 3)
86
- self.assertEqual(login("bad"), "Failed to sign in as robin.")
87
+ login("bad", "Failed to sign in as robin.")
87
88
  self.assertEqual(rec.login_failures, 4)
88
- self.assertEqual(
89
- login("bad"), "Too many authentication failures from 127.0.0.1"
90
- )
89
+ login("bad", "Too many authentication failures from 127.0.0.1")
91
90
 
92
91
  # login_failures doesn't continue to increase when the ip is blacklisted:
93
92
  self.assertEqual(rec.login_failures, 4)
94
93
 
95
94
  # Even with the right password you cannot unlock a blacklisted ip
96
- self.assertEqual(
97
- login(dd.plugins.users.demo_password), "Too many authentication failures from 127.0.0.1"
98
- )
95
+ login(dd.plugins.users.demo_password,
96
+ "Too many authentication failures from 127.0.0.1")
99
97
 
100
98
  # After max_blacklist_time, the IP gets removed from the blacklist, but
101
99
  # every new failure will now blacklist it again, the
@@ -103,24 +101,20 @@ class TestCase(DemoTestCase):
103
101
 
104
102
  # time.sleep(1.5)
105
103
  time.sleep(5)
106
- self.assertEqual(login("bad"), "Failed to sign in as robin.")
104
+ login("bad", "Failed to sign in as robin.")
107
105
  self.assertEqual(rec.login_failures, 5)
108
- self.assertEqual(
109
- login("bad"), "Too many authentication failures from 127.0.0.1"
110
- )
106
+ login("bad", "Too many authentication failures from 127.0.0.1")
111
107
  self.assertEqual(rec.login_failures, 5)
112
108
 
113
- time.sleep(1.5)
114
- self.assertEqual(
115
- login(dd.plugins.users.demo_password),
116
- "Now signed in as Robin Rood")
109
+ time.sleep(5)
110
+ login(dd.plugins.users.demo_password, "Now signed in as Robin Rood")
117
111
 
118
112
  # Once you manage to authenticate, your ip address gets removed from the
119
113
  # blacklist, i.e. when you log out and in for some reason, you get again
120
114
  # max_failed_auth_per_ip attempts
121
115
 
122
116
  self.assertEqual(ipdict.ip_records, {})
123
- self.assertEqual(login("bad"), "Failed to sign in as robin.")
117
+ login("bad", "Failed to sign in as robin.")
124
118
  rec = ipdict.ip_records["127.0.0.1"]
125
119
  self.assertEqual(rec.login_failures, 1)
126
120
 
lino/mixins/__init__.py CHANGED
@@ -121,14 +121,14 @@ class Modified(model.Model):
121
121
 
122
122
  class Created(model.Model):
123
123
  """
124
- Adds a timestamp field which holds the creation time of every
125
- individual database object.
124
+ Adds a timestamp field that holds the creation time of every
125
+ individual :term:`database row`.
126
126
 
127
127
  .. attribute:: created
128
128
 
129
- The time when this object was created.
129
+ The time when this :term:`database row` was created.
130
130
 
131
- Does nut use Django's `auto_now` and `auto_now_add` features
131
+ Does not use Django's `auto_now` and `auto_now_add` features
132
132
  because their deserialization would be problematic.
133
133
  """
134
134
 
@@ -142,7 +142,7 @@ class Created(model.Model):
142
142
  return naturaltime(self.created)
143
143
 
144
144
  def save(self, *args, **kwargs):
145
- if self.created is None and not settings.SITE.loading_from_dump:
145
+ if self.created is None: # and not settings.SITE.loading_from_dump:
146
146
  self.created = settings.SITE.now()
147
147
  super().save(*args, **kwargs)
148
148
 
lino/mixins/dupable.py CHANGED
@@ -39,6 +39,7 @@ from lino.core.actions import SubmitInsert
39
39
  from lino.utils import join_elems
40
40
  from lino.utils.html import E, tostring, mark_safe
41
41
  from lino.core import constants
42
+ from lino.modlib.checkdata.choicelists import Checker
42
43
 
43
44
 
44
45
  class CheckedSubmitInsert(SubmitInsert):
@@ -242,9 +243,6 @@ class Dupable(dd.Model):
242
243
  return qs[:limit]
243
244
 
244
245
 
245
- from lino.modlib.checkdata.choicelists import Checker
246
-
247
-
248
246
  class DupableChecker(Checker):
249
247
  """Checks for the following repairable problem:
250
248
 
@@ -255,7 +253,7 @@ class DupableChecker(Checker):
255
253
  verbose_name = _("Check for missing phonetic words")
256
254
  model = Dupable
257
255
 
258
- def get_checkdata_problems(self, obj, fix=False):
256
+ def get_checkdata_problems(self, ar, obj, fix=False):
259
257
  msg = obj.update_dupable_words(fix)
260
258
  if msg:
261
259
  yield (True, msg)
@@ -119,8 +119,11 @@ class Registrable(model.Model):
119
119
 
120
120
  def disabled_fields(self, ar):
121
121
  if not self.state.is_editable:
122
- return self._registrable_fields
123
- return super(Registrable, self).disabled_fields(ar)
122
+ # return self._registrable_fields
123
+ # Copy _registrable_fields otherwise _registrable_fields get
124
+ # modified as more disabled fields are added to the set.
125
+ return self._registrable_fields.copy()
126
+ return super().disabled_fields(ar)
124
127
 
125
128
  def get_row_permission(self, ar, state, ba):
126
129
  """Only rows in an editable state may be edited.
@@ -119,8 +119,8 @@ class About(EmptyTable):
119
119
  packages = set(["django"])
120
120
 
121
121
  items.append(
122
- E.li(gettext("Server timestamp"), " : ", E.b(dtfmt(site.lino_version)))
123
- )
122
+ E.li(gettext("Server timestamp"), " : ",
123
+ E.b(dtfmt(site.kernel.lino_version))))
124
124
 
125
125
  for p in site.installed_plugins:
126
126
  packages.add(p.app_name.split(".")[0])
@@ -64,8 +64,8 @@ class Checker(dd.Choice):
64
64
  ar.logger.info(msg.format(len(todo), len(done), cls.self))
65
65
 
66
66
  @classmethod
67
- def check_instance(cls, *args, **kwargs):
68
- return cls.self.get_checkdata_problems(*args, **kwargs)
67
+ def check_instance(cls, ar, *args, **kwargs):
68
+ return cls.self.get_checkdata_problems(ar, *args, **kwargs)
69
69
 
70
70
  def get_checkable_models(self):
71
71
  if self.model is None:
@@ -92,7 +92,7 @@ class Checker(dd.Choice):
92
92
 
93
93
  done = []
94
94
  todo = []
95
- for fixable, msg in self.get_checkdata_problems(obj, fix):
95
+ for fixable, msg in self.get_checkdata_problems(ar, obj, fix):
96
96
  if fixable:
97
97
  # attn: do not yet translate
98
98
  # msg = string_concat(u"(\u2605) ", msg)
@@ -126,7 +126,7 @@ class Checker(dd.Choice):
126
126
  prb.save()
127
127
  return (todo, done)
128
128
 
129
- def get_checkdata_problems(self, obj, fix=False):
129
+ def get_checkdata_problems(self, ar, obj, fix=False):
130
130
  return []
131
131
 
132
132
  def get_responsible_user(self, obj):
@@ -21,6 +21,9 @@ from lino.api import dd, rt, _
21
21
  from .choicelists import Checker, Checkers
22
22
  from .roles import CheckdataUser
23
23
 
24
+ MAX_LENGTH = 250
25
+ MORE = " (...)"
26
+
24
27
 
25
28
  class CheckerAction(dd.Action):
26
29
  fix_them = False
@@ -150,7 +153,7 @@ class Message(Controllable, UserAuthored):
150
153
  checker = Checkers.field(verbose_name=_("Checker"))
151
154
  # severity = Severities.field()
152
155
  # feedback = Feedbacks.field(blank=True)
153
- message = models.CharField(_("Message text"), max_length=250)
156
+ message = models.CharField(_("Message text"), max_length=MAX_LENGTH)
154
157
  # fixable = models.BooleanField(_("Fixable"), default=False)
155
158
 
156
159
  update_problem = UpdateMessage()
@@ -166,6 +169,11 @@ class Message(Controllable, UserAuthored):
166
169
  def __str__(self):
167
170
  return self.message
168
171
 
172
+ def full_clean(self):
173
+ if len(self.message) > MAX_LENGTH:
174
+ self.message = self.message[:MAX_LENGTH - len(MORE)] + MORE
175
+ super().full_clean()
176
+
169
177
  @classmethod
170
178
  def get_simple_parameters(cls):
171
179
  for p in super(Message, cls).get_simple_parameters():
@@ -301,9 +309,9 @@ def get_checkers_for(model):
301
309
  return checkers
302
310
 
303
311
 
304
- def check_instance(obj, **kwargs):
312
+ def check_instance(ar, obj, **kwargs):
305
313
  for chk in get_checkers_for(obj.__class__):
306
- for fixable, msg in chk.check_instance(obj, **kwargs):
314
+ for fixable, msg in chk.check_instance(ar, obj, **kwargs):
307
315
  if fixable:
308
316
  msg = f"(\u2605) {msg}"
309
317
  print(msg)
@@ -42,12 +42,16 @@ BODIES.items.insert(0, "")
42
42
 
43
43
 
44
44
  def objects():
45
+
45
46
  Comment = rt.models.comments.Comment
46
47
  User = rt.models.users.User
47
48
  Comment.auto_touch = False
48
49
  # use_linod = settings.SITE.use_linod
49
50
  # settings.SITE.use_linod = False
50
51
 
52
+ # avoid channels.exceptions.ChannelFull:
53
+ settings.SITE.loading_from_dump = True
54
+
51
55
  MENTIONED = Cycler()
52
56
  for model in rt.models_by_base(Commentable):
53
57
  if model.memo_command is not None:
@@ -429,7 +429,7 @@ class CommentChecker(Checker):
429
429
  model = Comment
430
430
  msg_missing = _("Missing owner in reply to comment.")
431
431
 
432
- def get_checkdata_problems(self, obj, fix=False):
432
+ def get_checkdata_problems(self, ar, obj, fix=False):
433
433
  if obj.reply_to_id and not obj.owner_id and obj.reply_to.owner_id:
434
434
  yield (True, self.msg_missing)
435
435
  if fix:
@@ -13,6 +13,7 @@ from lino.api import dd, rt, _
13
13
  from lino.core.actions import SubmitInsert
14
14
  from lino.core.gfks import gfk2lookup
15
15
  from lino.core.gfks import ContentType
16
+ from lino.modlib.checkdata.choicelists import Checker
16
17
 
17
18
 
18
19
  class CheckedSubmitInsert(SubmitInsert):
@@ -154,9 +155,6 @@ class Dupable(dd.Model):
154
155
  return qs[:limit]
155
156
 
156
157
 
157
- from lino.modlib.checkdata.choicelists import Checker
158
-
159
-
160
158
  class DupableChecker(Checker):
161
159
  """Checks for the following repairable problem:
162
160
 
@@ -167,7 +165,7 @@ class DupableChecker(Checker):
167
165
  verbose_name = _("Check for missing phonetic words")
168
166
  model = Dupable
169
167
 
170
- def get_checkdata_problems(self, obj, fix=False):
168
+ def get_checkdata_problems(self, ar, obj, fix=False):
171
169
  msg = obj.update_dupable_words(fix)
172
170
  if msg:
173
171
  yield (True, msg)
@@ -180,7 +178,7 @@ class SimilarObjectsChecker(Checker):
180
178
  model = Dupable
181
179
  verbose_name = _("Check for similar objects")
182
180
 
183
- def get_checkdata_problems(self, obj, fix=False):
181
+ def get_checkdata_problems(self, ar, obj, fix=False):
184
182
  lst = list(obj.find_similar_instances(1))
185
183
  if len(lst):
186
184
  msg = _("Similar clients: {clients}").format(
@@ -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.lino_version)
1484
+ yield "GEN_TIMESTAMP = %s;" % py2js(settings.SITE.kernel.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.lino_version:
141
+ if lv is None or float(lv) == settings.SITE.kernel.lino_version:
142
142
  return {}
143
143
  # print("20201217", lv, settings.SITE.kernel.code_mtime)
144
144
  cache.clear()
@@ -8,15 +8,16 @@ from lino.api import dd, rt, _
8
8
  if dd.get_plugin_setting("help", "use_contacts"):
9
9
 
10
10
  from lino.api.shell import help, contacts
11
+ from lino_xl.lib.contacts.models import PARTNER_NUMBERS_START_AT as PS
11
12
 
12
13
  def site_contact(type, company=None, **kwargs):
13
14
  return help.SiteContact(site_contact_type=type, company=company, **kwargs)
14
15
 
15
16
  def objects():
16
17
  yield site_contact("owner", settings.SITE.site_config.site_company)
17
- yield site_contact("serveradmin", contacts.Company.objects.get(pk=106))
18
+ yield site_contact("serveradmin", contacts.Company.objects.get(pk=PS+6))
18
19
  yield site_contact(
19
20
  "hotline",
20
- contact_person=contacts.Person.objects.get(pk=113),
21
+ contact_person=contacts.Person.objects.get(pk=PS+13),
21
22
  **dd.babelkw("remark", _("Mon and Fri from 11:30 to 12:00")),
22
23
  )
@@ -8,10 +8,9 @@ from pathlib import Path
8
8
  from lxml import etree
9
9
 
10
10
  from django.conf import settings
11
- from django.utils import translation
12
11
  from django.utils.html import mark_safe, escape
13
12
 
14
- from lino.api import dd
13
+ from lino.api import dd, _
15
14
  from lino.utils.xml import validate_xml
16
15
  from lino.utils.media import MediaFile
17
16
 
@@ -31,12 +30,20 @@ class XMLMaker(dd.Model):
31
30
  xml_file_template = None
32
31
  # xml_file_name = None
33
32
 
33
+ _xmlfile = None
34
+
35
+ @property
36
+ def xmlfile(self):
37
+ if self._xmlfile is None:
38
+ self._xmlfile = MediaFile(False, *self.get_xml_file_parts())
39
+ return self._xmlfile
40
+
34
41
  def get_xml_file_parts(self):
35
42
  yield 'xml'
36
43
  yield self.get_printable_target_stem() + ".xml"
37
44
 
38
45
  def get_xml_file(self):
39
- return MediaFile(False, *self.get_xml_file_parts())
46
+ return self.xmlfile
40
47
 
41
48
  def make_xml_file(self, ar):
42
49
  renderer = settings.SITE.plugins.jinja.renderer
@@ -48,9 +55,9 @@ class XMLMaker(dd.Model):
48
55
  # parts = [
49
56
  # dd.plugins.accounting.xml_media_dir,
50
57
  # self.xml_file_name.format(self=self)]
51
- xmlfile = self.get_xml_file()
58
+ xmlfile = self.xmlfile
52
59
  # xmlfile = Path(settings.MEDIA_ROOT, *parts)
53
- ar.logger.info("Make %s from %s ...", xmlfile.path, self)
60
+ ar.logger.debug("Make %s from %s ...", xmlfile.path, self)
54
61
  xmlfile.path.parent.mkdir(exist_ok=True, parents=True)
55
62
  xmlfile.path.write_text(xml)
56
63
  # xmlfile.write_text(etree.tostring(xml))
@@ -74,3 +81,9 @@ class XMLMaker(dd.Model):
74
81
  # return mark_safe(f"""<a href="{url}">{url}</a>""")
75
82
  # return (xmlfile, url)
76
83
  return xmlfile
84
+
85
+ @dd.displayfield(_("XML file"))
86
+ def xml_file(self, ar):
87
+ mf = self.xmlfile
88
+ href = settings.SITE.media_root / mf.url
89
+ return mark_safe(f"<a href=\"{href}\" target=\"blank\">{mf.path.name}</a>")
@@ -55,7 +55,7 @@ class SystemTaskChecker(Checker):
55
55
  verbose_name = _("Check for missing system tasks")
56
56
  model = None
57
57
 
58
- def get_checkdata_problems(self, obj, fix=False):
58
+ def get_checkdata_problems(self, ar, obj, fix=False):
59
59
  for proc in Procedures.get_list_items():
60
60
  if proc.class_name == "linod.SystemTask":
61
61
  if SystemTask.objects.filter(procedure=proc).count() == 0: