Coverage for src/hods/tui/delta.py: 100.00%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""hods - home directory synchronization.
3Copyright (C) 2016-2020 Mathias Stelzer <knoppo@rolln.de>
5hods is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
10hods is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
15You should have received a copy of the GNU General Public License
16along with this program. If not, see <http://www.gnu.org/licenses/>.
17"""
18import logging
19import subprocess
21import urwid
23from hods.delta import ApplyError, Delta, DeltaBuilder, Template
24from hods.delta import DeltaDirectory, Symlink
25from hods.tui.base.list import AppItemMixin, button_list
26from hods.tui.base.view import ErrorWindow, ProcessErrorWindow, SuccessWindow, Window
27from hods.tui.config.tree import AppTreeDecoration, PathTree, PathTreeBox, PathTreeItem
29logger = logging.getLogger(__name__)
32class BaseDeltaTreeItem(PathTreeItem):
33 """A base tree item used in a home delta tree."""
35 default_attr = 'body'
37 def get_attr(self):
38 """Return an urwid attribute for the paths action."""
39 action = self.object.get_action()
40 if action is None:
41 return self.default_attr
42 if action == self.object.create:
43 return 'success'
44 if action == self.object.replace:
45 return 'warning'
46 if action == self.object.delete:
47 return 'error'
48 return super().get_attr() # pragma: no cover
50 def get_label(self):
51 """Return the path basename and its action as label."""
52 label = self.object.basename
53 action = self.object.get_action_label()
54 if action:
55 label += ' ({})'.format(action)
56 return label
59class DeltaItem(PathTreeItem):
60 """The tree item for the delta root node."""
62 def get_attr(self):
63 """Return the `urwid` attribute map to style the widget."""
64 return 'directory'
66 def get_label(self):
67 """Return fixed ~/ as label for the home directory."""
68 return '~/'
71class DirectoryItem(BaseDeltaTreeItem):
72 """A delta tree item to display a directory."""
74 default_attr = 'directory'
77class SymlinkItem(AppItemMixin, BaseDeltaTreeItem):
78 """A delta tree item to display a symlink."""
80 default_attr = 'symlink'
83class TemplateItem(AppItemMixin, BaseDeltaTreeItem):
84 """A delta tree item to display a symlink."""
86 default_attr = 'template'
89class DeltaTreeDecoration(AppTreeDecoration):
90 """A tree decoration to display a home delta tree."""
92 def __getitem__(self, item):
93 """Return the according tree widget for the given item."""
94 if isinstance(item, Delta):
95 return DeltaItem(item)
96 if isinstance(item, DeltaDirectory):
97 return DirectoryItem(item)
98 if isinstance(item, Symlink):
99 return SymlinkItem(self.app, item)
100 if isinstance(item, Template):
101 return TemplateItem(self.app, item)
102 return super().__getitem__(item) # pragma: no cover
105class DeltaWindow(Window):
106 """Window to show local changes."""
108 def __init__(self, app, **kwargs):
109 """Initialize window.
111 :param app: `hods.__main__.TUI` instance
112 :param kwargs: passed to `hods.tui.base.windows.Window`
113 """
114 self.delta = DeltaBuilder(app.config).build()
115 tree = PathTree(app, self.delta)
116 decorated_tree = DeltaTreeDecoration(app, tree)
117 self.treebox = PathTreeBox(decorated_tree)
119 super().__init__(app, title='Update Summary', padding=0, **kwargs)
121 def get_body(self):
122 """Get main widget to display."""
123 return urwid.Columns([
124 urwid.AttrMap(self.treebox, 'body'),
125 button_list([
126 (urwid.Button('Update', on_press=self.update), 'success'),
127 (urwid.Button('Cancel', on_press=lambda btn: self.close()), 'warning'),
128 ], column=True),
129 ], dividechars=1)
131 def update(self, btn):
132 """Apply the delta to the home directory and close the window."""
133 for feature in self.app.config.tree.installed_features:
134 try:
135 with self.app.pause_loop():
136 feature.hooks.update.pre.run()
137 except subprocess.CalledProcessError as e:
138 ProcessErrorWindow(self.app, e, '{} pre-hook'.format(feature.name), parent=self).show()
139 return
141 try:
142 self.delta.apply()
143 except ApplyError as e:
144 self.treebox.refresh()
145 msg = 'Failed to {} "{}":\n{}\nFix manually and update again'.format(e.action, e.node.path, e.message)
146 ErrorWindow(self.app, msg, parent=self).show()
147 return
149 for feature in self.app.config.tree.installed_features:
150 try:
151 with self.app.pause_loop():
152 feature.hooks.update.post.run()
153 except subprocess.CalledProcessError as e:
154 ProcessErrorWindow(self.app, e, '{} post-hook'.format(feature.name), parent=self).show()
155 return
157 SuccessWindow(self.app, 'Successfully updated your home directory!').show()