2018-12-20 18:14:19 +03:00
|
|
|
import os
|
2018-12-21 00:48:45 +03:00
|
|
|
import shutil
|
2018-12-22 15:26:47 +03:00
|
|
|
import tempfile
|
2019-01-12 13:57:40 +03:00
|
|
|
import time
|
|
|
|
|
import zipfile
|
2019-01-03 23:32:28 +03:00
|
|
|
from datetime import datetime
|
2018-12-21 00:48:45 +03:00
|
|
|
from enum import Enum
|
2018-12-20 18:14:19 +03:00
|
|
|
|
2018-12-20 22:59:01 +03:00
|
|
|
from app.commons import run_idle
|
2019-12-22 20:42:29 +03:00
|
|
|
from app.settings import SettingsType
|
2018-12-20 22:59:01 +03:00
|
|
|
from app.ui.dialogs import show_dialog, DialogType
|
2019-01-12 13:57:40 +03:00
|
|
|
from app.ui.main_helper import append_text_to_tview
|
2019-01-12 20:08:17 +03:00
|
|
|
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey
|
2018-12-20 18:14:19 +03:00
|
|
|
|
|
|
|
|
|
2018-12-21 00:48:45 +03:00
|
|
|
class RestoreType(Enum):
|
|
|
|
|
BOUQUETS = 0
|
|
|
|
|
ALL = 1
|
|
|
|
|
|
|
|
|
|
|
2018-12-20 18:14:19 +03:00
|
|
|
class BackupDialog:
|
2019-12-13 13:31:07 +03:00
|
|
|
def __init__(self, transient, settings, callback):
|
2018-12-21 00:48:45 +03:00
|
|
|
handlers = {"on_restore_bouquets": self.on_restore_bouquets,
|
|
|
|
|
"on_restore_all": self.on_restore_all,
|
2018-12-20 22:59:01 +03:00
|
|
|
"on_remove": self.on_remove,
|
|
|
|
|
"on_view_popup_menu": self.on_view_popup_menu,
|
2019-01-12 13:57:40 +03:00
|
|
|
"on_info_button_toggled": self.on_info_button_toggled,
|
|
|
|
|
"on_info_bar_close": self.on_info_bar_close,
|
2019-01-12 18:10:04 +03:00
|
|
|
"on_cursor_changed": self.on_cursor_changed,
|
2019-01-12 20:08:17 +03:00
|
|
|
"on_resize": self.on_resize,
|
|
|
|
|
"on_key_release": self.on_key_release}
|
2018-12-20 18:14:19 +03:00
|
|
|
|
|
|
|
|
builder = Gtk.Builder()
|
|
|
|
|
builder.set_translation_domain("demon-editor")
|
|
|
|
|
builder.add_from_file(UI_RESOURCES_PATH + "backup_dialog.glade")
|
|
|
|
|
builder.connect_signals(handlers)
|
|
|
|
|
|
2019-12-13 13:31:07 +03:00
|
|
|
self._settings = settings
|
2019-12-22 20:42:29 +03:00
|
|
|
self._s_type = settings.setting_type
|
|
|
|
|
self._data_path = self._settings.data_local_path
|
|
|
|
|
self._backup_path = self._settings.backup_local_path or self._data_path + "backup/"
|
2018-12-21 00:48:45 +03:00
|
|
|
self._open_data_callback = callback
|
2018-12-20 18:14:19 +03:00
|
|
|
self._dialog_window = builder.get_object("dialog_window")
|
|
|
|
|
self._dialog_window.set_transient_for(transient)
|
|
|
|
|
self._model = builder.get_object("main_list_store")
|
2018-12-20 22:59:01 +03:00
|
|
|
self._main_view = builder.get_object("main_view")
|
2019-01-12 13:57:40 +03:00
|
|
|
self._text_view = builder.get_object("text_view")
|
|
|
|
|
self._text_view_scrolled_window = builder.get_object("text_view_scrolled_window")
|
|
|
|
|
self._info_check_button = builder.get_object("info_check_button")
|
2018-12-20 22:59:01 +03:00
|
|
|
self._info_bar = builder.get_object("info_bar")
|
|
|
|
|
self._message_label = builder.get_object("message_label")
|
2019-01-12 18:10:04 +03:00
|
|
|
# Setting the last size of the dialog window if it was saved
|
2019-12-13 13:31:07 +03:00
|
|
|
window_size = self._settings.get("backup_tool_window_size")
|
2019-01-12 18:10:04 +03:00
|
|
|
if window_size:
|
|
|
|
|
self._dialog_window.resize(*window_size)
|
2019-01-12 19:17:20 +03:00
|
|
|
|
2018-12-20 18:14:19 +03:00
|
|
|
self.init_data()
|
|
|
|
|
|
|
|
|
|
def show(self):
|
|
|
|
|
self._dialog_window.show()
|
|
|
|
|
|
|
|
|
|
def init_data(self):
|
|
|
|
|
try:
|
|
|
|
|
files = os.listdir(self._backup_path)
|
|
|
|
|
except FileNotFoundError as e:
|
2018-12-20 22:59:01 +03:00
|
|
|
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
2018-12-20 18:14:19 +03:00
|
|
|
else:
|
|
|
|
|
for file in filter(lambda x: x.endswith(".zip"), files):
|
|
|
|
|
self._model.append((file.rstrip(".zip"), False))
|
|
|
|
|
|
2018-12-21 00:48:45 +03:00
|
|
|
def on_restore_bouquets(self, item):
|
|
|
|
|
self.restore(RestoreType.BOUQUETS)
|
2018-12-20 22:59:01 +03:00
|
|
|
|
2018-12-21 00:48:45 +03:00
|
|
|
def on_restore_all(self, item):
|
|
|
|
|
self.restore(RestoreType.ALL)
|
2018-12-20 18:14:19 +03:00
|
|
|
|
|
|
|
|
def on_remove(self, item):
|
2018-12-20 22:59:01 +03:00
|
|
|
model, paths = self._main_view.get_selection().get_selected_rows()
|
|
|
|
|
if not paths:
|
|
|
|
|
show_dialog(DialogType.ERROR, self._dialog_window, "No selected item!")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._dialog_window) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2018-12-21 00:48:45 +03:00
|
|
|
itrs_to_delete = []
|
|
|
|
|
try:
|
|
|
|
|
for itr in map(model.get_iter, paths):
|
|
|
|
|
file_name = model.get_value(itr, 0)
|
2018-12-20 22:59:01 +03:00
|
|
|
os.remove("{}{}{}".format(self._backup_path, file_name, ".zip"))
|
2018-12-21 00:48:45 +03:00
|
|
|
itrs_to_delete.append(itr)
|
|
|
|
|
except FileNotFoundError as e:
|
|
|
|
|
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
|
|
|
|
else:
|
|
|
|
|
list(map(model.remove, itrs_to_delete))
|
2018-12-20 22:59:01 +03:00
|
|
|
|
|
|
|
|
def on_view_popup_menu(self, menu, event):
|
|
|
|
|
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
|
|
|
|
|
menu.popup(None, None, None, None, event.button, event.time)
|
|
|
|
|
|
2019-01-12 13:57:40 +03:00
|
|
|
def on_info_button_toggled(self, button):
|
|
|
|
|
active = button.get_active()
|
|
|
|
|
self._text_view_scrolled_window.set_visible(active)
|
|
|
|
|
if active:
|
|
|
|
|
self.on_cursor_changed(self._main_view)
|
|
|
|
|
|
2018-12-20 22:59:01 +03:00
|
|
|
@run_idle
|
|
|
|
|
def show_info_message(self, text, message_type):
|
|
|
|
|
self._info_bar.set_visible(True)
|
|
|
|
|
self._info_bar.set_message_type(message_type)
|
|
|
|
|
self._message_label.set_text(text)
|
|
|
|
|
|
|
|
|
|
def on_info_bar_close(self, bar=None, resp=None):
|
|
|
|
|
self._info_bar.set_visible(False)
|
2018-12-20 18:14:19 +03:00
|
|
|
|
2019-01-12 13:57:40 +03:00
|
|
|
def on_cursor_changed(self, view):
|
|
|
|
|
if not self._info_check_button.get_active():
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
model, paths = view.get_selection().get_selected_rows()
|
|
|
|
|
if paths:
|
|
|
|
|
try:
|
|
|
|
|
file_name = self._backup_path + model.get_value(model.get_iter(paths[0]), 0) + ".zip"
|
|
|
|
|
created = time.ctime(os.path.getctime(file_name))
|
|
|
|
|
self._text_view.get_buffer().set_text(
|
|
|
|
|
"Created: {}\n********** Files: **********\n".format(created))
|
|
|
|
|
with zipfile.ZipFile(file_name) as zip_file:
|
|
|
|
|
for name in zip_file.namelist():
|
|
|
|
|
append_text_to_tview(name + "\n", self._text_view)
|
|
|
|
|
except FileNotFoundError as e:
|
|
|
|
|
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
|
|
|
|
|
2018-12-21 00:48:45 +03:00
|
|
|
def restore(self, restore_type):
|
|
|
|
|
model, paths = self._main_view.get_selection().get_selected_rows()
|
|
|
|
|
if not paths:
|
|
|
|
|
show_dialog(DialogType.ERROR, self._dialog_window, "No selected item!")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if len(paths) > 1:
|
|
|
|
|
show_dialog(DialogType.ERROR, self._dialog_window, "Please, select only one item!")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._dialog_window) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2018-12-22 15:26:47 +03:00
|
|
|
file_name = model.get_value(model.get_iter(paths[0]), 0)
|
|
|
|
|
full_file_name = self._backup_path + file_name + ".zip"
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if restore_type is RestoreType.ALL:
|
2019-01-03 23:32:28 +03:00
|
|
|
clear_data_path(self._data_path)
|
2018-12-22 15:26:47 +03:00
|
|
|
shutil.unpack_archive(full_file_name, self._data_path)
|
|
|
|
|
elif restore_type is RestoreType.BOUQUETS:
|
|
|
|
|
tmp_dir = tempfile.gettempdir() + "/" + file_name
|
2019-12-22 20:42:29 +03:00
|
|
|
cond = (".tv", ".radio") if self._s_type is SettingsType.ENIGMA_2 else "bouquets.xml"
|
2018-12-22 15:26:47 +03:00
|
|
|
shutil.unpack_archive(full_file_name, tmp_dir)
|
|
|
|
|
for file in filter(lambda f: f.endswith(cond), os.listdir(self._data_path)):
|
|
|
|
|
os.remove(os.path.join(self._data_path, file))
|
|
|
|
|
for file in filter(lambda f: f.endswith(cond), os.listdir(tmp_dir)):
|
|
|
|
|
shutil.move(os.path.join(tmp_dir, file), self._data_path + file)
|
|
|
|
|
shutil.rmtree(tmp_dir)
|
|
|
|
|
except FileNotFoundError as e:
|
|
|
|
|
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
|
|
|
|
else:
|
|
|
|
|
self.show_info_message("Done!", Gtk.MessageType.INFO)
|
|
|
|
|
self._open_data_callback(self._data_path)
|
2018-12-21 00:48:45 +03:00
|
|
|
|
2019-01-12 18:10:04 +03:00
|
|
|
def on_resize(self, window):
|
2019-12-13 13:31:07 +03:00
|
|
|
if self._settings:
|
|
|
|
|
self._settings.add("backup_tool_window_size", window.get_size())
|
2019-01-12 18:10:04 +03:00
|
|
|
|
2019-01-12 20:08:17 +03:00
|
|
|
def on_key_release(self, view, event):
|
|
|
|
|
""" Handling keystrokes """
|
|
|
|
|
key_code = event.hardware_keycode
|
|
|
|
|
if not KeyboardKey.value_exist(key_code):
|
|
|
|
|
return
|
|
|
|
|
key = KeyboardKey(key_code)
|
|
|
|
|
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
|
|
|
|
|
|
|
|
|
if key is KeyboardKey.DELETE:
|
|
|
|
|
self.on_remove(view)
|
|
|
|
|
elif ctrl and key is KeyboardKey.E:
|
|
|
|
|
self.restore(RestoreType.ALL)
|
|
|
|
|
elif ctrl and key is KeyboardKey.R:
|
|
|
|
|
self.restore(RestoreType.BOUQUETS)
|
|
|
|
|
|
2018-12-20 18:14:19 +03:00
|
|
|
|
2019-05-19 00:37:07 +03:00
|
|
|
def backup_data(path, backup_path, move=True):
|
|
|
|
|
""" Creating data backup from a folder at the specified path
|
|
|
|
|
|
|
|
|
|
Returns full path to the compressed file.
|
|
|
|
|
"""
|
2019-01-12 19:17:20 +03:00
|
|
|
backup_path = "{}{}/".format(backup_path, datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
2019-01-03 23:32:28 +03:00
|
|
|
os.makedirs(os.path.dirname(backup_path), exist_ok=True)
|
2019-12-27 23:05:37 +03:00
|
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
2019-01-03 23:32:28 +03:00
|
|
|
# backup files in data dir(skipping dirs and satellites.xml)
|
|
|
|
|
for file in filter(lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
|
2019-05-19 00:37:07 +03:00
|
|
|
src, dst = os.path.join(path, file), backup_path + file
|
|
|
|
|
shutil.move(src, dst) if move else shutil.copy(src, dst)
|
2019-01-03 23:32:28 +03:00
|
|
|
# compressing to zip and delete remaining files
|
2019-05-19 00:37:07 +03:00
|
|
|
zip_file = shutil.make_archive(backup_path, "zip", backup_path)
|
2019-01-03 23:32:28 +03:00
|
|
|
shutil.rmtree(backup_path)
|
|
|
|
|
|
2019-05-19 00:37:07 +03:00
|
|
|
return zip_file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def restore_data(src, dst):
|
|
|
|
|
""" Unpacks backup data. """
|
|
|
|
|
clear_data_path(dst)
|
|
|
|
|
shutil.unpack_archive(src, dst)
|
|
|
|
|
|
2019-01-03 23:32:28 +03:00
|
|
|
|
|
|
|
|
def clear_data_path(path):
|
|
|
|
|
""" Clearing data at the specified path excluding satellites.xml file """
|
|
|
|
|
for file in filter(lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
|
|
|
|
|
os.remove(os.path.join(path, file))
|
|
|
|
|
|
|
|
|
|
|
2018-12-20 18:14:19 +03:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
pass
|