lino 25.3.2__py3-none-any.whl → 25.3.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.
- lino/__init__.py +1 -1
- lino/api/doctest.py +1 -0
- lino/core/site.py +9 -76
- lino/locale/bn/LC_MESSAGES/django.po +1112 -907
- lino/locale/de/LC_MESSAGES/django.mo +0 -0
- lino/locale/de/LC_MESSAGES/django.po +1066 -918
- lino/locale/django.pot +1041 -900
- lino/locale/es/LC_MESSAGES/django.po +1103 -902
- lino/locale/et/LC_MESSAGES/django.po +1103 -915
- lino/locale/fr/LC_MESSAGES/django.po +1105 -916
- lino/locale/nl/LC_MESSAGES/django.po +1102 -917
- lino/locale/pt_BR/LC_MESSAGES/django.po +1099 -900
- lino/locale/zh_Hant/LC_MESSAGES/django.po +1099 -900
- lino/management/commands/buildcache.py +48 -3
- lino/management/commands/dump2py.py +5 -7
- lino/management/commands/install.py +2 -2
- lino/management/commands/prep.py +4 -8
- lino/mixins/duplicable.py +1 -1
- lino/modlib/checkdata/models.py +33 -15
- lino/modlib/comments/fixtures/demo2.py +1 -9
- lino/modlib/extjs/views.py +1 -1
- lino/modlib/users/ui.py +4 -2
- lino/modlib/weasyprint/__init__.py +12 -20
- lino/modlib/weasyprint/config/weasyprint/base.weasy.html +10 -1
- lino/utils/__init__.py +1 -0
- lino/utils/dbhash.py +112 -0
- {lino-25.3.2.dist-info → lino-25.3.4.dist-info}/METADATA +1 -1
- {lino-25.3.2.dist-info → lino-25.3.4.dist-info}/RECORD +31 -31
- lino/management/commands/buildsite.py +0 -57
- {lino-25.3.2.dist-info → lino-25.3.4.dist-info}/WHEEL +0 -0
- {lino-25.3.2.dist-info → lino-25.3.4.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.3.2.dist-info → lino-25.3.4.dist-info}/licenses/COPYING +0 -0
@@ -1,13 +1,58 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2023 Rumma & Ko Ltd.
|
2
|
+
# Copyright 2009-2023 Rumma & Ko Ltd.
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
-
from
|
5
|
+
from click import confirm
|
6
|
+
from django.core.management.base import BaseCommand, CommandError
|
7
|
+
from django.core.management import call_command
|
6
8
|
from django.conf import settings
|
9
|
+
from lino import logger
|
7
10
|
|
8
11
|
|
9
12
|
class Command(BaseCommand):
|
13
|
+
"""Build the site cache files and run collectstatic for this Lino site."""
|
14
|
+
|
15
|
+
def add_arguments(self, parser):
|
16
|
+
super().add_arguments(parser)
|
17
|
+
parser.add_argument(
|
18
|
+
"-b", "--batch", "--noinput",
|
19
|
+
action="store_false",
|
20
|
+
dest="interactive",
|
21
|
+
default=True,
|
22
|
+
help="Do not prompt for input of any kind.",
|
23
|
+
),
|
24
|
+
|
10
25
|
def handle(self, *args, **options):
|
26
|
+
interactive = options.get("interactive")
|
11
27
|
verbosity = options.get("verbosity")
|
12
|
-
|
28
|
+
project_dir = settings.SITE.project_dir
|
29
|
+
|
30
|
+
options = dict(interactive=False, verbosity=verbosity)
|
31
|
+
|
32
|
+
if interactive:
|
33
|
+
msg = "Build everything for ({})".format(project_dir)
|
34
|
+
msg += ".\nAre you sure?"
|
35
|
+
if not confirm(msg, default=True):
|
36
|
+
raise CommandError("User abort.")
|
37
|
+
|
38
|
+
# the following log message was useful on Travis 20150104
|
39
|
+
if verbosity > 0:
|
40
|
+
logger.info("`buildsite` started on %s.", project_dir)
|
41
|
+
|
42
|
+
# pth = project_dir / "settings.py"
|
43
|
+
# if pth.exists():
|
44
|
+
# pth.touch()
|
45
|
+
|
46
|
+
call_command("collectstatic", **options)
|
47
|
+
|
13
48
|
settings.SITE.build_site_cache(force=True, verbosity=verbosity)
|
49
|
+
|
50
|
+
# if settings.SITE.is_installed("help"):
|
51
|
+
# call_command("makehelp", verbosity=verbosity)
|
52
|
+
|
53
|
+
# for p in settings.SITE.installed_plugins:
|
54
|
+
# p.on_buildsite(settings.SITE, verbosity=verbosity)
|
55
|
+
|
56
|
+
# settings.SITE.clear_site_config()
|
57
|
+
|
58
|
+
logger.info("`buildsite` finished on %s.", project_dir)
|
@@ -2,6 +2,10 @@
|
|
2
2
|
# Copyright 2013-2023 by Rumma & Ko Ltd.
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
+
from lino.utils.mldbc.fields import BabelCharField, BabelTextField
|
6
|
+
from lino.core.choicelists import ChoiceListField
|
7
|
+
from lino.core.utils import sorted_models_list, full_model_name
|
8
|
+
from lino.utils import puts
|
5
9
|
from io import open
|
6
10
|
from lino import logger
|
7
11
|
|
@@ -20,12 +24,6 @@ from datetime import timezone
|
|
20
24
|
|
21
25
|
utc = timezone.utc
|
22
26
|
|
23
|
-
from lino.utils import puts
|
24
|
-
from lino.core.utils import sorted_models_list, full_model_name
|
25
|
-
from lino.core.choicelists import ChoiceListField
|
26
|
-
|
27
|
-
from lino.utils.mldbc.fields import BabelCharField, BabelTextField
|
28
|
-
|
29
27
|
|
30
28
|
def is_pointer_to_contenttype(f):
|
31
29
|
if not settings.SITE.is_installed("contenttypes"):
|
@@ -402,7 +400,7 @@ def main(args):
|
|
402
400
|
if __name__ == '__main__':
|
403
401
|
import argparse
|
404
402
|
parser = argparse.ArgumentParser(description='Restore the data.')
|
405
|
-
parser.add_argument('--noinput', dest='interactive',
|
403
|
+
parser.add_argument('-b', '--noinput', '--batch', dest='interactive',
|
406
404
|
action='store_false', default=True,
|
407
405
|
help="Don't ask for confirmation before flushing the database.")
|
408
406
|
parser.add_argument('--quick', dest='quick',
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
import sys
|
6
6
|
import subprocess
|
7
|
-
from
|
7
|
+
from click import confirm
|
8
8
|
from django.core.management.base import BaseCommand
|
9
9
|
from django.conf import settings
|
10
10
|
|
@@ -54,5 +54,5 @@ class Command(BaseCommand):
|
|
54
54
|
return
|
55
55
|
# cmd = "pip install --upgrade --trusted-host svn.forge.pallavi.be {}".format(' '.join(reqs))
|
56
56
|
cmd = sys.executable + " -m pip install --upgrade pip {}".format(" ".join(reqs))
|
57
|
-
if not options["interactive"] or confirm("{}
|
57
|
+
if not options["interactive"] or confirm("{} ?".format(cmd), default=True):
|
58
58
|
runcmd(cmd)
|
lino/management/commands/prep.py
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2013-
|
2
|
+
# Copyright 2013-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
-
from pathlib import Path
|
6
5
|
from django.core.management import call_command
|
7
6
|
from django.core.management.base import BaseCommand, CommandError
|
8
7
|
from django.conf import settings
|
9
|
-
from
|
10
|
-
from rstgen.utils import confirm
|
11
|
-
# from lino.management.commands.initdb import Command as BaseCommand
|
12
|
-
# from lino.management.commands.initdb import CommandError
|
8
|
+
from lino.utils import dbhash
|
13
9
|
|
14
10
|
|
15
11
|
class Command(BaseCommand):
|
@@ -19,7 +15,7 @@ class Command(BaseCommand):
|
|
19
15
|
super().add_arguments(parser)
|
20
16
|
(
|
21
17
|
parser.add_argument(
|
22
|
-
"--noinput",
|
18
|
+
"-b", "--batch", "--noinput",
|
23
19
|
action="store_false",
|
24
20
|
dest="interactive",
|
25
21
|
default=True,
|
@@ -66,4 +62,4 @@ class Command(BaseCommand):
|
|
66
62
|
kwargs["removemedia"] = True
|
67
63
|
call_command("initdb", *args, **kwargs)
|
68
64
|
|
69
|
-
|
65
|
+
dbhash.mark_virgin()
|
lino/mixins/duplicable.py
CHANGED
lino/modlib/checkdata/models.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2015-
|
2
|
+
# Copyright 2015-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
5
|
from collections import OrderedDict
|
@@ -9,18 +9,17 @@ from django.conf import settings
|
|
9
9
|
from django.utils import translation
|
10
10
|
from django.template.defaultfilters import pluralize
|
11
11
|
|
12
|
+
from lino import logger
|
13
|
+
from lino.core import constants
|
12
14
|
from lino.core.gfks import gfk2lookup
|
13
15
|
from lino.modlib.gfks.mixins import Controllable
|
14
16
|
from lino.modlib.users.mixins import UserAuthored
|
15
17
|
from lino.modlib.linod.choicelists import background_task
|
16
18
|
from lino.core.roles import SiteStaff
|
17
|
-
|
18
19
|
from lino.api import dd, rt, _
|
19
20
|
|
20
21
|
from .choicelists import Checker, Checkers
|
21
|
-
|
22
22
|
from .roles import CheckdataUser
|
23
|
-
from lino.core import constants
|
24
23
|
|
25
24
|
|
26
25
|
class CheckerAction(dd.Action):
|
@@ -32,6 +31,8 @@ class CheckerAction(dd.Action):
|
|
32
31
|
Message = rt.models.checkdata.Message
|
33
32
|
gfk = Message.owner
|
34
33
|
for obj in objects:
|
34
|
+
if checkers is None:
|
35
|
+
checkers = get_checkers_for(obj.__class__)
|
35
36
|
qs = Message.objects.filter(**gfk2lookup(gfk, obj))
|
36
37
|
qs.delete()
|
37
38
|
for chk in checkers:
|
@@ -84,12 +85,12 @@ class UpdateMessagesByController(CheckerAction):
|
|
84
85
|
combo_group = "checkdata"
|
85
86
|
required_roles = dd.login_required()
|
86
87
|
|
87
|
-
def __init__(self, model):
|
88
|
-
|
89
|
-
|
88
|
+
# def __init__(self, model=None, **kwargs):
|
89
|
+
# self.model = model
|
90
|
+
# super().__init__(**kwargs)
|
90
91
|
|
91
92
|
def run_from_ui(self, ar, fix=None):
|
92
|
-
self.run_it(ar, fix,
|
93
|
+
self.run_it(ar, fix, None, ar.selected_rows)
|
93
94
|
# if fix is None:
|
94
95
|
# fix = self.fix_them
|
95
96
|
# Message = rt.models.checkdata.Message
|
@@ -109,6 +110,16 @@ class FixMessagesByController(UpdateMessagesByController):
|
|
109
110
|
fix_them = True
|
110
111
|
|
111
112
|
|
113
|
+
class QuickFixMessagesByController(UpdateMessagesByController):
|
114
|
+
# label = _("Fix data problems")
|
115
|
+
fix_them = True
|
116
|
+
combo_group = None
|
117
|
+
# icon_name = "lightning"
|
118
|
+
icon_name = None
|
119
|
+
button_text = ' ⚡ ' # 26A1
|
120
|
+
# button_text = "✓" # u"\u2713"
|
121
|
+
|
122
|
+
|
112
123
|
class FixAllProblems(CheckerAction):
|
113
124
|
select_rows = False
|
114
125
|
show_in_plain = True
|
@@ -119,7 +130,7 @@ class FixAllProblems(CheckerAction):
|
|
119
130
|
|
120
131
|
def run_from_ui(self, ar, fix=None):
|
121
132
|
mi = ar.master_instance
|
122
|
-
print(f"20250307 {mi}")
|
133
|
+
# print(f"20250307 {mi}")
|
123
134
|
self.run_it(ar, fix, get_checkers_for(mi.__class__), [mi])
|
124
135
|
ar.set_response(refresh=True)
|
125
136
|
|
@@ -264,11 +275,14 @@ def set_checkdata_actions(sender, **kw):
|
|
264
275
|
if m is None:
|
265
276
|
continue
|
266
277
|
assert m is not Message
|
267
|
-
m
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
278
|
+
# if hasattr(m, 'check_data'):
|
279
|
+
if (label := getattr(m, 'quickfix_checkdata_label', None)):
|
280
|
+
# print(f"20250324 Customized quickfix_checkdata_label {label} for {m}")
|
281
|
+
m.define_action(quick_fix=QuickFixMessagesByController(label=label))
|
282
|
+
else:
|
283
|
+
# print(f"20250324 Default checkdata buttons for {m}")
|
284
|
+
m.define_action(check_data=UpdateMessagesByController())
|
285
|
+
m.define_action(fix_problems=FixMessagesByController())
|
272
286
|
m.define_action(
|
273
287
|
show_problems=dd.ShowSlaveTable(
|
274
288
|
MessagesByOwner,
|
@@ -280,7 +294,11 @@ def set_checkdata_actions(sender, **kw):
|
|
280
294
|
|
281
295
|
|
282
296
|
def get_checkers_for(model):
|
283
|
-
|
297
|
+
checkers = []
|
298
|
+
for m, lst in get_checkable_models().items():
|
299
|
+
if m is not None and issubclass(model, m):
|
300
|
+
checkers += lst
|
301
|
+
return checkers
|
284
302
|
|
285
303
|
|
286
304
|
def check_instance(obj, **kwargs):
|
@@ -4,6 +4,7 @@
|
|
4
4
|
Adds some demo comments.
|
5
5
|
|
6
6
|
"""
|
7
|
+
|
7
8
|
import datetime
|
8
9
|
from lino.utils import i2t
|
9
10
|
from lino.utils import Cycler
|
@@ -32,12 +33,6 @@ plain1 = "Some plain text."
|
|
32
33
|
plain2 = "Two paragraphs of plain text.\n\nThe second paragraph."
|
33
34
|
# plain2 += " With an 👁 (U+1F441)." #5855 (Jane fails to store certain unicode characters)
|
34
35
|
|
35
|
-
imageDataURL = """data:image/png;base64,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"""
|
36
|
-
body_with_img = f"""\
|
37
|
-
<p>Here is an image:</p>
|
38
|
-
<p><img src="{imageDataURL}" class="bar"></p>\
|
39
|
-
"""
|
40
|
-
|
41
36
|
BODIES = Cycler(
|
42
37
|
[styled, table, lorem, short_lorem, breaking, cond_comment, plain1, plain2]
|
43
38
|
)
|
@@ -45,9 +40,6 @@ BODIES = Cycler(
|
|
45
40
|
BODIES.items.insert(0, "")
|
46
41
|
BODIES.items.insert(0, "")
|
47
42
|
|
48
|
-
if dd.is_installed('blogs'):
|
49
|
-
BODIES.items.append(body_with_img)
|
50
|
-
|
51
43
|
|
52
44
|
def objects():
|
53
45
|
Comment = rt.models.comments.Comment
|
lino/modlib/extjs/views.py
CHANGED
@@ -354,7 +354,7 @@ class ApiElement(View):
|
|
354
354
|
ar = ba.create_request(
|
355
355
|
request=request, renderer=settings.SITE.kernel.default_renderer)
|
356
356
|
if pk and pk != "-99999" and pk != "-99998":
|
357
|
-
if issubclass(rpt.model, models.Model):
|
357
|
+
if rpt.model is not None and issubclass(rpt.model, models.Model):
|
358
358
|
try:
|
359
359
|
ar.set_selected_pks(pk)
|
360
360
|
except rpt.model.DoesNotExist:
|
lino/modlib/users/ui.py
CHANGED
@@ -46,8 +46,8 @@ class UserDetail(dd.DetailLayout):
|
|
46
46
|
"""
|
47
47
|
|
48
48
|
main = """
|
49
|
-
box1 box2 #MembershipsByUser:20
|
50
|
-
|
49
|
+
box1 box2 #MembershipsByUser:20 remarks
|
50
|
+
AuthoritiesGiven:20 AuthoritiesTaken:20 SocialAuthsByUser:30
|
51
51
|
"""
|
52
52
|
|
53
53
|
# main_m = """
|
@@ -203,6 +203,7 @@ class AuthoritiesGiven(Authorities):
|
|
203
203
|
label = _("Authorities given")
|
204
204
|
column_names = "authorized"
|
205
205
|
auto_fit_column_widths = True
|
206
|
+
details_of_master_template = _("%(details)s by %(master)s")
|
206
207
|
|
207
208
|
|
208
209
|
class AuthoritiesTaken(Authorities):
|
@@ -211,6 +212,7 @@ class AuthoritiesTaken(Authorities):
|
|
211
212
|
label = _("Authorities taken")
|
212
213
|
column_names = "user"
|
213
214
|
auto_fit_column_widths = True
|
215
|
+
details_of_master_template = _("%(details)s by %(master)s")
|
214
216
|
|
215
217
|
|
216
218
|
if has_socialauth and dd.get_plugin_setting("users", "third_party_authentication"):
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2016-
|
1
|
+
# Copyright 2016-2025 Rumma & Ko Ltd
|
2
2
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
3
3
|
"""This plugins installs two build methods for generating
|
4
4
|
:term:`printable documents <printable document>` using `weasyprint
|
@@ -25,31 +25,17 @@ from lino.api import ad, _
|
|
25
25
|
|
26
26
|
|
27
27
|
class Plugin(ad.Plugin):
|
28
|
-
"See :doc:`/dev/plugins`."
|
29
28
|
|
30
29
|
verbose_name = _("WeasyPrint")
|
31
|
-
|
32
30
|
needs_plugins = ["lino.modlib.jinja"]
|
33
31
|
|
34
32
|
header_height = 20
|
35
|
-
"""Height of header in mm. Set to `None` if you want no header."""
|
36
|
-
|
37
33
|
footer_height = 20
|
38
|
-
"""Height of footer in mm. Set to `None` if you want no header."""
|
39
|
-
|
40
34
|
top_right_width = None
|
41
|
-
|
42
|
-
"""
|
43
|
-
|
35
|
+
page_background_image = None
|
44
36
|
top_right_image = None
|
45
|
-
"""The first image file found in config named either top-right.jpg or top-right.png."""
|
46
|
-
|
47
37
|
header_image = None
|
48
|
-
"""The first image file found in config named either header.jpg or header.png."""
|
49
|
-
|
50
38
|
margin = 10
|
51
|
-
"""Top and bottom page margin in mm."""
|
52
|
-
|
53
39
|
margin_left = 17
|
54
40
|
margin_right = 10
|
55
41
|
|
@@ -57,8 +43,8 @@ class Plugin(ad.Plugin):
|
|
57
43
|
yield "imagesize"
|
58
44
|
|
59
45
|
def pre_site_startup(self, site):
|
60
|
-
|
61
|
-
|
46
|
+
for ext in ("jpg", "png"):
|
47
|
+
if self.header_height:
|
62
48
|
fn = site.confdirs.find_config_file("top-right." + ext, "weasyprint")
|
63
49
|
if fn:
|
64
50
|
self.top_right_image = fn
|
@@ -70,6 +56,12 @@ class Plugin(ad.Plugin):
|
|
70
56
|
self.top_right_width = self.header_height * w / h
|
71
57
|
fn = site.confdirs.find_config_file("header." + ext, "weasyprint")
|
72
58
|
if fn:
|
59
|
+
# site.logger.info("Found header_image %s", fn)
|
73
60
|
self.header_image = fn
|
74
|
-
|
75
|
-
|
61
|
+
if self.page_background_image is None:
|
62
|
+
fn = site.confdirs.find_config_file(
|
63
|
+
"page-background." + ext, "weasyprint")
|
64
|
+
if fn:
|
65
|
+
# site.logger.info("Found page_background_image %s", fn)
|
66
|
+
self.page_background_image = fn
|
67
|
+
super().pre_site_startup(site)
|
@@ -53,7 +53,7 @@ p {
|
|
53
53
|
}
|
54
54
|
|
55
55
|
div.recipient {
|
56
|
-
position:relative; left:
|
56
|
+
position:relative; left:{{100-dd.plugins.weasyprint.margin_left}}mm;
|
57
57
|
height:30mm;
|
58
58
|
width:80mm;
|
59
59
|
border: 1px solid grey;
|
@@ -80,6 +80,15 @@ div.recipient {
|
|
80
80
|
{%- endif -%}
|
81
81
|
margin-left: {{dd.plugins.weasyprint.margin_left}}mm;
|
82
82
|
margin-right: {{dd.plugins.weasyprint.margin_right}}mm;
|
83
|
+
{%- if dd.plugins.weasyprint.page_background_image -%}
|
84
|
+
{#
|
85
|
+
background: url(file://{{dd.plugins.weasyprint.page_background_image}}) no-repeat center center fixed;
|
86
|
+
#}
|
87
|
+
background-image: url(file://{{dd.plugins.weasyprint.page_background_image}});
|
88
|
+
background-repeat: no-repeat;
|
89
|
+
background-attachment: fixed;
|
90
|
+
background-size: contain;
|
91
|
+
{%- endif -%}
|
83
92
|
font-family: "Liberation sans", "arial";
|
84
93
|
font-size: 10pt;
|
85
94
|
{%- block bottomleft %}
|
lino/utils/__init__.py
CHANGED
lino/utils/dbhash.py
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
# Copyright 2009-2025 Rumma & Ko Ltd
|
3
|
+
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
|
+
|
5
|
+
"""
|
6
|
+
Utilities around a "database hash".
|
7
|
+
See :doc:`/utils/dbhash`.
|
8
|
+
"""
|
9
|
+
|
10
|
+
import json
|
11
|
+
from importlib import import_module
|
12
|
+
from pathlib import Path
|
13
|
+
from django.conf import settings
|
14
|
+
from django.apps import apps
|
15
|
+
from django.db.models.deletion import ProtectedError
|
16
|
+
|
17
|
+
mod = import_module(settings.SETTINGS_MODULE)
|
18
|
+
HASH_FILE = Path(mod.__file__).parent / "dbhash.json"
|
19
|
+
|
20
|
+
|
21
|
+
def fmn(m):
|
22
|
+
return f"{m._meta.app_label}.{m._meta.object_name}"
|
23
|
+
|
24
|
+
|
25
|
+
def compute_dbhash():
|
26
|
+
"""
|
27
|
+
Return a dictionary with a hash value of the current database content.
|
28
|
+
"""
|
29
|
+
rv = dict()
|
30
|
+
for m in apps.get_models(include_auto_created=True):
|
31
|
+
k = fmn(m)
|
32
|
+
if k != "sessions.Session":
|
33
|
+
# rv[k] = m.objects.count()
|
34
|
+
rv[k] = list(m.objects.values_list('pk', flat=True))
|
35
|
+
return rv
|
36
|
+
|
37
|
+
|
38
|
+
def mark_virgin():
|
39
|
+
"""
|
40
|
+
Mark the database as virgin. This is called by :manage:`prep`.
|
41
|
+
"""
|
42
|
+
dbhash = compute_dbhash()
|
43
|
+
with HASH_FILE.open("w") as fp:
|
44
|
+
json.dump(dbhash, fp)
|
45
|
+
|
46
|
+
|
47
|
+
def load_dbhash():
|
48
|
+
"""
|
49
|
+
Load the dbhash that was saved in :xfile:`dbhash.json`
|
50
|
+
"""
|
51
|
+
if not HASH_FILE.exists():
|
52
|
+
raise Exception(
|
53
|
+
f"No file {HASH_FILE} (did you run `django-admin prep`?)")
|
54
|
+
with HASH_FILE.open("r") as fp:
|
55
|
+
return json.load(fp)
|
56
|
+
|
57
|
+
|
58
|
+
def check_virgin(restore=True, verbose=True):
|
59
|
+
"""
|
60
|
+
Verify whether the database is virgin. Print the differences if there
|
61
|
+
are any.
|
62
|
+
"""
|
63
|
+
new = compute_dbhash()
|
64
|
+
old = load_dbhash()
|
65
|
+
|
66
|
+
first_diff = True
|
67
|
+
can_restore = True
|
68
|
+
must_delete = {}
|
69
|
+
for k, v in new.items():
|
70
|
+
v = set(v)
|
71
|
+
oldv = set(old.get(k, None))
|
72
|
+
if oldv != v:
|
73
|
+
if first_diff:
|
74
|
+
if verbose:
|
75
|
+
print(f"Database {HASH_FILE.parent} isn't virgin:")
|
76
|
+
first_diff = False
|
77
|
+
diffs = []
|
78
|
+
added = v - oldv
|
79
|
+
if len(added):
|
80
|
+
diffs.append(f"{len(added)} rows added")
|
81
|
+
must_delete[apps.get_model(k)] = added
|
82
|
+
# for pk in added:
|
83
|
+
# must_delete.append(m.objects.get(pk=pk))
|
84
|
+
if (removed := len(oldv-v)):
|
85
|
+
diffs.append(f"{removed} rows deleted")
|
86
|
+
can_restore = False
|
87
|
+
if verbose:
|
88
|
+
print(f"- {k}: {', '.join(diffs)}")
|
89
|
+
if len(must_delete) == 0 or not restore:
|
90
|
+
return
|
91
|
+
if not can_restore:
|
92
|
+
raise Exception(
|
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
|
+
must_delete = list(must_delete.items())
|
98
|
+
while len(must_delete):
|
99
|
+
todo = []
|
100
|
+
hope = False
|
101
|
+
for m, added in must_delete:
|
102
|
+
try:
|
103
|
+
m.objects.filter(pk__in=added).delete()
|
104
|
+
# obj.delete()
|
105
|
+
hope = True
|
106
|
+
except Exception:
|
107
|
+
todo.append((m, added))
|
108
|
+
if not hope:
|
109
|
+
raise Exception(f"Failed to delete {todo}")
|
110
|
+
must_delete = todo
|
111
|
+
if verbose:
|
112
|
+
print("Database has been restored.")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lino
|
3
|
-
Version: 25.3.
|
3
|
+
Version: 25.3.4
|
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
|