Pull to refresh

Простая напоминалка на Linux

Reading time 5 min
Views 50K


Для людей, которые работают в офисе с персональным компьютером — бухгалтеров, инженеров, секретарей, менеджеров, специалистов, экспертов, начальников структурных подразделений и тем более директоров — сегодня обычным делом является сопровождать решение нескольких вопросов одновременно.

К примеру, звонит клиент и просит внести изменения в договор. Ты кладешь трубку — и тут звонит твой коллега и просит тебя направить ему давно забытый материал, который нужно еще постараться отыскать. Не успеваешь ты договорить с коллегой, как звонит на сотовый директор и просит составить ему небольшой отчет. А ведь до этого ты занимался своим вопросом! Нужно всё запомнить, ничего не упустить! Типичная ситуация, не правда ли?

Для того, чтобы все успевать в таких ситуациях, поможет простая напоминалка. Но что такое простая напоминалка? Каковы критерии ее простоты?
Для меня «простой напоминалкой» является та, которая действует по следующему принципу:

  • Открываешь диалоговое окно напоминалки горячей клавишей (ну, или сочетанием клавиш, например Ctrl+Shift+X)
  • Вводишь время и текст напоминания простыми понятными словами (например, «через 15 минут скинуть Алексею материал», «в 11 отчет директору», «в 13-15 обед», «завтра в 15:10 проследить за письмом», «в среду в 10 в налоговую»)
  • Нажимаешь Enter.
  • В заданное время выскакивает напоминалка, которую можно закрыть или отложить.


Лучшим, как мне кажется, решением такой задачи является программа XMinder. Наверное, если бы я писал техническое задание на разработку простой напоминалки, она бы выглядела именно как XMinder.
Этой программой я пользовался долгое время, пока однажды не решил установить на рабочем компьютере операционную систему Linux (к сожалению, программа XMinder написана только под Windows).

Потеря такой замечательной напоминалки была существенной, необходимо было находить выход и… я решил написать программу сам.

Ранее я имел небольшой опыт написания программ в html, php, actionscript (flash). Однако для такой задачи решил выбрать связку Python+Bash+Zenity+At.
Почему Python? — Потому что по нему нашлась хорошая документация, потому что по умолчанию он установлен в моем дистрибутиве Linux Mint 17. Уже после первых шагов осваивания нового языка я понимал, что решение задачи мне будет посильно.
Почему Bash? — Это отдельная история и связана она с функцией «Отложить» в моей напоминалке.
Почему Zenity? — Потому что просто, лаконично и опять же — встроено по умолчанию в большинство дистрибутивов LInux.
Почему At? — Так ведь именно эта программа всю задачу и решает! И умеет хранить данные даже после перезагрузки компьютера!

Таким образом, я только собрал в единое целое пару программ и добавил нужный синтаксис.
В итоге у меня получилось 2 файлика:

remind.py:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# RemindMe v1.5 created by Dennis Smal' in 2014 godgrace@mail.ru

from __future__ import print_function

import subprocess
import re
import sys

def replace_all(t, d):
    """Общая функция для подмены переменных"""
    for i, j in d.iteritems():
        t = t.replace(i, j, 1)
    return t

def get_datex(text):
    """Извлекает из текста дату и подстроку с датой, которую нужно удалить"""
    whatdate = ''
    delwhatdate = ''
    datex = re.findall(r'\d{2}[.,-]\d{2}[.,-]\d{4}|\d{1}[.,-]\d{2}[.,-]\d{4}',text) # ищем дату в формате 19.08.2014 или 19-08-2014 или 19,08,2014
    if datex:
        date = datex[0].replace('-','.').replace(',','.') # преобразуем дату в формат 19.08.2014
        whatdate = date
        delwhatdate = datex[0]+' '
    return whatdate, delwhatdate

def get_day(text):
    """Извлекает из текста день недели и подстроку, которую нужно удалить"""
    when = ''
    delday = ''

    day = re.findall('завтра|Завтра|в понедельник|во вторник|в среду|в четверг|в пятницу|в субботу|в воскресенье',text)
    daywithoutin = re.findall('понедельник|вторник|среда|четверг|пятница|суббота|воскресенье',text)
    if day:
        ind = {'завтра':'tomorrow', 'Завтра':'tomorrow', 'в понедельник':'mon', 'во вторник':'tue', 'в среду':'wed', 'в четверг':'thu', 'в пятницу':'fri', 'в субботу':'sat', 'в воскресенье':'sun'}
        when = replace_all(day[0], ind)
        delday = day[0]+' '
    elif daywithoutin:
	ind = {'понедельник':'mon', 'вторник':'tue', 'среда':'wed', 'четверг':'thu', 'пятница':'fri', 'суббота':'sat', 'воскресенье':'sun'}
        when = replace_all(daywithoutin[0], ind)
        delday = daywithoutin[0]+' '
    return (when, delday)

def get_clock(text):
    """Извлекает из текста время и подстроку, которую нужно удалить"""
    how = ''
    delclock = ''
    clock = re.findall('минуты |часа |дня |минуту |часов |день |минут |час |дней ',text)
    if clock: # смотрим, есть ли указание на часы, минуты, дни
        clockbank = {'минут ':'min', 'час ':'hour', 'дней ':'days', 'минуту ':'min', 'часа ':'hours', 'дня ':'days', 'минуты ':'min', 'часов ':'hours', 'день ':'days'}
        how = replace_all(clock[0], clockbank)
        delclock = clock[0]
    return (how, delclock)

def add_task(out, x):
    """Добавляет напоминание в очередь at"""
    #для отладки, чтобы долго не ждать
    #x = 'at now'
    #print (x)
    cmd = 'echo "DISPLAY=:0 ~/remindme/task %s" | %s' % (out, x)
    subprocess.Popen(cmd, shell=True)
    
        
def main(when="Через 15 минут", reminder=""):
    warn_cmd = [
            'zenity',
            '--warning',
            '--text="Попробуйте ещё раз.."'
            ]
    cmd = [
            'zenity',
            '--entry',
            '--title=Напоминалка',
            '--text=Введите напоминание',
            '--entry-text={} {}'.format(when, reminder),
            '--width=400'
            ]

    loop = True
    while loop:
        get = subprocess.check_output(cmd) # получаем текст
        text = get+' ' # добавляем в конец пробел, чтобы отрабатывать уведомления типа "напомнить мне через 10 минут". Если бы пробела не было, параметр clock был бы пуст. В параметре clock после слова "час" тоже стоит пробел, чтобы различать поиск "час" и "часов".
        find = re.findall('ерез [0-9]+|В [0-9:-]+|в [0-9:-]+|ерез час',text)

        if get: # убеждаемся, заполнено ли поле ввода
            if find: # убеждаемся, указано ли время напоминания
                what = find[0].split()
                timex = what[1].replace('-',':').replace('час','1')

                if len(timex) > 2: # заменяет выражения типа "в 10" на "в 10:00"
                    time = timex
                else:
                    time = timex+':00'	
                
                whatdate, delwhatdate = get_datex(text)
                when, delday = get_day(text)
                how, delclock = get_clock(text)

                reps = {'ерез':'at now + %s %s' % (timex,how),'В':'at %s %s %s' % (time,when,whatdate),'в':'at %s %s %s' % (time,when,whatdate)}
                wors = {'Через %s %s' % (what[1],delclock):'','через %s %s' % (what[1],delclock):'','В %s ' % what[1]:'','в %s ' % what[1]:'', '%s' % delday:'', 'Через час':'', 'через час':'', '%s' % delwhatdate:'',} # какие слова мы будем удалять
                x = replace_all(what[0], reps) # это время, на которое запланировано появление напоминания
                out = replace_all(text, wors) # это текст напоминания

                add_task(out, x)
                loop = False

            else:
                error = subprocess.check_output(warn_cmd)
        else:
            loop = False

def usage():
    s = "Использование: {} [Время напоминания [Напоминание]]".format(__file__)
    print(s)

if __name__ == "__main__":
    if len(sys.argv) <= 3:
        main(*sys.argv[1:])
    else:
        usage()


и task:
#!/bin/bash
zenity --question --title=Напоминание --ok-label=Отложить --cancel-label=Ok --text="$*"
case $? in
0) ~/remindme/remind.py "Через 15 минут" "$*"
;;
1)
;;
esac


Файлы нужно положить в одну директорию (у меня это ~/remindme) и сделать исполняемыми (например командой «chmod +x»).
На файл remind.py необходимо назначать горячие клавиши (в разных дистрибутивах это делается по-разному), например, сочетание клавиш Ctrl+Shift+X.

Файлы для скачивания доступны по ссылке.
Проект выложен на GitHub.
Tags:
Hubs:
+27
Comments 63
Comments Comments 63

Articles