1
0
Fork 0
mirror of https://github.com/borgbase/vorta synced 2024-12-21 23:33:13 +00:00

Schedule tab refactoring. By @shivansh02 (#2035)

This commit is contained in:
Shivansh Singh 2024-07-19 19:51:58 +05:30 committed by GitHub
parent 513e8e2cbd
commit d0a996c1ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 1276 additions and 1090 deletions

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogPage</class>
<widget class="QWidget" name="LogPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>687</width>
<height>384</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableWidget" name="logPage">
<property name="font">
<font>
<pointsize>11</pointsize>
<bold>false</bold>
</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>Repository</string>
</property>
</column>
<column>
<property name="text">
<string>Returncode</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QLabel" name="logLink">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;file:///&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0984e3;&quot;&gt;View the logs&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="indent">
<number>0</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NetworksPage</class>
<widget class="QWidget" name="NetworksPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>687</width>
<height>384</height>
</rect>
</property>
<attribute name="label">
<string>Networks</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_8">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QLabel" name="wifiListLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Allowed Networks:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="wifiListWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="meteredNetworksCheckBox">
<property name="text">
<string>Run backups over metered networks</string>
</property>
<property name="tristate">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,597 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SchedulePage</class>
<widget class="QWidget" name="schedule">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>687</width>
<height>384</height>
</rect>
</property>
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<attribute name="label">
<string>Schedule</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="formAlignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
<property name="horizontalSpacing">
<number>8</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Backup schedule:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="scheduleOffRadio">
<property name="text">
<string>Manual only</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>4</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QRadioButton" name="scheduleIntervalRadio">
<property name="text">
<string>Backup periodically</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="framePeriodic">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutPeriodic">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Interval:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="scheduleIntervalCount">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>740</number>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="scheduleIntervalUnit"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QRadioButton" name="scheduleFixedRadio">
<property name="text">
<string>Backup daily</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frameDaily">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutDaily">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Time:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QTimeEdit" name="scheduleFixedTime"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="5" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Missed backups:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="missedBackupsCheckBox">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Run missed backups on startup or wakeup</string>
</property>
</widget>
</item>
<item row="7" column="1">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Autopruning:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="pruneCheckBox">
<property name="text">
<string>Prune after each backup</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Validation:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<item>
<widget class="QCheckBox" name="validationCheckBox">
<property name="text">
<string>Validate repository data</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frameValidation">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutValidation">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Interval:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="validationWeeksCount">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>52</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>weeks</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="compaction_label">
<property name="text">
<string>Compaction:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<item>
<widget class="QCheckBox" name="compactionCheckBox">
<property name="text">
<string>Compact repository</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frameCompaction">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutCompaction">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="interval_label">
<property name="text">
<string>Interval:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_interval">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="compactionWeeksCount">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>52</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="compaction_weeks_label">
<property name="text">
<string>weeks</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_14">
<property name="font">
<font>
<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>None scheduled</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<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>
<resources/>
<connections/>
</ui>

View file

@ -33,7 +33,7 @@
<bold>false</bold>
</font>
</property>
<widget class="QWidget" name="schedule">
<widget class="QWidget" name="page_1">
<property name="geometry">
<rect>
<x>0</x>
@ -42,588 +42,12 @@
<height>384</height>
</rect>
</property>
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<attribute name="label">
<string>Schedule</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="formAlignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
<property name="horizontalSpacing">
<number>8</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Backup schedule:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="scheduleOffRadio">
<property name="text">
<string>Manual only</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>4</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QRadioButton" name="scheduleIntervalRadio">
<property name="text">
<string>Backup periodically</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="framePeriodic">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutPeriodic">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Interval:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="scheduleIntervalCount">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>740</number>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="scheduleIntervalUnit"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QRadioButton" name="scheduleFixedRadio">
<property name="text">
<string>Backup daily</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frameDaily">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutDaily">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Time:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QTimeEdit" name="scheduleFixedTime"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="5" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Missed backups:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="missedBackupsCheckBox">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Run missed backups on startup or wakeup</string>
</property>
</widget>
</item>
<item row="7" column="1">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Autopruning:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="pruneCheckBox">
<property name="text">
<string>Prune after each backup</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Validation:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<item>
<widget class="QCheckBox" name="validationCheckBox">
<property name="text">
<string>Validate repository data</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frameValidation">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutValidation">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Interval:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="validationWeeksCount">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>52</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>weeks</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="compaction_label">
<property name="text">
<string>Compaction:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<item>
<widget class="QCheckBox" name="compactionCheckBox">
<property name="text">
<string>Compact repository</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frameCompaction">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layoutCompaction">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="interval_label">
<property name="text">
<string>Interval:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_interval">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="compactionWeeksCount">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>52</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="compaction_weeks_label">
<property name="text">
<string>weeks</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_14">
<property name="font">
<font>
<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>None scheduled</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<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 class="QGridLayout" name="scheduleLayout">
</layout>
</widget>
</widget>
<widget class="QWidget" name="page_2">
<property name="geometry">
<rect>
@ -636,49 +60,9 @@
<attribute name="label">
<string>Networks</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_8">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QLabel" name="wifiListLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Allowed Networks:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="wifiListWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="meteredNetworksCheckBox">
<property name="text">
<string>Run backups over metered networks</string>
</property>
<property name="tristate">
<bool>false</bool>
</property>
</widget>
</item>
<layout class="QGridLayout" name="networksLayout">
</layout>
</widget>
</widget>
<widget class="QWidget" name="page">
<property name="geometry">
<rect>
@ -691,67 +75,10 @@
<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>
<bold>false</bold>
</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>Repository</string>
</property>
</column>
<column>
<property name="text">
<string>Returncode</string>
</property>
</column>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="logLink">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;file:///&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0984e3;&quot;&gt;View the logs&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="indent">
<number>0</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<layout class="QGridLayout" name="logLayout">
</layout>
</widget>
<widget class="QWidget" name="page_3">
</widget>
<widget class="QWidget" name="page">
<property name="geometry">
<rect>
<x>0</x>
@ -763,178 +90,9 @@
<attribute name="label">
<string>Shell Commands</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Run custom shell commands before and after each backup. The actual backup and post-backup command will only run, if the pre-backup command exits without error (return code 0). Available variables: &lt;span style=&quot; font-family:'Courier';&quot;&gt;$repo_url, $profile_name, $profile_slug, $returncode&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Pre-backup:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="preBackupCmdLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font-family:'Courier';</string>
</property>
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>echo &quot;Before backup of $repo_url&quot;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Post-backup:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="postBackupCmdLineEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font-family:'Courier';</string>
</property>
<property name="placeholderText">
<string>echo &quot;Backup of $repo_url ended with $returncode.&quot;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Extra arguments for &lt;span style=&quot; font-style:italic;&quot;&gt;borg create&lt;/span&gt;. Possible options are listed in the &lt;a href=&quot;https://borgbackup.readthedocs.io/en/stable/usage/create.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0984e3;&quot;&gt;the borg documentation&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="createCmdLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-family:'Courier';</string>
</property>
<property name="placeholderText">
<string>--dry-run --noflags</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<layout class="QGridLayout" name="shellCommandsLayout">
</layout>
</widget>
</widget>
</widget>
</item>
</layout>

View file

@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ShellCommandsPage</class>
<widget class="QWidget" name="ShellCommandsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>687</width>
<height>384</height>
</rect>
</property>
<attribute name="label">
<string>Shell Commands</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Run custom shell commands before and after each backup. The actual backup and post-backup command will only run, if the pre-backup command exits without error (return code 0). Available variables: &lt;span style=&quot; font-family:'Courier';&quot;&gt;$repo_url, $profile_name, $profile_slug, $returncode&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Pre-backup:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="preBackupCmdLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font-family:'Courier';</string>
</property>
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>echo &quot;Before backup of $repo_url&quot;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Post-backup:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="postBackupCmdLineEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font-family:'Courier';</string>
</property>
<property name="placeholderText">
<string>echo &quot;Backup of $repo_url ended with $returncode.&quot;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Extra arguments for &lt;span style=&quot; font-style:italic;&quot;&gt;borg create&lt;/span&gt;. Possible options are listed in the &lt;a href=&quot;https://borgbackup.readthedocs.io/en/stable/usage/create.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0984e3;&quot;&gt;the borg documentation&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="createCmdLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-family:'Courier';</string>
</property>
<property name="placeholderText">
<string>--dry-run --noflags</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,59 @@
from PyQt6 import uic
from PyQt6.QtWidgets import (
QAbstractItemView,
QHeaderView,
QTableWidgetItem,
)
from vorta import config
from vorta.store.models import EventLogModel
from vorta.utils import get_asset
uifile = get_asset('UI/logpage.ui')
LogTableUI, LogTableBase = uic.loadUiType(uifile)
class LogTableColumn:
Time = 0
Category = 1
Subcommand = 2
Repository = 3
ReturnCode = 4
class LogPage(LogTableBase, LogTableUI):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.init_ui()
def init_ui(self):
self.logPage.setAlternatingRowColors(True)
header = self.logPage.horizontalHeader()
header.setVisible(True)
[header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents) for i in range(5)]
header.setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch)
self.logPage.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.logPage.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self.logLink.setText(
f'<a href="file://{config.LOG_DIR}"><span style="text-decoration:'
'underline; color:#0984e3;">Click here</span></a> for complete logs.'
)
self.populate_logs()
def populate_logs(self):
event_logs = [s for s in EventLogModel.select().order_by(EventLogModel.start_time.desc())]
sorting = self.logPage.isSortingEnabled()
self.logPage.setSortingEnabled(False)
self.logPage.setRowCount(len(event_logs))
for row, log_line in enumerate(event_logs):
formatted_time = log_line.start_time.strftime('%Y-%m-%d %H:%M')
self.logPage.setItem(row, LogTableColumn.Time, QTableWidgetItem(formatted_time))
self.logPage.setItem(row, LogTableColumn.Category, QTableWidgetItem(log_line.category))
self.logPage.setItem(row, LogTableColumn.Subcommand, QTableWidgetItem(log_line.subcommand))
self.logPage.setItem(row, LogTableColumn.Repository, QTableWidgetItem(log_line.repo_url))
self.logPage.setItem(row, LogTableColumn.ReturnCode, QTableWidgetItem(str(log_line.returncode)))
self.logPage.setSortingEnabled(sorting)

View file

@ -80,7 +80,7 @@ def __init__(self, parent=None):
self.tabWidget.setCurrentIndex(0)
self.repoTab.repo_changed.connect(self.archiveTab.populate_from_profile)
self.repoTab.repo_changed.connect(self.scheduleTab.populate_from_profile)
self.repoTab.repo_changed.connect(self.scheduleTab.schedulePage.populate_from_profile)
self.repoTab.repo_added.connect(self.archiveTab.refresh_archive_list)
self.miscTab.refresh_archive.connect(self.archiveTab.populate_from_profile)
@ -110,8 +110,8 @@ def __init__(self, parent=None):
# OS-specific startup options:
if not get_network_status_monitor().is_network_status_available():
# Hide Wifi-rule section in schedule tab.
self.scheduleTab.wifiListLabel.hide()
self.scheduleTab.wifiListWidget.hide()
self.scheduleTab.networksPage.wifiListLabel.hide()
self.scheduleTab.networksPage.wifiListWidget.hide()
self.scheduleTab.page_2.hide()
self.scheduleTab.toolBox.removeItem(1)
@ -185,7 +185,7 @@ def profile_selection_changed_action(self, index):
self.archiveTab.populate_from_profile()
self.repoTab.populate_from_profile()
self.sourceTab.populate_from_profile()
self.scheduleTab.populate_from_profile()
self.scheduleTab.schedulePage.populate_from_profile()
SettingsModel.update({SettingsModel.str_value: self.current_profile.id}).where(
SettingsModel.key == 'previous_profile_id'
).execute()
@ -260,8 +260,8 @@ def profile_imported_event(profile):
self.tr('Profile {} imported.').format(profile.name),
)
self.repoTab.populate_from_profile()
self.scheduleTab.populate_logs()
self.scheduleTab.populate_wifi()
self.scheduleTab.logPage.populate_logs()
self.scheduleTab.networksPage.populate_wifi()
self.miscTab.populate()
self.populate_profile_selector()
@ -323,7 +323,7 @@ def backup_started_event(self):
def backup_finished_event(self):
self.archiveTab.populate_from_profile()
self.repoTab.init_repo_stats()
self.scheduleTab.populate_logs()
self.scheduleTab.logPage.populate_logs()
if not self.app.jobs_manager.is_worker_running() and (
self.archiveTab.remaining_refresh_archives == 0 or self.archiveTab.remaining_refresh_archives == 1

View file

@ -0,0 +1,60 @@
from PyQt6 import uic
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QCheckBox, QLabel, QListWidget, QListWidgetItem
from vorta.store.models import BackupProfileMixin, WifiSettingModel
from vorta.utils import get_asset, get_sorted_wifis
uifile = get_asset('UI/networkspage.ui')
NetworksUI, NetworksBase = uic.loadUiType(uifile)
class NetworksPage(NetworksBase, NetworksUI, BackupProfileMixin):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.wifiListLabel: QLabel = self.findChild(QLabel, 'wifiListLabel')
self.meteredNetworksCheckBox: QCheckBox = self.findChild(QCheckBox, 'meteredNetworksCheckBox')
self.wifiListWidget: QListWidget = self.findChild(QListWidget, 'wifiListWidget')
self.populate_wifi()
self.setup_connections()
def setup_connections(self):
self.meteredNetworksCheckBox.stateChanged.connect(self.on_metered_networks_state_changed)
self.wifiListWidget.itemChanged.connect(self.save_wifi_item)
def on_metered_networks_state_changed(self, state):
profile = self.profile()
attr = 'dont_run_on_metered_networks'
new_value = state != Qt.CheckState.Checked
self.save_profile_attr(attr, new_value)
self.meteredNetworksCheckBox.setChecked(False if profile.dont_run_on_metered_networks else True)
def populate_wifi(self):
self.wifiListWidget.clear()
profile = self.profile()
if profile:
for wifi in get_sorted_wifis(profile):
item = QListWidgetItem()
item.setText(wifi.ssid)
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
if wifi.allowed:
item.setCheckState(Qt.CheckState.Checked)
else:
item.setCheckState(Qt.CheckState.Unchecked)
self.wifiListWidget.addItem(item)
def save_wifi_item(self, item):
profile = self.profile()
if profile:
db_item = WifiSettingModel.get(ssid=item.text(), profile=profile.id)
db_item.allowed = item.checkState() == Qt.CheckState.Checked
db_item.save()
def save_profile_attr(self, attr, new_value):
profile = self.profile()
if profile:
setattr(profile, attr, new_value)
profile.save()

View file

@ -0,0 +1,137 @@
from PyQt6 import QtCore, uic
from PyQt6.QtCore import QDateTime, QLocale
from PyQt6.QtWidgets import QApplication
from vorta.i18n import get_locale
from vorta.scheduler import ScheduleStatusType
from vorta.store.models import BackupProfileMixin
from vorta.utils import get_asset
uifile = get_asset('UI/schedulepage.ui')
SchedulePageUI, SchedulePageBase = uic.loadUiType(uifile)
class SchedulePage(SchedulePageBase, SchedulePageUI, BackupProfileMixin):
def __init__(self, parent=None):
super().__init__(parent)
self.app = QApplication.instance()
self.setupUi(self)
self.schedulerRadioMapping = {
'off': self.scheduleOffRadio,
'interval': self.scheduleIntervalRadio,
'fixed': self.scheduleFixedRadio,
}
self.populate_from_profile()
self.scheduleIntervalUnit.addItem(self.tr('Minutes'), 'minutes')
self.scheduleIntervalUnit.addItem(self.tr('Hours'), 'hours')
self.scheduleIntervalUnit.addItem(self.tr('Days'), 'days')
self.scheduleIntervalUnit.addItem(self.tr('Weeks'), 'weeks')
self.framePeriodic.setEnabled(False)
self.frameDaily.setEnabled(False)
self.frameValidation.setEnabled(False)
self.frameCompaction.setEnabled(False)
self.scheduleIntervalRadio.toggled.connect(self.framePeriodic.setEnabled)
self.scheduleFixedRadio.toggled.connect(self.frameDaily.setEnabled)
self.validationCheckBox.toggled.connect(self.frameValidation.setEnabled)
self.compactionCheckBox.toggled.connect(self.frameCompaction.setEnabled)
for label, obj in self.schedulerRadioMapping.items():
obj.clicked.connect(self.on_scheduler_change)
self.scheduleIntervalCount.valueChanged.connect(self.on_scheduler_change)
self.scheduleIntervalUnit.currentIndexChanged.connect(self.on_scheduler_change)
self.scheduleFixedTime.timeChanged.connect(self.on_scheduler_change)
self.missedBackupsCheckBox.stateChanged.connect(
lambda new_val, attr='schedule_make_up_missed': self.save_profile_attr(attr, new_val)
)
self.pruneCheckBox.stateChanged.connect(lambda new_val, attr='prune_on': self.save_profile_attr(attr, new_val))
self.validationCheckBox.stateChanged.connect(
lambda new_val, attr='validation_on': self.save_profile_attr(attr, new_val)
)
self.validationWeeksCount.valueChanged.connect(
lambda new_val, attr='validation_weeks': self.save_profile_attr(attr, new_val)
)
self.compactionCheckBox.stateChanged.connect(
lambda new_val, attr='compaction_on': self.save_profile_attr(attr, new_val)
)
self.compactionWeeksCount.valueChanged.connect(
lambda new_val, attr='compaction_weeks': self.save_profile_attr(attr, new_val)
)
self.app.scheduler.schedule_changed.connect(lambda pid: self.draw_next_scheduled_backup())
self.app.paletteChanged.connect(lambda p: self.set_icons())
def on_scheduler_change(self, _):
profile = self.profile()
for label, obj in self.schedulerRadioMapping.items():
if obj.isChecked():
profile.schedule_mode = label
profile.schedule_interval_unit = self.scheduleIntervalUnit.currentData()
profile.schedule_interval_count = self.scheduleIntervalCount.value()
qtime = self.scheduleFixedTime.time()
profile.schedule_fixed_hour, profile.schedule_fixed_minute = (
qtime.hour(),
qtime.minute(),
)
profile.save()
self.app.scheduler.set_timer_for_profile(profile.id)
self.draw_next_scheduled_backup()
def populate_from_profile(self):
profile = self.profile()
self.schedulerRadioMapping[profile.schedule_mode].setChecked(True)
self.scheduleIntervalUnit.setCurrentIndex(self.scheduleIntervalUnit.findData(profile.schedule_interval_unit))
self.scheduleIntervalCount.setValue(profile.schedule_interval_count)
self.scheduleFixedTime.setTime(QtCore.QTime(profile.schedule_fixed_hour, profile.schedule_fixed_minute))
self.validationCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.validation_on else QtCore.Qt.CheckState.Unchecked
)
self.validationWeeksCount.setValue(profile.validation_weeks)
self.compactionCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.compaction_on else QtCore.Qt.CheckState.Unchecked
)
self.compactionWeeksCount.setValue(profile.compaction_weeks)
self.pruneCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.prune_on else QtCore.Qt.CheckState.Unchecked
)
self.missedBackupsCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.schedule_make_up_missed else QtCore.Qt.CheckState.Unchecked
)
self.draw_next_scheduled_backup()
def draw_next_scheduled_backup(self):
status = self.app.scheduler.next_job_for_profile(self.profile().id)
if status.type in (
ScheduleStatusType.SCHEDULED,
ScheduleStatusType.TOO_FAR_AHEAD,
):
time = QDateTime.fromMSecsSinceEpoch(int(status.time.timestamp() * 1000))
text = get_locale().toString(time, QLocale.FormatType.LongFormat)
elif status.type == ScheduleStatusType.NO_PREVIOUS_BACKUP:
text = self.tr('Run a manual backup first')
else:
text = self.tr('None scheduled')
self.nextBackupDateTimeLabel.setText(text)
self.nextBackupDateTimeLabel.repaint()
def save_profile_attr(self, attr, new_value):
profile = self.profile()
setattr(profile, attr, new_value)
profile.save()
def save_repo_attr(self, attr, new_value):
repo = self.profile().repo
setattr(repo, attr, new_value)
repo.save()

View file

@ -1,253 +1,55 @@
from PyQt6 import QtCore, uic
from PyQt6.QtCore import QDateTime, QLocale, Qt
from PyQt6.QtWidgets import (
QAbstractItemView,
QApplication,
QHeaderView,
QListWidgetItem,
QTableWidgetItem,
)
from PyQt6 import uic
from PyQt6.QtWidgets import QApplication
from vorta import application, config
from vorta.i18n import get_locale
from vorta.scheduler import ScheduleStatusType
from vorta.store.models import BackupProfileMixin, EventLogModel, WifiSettingModel
from vorta.utils import get_asset, get_sorted_wifis
from vorta import application
from vorta.store.models import BackupProfileMixin
from vorta.utils import get_asset
from vorta.views.log_page import LogPage
from vorta.views.networks_page import NetworksPage
from vorta.views.schedule_page import SchedulePage
from vorta.views.shell_commands_page import ShellCommandsPage
from vorta.views.utils import get_colored_icon
uifile = get_asset('UI/scheduletab.ui')
ScheduleUI, ScheduleBase = uic.loadUiType(uifile)
class LogTableColumn:
Time = 0
Category = 1
Subcommand = 2
Repository = 3
ReturnCode = 4
class ScheduleTab(ScheduleBase, ScheduleUI, BackupProfileMixin):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(parent)
self.app: application.VortaApp = QApplication.instance()
self.toolBox.setCurrentIndex(0)
self.schedulerRadioMapping = {
'off': self.scheduleOffRadio,
'interval': self.scheduleIntervalRadio,
'fixed': self.scheduleFixedRadio,
}
# Set up log table
self.logTableWidget.setAlternatingRowColors(True)
header = self.logTableWidget.horizontalHeader()
self.logLink.setText(
f'<a href="file://{config.LOG_DIR}"><span style="text-decoration:'
'underline; color:#0984e3;">Click here</span></a> for complete logs.'
)
header.setVisible(True)
[header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents) for i in range(5)]
header.setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch)
self.logTableWidget.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.logTableWidget.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
# Scheduler intervals we know
self.scheduleIntervalUnit.addItem(self.tr('Minutes'), 'minutes')
self.scheduleIntervalUnit.addItem(self.tr('Hours'), 'hours')
self.scheduleIntervalUnit.addItem(self.tr('Days'), 'days')
self.scheduleIntervalUnit.addItem(self.tr('Weeks'), 'weeks')
# Enable/Disable entries on button state changed
self.framePeriodic.setEnabled(False)
self.frameDaily.setEnabled(False)
self.frameValidation.setEnabled(False)
self.frameCompaction.setEnabled(False)
self.scheduleIntervalRadio.toggled.connect(self.framePeriodic.setEnabled)
self.scheduleFixedRadio.toggled.connect(self.frameDaily.setEnabled)
self.validationCheckBox.toggled.connect(self.frameValidation.setEnabled)
self.compactionCheckBox.toggled.connect(self.frameCompaction.setEnabled)
# POPULATE with data
self.populate_from_profile()
self.set_icons()
self.init_log_page()
self.init_shell_commands_page()
self.init_networks_page()
self.init_schedule_page()
# Connect events
self.app.backup_finished_event.connect(self.populate_logs)
self.app.backup_finished_event.connect(self.logPage.populate_logs)
# Scheduler events
for label, obj in self.schedulerRadioMapping.items():
obj.clicked.connect(self.on_scheduler_change)
self.scheduleIntervalCount.valueChanged.connect(self.on_scheduler_change)
self.scheduleIntervalUnit.currentIndexChanged.connect(self.on_scheduler_change)
self.scheduleFixedTime.timeChanged.connect(self.on_scheduler_change)
def init_log_page(self):
self.logPage = LogPage(self)
self.logLayout.addWidget(self.logPage)
self.logPage.show()
# Network and shell commands events
self.meteredNetworksCheckBox.stateChanged.connect(
lambda new_val, attr='dont_run_on_metered_networks': self.save_profile_attr(attr, not new_val)
)
self.postBackupCmdLineEdit.textEdited.connect(
lambda new_val, attr='post_backup_cmd': self.save_profile_attr(attr, new_val)
)
self.preBackupCmdLineEdit.textEdited.connect(
lambda new_val, attr='pre_backup_cmd': self.save_profile_attr(attr, new_val)
)
self.createCmdLineEdit.textEdited.connect(
lambda new_val, attr='create_backup_cmd': self.save_repo_attr(attr, new_val)
)
self.missedBackupsCheckBox.stateChanged.connect(
lambda new_val, attr='schedule_make_up_missed': self.save_profile_attr(attr, new_val)
)
self.pruneCheckBox.stateChanged.connect(lambda new_val, attr='prune_on': self.save_profile_attr(attr, new_val))
self.validationCheckBox.stateChanged.connect(
lambda new_val, attr='validation_on': self.save_profile_attr(attr, new_val)
)
self.validationWeeksCount.valueChanged.connect(
lambda new_val, attr='validation_weeks': self.save_profile_attr(attr, new_val)
)
self.compactionCheckBox.stateChanged.connect(
lambda new_val, attr='compaction_on': self.save_profile_attr(attr, new_val)
)
self.compactionWeeksCount.valueChanged.connect(
lambda new_val, attr='compaction_weeks': self.save_profile_attr(attr, new_val)
)
def init_shell_commands_page(self):
self.shellCommandsPage = ShellCommandsPage(self)
self.shellCommandsLayout.addWidget(self.shellCommandsPage)
self.shellCommandsPage.show()
# Connect to schedule update
self.app.scheduler.schedule_changed.connect(lambda pid: self.draw_next_scheduled_backup())
def init_networks_page(self):
self.networksPage = NetworksPage(self)
self.networksLayout.addWidget(self.networksPage)
self.networksPage.show()
# Connect to palette change
self.app.paletteChanged.connect(lambda p: self.set_icons())
def on_scheduler_change(self, _):
profile = self.profile()
# Save scheduler settings, apply new scheduler and display next task for profile.
for label, obj in self.schedulerRadioMapping.items():
if obj.isChecked():
profile.schedule_mode = label
profile.schedule_interval_unit = self.scheduleIntervalUnit.currentData()
profile.schedule_interval_count = self.scheduleIntervalCount.value()
qtime = self.scheduleFixedTime.time()
profile.schedule_fixed_hour, profile.schedule_fixed_minute = (
qtime.hour(),
qtime.minute(),
)
profile.save()
self.app.scheduler.set_timer_for_profile(profile.id)
self.draw_next_scheduled_backup()
def init_schedule_page(self):
self.schedulePage = SchedulePage(self)
self.scheduleLayout.addWidget(self.schedulePage)
self.schedulePage.show()
def set_icons(self):
self.toolBox.setItemIcon(0, get_colored_icon('clock-o'))
self.toolBox.setItemIcon(1, get_colored_icon('wifi'))
self.toolBox.setItemIcon(2, get_colored_icon('tasks'))
self.toolBox.setItemIcon(3, get_colored_icon('terminal'))
def populate_from_profile(self):
"""Populate current view with data from selected profile."""
profile = self.profile()
self.schedulerRadioMapping[profile.schedule_mode].setChecked(True)
# Set interval scheduler options
self.scheduleIntervalUnit.setCurrentIndex(self.scheduleIntervalUnit.findData(profile.schedule_interval_unit))
self.scheduleIntervalCount.setValue(profile.schedule_interval_count)
# Set fixed daily time scheduler options
self.scheduleFixedTime.setTime(QtCore.QTime(profile.schedule_fixed_hour, profile.schedule_fixed_minute))
# Set borg-check options
self.validationCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.validation_on else QtCore.Qt.CheckState.Unchecked
)
self.validationWeeksCount.setValue(profile.validation_weeks)
# set borg compact options
self.compactionCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.compaction_on else QtCore.Qt.CheckState.Unchecked
)
self.compactionWeeksCount.setValue(profile.compaction_weeks)
# Other checkbox options
self.pruneCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.prune_on else QtCore.Qt.CheckState.Unchecked
)
self.missedBackupsCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.schedule_make_up_missed else QtCore.Qt.CheckState.Unchecked
)
self.meteredNetworksCheckBox.setChecked(False if profile.dont_run_on_metered_networks else True)
self.preBackupCmdLineEdit.setText(profile.pre_backup_cmd)
self.postBackupCmdLineEdit.setText(profile.post_backup_cmd)
if profile.repo:
self.createCmdLineEdit.setText(profile.repo.create_backup_cmd)
self.createCmdLineEdit.setEnabled(True)
else:
self.createCmdLineEdit.setEnabled(False)
self.populate_wifi()
self.populate_logs()
self.draw_next_scheduled_backup()
def draw_next_scheduled_backup(self):
status = self.app.scheduler.next_job_for_profile(self.profile().id)
if status.type in (
ScheduleStatusType.SCHEDULED,
ScheduleStatusType.TOO_FAR_AHEAD,
):
time = QDateTime.fromMSecsSinceEpoch(int(status.time.timestamp() * 1000))
text = get_locale().toString(time, QLocale.FormatType.LongFormat)
elif status.type == ScheduleStatusType.NO_PREVIOUS_BACKUP:
text = self.tr('Run a manual backup first')
else:
text = self.tr('None scheduled')
self.nextBackupDateTimeLabel.setText(text)
self.nextBackupDateTimeLabel.repaint()
def populate_wifi(self):
self.wifiListWidget.clear()
for wifi in get_sorted_wifis(self.profile()):
item = QListWidgetItem()
item.setText(wifi.ssid)
item.setFlags(item.flags() | QtCore.Qt.ItemFlag.ItemIsUserCheckable)
if wifi.allowed:
item.setCheckState(QtCore.Qt.CheckState.Checked)
else:
item.setCheckState(QtCore.Qt.CheckState.Unchecked)
self.wifiListWidget.addItem(item)
self.wifiListWidget.itemChanged.connect(self.save_wifi_item)
def save_wifi_item(self, item):
db_item = WifiSettingModel.get(ssid=item.text(), profile=self.profile().id)
db_item.allowed = item.checkState() == Qt.CheckState.Checked
db_item.save()
def save_profile_attr(self, attr, new_value):
profile = self.profile()
setattr(profile, attr, new_value)
profile.save()
def save_repo_attr(self, attr, new_value):
repo = self.profile().repo
setattr(repo, attr, new_value)
repo.save()
def populate_logs(self):
event_logs = [s for s in EventLogModel.select().order_by(EventLogModel.start_time.desc())]
sorting = self.logTableWidget.isSortingEnabled()
self.logTableWidget.setSortingEnabled(False) # disable sorting while modifying the table.
self.logTableWidget.setRowCount(len(event_logs)) # go ahead and set table length and then update the rows
for row, log_line in enumerate(event_logs):
formatted_time = log_line.start_time.strftime('%Y-%m-%d %H:%M')
self.logTableWidget.setItem(row, LogTableColumn.Time, QTableWidgetItem(formatted_time))
self.logTableWidget.setItem(row, LogTableColumn.Category, QTableWidgetItem(log_line.category))
self.logTableWidget.setItem(row, LogTableColumn.Subcommand, QTableWidgetItem(log_line.subcommand))
self.logTableWidget.setItem(row, LogTableColumn.Repository, QTableWidgetItem(log_line.repo_url))
self.logTableWidget.setItem(
row,
LogTableColumn.ReturnCode,
QTableWidgetItem(str(log_line.returncode)),
)
self.logTableWidget.setSortingEnabled(sorting) # restore sorting now that modifications are done

View file

@ -0,0 +1,45 @@
from PyQt6 import uic
from PyQt6.QtWidgets import QLineEdit, QWidget
from vorta.store.models import BackupProfileMixin
from vorta.utils import get_asset
class ShellCommandsPage(QWidget, BackupProfileMixin):
def __init__(self, parent=None):
super().__init__(parent)
uifile = get_asset('UI/shellcommandspage.ui')
uic.loadUi(uifile, self)
self.preBackupCmdLineEdit: QLineEdit = self.findChild(QLineEdit, 'preBackupCmdLineEdit')
self.postBackupCmdLineEdit: QLineEdit = self.findChild(QLineEdit, 'postBackupCmdLineEdit')
self.createCmdLineEdit: QLineEdit = self.findChild(QLineEdit, 'createCmdLineEdit')
profile = self.profile()
if profile.repo:
self.createCmdLineEdit.setText(profile.repo.create_backup_cmd)
self.createCmdLineEdit.setEnabled(True)
else:
self.createCmdLineEdit.setEnabled(False)
self.setup_connections()
def setup_connections(self):
self.preBackupCmdLineEdit.textEdited.connect(
lambda new_val, attr='pre_backup_cmd': self.save_profile_attr(attr, new_val)
)
self.postBackupCmdLineEdit.textEdited.connect(
lambda new_val, attr='post_backup_cmd': self.save_profile_attr(attr, new_val)
)
self.createCmdLineEdit.textEdited.connect(
lambda new_val, attr='create_backup_cmd': self.save_repo_attr(attr, new_val)
)
def save_profile_attr(self, attr, new_value):
profile = self.profile()
setattr(profile, attr, new_value)
profile.save()
def save_repo_attr(self, attr, new_value):
repo = self.profile().repo
setattr(repo, attr, new_value)
repo.save()

View file

@ -19,4 +19,4 @@ def test_create(qapp, qtbot, archive_env):
assert ArchiveModel.select().count() == 7
assert main.createStartBtn.isEnabled()
assert main.archiveTab.archiveTable.rowCount() == 7
assert main.scheduleTab.logTableWidget.rowCount() == 2
assert main.scheduleTab.logPage.logPage.rowCount() == 2

View file

@ -251,7 +251,7 @@ def test_create(qapp, borg_json_output, mocker, qtbot):
assert RepoModel.get(id=1).unique_size == 15520474
assert main.createStartBtn.isEnabled()
assert main.archiveTab.archiveTable.rowCount() == 3
assert main.scheduleTab.logTableWidget.rowCount() == 1
assert main.scheduleTab.logPage.logPage.rowCount() == 1
@pytest.mark.parametrize(

View file

@ -21,7 +21,7 @@ def clockmock(monkeypatch):
def test_schedule_tab(qapp: VortaApp, qtbot, clockmock):
main = qapp.main_window
tab = main.scheduleTab
tab = main.scheduleTab.schedulePage
# setup
time_now = dt(2020, 5, 6, 4, 30)