implemented TODO system and fixed handling empty journal state

master
Master 2019-10-15 22:24:58 -04:00
parent 24606580e3
commit f93e5b7f70
2 changed files with 146 additions and 34 deletions

View File

@ -1,5 +1,6 @@
[filesystem] [filesystem]
journal=/home/bagira/journal journal=/home/bagira/journal
task_tracker=/home/bagira/journal/task_tracker
[tools] [tools]
editor=/usr/bin/nano editor=/usr/bin/nano

View File

@ -7,6 +7,8 @@ from dialog import Dialog
from datetime import datetime from datetime import datetime
import subprocess import subprocess
import re import re
import glob
import hashlib
solution_description="Journaling system for daily needs." solution_description="Journaling system for daily needs."
epilog="Designed and written by Chris Punches.\n SILO GROUP LLC, 2019. ALL RIGHTS RESERVED." epilog="Designed and written by Chris Punches.\n SILO GROUP LLC, 2019. ALL RIGHTS RESERVED."
@ -22,6 +24,7 @@ class Config():
self.config.read( self.get_conf_location() ) self.config.read( self.get_conf_location() )
self.journal_path = self.config['filesystem']['journal'] self.journal_path = self.config['filesystem']['journal']
self.editor = self.config['tools']['editor'] self.editor = self.config['tools']['editor']
self.tracker = self.config['filesystem']['task_tracker']
def get_journal_path(self): def get_journal_path(self):
return self.journal_path return self.journal_path
@ -29,24 +32,60 @@ class Config():
def get_editor(self): def get_editor(self):
return self.editor return self.editor
def get_tracker(self):
return self.tracker
class Task():
def __init__(self, filename, text, position):
self.filename = filename
self.topic = Journaler.topic_from_filename( self.filename )
self.creation_date = Journaler.date_from_filename(self.filename)
self.position = position
self.text = text
self.hash = self._this_hash()
def _this_hash(self):
return hashlib.md5(
"{}:{}:{}".format(
self.filename,
self.position,
self.text
).encode('utf-8')
).hexdigest()[:8]
class Journaler(): class Journaler():
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
self.d = Dialog( dialog="dialog", autowidgetsize=True) self.d = Dialog( dialog="dialog", autowidgetsize=True)
self.d.set_background_title("SILO JOURNALING SYSTEM")
@staticmethod
def topic_from_filename( filename ):
try:
topic = re.search( "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_(.+?)$", filename ).group(1)
except AttributeError:
topic = "NO_TOPIC"
return topic
@staticmethod
def date_from_filename( filename ):
try:
date = re.search( "([0-9][0-9][0-9][-9]-[0-9][0-9]-[0-9][0-9])_.+?$", filename ).group(1)
except AttributeError:
date = "NO_TOPIC"
return date
# generate the filepath for a specific entry # generate the filepath for a specific entry
def _get_entry_filename(self, topic, date): def _get_entry_filename(self, topic, date):
# TODO add sanity checks # TODO add sanity checks
return "{0}/{1}_{2}".format( self.config.get_journal_path(), date, topic ) return "{0}/{1}_{2}".format( self.config.get_journal_path(), date, topic )
# open a specific entry for a supplied topic and date # open a specific entry for a supplied topic and date
def _open_entry(self, topic, date): def _open_entry(self, topic, date):
subprocess.run( [ self.config.get_editor(), self._get_entry_filename( topic, date ) ] ) subprocess.run( [ self.config.get_editor(), self._get_entry_filename( topic, date ) ] )
# gemerate the current date in a standard format # gemerate the current date in a standard format
def _get_current_date(self): def _get_current_date(self):
return datetime.today().strftime('%Y-%m-%d') return datetime.today().strftime('%Y-%m-%d')
@ -56,19 +95,17 @@ class Journaler():
entry_list = os.listdir( self.config.get_journal_path() ) entry_list = os.listdir( self.config.get_journal_path() )
return entry_list return entry_list
# get all topics for all dates # get all topics for all dates
def _get_all_topics(self): def _get_all_topics(self):
topics = list() topics = list()
for item in self._get_all_entries(): for item in self._get_all_entries():
try: try:
found = re.search("[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_(.+?)$", item ).group(1) found = re.search("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_(.+?)$", item ).group(1)
topics.append( found ) topics.append( found )
except AttributeError: except AttributeError:
pass pass
return sorted( list( dict.fromkeys( topics ) ) ) return sorted( list( dict.fromkeys( topics ) ) )
# get all topics for all dates # get all topics for all dates
def _get_all_dates(self): def _get_all_dates(self):
dates = list() dates = list()
@ -80,7 +117,6 @@ class Journaler():
pass pass
return sorted( list( dict.fromkeys( dates ) ) ) return sorted( list( dict.fromkeys( dates ) ) )
# get all topics associated with a date # get all topics associated with a date
def _get_topics(self, date): def _get_topics(self, date):
topics = list() topics = list()
@ -93,7 +129,6 @@ class Journaler():
pass pass
return sorted( list( dict.fromkeys( topics ) ) ) return sorted( list( dict.fromkeys( topics ) ) )
# get all dates associated with a topic # get all dates associated with a topic
def _get_dates(self, topic): def _get_dates(self, topic):
dates = list() dates = list()
@ -106,7 +141,6 @@ class Journaler():
pass pass
return sorted( list( dict.fromkeys( dates ) ) ) return sorted( list( dict.fromkeys( dates ) ) )
# browse topics for a specific date # browse topics for a specific date
def do_topic_browse_by_date(self, date): def do_topic_browse_by_date(self, date):
print( "topic browse by date: {}".format( date ) ) print( "topic browse by date: {}".format( date ) )
@ -139,7 +173,6 @@ class Journaler():
else: else:
self.do_browse_topics() self.do_browse_topics()
# browse dates for a specific topic # browse dates for a specific topic
def do_date_browse_by_topic(self, topic): def do_date_browse_by_topic(self, topic):
dates = self._get_dates(topic) dates = self._get_dates(topic)
@ -167,40 +200,119 @@ class Journaler():
else: else:
self.do_browse_topics() self.do_browse_topics()
# return a list of tasks
def _get_todos(self):
for file in sorted( glob.glob(f"{self.config.get_journal_path()}**/*", recursive=False) ):
with open( file ) as _file:
for position, line in enumerate(_file.readlines()):
m = re.search("^TODO(.+?)$", line.rstrip().lstrip() )
if m:
print("match found")
task = Task(
file,
m.group(1).rstrip().lstrip(),
position
)
if not self._check_task_completion( task ):
yield task
# check if task is completed
def _check_task_completion( self, task ):
# check if self.config.tracker file is there
# create it if not exists
# open self.config.tracker file
# iterate through lines
# find line that begins with hash
with open( self.config.tracker, 'w+' ) as tracker_file:
for line in tracker_file:
m = re.search( "^{0}\t.*$".format( task.hash ), line )
print(line)
if m:
return True
return False
def _mark_task_complete(self, task):
print( "marking task {} complete".format( task.hash ) )
with open( self.config.tracker, 'a+' ) as tracker_file:
tracker_file.write(
"{0}\t{1}:{2}\t{3}\t{4}\n".format(
task.hash,
task.creation_date,
self._get_current_date(),
task.topic,
task.text
)
)
def do_browse_todo_entries(self): def do_browse_todo_entries(self):
print( "not yet implemented" ) choices = list()
for todo in self._get_todos():
print(todo.text)
choices.append(
( todo.hash, "({0}) {1}".format(todo.topic, todo.text) , False )
)
code, hashes_marked_complete = self.d.checklist(
text="TODO Entries",
choices=choices,
backtitle="Select tasks to complete.",
width=self.d.maxsize()[1],
cancel_label="Exit",
ok_label="Complete"
)
if code == self.d.OK:
# it only returns the hashes
# we want to create a new list of tasks for tasks that should be completed
tasks_to_complete = list()
# iterate through all tasks so nothing gets missed
for todo in self._get_todos():
# in each iteration we want to iterate through the hashes marked complete and add tasks
# with a matching hash to that new list
for item in hashes_marked_complete:
if todo.hash == item:
tasks_to_complete.append(todo)
for selection in tasks_to_complete:
self._mark_task_complete( selection )
def create_new_topic_ui(self):
input_code, input_topic = self.d.inputbox("Creating a new topic for today: {}".format(self._get_current_date()),
width=self.d.maxsize()[1])
if input_code == self.d.OK:
self.do_new_topic(input_topic)
self.do_browse_topics()
else:
self.do_browse_topics()
# browse topics for all dates # browse topics for all dates
def do_browse_topics(self): def do_browse_topics(self):
topics = self._get_all_topics() topics = self._get_all_topics()
menu_choices = list() if len(topics) > 0:
for i in range(len(topics)): menu_choices = list()
menu_choices.append( ( topics[i], "{} total entries.".format( len( self._get_dates( topics[i] ) ) ) ) ) for i in range(len(topics)):
menu_choices.append( ( topics[i], "{} total entries.".format( len( self._get_dates( topics[i] ) ) ) ) )
code, topic = self.d.menu(
"Select a topic whose dated entries you want to browse:",
choices=menu_choices,
width=self.d.maxsize()[1],
cancel_label="Exit",
ok_label="Select",
extra_button=True,
extra_label="New topic for today..."
)
code, topic = self.d.menu( if code == self.d.OK:
"Select a topic whose dated entries you want to browse:", self.do_date_browse_by_topic(topic)
choices=menu_choices, elif code == self.d.EXTRA:
width=self.d.maxsize()[1], self.create_new_topic_ui()
cancel_label="Back", else:
ok_label="Select", # no topics yet, create one
extra_button=True, self.create_new_topic_ui()
extra_label="New topic for today..."
)
if code == self.d.OK:
self.do_date_browse_by_topic(topic)
elif code == self.d.EXTRA:
input_code, input_topic = self.d.inputbox( "Creating a new topic for today: {}".format( self._get_current_date() ), width=self.d.maxsize()[1] )
if input_code == self.d.OK:
self.do_new_topic( input_topic )
self.do_browse_topics()
else:
self.do_browse_topics()
# browse dates for all topics # browse dates for all topics
def do_list_dates(self): def do_list_dates(self):
@ -219,7 +331,6 @@ class Journaler():
if code == self.d.OK: if code == self.d.OK:
self.do_topic_browse_by_date( date ) self.do_topic_browse_by_date( date )
def do_new_topic(self, topic): def do_new_topic(self, topic):
self._open_entry( topic, self._get_current_date() ) self._open_entry( topic, self._get_current_date() )