implemented TODO system and fixed handling empty journal state
parent
24606580e3
commit
f93e5b7f70
|
@ -1,5 +1,6 @@
|
|||
[filesystem]
|
||||
journal=/home/bagira/journal
|
||||
task_tracker=/home/bagira/journal/task_tracker
|
||||
|
||||
[tools]
|
||||
editor=/usr/bin/nano
|
||||
|
|
179
silojourn.py
179
silojourn.py
|
@ -7,6 +7,8 @@ from dialog import Dialog
|
|||
from datetime import datetime
|
||||
import subprocess
|
||||
import re
|
||||
import glob
|
||||
import hashlib
|
||||
|
||||
solution_description="Journaling system for daily needs."
|
||||
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.journal_path = self.config['filesystem']['journal']
|
||||
self.editor = self.config['tools']['editor']
|
||||
self.tracker = self.config['filesystem']['task_tracker']
|
||||
|
||||
def get_journal_path(self):
|
||||
return self.journal_path
|
||||
|
@ -29,24 +32,60 @@ class Config():
|
|||
def get_editor(self):
|
||||
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():
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
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
|
||||
def _get_entry_filename(self, topic, date):
|
||||
# TODO add sanity checks
|
||||
return "{0}/{1}_{2}".format( self.config.get_journal_path(), date, topic )
|
||||
|
||||
|
||||
# open a specific entry for a supplied topic and date
|
||||
def _open_entry(self, topic, date):
|
||||
subprocess.run( [ self.config.get_editor(), self._get_entry_filename( topic, date ) ] )
|
||||
|
||||
|
||||
# gemerate the current date in a standard format
|
||||
def _get_current_date(self):
|
||||
return datetime.today().strftime('%Y-%m-%d')
|
||||
|
@ -56,19 +95,17 @@ class Journaler():
|
|||
entry_list = os.listdir( self.config.get_journal_path() )
|
||||
return entry_list
|
||||
|
||||
|
||||
# get all topics for all dates
|
||||
def _get_all_topics(self):
|
||||
topics = list()
|
||||
for item in self._get_all_entries():
|
||||
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 )
|
||||
except AttributeError:
|
||||
pass
|
||||
return sorted( list( dict.fromkeys( topics ) ) )
|
||||
|
||||
|
||||
# get all topics for all dates
|
||||
def _get_all_dates(self):
|
||||
dates = list()
|
||||
|
@ -80,7 +117,6 @@ class Journaler():
|
|||
pass
|
||||
return sorted( list( dict.fromkeys( dates ) ) )
|
||||
|
||||
|
||||
# get all topics associated with a date
|
||||
def _get_topics(self, date):
|
||||
topics = list()
|
||||
|
@ -93,7 +129,6 @@ class Journaler():
|
|||
pass
|
||||
return sorted( list( dict.fromkeys( topics ) ) )
|
||||
|
||||
|
||||
# get all dates associated with a topic
|
||||
def _get_dates(self, topic):
|
||||
dates = list()
|
||||
|
@ -106,7 +141,6 @@ class Journaler():
|
|||
pass
|
||||
return sorted( list( dict.fromkeys( dates ) ) )
|
||||
|
||||
|
||||
# browse topics for a specific date
|
||||
def do_topic_browse_by_date(self, date):
|
||||
print( "topic browse by date: {}".format( date ) )
|
||||
|
@ -139,7 +173,6 @@ class Journaler():
|
|||
else:
|
||||
self.do_browse_topics()
|
||||
|
||||
|
||||
# browse dates for a specific topic
|
||||
def do_date_browse_by_topic(self, topic):
|
||||
dates = self._get_dates(topic)
|
||||
|
@ -167,40 +200,119 @@ class Journaler():
|
|||
else:
|
||||
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):
|
||||
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
|
||||
def do_browse_topics(self):
|
||||
topics = self._get_all_topics()
|
||||
|
||||
menu_choices = list()
|
||||
for i in range(len(topics)):
|
||||
menu_choices.append( ( topics[i], "{} total entries.".format( len( self._get_dates( topics[i] ) ) ) ) )
|
||||
if len(topics) > 0:
|
||||
menu_choices = list()
|
||||
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(
|
||||
"Select a topic whose dated entries you want to browse:",
|
||||
choices=menu_choices,
|
||||
width=self.d.maxsize()[1],
|
||||
cancel_label="Back",
|
||||
ok_label="Select",
|
||||
extra_button=True,
|
||||
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()
|
||||
|
||||
if code == self.d.OK:
|
||||
self.do_date_browse_by_topic(topic)
|
||||
elif code == self.d.EXTRA:
|
||||
self.create_new_topic_ui()
|
||||
else:
|
||||
# no topics yet, create one
|
||||
self.create_new_topic_ui()
|
||||
|
||||
# browse dates for all topics
|
||||
def do_list_dates(self):
|
||||
|
@ -219,7 +331,6 @@ class Journaler():
|
|||
if code == self.d.OK:
|
||||
self.do_topic_browse_by_date( date )
|
||||
|
||||
|
||||
def do_new_topic(self, topic):
|
||||
self._open_entry( topic, self._get_current_date() )
|
||||
|
||||
|
|
Loading…
Reference in New Issue