mirror of https://github.com/borgbase/vorta
Add EventLog
This commit is contained in:
parent
015b25f34b
commit
b9d4d57f33
|
@ -2,11 +2,18 @@ import sys
|
|||
from PyQt5.QtWidgets import QApplication
|
||||
from vorta.tray_menu import TrayMenu
|
||||
from vorta.scheduler import init_scheduler
|
||||
from vorta.models import BackupProfileModel
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
app.thread = None
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
TrayMenu(app)
|
||||
app.scheduler = init_scheduler()
|
||||
app.profile = BackupProfileModel.get(id=1)
|
||||
|
||||
if not getattr(sys, 'frozen', False):
|
||||
from .views.main_window import MainWindow
|
||||
ex = MainWindow()
|
||||
ex.show()
|
||||
|
||||
sys.exit(app.exec_())
|
||||
|
|
|
@ -54,7 +54,10 @@
|
|||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="repoTabSlot">
|
||||
<property name="sizePolicy">
|
||||
|
|
|
@ -100,8 +100,11 @@
|
|||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -122,7 +125,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveButton">
|
||||
|
|
|
@ -22,8 +22,14 @@
|
|||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QToolBox::tab {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="schedule">
|
||||
<property name="geometry">
|
||||
|
@ -31,7 +37,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>663</width>
|
||||
<height>415</height>
|
||||
<height>381</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
|
@ -40,140 +46,205 @@
|
|||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<attribute name="icon">
|
||||
<iconset>
|
||||
<normaloff>../icons/clock-o.svg</normaloff>../icons/clock-o.svg</iconset>
|
||||
</attribute>
|
||||
<attribute name="label">
|
||||
<string>Schedule</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>661</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Backup Manually</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Backup every </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_2"/>
|
||||
</item>
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>hours at</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>minutes past the hour.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_3">
|
||||
<property name="text">
|
||||
<string>Backup daily at</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTimeEdit" name="timeEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="scheduleOffRadio">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Backup Manually</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="scheduleIntervalRadio">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Backup every </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="scheduleIntervalHours">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>48</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>hours at</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="scheduleIntervalMinutes">
|
||||
<property name="maximum">
|
||||
<number>59</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>minutes past the hour.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="scheduleFixedRadio">
|
||||
<property name="text">
|
||||
<string>Backup daily at</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTimeEdit" name="scheduleFixedTime"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="scheduleApplyButton">
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Next Backup:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="nextBackupDateTimeLabel">
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<property name="geometry">
|
||||
|
@ -181,22 +252,82 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>663</width>
|
||||
<height>415</height>
|
||||
<height>381</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Rules</string>
|
||||
<attribute name="icon">
|
||||
<iconset>
|
||||
<normaloff>../icons/wifi.svg</normaloff>../icons/wifi.svg</iconset>
|
||||
</attribute>
|
||||
<widget class="QListWidget" name="wifiListWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>30</y>
|
||||
<width>256</width>
|
||||
<height>192</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<attribute name="label">
|
||||
<string>Networks</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QListWidget" name="wifiListWidget"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Allowed Networks:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page">
|
||||
<attribute name="icon">
|
||||
<iconset>
|
||||
<normaloff>../icons/tasks.svg</normaloff>../icons/tasks.svg</iconset>
|
||||
</attribute>
|
||||
<attribute name="label">
|
||||
<string>Log</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="logTableWidget">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Time</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Category</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Subcommand</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Message</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Returncode</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1024 544v448q0 14-9 23t-23 9h-320q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h224v-352q0-14 9-23t23-9h64q14 0 23 9t9 23zm416 352q0-148-73-273t-198-198-273-73-273 73-198 198-73 273 73 273 198 198 273 73 273-73 198-198 73-273zm224 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg>
|
After Width: | Height: | Size: 507 B |
|
@ -0,0 +1,9 @@
|
|||
<RCC>
|
||||
<qresource prefix="/icons">
|
||||
<file>wifi.svg</file>
|
||||
<file>tasks.svg</file>
|
||||
<file>clock-o.svg</file>
|
||||
<file>server.svg</file>
|
||||
<file>window-restore.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M128 1408h1024v-128h-1024v128zm0-512h1024v-128h-1024v128zm1568 448q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm-1568-960h1024v-128h-1024v128zm1568 448q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm0-512q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm96 832v384h-1792v-384h1792zm0-512v384h-1792v-384h1792zm0-512v384h-1792v-384h1792z"/></svg>
|
After Width: | Height: | Size: 507 B |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1024 1408h640v-128h-640v128zm-384-512h1024v-128h-1024v128zm640-512h384v-128h-384v128zm512 832v256q0 26-19 45t-45 19h-1664q-26 0-45-19t-19-45v-256q0-26 19-45t45-19h1664q26 0 45 19t19 45zm0-512v256q0 26-19 45t-45 19h-1664q-26 0-45-19t-19-45v-256q0-26 19-45t45-19h1664q26 0 45 19t19 45zm0-512v256q0 26-19 45t-45 19h-1664q-26 0-45-19t-19-45v-256q0-26 19-45t45-19h1664q26 0 45 19t19 45z"/></svg>
|
After Width: | Height: | Size: 530 B |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="2048" height="1792" viewBox="0 0 2048 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1024 1523q-20 0-93-73.5t-73-93.5q0-32 62.5-54t103.5-22 103.5 22 62.5 54q0 20-73 93.5t-93 73.5zm270-271q-2 0-40-25t-101.5-50-128.5-25-128.5 25-101 50-40.5 25q-18 0-93.5-75t-75.5-93q0-13 10-23 78-77 196-121t233-44 233 44 196 121q10 10 10 23 0 18-75.5 93t-93.5 75zm273-272q-11 0-23-8-136-105-252-154.5t-268-49.5q-85 0-170.5 22t-149 53-113.5 62-79 53-31 22q-17 0-92-75t-75-93q0-12 10-22 132-132 320-205t380-73 380 73 320 205q10 10 10 22 0 18-75 93t-92 75zm271-271q-11 0-22-9-179-157-371.5-236.5t-420.5-79.5-420.5 79.5-371.5 236.5q-11 9-22 9-17 0-92.5-75t-75.5-93q0-13 10-23 187-186 445-288t527-102 527 102 445 288q10 10 10 23 0 18-75.5 93t-92.5 75z"/></svg>
|
After Width: | Height: | Size: 793 B |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="2048" height="1792" viewBox="0 0 2048 1792" xmlns="http://www.w3.org/2000/svg"><path d="M256 1536h768v-512h-768v512zm1024-512h512v-768h-768v256h96q66 0 113 47t47 113v352zm768-864v960q0 66-47 113t-113 47h-608v352q0 66-47 113t-113 47h-960q-66 0-113-47t-47-113v-960q0-66 47-113t113-47h608v-352q0-66 47-113t113-47h960q66 0 113 47t47 113z"/></svg>
|
After Width: | Height: | Size: 393 B |
|
@ -10,7 +10,7 @@ from PyQt5 import QtCore
|
|||
from PyQt5.QtWidgets import QApplication
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from .models import SourceDirModel, BackupProfileModel
|
||||
from .models import SourceDirModel, BackupProfileModel, EventLogModel
|
||||
|
||||
|
||||
class BorgThread(QtCore.QThread):
|
||||
|
@ -31,18 +31,22 @@ class BorgThread(QtCore.QThread):
|
|||
env['BORG_HOSTNAME_IS_UNIQUE'] = '1'
|
||||
if params.get('password') and params['password']:
|
||||
env['BORG_PASSPHRASE'] = params['password']
|
||||
params['password'] = '***'
|
||||
|
||||
env['BORG_RSH'] = 'ssh -oStrictHostKeyChecking=no'
|
||||
if params.get('ssh_key') and params['ssh_key']:
|
||||
env['BORG_RSH'] += f' -i ~/.ssh/{params["ssh_key"]}'
|
||||
|
||||
self.env = env
|
||||
self.profile = BackupProfileModel.get(id=1)
|
||||
self.params = params
|
||||
self.process = None
|
||||
|
||||
def run(self):
|
||||
log_entry = EventLogModel(category='borg-run', subcommand=self.cmd[1], params=self.params)
|
||||
log_entry.save()
|
||||
self.process = Popen(self.cmd, stdout=PIPE, stderr=PIPE, bufsize=1, universal_newlines=True, env=self.env)
|
||||
for line in iter(self.process.stderr.readline, b''):
|
||||
for line in iter(self.process.stderr.readline, ''):
|
||||
try:
|
||||
parsed = json.loads(line)
|
||||
if parsed['type'] == 'log_message':
|
||||
|
@ -57,12 +61,15 @@ class BorgThread(QtCore.QThread):
|
|||
result = {
|
||||
'params': self.params,
|
||||
'returncode': self.process.returncode,
|
||||
'cmd': self.cmd
|
||||
}
|
||||
try:
|
||||
result['data'] = json.loads(stdout)
|
||||
except:
|
||||
result['data'] = {}
|
||||
|
||||
log_entry.returncode = self.process.returncode
|
||||
log_entry.save()
|
||||
self.result.emit(result)
|
||||
|
||||
@classmethod
|
||||
|
@ -80,9 +87,6 @@ class BorgThread(QtCore.QThread):
|
|||
|
||||
params = {'password': keyring.get_password("vorta-repo", profile.repo.url)}
|
||||
|
||||
print(params)
|
||||
|
||||
|
||||
if app.thread and app.thread.isRunning():
|
||||
ret['message'] = 'Backup is already in progress.'
|
||||
return ret
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
import peewee
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from .config import SETTINGS_DIR
|
||||
|
||||
class JSONField(peewee.TextField):
|
||||
"""
|
||||
Class to "fake" a JSON field with a text field. Not efficient but works nicely
|
||||
|
||||
From: https://gist.github.com/rosscdh/f4f26758b0228f475b132c688f15af2b
|
||||
"""
|
||||
def db_value(self, value):
|
||||
"""Convert the python value for storage in the database."""
|
||||
return value if value is None else json.dumps(value)
|
||||
|
||||
def python_value(self, value):
|
||||
"""Convert the database value to a pythonic value."""
|
||||
return value if value is None else json.loads(value)
|
||||
|
||||
db = peewee.SqliteDatabase(os.path.join(SETTINGS_DIR, 'settings.db'))
|
||||
|
||||
|
||||
|
@ -29,6 +44,11 @@ class BackupProfileModel(peewee.Model):
|
|||
compression = peewee.CharField(default='lz4')
|
||||
exclude_patterns = peewee.TextField(null=True)
|
||||
exclude_if_present = peewee.TextField(null=True)
|
||||
schedule_mode = peewee.CharField(default='off')
|
||||
schedule_interval_hours = peewee.IntegerField(default=3)
|
||||
schedule_interval_minutes = peewee.IntegerField(default=42)
|
||||
schedule_fixed_hour = peewee.IntegerField(default=3)
|
||||
schedule_fixed_minute = peewee.IntegerField(default=42)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
|
@ -58,7 +78,33 @@ class SnapshotModel(peewee.Model):
|
|||
database = db
|
||||
|
||||
|
||||
class WifiSettingModel(peewee.Model):
|
||||
"""Save Wifi Settings"""
|
||||
ssid = peewee.CharField()
|
||||
last_connected = peewee.DateTimeField()
|
||||
allowed = peewee.BooleanField(default=True)
|
||||
profile = peewee.ForeignKeyField(BackupProfileModel, default=1)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
|
||||
class EventLogModel(peewee.Model):
|
||||
"""Keep a log of background jobs."""
|
||||
start_time = peewee.DateTimeField(default=datetime.utcnow)
|
||||
category = peewee.CharField()
|
||||
subcommand = peewee.CharField(null=True)
|
||||
message = peewee.CharField(null=True)
|
||||
returncode = peewee.IntegerField(default=1)
|
||||
params = JSONField(null=True)
|
||||
profile = peewee.ForeignKeyField(BackupProfileModel, default=1)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
|
||||
db.connect()
|
||||
db.create_tables([RepoModel, BackupProfileModel, SourceDirModel, SnapshotModel])
|
||||
db.create_tables([RepoModel, BackupProfileModel, SourceDirModel,
|
||||
SnapshotModel, WifiSettingModel, EventLogModel])
|
||||
|
||||
BackupProfileModel.get_or_create(id=1, name='Default')
|
||||
|
|
|
@ -1,12 +1,31 @@
|
|||
from apscheduler.schedulers.qt import QtScheduler
|
||||
from apscheduler.triggers import cron
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from .models import BackupProfileModel
|
||||
|
||||
|
||||
def tick():
|
||||
print('scheduler')
|
||||
print('scheduler running')
|
||||
|
||||
|
||||
def init_scheduler():
|
||||
app = QApplication.instance()
|
||||
if hasattr(app, 'scheduler') and app.scheduler is not None:
|
||||
app.scheduler.shutdown()
|
||||
|
||||
s = QtScheduler()
|
||||
trigger = cron.CronTrigger(second='*/3')
|
||||
|
||||
profile = BackupProfileModel.get(id=1)
|
||||
if profile.schedule_mode == 'off':
|
||||
return None
|
||||
elif profile.schedule_mode == 'interval':
|
||||
trigger = cron.CronTrigger(hour=f'*/{profile.schedule_interval_hours}',
|
||||
minute=profile.schedule_interval_minutes)
|
||||
elif profile.schedule_mode == 'fixed':
|
||||
trigger = cron.CronTrigger(hour=profile.schedule_fixed_hour,
|
||||
minute=profile.schedule_fixed_minute)
|
||||
|
||||
s.add_job(tick, trigger, id='create-backup')
|
||||
s.start()
|
||||
return s
|
||||
|
|
|
@ -5,7 +5,9 @@ from paramiko.rsakey import RSAKey
|
|||
from paramiko.ecdsakey import ECDSAKey
|
||||
from paramiko.ed25519key import Ed25519Key
|
||||
from paramiko import SSHException
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from .models import WifiSettingModel
|
||||
|
||||
def get_private_keys():
|
||||
"""Find SSH keys in standard folder."""
|
||||
|
@ -41,7 +43,7 @@ def prettyBytes(size):
|
|||
n = 0
|
||||
Dic_powerN = {0: '', 1: 'K', 2: 'M', 3: 'G', 4: 'T'}
|
||||
while size > power:
|
||||
size /= power
|
||||
size /= power
|
||||
n += 1
|
||||
return str(round(size))+Dic_powerN[n]+'B'
|
||||
|
||||
|
@ -58,12 +60,16 @@ def get_asset(path):
|
|||
|
||||
def get_sorted_wifis():
|
||||
"""Get SSIDs from OS and merge with settings in DB."""
|
||||
app = QApplication.instance()
|
||||
|
||||
wifis = plistlib.load(open('/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist', 'rb'))['KnownNetworks']
|
||||
out = []
|
||||
plist_file = open('/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist', 'rb')
|
||||
wifis = plistlib.load(plist_file)['KnownNetworks']
|
||||
if wifis:
|
||||
for wifi in wifis.values():
|
||||
timestamp = wifi['LastConnected']
|
||||
ssid = wifi['SSIDString']
|
||||
out.append({'ssid': ssid, 'last_connected': timestamp, 'allowed': True})
|
||||
return out
|
||||
WifiSettingModel.get_or_create(ssid=ssid, profile=app.profile,
|
||||
defaults={'last_connected': timestamp,
|
||||
'allowed': True})
|
||||
|
||||
return WifiSettingModel.select().order_by(-WifiSettingModel.last_connected)
|
||||
|
|
|
@ -63,7 +63,6 @@ class RepoTab(RepoBase, RepoUI):
|
|||
else:
|
||||
self.profile.ssh_key = self.sshComboBox.itemData(index)
|
||||
self.profile.save()
|
||||
print('set ssh key to', self.profile.ssh_key)
|
||||
|
||||
|
||||
def compression_select_action(self, index):
|
||||
|
|
|
@ -1,27 +1,88 @@
|
|||
import plistlib
|
||||
|
||||
from PyQt5 import uic, QtCore
|
||||
from PyQt5.QtWidgets import QFileDialog, QListWidgetItem
|
||||
from ..models import SourceDirModel
|
||||
from PyQt5.QtWidgets import QListWidgetItem, QApplication, QTableView, QHeaderView, QTableWidgetItem
|
||||
from ..utils import get_asset, get_sorted_wifis
|
||||
from ..scheduler import init_scheduler
|
||||
from ..models import EventLogModel
|
||||
|
||||
uifile = get_asset('UI/scheduletab.ui')
|
||||
ScheduleUI, ScheduleBase = uic.loadUiType(uifile)
|
||||
|
||||
|
||||
class ScheduleTab(ScheduleBase, ScheduleUI):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(parent)
|
||||
self.profile = self.window().profile
|
||||
self.app = QApplication.instance()
|
||||
|
||||
self.schedulerRadioMapping = {
|
||||
'off': self.scheduleOffRadio,
|
||||
'interval': self.scheduleIntervalRadio,
|
||||
'fixed': self.scheduleFixedRadio
|
||||
}
|
||||
self.schedulerRadioMapping[self.profile.schedule_mode].setChecked(True)
|
||||
|
||||
self.scheduleIntervalHours.setValue(self.profile.schedule_interval_hours)
|
||||
self.scheduleIntervalMinutes.setValue(self.profile.schedule_interval_minutes)
|
||||
self.scheduleFixedTime.setTime(
|
||||
QtCore.QTime(self.profile.schedule_fixed_hour, self.profile.schedule_fixed_minute))
|
||||
|
||||
self.scheduleApplyButton.clicked.connect(self.on_scheduler_apply)
|
||||
|
||||
self.set_next_backup_datetime()
|
||||
self.init_wifi()
|
||||
self.init_logs()
|
||||
|
||||
def init_wifi(self):
|
||||
for wifi in get_sorted_wifis():
|
||||
item = QListWidgetItem()
|
||||
item.setText(wifi['ssid'])
|
||||
item.setText(wifi.ssid)
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
|
||||
if wifi['allowed']:
|
||||
if wifi.allowed:
|
||||
item.setCheckState(QtCore.Qt.Checked)
|
||||
self.wifiListWidget.addItem(item)
|
||||
|
||||
def init_logs(self):
|
||||
header = self.logTableWidget.horizontalHeader()
|
||||
header.setVisible(True)
|
||||
[header.setSectionResizeMode(i, QHeaderView.ResizeToContents) for i in range(5)]
|
||||
header.setSectionResizeMode(3, QHeaderView.Stretch)
|
||||
|
||||
self.logTableWidget.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.logTableWidget.setEditTriggers(QTableView.NoEditTriggers)
|
||||
|
||||
event_logs = [s for s in EventLogModel.select()]
|
||||
|
||||
for row, log_line in enumerate(event_logs):
|
||||
self.logTableWidget.insertRow(row)
|
||||
formatted_time = log_line.start_time.strftime('%Y-%m-%d %H:%M')
|
||||
self.logTableWidget.setItem(row, 0, QTableWidgetItem(formatted_time))
|
||||
self.logTableWidget.setItem(row, 1, QTableWidgetItem(log_line.category))
|
||||
self.logTableWidget.setItem(row, 2, QTableWidgetItem(log_line.subcommand))
|
||||
self.logTableWidget.setItem(row, 3, QTableWidgetItem(log_line.message))
|
||||
self.logTableWidget.setItem(row, 4, QTableWidgetItem(str(log_line.returncode)))
|
||||
self.logTableWidget.setRowCount(len(event_logs))
|
||||
|
||||
def set_next_backup_datetime(self):
|
||||
if self.app.scheduler is not None:
|
||||
job = self.app.scheduler.get_job('create-backup')
|
||||
self.nextBackupDateTimeLabel.setText(job.next_run_time.strftime('%Y-%m-%d %H:%M'))
|
||||
else:
|
||||
self.nextBackupDateTimeLabel.setText('Off')
|
||||
self.nextBackupDateTimeLabel.repaint()
|
||||
|
||||
def on_scheduler_apply(self):
|
||||
for label, obj in self.schedulerRadioMapping.items():
|
||||
if obj.isChecked():
|
||||
self.profile.schedule_mode = label
|
||||
self.profile.schedule_interval_hours = self.scheduleIntervalHours.value()
|
||||
self.profile.schedule_interval_minutes = self.scheduleIntervalMinutes.value()
|
||||
qtime = self.scheduleFixedTime.time()
|
||||
self.profile.schedule_fixed_hour, self.profile.schedule_fixed_minute = qtime.hour(), qtime.minute()
|
||||
self.profile.save()
|
||||
self.app.scheduler = init_scheduler()
|
||||
self.set_next_backup_datetime()
|
||||
|
||||
|
||||
|
||||
def init_log(self):
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue