Parte da série "Como eu me organizo":
No artigo anterior dessa série, eu falei sobre todas as minhas customizações no Taskwarrior e no VIT, e meu fluxo de trabalho com eles, que me permite organizar e concluir minhas tarefas. No entanto, apenas tarefas não são o suficiente para se organizar.
Outro componente crucial na organização é ter um calendário. Ele te permite estar ciente de tarefas e eventos que são sensíveis ao tempo (por exemplo, com data de entrega), e também tomar decisões bem informadas no momento de marcar novos eventos. Claro que nada disso é novo, e é até parte do GTD.
Mas algo que não é parte do GTD, e que eu senti falta, é algo para estabelecer uma rotina. O GTD é um ótimo sistema para acompanhar as diferentes tarefas de cada projeto na sua vida, e para concluí-los, mas ele não tem a menor preocupação em reservar diferentes porções do seu dia ou semana para fazer os básicos (como comer), fazer algo regularmente (como praticar piano), ou avançar nas tarefas gerais. Ele apenas organiza o que fazer e em quais contextos, mas não exatamente quando, o que pode deixar a desejar, quando se está tentando aproveitar bem o tempo.
Portanto, além de ter o VIT como meu organizador central de tarefas, meu sistema também precisa
Vamos ver sobre cada um deles.
Antes de mais nada, o próprio Taskwarrior na verdade tem um calendário, que pode
ser visto com task calendar
, mas sinceramente ele é inútil. Só é possível
ver quais dias tem tarefas, mas não quais são essas tarefas.
Eu queria um calendário que me desse uma boa visão geral dos compromissos mensais e semanais, que fosse leve (de preferência de terminal), e customizável o suficiente para ser integrado ao VIT. Eu acabei usando o calcurse.
Agora, a grande questão era: Como eu posso ter minhas tarefas aparecendo no calcurse? Bom, já que o calcurse usa um arquivo de texto com uma sintaxe simples para armazenar todos os compromissos, eu só precisava de um script que lesse todas as tarefas do Taskwarrior e escrevesse na saída os compromissos usando a sintaxe do calcurse. E esse foi o script que eu escrevi:
#!/bin/python from tasklib import TaskWarrior import sys tw = TaskWarrior('/home/nfraprado/.task/data') # Parse appointments apts = tw.tasks.filter('(status:pending or status:waiting or status:completed)', type='cal') for apt in apts: start = apt['scheduled'] if start is None: sys.stderr.write(f"Apt '{apt}' has no sched date!\n") continue summary = str(apt) if start.hour == 0 and start.minute == 0: start_fmt = start.strftime("%m/%d/%Y") print(f"{start_fmt} [1] {summary}") else: start_fmt = start.strftime("%m/%d/%Y @ %H:%M") if apt['due']: end_fmt = apt['due'].strftime("%m/%d/%Y @ %H:%M") else: end_fmt = start_fmt print(f"{start_fmt} -> {end_fmt}|{summary}") # Parse due dates for next actions and projects tasks = tw.tasks.filter('(status:pending or status:waiting) and (type:next or ' 'type:objective or type:standby)') for task in tasks: for date_type, label in [('due', "Prazo final: "), ('scheduled', "Prazo inicial: ")]: if not task[date_type]: # Skip tasks with no date continue start = task[date_type] proj = "Projeto: " if task['type'] == "objective" else "" summary = label + proj + str(task) if start.hour == 0 and start.minute == 0: start_fmt = start.strftime("%m/%d/%Y") print(f"{start_fmt} [1] {summary}") else: start_fmt = start.strftime("%m/%d/%Y @ %H:%M") end_fmt = start_fmt print(f"{start_fmt} -> {end_fmt}|{summary}")
Esse script basicamente define quais as tarefas que vão ser mostradas no
calendário e em qual formato. Primeiro, tem as tarefas cal
, que, se você
lembrar do artigo passado, são meus compromissos, e são a principal coisa a ser
mostrada no calendário. Cada uma delas é convertida em uma entrada no calcurse,
com a data scheduled
usada como a hora de início do compromisso, e a data
due
como a data de término. O texto do compromisso é simplesmente o texto de
descrição da tarefa.
O outro caso são as tarefas normais, que aparecem não como um evento contínuo no
calendário, mas sim uma entrada para a data de início, para me mostrar quando eu
posso começar a fazer a tarefa, e outra para a data de término, para me mostrar
até quando eu preciso concluí-la. Além disso, essas sim possuem identificadores
a mais no texto de descrição do compromisso, para diferenciá-los dos
compromissos normais. Para isso, o script adiciona "Prazo inicial: " à descrição
da data de início e "Prazo final: " à descrição da data de término. Por fim, se
a tarefa é do tipo objective
, ela além disso tem "Projeto: " adicionado à
sua descrição no calendário, significando que essa data é relevante para o
projeto como um todo e não a uma única próxima ação.
Inicialmente, era isso. Eu mapeei uma tecla no VIT para rodar esse programa e
então recarregava os compromissos do calcurse com R
. Dessa forma, eu
precisava pressionar duas teclas em duas janelas diferentes para ver o
calendário atualizado.
Depois de um tempo, eu descobri que o calcurse também suporta hooks (assim
como o Taskwarrior, como mostrado no artigo anterior) e adicionei um hook
pre-load
com o seguinte:
taskwarrior-task2cal > /home/nfraprado/.calcurse/apts
O que significa que quando eu pressiono R
no calcurse, ele automaticamente
roda meu script para exportar as tarefas para o arquivo do calcurse, e portanto
eu agora preciso apertar uma única tecla no calcurse para ver meu calendário
atualizado! 🙂
O gif a seguir mostra tarefas next
e cal
sendo adicionadas no
Taskwarrior e automaticamente aparecendo no calcurse:
Outra pequena coisa que eu tenho é a configuração notification.command
do
calcurse com o seguinte:
calcurse --next | sed -n -e '2s/.*\] \(.*\)/\1/p' | xargs -I '{}' notify-send ' Upcomming appointment' '{}'
Isso faz com que ele mostre uma notificação no meu sistema algum tempo (configurável, eu uso 10 minutos) antes de cada compromisso, com sua descrição.
O primeiro passo para manter uma rotina é, claro, criá-la.
Eu queria um jeito simples e fácil de definir e depois editar minha rotina, então eu a implementei usando dicionários em python, onde a rotina de cada dia da semana é dada por um dicionário separado.
A ideia é que a chave define a hora de início de uma ação, e o valor correspondente define a ação em si. A ação é considerada a atual a partir dessa hora até a hora da próxima ação. Por exemplo, eu tenho o seguinte dicionário base para as rotinas:
base = { '08': "Banho+café", '12': "Almoçar", '15': "Piano", '19': "Jantar", '24': "Dormir", }
Se ele fosse usado como uma rotina, significaria que a rotina começa com "Banho+café" das 8 da manhã até meio-dia, quando mudaria para "Almoçar", e assim por diante.
Como todo dicionário em python, eu posso estendê-lo para implementar a rotina de um dia:
segunda = dict(base) segunda.update({ '09': "Tarefas", })
Agora, considerando segunda
como a rotina, "Banho+café" só vai até às 9,
quando muda para "Tarefas", que por sua vez vai até meio-dia, quando "Almoçar"
começa, igual antes, etc.
Um valor também pode ser deletado, como sempre, usando del segunda['09']
,
por exemplo.
Para definir a minha rotina semanal nesse sistema, eu preciso apenas criar um
dicionário para cada dia da semana usando nomes específicos para as variáveis
(segunda
, terça
, quarta
, quinta
, sexta
, sábado
e
domingo
).
Eu gosto desse sistema porque eu posso adicionar ações simplesmente adicionando seu nome e hora de início, e também porque eu posso adicionar ações comuns em um dicionário base que é estendido pelo dicionário de cada dia, reduzindo redundância.
Em seguida, eu tenho um módulo python que sabe como ler cada um dos dicionários para retornar as informações de interesse:
import datetime import cur_sched scheds = [cur_sched.segunda, cur_sched.terça, cur_sched.quarta, cur_sched.quinta, cur_sched.sexta, cur_sched.sábado, cur_sched.domingo] gran = 30 max_minute = 60 - gran def get_cur_wday_time(): weekday = datetime.datetime.today().weekday() hour = datetime.datetime.today().hour minute = (datetime.datetime.today().minute // gran) * gran if hour == 0: hour = 24 return weekday, hour, minute def format_time(hour, minute): if minute > 0: time = f"{hour:02}h{minute:02}" else: time = f"{hour:02}" return time def get_current(): weekday, hour, minute = get_cur_wday_time() return get_sched(weekday, hour, minute) def get_sched(weekday, hour, minute): for m in range(minute, -1, -gran): try: return scheds[weekday][format_time(hour, m)] except: pass for h in range(hour - 1, 1, -1): for m in range(max_minute, -1, -gran): try: return scheds[weekday][format_time(h, m)] except: pass return '' def get_new(): weekday, hour, minute = get_cur_wday_time() try: return scheds[weekday][format_time(hour, minute)] except: return ''
get_current()
retorna a ação atual da rotina com base na hora e dia atuais.
get_new()
faz o mesmo, mas apenas se a ação acabou de começar. Por exemplo,
se "Piano" vai das 3 até às 4 da tarde, e considerando uma granularidade de 30
minutos (que eu estou usando atualmente), ela vai retornar "Piano" apenas entre
3 e 3:30 da tarde.
Para que eu sempre possa facilmente ver qual é a ação atual com base na minha rotina, eu tenho um bloco do i3blocks específico para isso na barra do meu sistema:
Ele simplesmente chama o get_current()
do módulo python anterior.
Mas apenas saber a ação atual não é o suficiente, eu preciso ser notificado
quando a ação atual da rotina mudar. É por isso que eu também tenho um job do
cron que roda a cada 30 minutos e executa a get_new()
para checar se a ação
atual da rotina mudou e se sim, me mostra uma notificação:
Por fim, também é útil ver a rotina completa da semana de vez em quando, então eu também tenho um script que mostra ela no terminal, usando uma cor diferente para cada ação na rotina, e com as cores definidas aleatoriamente (então elas mudam a cada execução do programa):
#!/bin/python import schedule import colored weekday_name = ["2a", "3a", "4a", "5a", "6a", "Sáb", "Dom"] color = True print(" ", end='') for weekday in range(0, 7): print(f"{weekday_name[weekday]:15}", end='') print() def get_color(text): hex_num = hex(hash(text) % (16 ** 6)) hex_num6 = hex_num[:2] + hex_num[2:].rjust(6, '0') return hex_num6.replace('0x', '#') for hour in range(8, 25): for minute in range(0, schedule.max_minute + 1, schedule.gran): if hour == 24 and minute != 0: break print(f"{schedule.format_time(hour, minute):5}" + " ", end='') for weekday in range(0, 7): text = schedule.get_sched(weekday, hour, minute) if color: print(colored.stylize(f"{text:15}", colored.fg(get_color(text))), end='') else: print(f"{text:15}", end='') print()
Como eu já uso i3blocks como a barra do meu sistema, eu adicionei também um bloco com um resumo da situação das minhas de tarefas para me ajudar a ficar atento a elas e revisá-las regularmente (e não apenas durante a revisão semanal):
O texto no começo mostra o contexto atual, nesse caso, sp
. Os três números
seguintes são o número de tarefas in
pendentes (em amarelo), o número de
projetos "empacados" (em magenta) e o número de tarefas com prazo final para
essa semana (em vermelho).
E isso é tudo sobre meu sistema de organização. É basicamente o VIT sobre o Taskwarrior para organizar minhas tarefas, o calcurse para mostrar meu calendário, e blocos na barra do sistema e notificações para me ajudar a acompanhar e para avisar sobre as tarefas, rotina e compromissos.
Esse sistema funciona bem, mas ainda há melhorias possíveis. Principalmente
integração com o meu celular. Como eu mencionei anteriormente, isso não é um
problema no momento já que estou sempre em casa, mas assim que a pandemia
acabar, eu preciso de um bom jeito de ter minhas tarefas no celular, e
sincronizadas com o meu computador. Eu preciso no mínimo poder facilmente
adicionar tarefas in
, e ver meus relatórios, opcionalmente com algum
filtro. Também vou precisar de um calendário com a mesma integração com o
Taskwarrior que eu tenho no computador. Quem sabe com todo o movimento de
"Convergência" acontecendo na Purism, eu acabe comprando um Librem 5 e tendo
um sistema bem parecido nos dois dispositivos 😃.