"Fossies" - the Fresh Open Source Software Archive

Member "email-reminder-0.8.1/email-reminder-editor" (19 Sep 2020, 14275 Bytes) of package /linux/privat/email-reminder-0.8.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "email-reminder-editor": 0.8.0_vs_0.8.1.

    1 #!/usr/bin/python3
    2 """
    3 Email-Reminder Editor - edit special occasion reminders
    4 
    5 Copyright (C) 2020 by Francois Marier
    6 
    7 This program is free software: you can redistribute it and/or modify
    8 it under the terms of the GNU Affero General Public License as published by
    9 the Free Software Foundation, either version 3 of the License, or
   10 (at your option) any later version.
   11 
   12 This program is distributed in the hope that it will be useful,
   13 but WITHOUT ANY WARRANTY; without even the implied warranty of
   14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15 GNU Affero General Public License for more details.
   16 
   17 You should have received a copy of the GNU Affero General Public License
   18 along with this program.  If not, see <https://www.gnu.org/licenses/>.
   19 """
   20 
   21 import argparse
   22 import pwd
   23 import os
   24 from pathlib import Path
   25 import subprocess
   26 import sys
   27 
   28 from PySide2.QtCore import QModelIndex, Qt
   29 from PySide2.QtGui import QIcon, QKeySequence
   30 from PySide2.QtWidgets import (
   31     QAbstractItemView, QApplication, QCheckBox, QDialog, QHBoxLayout, QLabel,
   32     QLineEdit, QMainWindow, QMessageBox, QMenuBar, QPushButton, QSpinBox,
   33     QTableView, QTabWidget, QToolBar, QVBoxLayout)
   34 
   35 from EmailReminder.EventList import EventList
   36 
   37 APP = QApplication(sys.argv)
   38 
   39 CONFIG_FILE = '.email-reminders'
   40 SEND_REMINDERS = '/usr/bin/send-reminders'
   41 VERSION = '0.8.1'
   42 
   43 
   44 class EventTab:
   45     def __init__(self, model):
   46         self.model = model
   47         self.table = QTableView()
   48         self.table.setModel(model)
   49         self.table.setShowGrid(False)
   50         self.table.verticalHeader().setVisible(False)
   51         self.table.horizontalHeader().setStretchLastSection(True)
   52         self.table.setCornerButtonEnabled(False)
   53         self.table.setSelectionBehavior(QTableView.SelectRows)
   54         self.table.setSelectionMode(QAbstractItemView.SingleSelection)
   55         self.table.setSortingEnabled(False)  # TODO: enable this (bug 724121)
   56 
   57     def getTable(self):
   58         return self.table
   59 
   60     def addEvent(self):
   61         if self.model.append():
   62             last = self.model.rowCount() - 1
   63             index = self.model.index(last, 0)
   64             self.table.setCurrentIndex(index)
   65             self.table.scrollToBottom()
   66 
   67     def deleteSelectedEvent(self):
   68         indexes = self.table.selectionModel().selectedIndexes()
   69         if indexes and indexes[0].isValid():
   70             if self.model.delete(indexes[0].row()):
   71                 self.table.setCurrentIndex(QModelIndex())
   72 
   73     def getSelectedEventReminders(self):
   74         indexes = self.table.selectionModel().selectedIndexes()
   75         if indexes and indexes[0].isValid():
   76             return self.model.getReminders(indexes[0].row())
   77         return None
   78 
   79     def setSelectedEventReminders(self, values):
   80         indexes = self.table.selectionModel().selectedIndexes()
   81         if indexes and indexes[0].isValid():
   82             self.model.setReminders(indexes[0].row(), values)
   83 
   84 
   85 class TabWidget(QTabWidget):
   86     def __init__(self, *args, **kwargs):
   87         super(TabWidget, self).__init__(*args, **kwargs)
   88 
   89         self.eventTabs = []
   90 
   91     def append(self, model):
   92         event = EventTab(model)
   93         self.eventTabs.append(event)
   94         self.addTab(event.getTable(), model.LABEL)
   95 
   96     def addEvent(self):
   97         self.eventTabs[self.currentIndex()].addEvent()
   98 
   99     def deleteEvent(self):
  100         self.eventTabs[self.currentIndex()].deleteSelectedEvent()
  101 
  102     def getEventReminders(self):
  103         return self.eventTabs[self.currentIndex()].getSelectedEventReminders()
  104 
  105     def setEventReminders(self, values):
  106         self.eventTabs[self.currentIndex()].setSelectedEventReminders(values)
  107 
  108 
  109 class EditRemindersDialog(QDialog):
  110 
  111     def __init__(self, tabs, *args, **kwargs):
  112         super(EditRemindersDialog, self).__init__(*args, **kwargs)
  113 
  114         self.tabs = tabs
  115 
  116         self.setWindowTitle("Edit reminders")
  117 
  118         self.event_name_label = QLabel("Current reminders for '':")
  119 
  120         self.same_day_checkbox = QCheckBox("&Same day")
  121         self.same_day_checkbox.setChecked(True)
  122 
  123         days_in_advance_layout = QHBoxLayout()
  124         self.days_in_advance_checkbox = QCheckBox("&Days in advance:")
  125         self.days_in_advance_spinbox = QSpinBox()
  126         self.days_in_advance_spinbox.setRange(1, 364)
  127         self.days_in_advance_spinbox.setSingleStep(1)
  128         self.days_in_advance_spinbox.setValue(1)
  129         self.days_in_advance_spinbox.setDisabled(True)
  130         self.days_in_advance_checkbox.stateChanged.connect(self.toggleSpinBox)
  131         days_in_advance_layout.addWidget(self.days_in_advance_checkbox)
  132         days_in_advance_layout.addWidget(self.days_in_advance_spinbox)
  133 
  134         close_layout = QHBoxLayout()
  135         close_button = QPushButton("Close")
  136         close_button.clicked.connect(self.accept)
  137         close_button.setDefault(True)
  138         close_layout.addStretch()
  139         close_layout.addWidget(close_button)
  140 
  141         layout = QVBoxLayout()
  142         layout.addWidget(self.event_name_label)
  143         layout.addWidget(self.same_day_checkbox)
  144         layout.addLayout(days_in_advance_layout)
  145         layout.addLayout(close_layout)
  146         self.setLayout(layout)
  147 
  148         self.accepted.connect(self.save)
  149 
  150     def toggleSpinBox(self):
  151         self.days_in_advance_spinbox.setDisabled(
  152             not self.days_in_advance_checkbox.isChecked())
  153 
  154     def load(self):
  155         reminders = self.tabs.getEventReminders()
  156         if reminders:
  157             self.event_name_label.setText(
  158                 "Current reminders for '%s':" % reminders['name'])
  159             self.same_day_checkbox.setChecked(reminders['sameday'])
  160             self.days_in_advance_checkbox.setChecked(
  161                 reminders['daysinadvance'] > 0)
  162             self.days_in_advance_spinbox.setValue(reminders['daysinadvance'])
  163         return reminders is not None
  164 
  165     def save(self):
  166         values = {'sameday': self.same_day_checkbox.isChecked(),
  167                   'daysinadvance': 0}
  168         if self.days_in_advance_checkbox.isChecked():
  169             values['daysinadvance'] = self.days_in_advance_spinbox.value()
  170         self.tabs.setEventReminders(values)
  171 
  172 
  173 class PreferencesDialog(QDialog):
  174 
  175     def __init__(self, events, *args, **kwargs):
  176         super(PreferencesDialog, self).__init__(*args, **kwargs)
  177 
  178         self.events = events
  179 
  180         self.setWindowTitle("Preferences")
  181 
  182         name_layout = QHBoxLayout()
  183         self.first_name_edit = QLineEdit()
  184         self.last_name_edit = QLineEdit()
  185         name_layout.addWidget(QLabel("Name:"))
  186         name_layout.addWidget(self.first_name_edit)
  187         name_layout.addWidget(self.last_name_edit)
  188 
  189         email_layout = QHBoxLayout()
  190         self.email_edit = QLineEdit()
  191         email_layout.addWidget(QLabel("Email:"))
  192         email_layout.addWidget(self.email_edit)
  193 
  194         close_layout = QHBoxLayout()
  195         close_button = QPushButton("Close")
  196         close_button.clicked.connect(self.accept)
  197         close_button.setDefault(True)
  198         close_layout.addStretch()
  199         close_layout.addWidget(close_button)
  200 
  201         layout = QVBoxLayout()
  202         layout.addWidget(
  203             QLabel("Set the default recipient for the reminder emails:"))
  204         layout.addLayout(name_layout)
  205         layout.addLayout(email_layout)
  206         layout.addLayout(close_layout)
  207         self.setLayout(layout)
  208 
  209         self.accepted.connect(self.save)
  210 
  211     def load(self):
  212         user = self.events.getUser()
  213         if not user['first_name'] and not user['last_name']:
  214             # Default to the full name of the OS account.
  215             unixName = pwd.getpwuid(os.getuid())[4].split(',')[0]
  216             user['first_name'] = unixName.split(' ')[0]
  217             user['last_name'] = unixName.split(' ')[1]
  218             self.events.setUser({
  219                 'first_name': user['first_name'],
  220                 'last_name': user['last_name'],
  221             })
  222 
  223         if not user['email']:
  224             user['email'] = os.environ['EMAIL']
  225             self.events.setUser({
  226                 'email': user['email'],
  227             })
  228 
  229         self.first_name_edit.setText(user['first_name'])
  230         self.last_name_edit.setText(user['last_name'])
  231         self.email_edit.setText(user['email'])
  232 
  233     def save(self):
  234         self.events.setUser({
  235             'first_name': self.first_name_edit.text(),
  236             'last_name': self.last_name_edit.text(),
  237             'email': self.email_edit.text(),
  238         })
  239 
  240 
  241 class MainWindow(QMainWindow):
  242 
  243     def __init__(self, events, *args, **kwargs):
  244         super(MainWindow, self).__init__(*args, **kwargs)
  245 
  246         self.events = events
  247 
  248         self.setWindowTitle("Email-Reminder Editor")
  249 
  250         self.tabs = TabWidget()
  251         for eventType in EventList.SUPPORTED_TYPES:
  252             self.tabs.append(events.model(eventType))
  253         self.tabs.setDocumentMode(True)
  254         self.setCentralWidget(self.tabs)
  255 
  256         self.addToolBar(MainToolbar(self, self.tabs))
  257         self.setMenuBar(MainMenu(self, self.events, self.tabs))
  258 
  259         self.preferences_dialog = PreferencesDialog(self.events)
  260         self.edit_reminders_dialog = EditRemindersDialog(self.tabs)
  261 
  262     def show_preferences(self):
  263         self.preferences_dialog.load()
  264         self.preferences_dialog.exec_()
  265 
  266     def show_edit_reminders(self):
  267         if self.edit_reminders_dialog.load():
  268             self.edit_reminders_dialog.exec_()
  269 
  270     def closeEvent(self, event):
  271         user = self.events.getUser()
  272         if user['email']:
  273             event.accept()
  274             return
  275 
  276         # Warn the user about a missing email address.
  277         confirmation_box = QMessageBox()
  278         confirmation_box.setText("You will not receive any reminders since "
  279                                  "you have not set your email address.")
  280         confirmation_box.setInformativeText("Would you like to set your email "
  281                                             "address in the preferences now "
  282                                             "or quit?")
  283         confirmation_box.setIcon(QMessageBox.Question)
  284         confirmation_box.addButton("Open Preferences", QMessageBox.YesRole)
  285         confirmation_box.addButton("Quit", QMessageBox.NoRole)
  286 
  287         if confirmation_box.exec():
  288             event.accept()
  289         else:
  290             event.ignore()
  291             self.show_preferences()
  292 
  293 
  294 class MainToolbar(QToolBar):
  295 
  296     def __init__(self, window, tabs, *args, **kwargs):
  297         super(MainToolbar, self).__init__(*args, **kwargs)
  298 
  299         self.setMovable(False)
  300         self.setFloatable(False)
  301         self.setContextMenuPolicy(Qt.PreventContextMenu)
  302 
  303         # Maintain the original look-and-feel.
  304         self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
  305 
  306         new_event_action = self.addAction(QIcon.fromTheme('document-new'),
  307                                           'New')
  308         new_event_action.triggered.connect(tabs.addEvent)
  309         delete_event_action = self.addAction(QIcon.fromTheme('edit-delete'),
  310                                              'Delete')
  311         delete_event_action.triggered.connect(tabs.deleteEvent)
  312         self.addSeparator()
  313         edit_event_action = self.addAction('Edit reminders')
  314         edit_event_action.triggered.connect(window.show_edit_reminders)
  315 
  316 
  317 class MainMenu(QMenuBar):
  318 
  319     def __init__(self, window, events, tabs, *args, **kwargs):
  320         super(MainMenu, self).__init__(*args, **kwargs)
  321 
  322         self.events = events
  323 
  324         self.setNativeMenuBar(True)
  325 
  326         file_menu = self.addMenu('File')
  327         test_config_action = file_menu.addAction("Test configuration")
  328         test_config_action.triggered.connect(self.runTest)
  329 
  330         file_menu.addSeparator()
  331 
  332         quit_action = file_menu.addAction("Quit")
  333         quit_action.setShortcut(QKeySequence.Quit)
  334         quit_action.triggered.connect(window.close)
  335 
  336         edit_menu = self.addMenu('Edit')
  337         new_event_action = edit_menu.addAction("New event")
  338         new_event_action.setShortcut(QKeySequence.New)
  339         new_event_action.triggered.connect(tabs.addEvent)
  340         delete_event_action = edit_menu.addAction("Delete event")
  341         delete_event_action.setShortcut(QKeySequence.Delete)
  342         delete_event_action.triggered.connect(tabs.deleteEvent)
  343         edit_event_action = edit_menu.addAction("Edit reminders...")
  344         edit_event_action.setShortcut(QKeySequence("Ctrl+e"))
  345         edit_event_action.triggered.connect(window.show_edit_reminders)
  346         edit_menu.addSeparator()
  347         preferences_event_action = edit_menu.addAction("Preferences...")
  348         preferences_event_action.setShortcut(QKeySequence.Preferences)
  349         preferences_event_action.triggered.connect(window.show_preferences)
  350 
  351     def runTest(self):
  352         warning_box = QMessageBox()
  353         warning_box.setIcon(QMessageBox.Warning)
  354         if not os.access(SEND_REMINDERS, os.X_OK):
  355             warning_box.setText("Cannot run '%s'." % SEND_REMINDERS)
  356             warning_box.setInformativeText("Check your installation.")
  357             warning_box.exec()
  358             return
  359 
  360         self.events.save()
  361 
  362         result = subprocess.run([SEND_REMINDERS], stdout=subprocess.PIPE,
  363                                 stderr=subprocess.STDOUT, check=False)
  364         if result.stdout:
  365             warning_box.setText("Error while running '%s':" % SEND_REMINDERS)
  366             warning_box.setInformativeText(result.stdout.decode('utf-8'))
  367             warning_box.exec()
  368 
  369 
  370 def main():
  371     parser = argparse.ArgumentParser(description="Simple editor for modifying "
  372                                      "special occasion email reminders")
  373     parser.add_argument('-s', '--simulate', dest='simulate',
  374                         action='store_true', help="don't save any changes")
  375     parser.add_argument('-v', '--verbose', dest='verbose',
  376                         action='store_true', help="print more information")
  377     parser.add_argument('-V', '--version', action='version',
  378                         version='Email-Reminder Editor %s' % VERSION)
  379     args = parser.parse_args()
  380 
  381     events = EventList(args.simulate, args.verbose)
  382     if not events.load(os.path.join(Path.home(), CONFIG_FILE)):
  383         return 1
  384 
  385     QIcon.setThemeName('gnome')  # TODO: remove (icon+name toolbar buttons)
  386     window = MainWindow(events)
  387     window.show()
  388     status = APP.exec_()
  389     events.save()
  390     return status
  391 
  392 
  393 sys.exit(main())