From fb009050a0117827a62134f3e46524f90b1d375d Mon Sep 17 00:00:00 2001 From: madhaven Date: Sun, 11 Sep 2022 19:56:14 +0530 Subject: [PATCH] Model View Architecture? added DiaryController class for user interface. Concerns separated. tests. --- README.md | 7 +- diary.py | 511 +++++++++++++++++++--------------- tests/test_Diary.py | 146 ++++++---- tests/test_DiaryController.py | 37 +++ tests/test_Entry.py | 23 +- 5 files changed, 434 insertions(+), 290 deletions(-) create mode 100644 tests/test_DiaryController.py diff --git a/README.md b/README.md index df1c4ba..f259941 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Diary The diary app is intended to be called from a command prompt and can be used to quickly log entries of your day and search for past entries. -The read option enables you to replay entries the way you typed it into the console. +The read option enables you to replay entries the way you typed it into the console. +The setup is quite clumsy for a standalone script. After having added the .py extension and the program directory to path, the Diary interface could be called with one of three ways to start it up. @@ -31,3 +32,7 @@ After having added the .py extension and the program directory to path, the Diar >diary [searchall | findall] [ string [ string2 [...]]] >``` >helps you search for strings in your file. The ```searchall / findall``` also works in a similar fashion except that it fetches those entries that simultaneously have all the search strings in it. + +The `DiaryController` class is responsible for handling user interaction to access the `Diary`. +The `Diary` class contains a list of `Entry` instances that contain a record of the user's input. + \ No newline at end of file diff --git a/diary.py b/diary.py index 13acc16..66a513b 100644 --- a/diary.py +++ b/diary.py @@ -1,15 +1,16 @@ -try: version = '3.2' - testing = version[-5:] == 'debug' - if testing: import traceback - from os import sep, path, system - import sys +testing = version[-5:] == 'debug' +if testing: import traceback - from time import sleep - from datetime import datetime, timedelta - from msvcrt import getch, kbhit - import re, calendar +from os import sep, path, system +import sys +from abc import abstractmethod +from time import sleep +from datetime import datetime, timedelta +from msvcrt import getch, kbhit +import re, calendar +try: try: exec(open(sep.join([path.expanduser('~'), 'diary_config']), 'r').read()) except: @@ -31,21 +32,35 @@ exit() class EmergencyStop(Exception): - pass + '''raised when User presses Ctrl+C during the record of an entry.''' + def __init__(self, *args: object) -> None: + self.entry = args[0] + super().__init__(args[1:]) class Entry: '''stores an entry that the user makes''' - def __init__(self, text:str='', time:datetime=None, intervals:list=None, printdate:bool=False): + def __init__(self, text:str='', time:datetime=None, intervals:list=[], printdate:bool=False): '''set empty strings''' self.text:str = text self.time:datetime = time - self.intervals:list = intervals self.printdate:bool = printdate + if intervals and len(intervals)==len(text): + self.intervals:list = intervals + else: + self.intervals:list = [0 for _ in range(len(text))] - def __bool__(self): + def __bool__(self) -> bool: return len(self.text) > 0 + def __eq__(self, __o: "Entry") -> bool: + if self.text != __o.text: + return False + for x, y in zip(self.intervals, __o.intervals): + if x != y: + return False + return True + def __str__(self): ''' Convert user input information and returns data alone\n @@ -54,156 +69,269 @@ def __str__(self): ''' text = '' for x in self.text: - if x == '\b': text = text[:-1] - else: text += x + text = text[:-1] if x == '\b' else text + x return text + + def addChar(self, char:str, time:float): + '''adds another character to the entry.\n + the time attribute saves the time taken before/after the keypress.''' + self.text += char + self.intervals += [time] + +class Diary: + ''' + class to handle all Diary interactions + ''' + headerFormat = 'diary v%s github.com/madhaven/diary\n' + entryFormat = '\n%s%s%s\n' + version = version + + def __init__(self, filename:str, version:str=version): + '''initializes the Diary''' + self.entries = [] + self.file = filename # TODO: add check for file + self.printdate = False + + def add(self, *entries): + '''writes the entry/entries to the file''' + with open(self.file, 'a') as f: + if f.tell()==0: + f.writelines([self.headerFormat%self.version]) + for entry in entries: + if entry: + f.write(self.entryFormat%(entry.time.ctime(), entry.text, str(entry.intervals))) + + def load(self): + ''' + Scans the file specified on creation and initializes the list of Entry objects\n + This replaces any existing Entries in memory with data from the file\n + \n + Only recommended when reading entries as adding entries to the diary do not require any data in memory + ''' + entries = [] + try: + with open(self.file, 'r') as f: + if 'diary' in f.readline().split(): + #version management + f.readline() + + while True: + text = f.readline() + if not text: + # TODO: escape blank lines / add info for SESSION concept in Diary + break + intervals = [float(time) for time in f.readline()[1:-2].split(', ')] + f.readline() + + entry = Entry(text[24:], datetime.strptime(text[:24], '%a %b %d %H:%M:%S %Y'), intervals) + if ( + len(entries) == 0 or + entries[-1].time.day != entry.time.day + ): entry.printdate = True + entries.append(entry) + self.entries = entries + + except FileNotFoundError as e: + print("\nYou do not have a diary file at %s. Make sure your file location is configured properly."%self.file) + raise e + + def filter(self, year:int=None, month:int=None, day:int=None) -> list: + '''fetches diary records acc to date match''' + self.load() + return [ + entry for entry in self.entries + if ((not year or year==entry.time.year) and + (not month or month==entry.time.month) and + (not day or day==entry.time.day)) + ] + + def search(self, *args, strictMode:bool=False) -> list: + '''Search/Find keywords, returns a list of Entry objects''' + + results = [] + self.load() + if strictMode: + for entry in self.entries: + entry_str = str(entry).lower() + for i, s in enumerate(args): + if s.lower() not in entry_str: + break + elif i == len(args) - 1: + results.append(entry) + else: + for entry in self.entries: + entry_str = str(entry).lower() + for i, s in enumerate(args): + if s.lower() in entry_str: + results.append(entry) + break + return results + + def export(self, args:str=None): + '''Handles Export options''' + if not args: + print( + 'not implemented', + # 'usage','-----', + # 'diary export[to] text/txt', + # 'diary export[to] csv', + sep = '\n' + ) + return + +class DiaryController(): + '''provide access to Diary''' - def record(self): - '''method that records an entry, an entry ends when the Return key is pressed''' + def __init__(self, diary:Diary, stopWord:str='bye', typespeed:float=1.5): + self.stopWord:str = stopWord + self.typespeed:int = typespeed + self.diary:Diary = diary + self.version:str = version + + def _showVersion(self, args=None): + '''Shows Controller Version''' + print("Diary.py v" + self.version + "\ngithub.com/madhaven/Diary") + + def _showInfo(self): + '''Shows Manual''' + self._showVersion() + print( + '\nUsage','-----', + 'diary log|entry - to add to your diary', + 'diary version - to check which version your diary is running', + 'diary read - to access older entries or logs that you have made', + 'diary search|find - to search for keywords', + 'diary searchall|findall - to search for entries containing all the keywords', + 'diary export - to export your entries to portable formats | NOT AVAILABLE', + sep='\n' + ) + + def main(self, *args): + '''Main entry point into Diary. parses cli args to select menu''' + + if not args: + self._showInfo() + elif args[0] in ['log', 'entry']: + self.log() + elif args[0] in ['read', 'show']: + self.read(*args[1:]) + elif args[0] in ['search', 'find']: + self.search(*args[1:]) + elif args[0] in ['searchall', 'findall', 'search all', 'find all']: + self.search(*args[1:], strictMode=True) + elif args[0] in ['export as', 'export to', 'export']: + self.diary.export(*args[1:]) + elif args[0] in ['version', '--version']: + self._showVersion() + else: + self._showInfo() + + def log(self): + ''' + To record diary entries\n + Initiates a loop of Entry records\n + Loop ends when the stop word is found in an Entry\n + `entry` is an injected variable that defaults to `Entry` instance + ''' try: - entry = [] - #record current time - self.time = datetime.now() - t1 = self.time.timestamp() + while True: + entry = self.record() + self.diary.add(entry) + if self.stopWord in str(entry).lower(): + break + except EmergencyStop as e: + self.diary.add(e.entry) + system('cls') + except Exception as e: + print('your last entry was broken: %s'%entry) + raise e + + def record(self) -> Entry: + '''method to record an entry from the cli interface. A single entry ends when the return key is pressed. The diary log ends if the stopword is found in the entry.''' + try: + # record current time + entry = Entry(time=datetime.now()) + t1 = entry.time.timestamp() while True: - #get pressed character + # get pressed character char = str(getch())[2:-1] - - #format time + + # format time t2 = datetime.now().timestamp() if (t2-t1) > 60: t2, t1 = (t2-t1) - int(t2-t1) + 5, t2 else: t2, t1 = t2 - t1, t2 t2 = round(t2, 4) - - #Return key + + # return key if char == '\\r': - #do nothing if text is empty - if len(entry) == 0: + # do nothing if text is empty + if not entry: continue - - #add a new line character to the entry and format the entry object - entry.append(['\n', t2]) - self.text = ''.join(list(zip(*entry))[0]) - self.intervals = list(list(zip(*entry))[1]) + + # add a new line character to the entry and format the entry object + entry.addChar('\n', t2) print() - return self - - #Ctrl+C + return entry + + # ctrl+c elif char == '\\x03': - if len(entry) != 0: - entry.append(['\n', t2]) - self.text = ''.join(list(zip(*entry))[0]) - self.intervals = list(list(zip(*entry))[1]) - raise EmergencyStop - - #Escape char + if entry: + entry.addChar('\n', t2) + raise EmergencyStop(entry) + + # escape char elif char == '\\\\': print('\\', end='', flush=True) - char = "\\" - - #Tabspace + char = '\\' + + # tabspace elif char == '\\t': print('\t', end='', flush=True) char = '\t' - - #Backspace? + + # backspace? elif char == '\\x08': print('\b \b', end='', flush=True) char = '\b' - #stray characters + # stray char elif len(char) > 1: char = str(getch())[2:-1] continue - - #normal character? + + # normal char else: print(char, end='', flush=True) - - #add character to entry list. - entry.append([char, t2]) - except Exception as e: + # add char to entry list + entry.addChar(char, t2) + + except Exception as e: print(e) raise e - def print(self, speed=1.5): - ''' - prints the entry\n - Contains sleep() to imitate the user's type speed\n - self.printdate decides whether or not to print the timestamp\n - ''' - skipFactor = 1 - if self.printdate: print('\n'+self.time.ctime(), flush=True) - for letter, time in zip(self.text, self.intervals): - sleep(time*skipFactor/speed) - if kbhit() and str(getch())[2:-1] in ['\\r', ' ']: skipFactor=0 - print( - '\b \b' if letter=='\b' else letter, - end='', flush=True - ) - -class Diary(): - ''' - class to handle all Diary interactions - ''' - headerFormat = 'diary v%s github.com/madhaven/diary' - entryFormat = '\n\n%s%s%s' - version = version - - def __init__(self, filename:str, typespeed:float=1.5, stopWord:str='bye', version:str=version): - '''initializes the Diary''' - self.entries = [] - self.file = filename - self.printdate = False - self.typespeed = typespeed - self.sessionStopWord = stopWord - - def add(self, *entries): - '''writes the entry/entries to the file''' - with open(self.file, 'a') as f: - if f.tell()==0: - f.writelines([self.headerFormat%self.version]) - for entry in entries: - if entry: - f.write(self.entryFormat%(entry.time.ctime(), entry.text, str(entry.intervals))) - - def record(self, entry:Entry=Entry()): - ''' - To add diary entries to the file\n - Initiates a loop of Entry records\n - Loop ends when the stop word is found in an Entry\n - `entry` is an injected variable that defaults to `Entry` instance - ''' - try: - while True: - entry.record() - self.add(entry) - if self.sessionStopWord in str(entry).lower(): - break - except EmergencyStop as e: - self.add(entry) - except Exception as e: - print('your last entry : '+str(entry)) - raise e - - def read(self, args:list): + def read(self, *args): '''displays the diary entries of the queried day''' if not args: print( - 'usage','-----','diary read all','diary read today', - 'diary read yesterday','diary read [YYYY] [Mon] [DD]', sep='\n' + 'usage', + '-----', + 'diary read all', + 'diary read today', + 'diary read yesterday', + 'diary read [YYYY] [Mon] [DD]', + sep='\n' ) return if args[0]=='all': year, month, day = None, None, None elif args[0] in ['yesterday', 'today']: - args = datetime.now()-timedelta(1) if args[0]=='yesterday' else datetime.now() + args = datetime.now()-timedelta(days=1) if args[0]=='yesterday' else datetime.now() year, month, day = args.year, args.month, args.day else: try: @@ -211,67 +339,33 @@ def read(self, args:list): year = int(year[0]) if year else None monthname = list(filter(re.compile(r'^[a-zA-Z]{3}.*$').match, args)) monthname = monthname[0] if monthname else None - month = {month[:3].lower(): index for index, month in enumerate(calendar.month_abbr) if month}[monthname[:3]] + month = { + month[:3].lower(): index + for index, month in enumerate(calendar.month_abbr) + if month + }[monthname[:3]] if monthname else None day = list(filter(re.compile(r'^\d{1,2}$').match, args)) day = int(day[0]) if day else None except: print("That date doesn't look right") + if testing: traceback.print_exc() return - self.load() - readlist = [ - entry for entry in self.entries - if ((not year or year==entry.time.year) and - (not month or month==entry.time.month) and - (not day or day==entry.time.day)) - ] - print('found '+str(len(readlist))+' entries,') - try: - for entry in readlist: entry.print() + readlist = self.diary.filter(year, month, day) + print('found %s entries,'%str(len(readlist))) + try: + for entry in readlist: + self.printEntry(entry) except KeyboardInterrupt as e: print("\nDiary closed") return - - def load(self): - ''' - Scans the file specified on creation and returns a list of Entry objects\n - This replaces any existing Entries in memory with data from the file\n - \n - Only recommended when reading entries as adding entries to the diary do not require any data in memory - ''' - entries = [] - try: - with open(self.file, 'r') as f: - if 'diary' in f.readline().split(): - #version management - f.readline() - - while True: - text = f.readline() - if not text: - #escape blank lines / add info for SESSION concept in Diary - break - intervals = [float(time) for time in f.readline()[1:-2].split(', ')] - f.readline() - - entry = Entry(text[24:], datetime.strptime(text[:24], '%a %b %d %H:%M:%S %Y'), intervals) - if ( - len(entries) == 0 or - entries[-1].time.day != entry.time.day - ): entry.printdate = True - entries.append(entry) - - self.entries = entries - return self - except FileNotFoundError as e: - print("\nYou do not have a diary file at %s. Make sure your file location is configured properly."%self.file) - raise e - def search(self, args, strictMode:bool=False): + def search(self, *args, strictMode:bool=False): '''Search/Find keywords''' if not args: print( - 'usage','-----', + 'usage', + '-----', 'diary search [search_text [search_text2 [...]]]', 'diary searchall [search_text [search_text2 [...]]]', 'diary searchall matches all strings together', @@ -280,35 +374,30 @@ def search(self, args, strictMode:bool=False): ) return - self.load() - result_count = 0 - if strictMode: - for entry in self.entries: - for i, s in enumerate(args): - if s.lower() not in str(entry).lower(): break - elif i == len(args) - 1: - result_count += 1 - print(entry.time.strftime('%Y %b %d %H:%M:%S %a'), '|', str(entry), end='') - else: - for entry in self.entries: - for i, s in enumerate(args): - if s.lower() in str(entry).lower(): - result_count += 1 - print(entry.time.strftime('%Y %b %d %H:%M:%S %a'), '|', str(entry), end='') - break - print(result_count, "entries found") + results = self.diary.search(*args, strictMode=strictMode) + for entry in results: + print(entry.time.strftime('%Y %b %d %H:%M:%S %a'), '|', str(entry), end='') + count = len(results) + print(count, "%s found"%("entries" if count>1 else "entry")) - def export(self, args:str=None): - '''Handles Export options''' - if not args: - print( - 'not implemented', - # 'usage','-----', - # 'diary export[to] text/txt', - # 'diary export[to] csv', - sep = '\n' + def printEntry(self, entry:Entry, speed:int=None): + ''' + prints the entry\n + Contains sleep() to imitate the user's type speed\n + self.printdate decides whether or not to print the timestamp\n + ''' + speed = self.typespeed if not speed else speed + skipFactor = 1 # speed when user decides to skip an entry + if entry.printdate: + print('\n' + entry.time.ctime(), flush=True) + for letter, time in zip(entry.text, entry.intervals): + sleep(time * skipFactor / speed) + if kbhit() and str(getch())[2:-1] in ['\\r', ' ']: + skipFactor=0 + print( + '\b \b' if letter=='\b' else letter, + end='', flush=True ) - return def log(*args, pause=False, **kwargs): @@ -316,25 +405,6 @@ def log(*args, pause=False, **kwargs): if testing: print(*args, **kwargs) if pause:input() - - -def diaryversion(args=None): - '''Shows version''' - print("Diary.py v"+version+"\ngithub.com/madhaven/Diary") - - -def diaryinfo(): - '''Shows Manual''' - diaryversion() - print( - '\nUsage','-----', - 'diary log|entry - to add to your diary', - 'diary version - to check which version your diary is running', - 'diary read - to access older entries or logs that you have made', - 'diary search - to search for keywords', - 'diary export - to export your entries to portable formats | NOT AVAILABLE', - sep='\n' - ) if __name__ == '__main__': @@ -342,22 +412,15 @@ def diaryinfo(): #get cli args cliargs = [arg.lower() for arg in sys.argv][1:] log('DIARY cli args', sys.argv, '->', cliargs) - diary = Diary(filename=filename, typespeed=typespeed) + diary = Diary(filename=filename) + controller = DiaryController(diary, typespeed=typespeed) - # parse cli args to select menu try: - if not cliargs: diaryinfo() - elif cliargs[0] in ['log', 'entry']: diary.record() - elif cliargs[0] in ['read', 'show']: diary.read(cliargs[1:]) - elif cliargs[0] in ['search', 'find']: diary.search(cliargs[1:]) - elif cliargs[0] in ['searchall', 'findall', 'search all', 'find all']: diary.search(cliargs[1:], True) - elif cliargs[0] in ['export as', 'export to', 'export']: diary.export(cliargs[1:]) - elif cliargs[0] in ['version', '--version']: diaryversion() - else: diaryinfo() + controller.main(*cliargs) except Exception as e: print( 'An error crashed the diary :', str(e), 'please report issues to https://github.com/madhaven/Diary/issues', sep='\n', end='\n\n' ) - if testing: traceback.print_exc() + if testing: traceback.print_exc() \ No newline at end of file diff --git a/tests/test_Diary.py b/tests/test_Diary.py index 21bbb8e..a6b453f 100644 --- a/tests/test_Diary.py +++ b/tests/test_Diary.py @@ -1,33 +1,10 @@ -from datetime import datetime +from datetime import date, datetime, timedelta import os -from unittest import TestCase +from unittest import TestCase, expectedFailure, skip from diary import Diary, Entry -class EntryTest(Entry): - - def __init__(self, maxCount, stopWord, text: str, time: datetime, intervals: list = None, printdate: bool = False): - super().__init__(text, time, intervals, printdate) - self.maxCount = maxCount - self.stopWord = stopWord - self.count = 0 - self.text = '%s\n'%text - - def record(self): - if self.count >= self.maxCount: - self.text, self.intervals = self.stopWord+'\n', [.1 for _ in range(len(self.stopWord)+1)] - else: - self.intervals = [.1 for _ in range(len(self.text))] - self.count += 1 - self.printdate = False - return self - -class EntryExceptionTest(EntryTest): - - def record(self): - raise KeyboardInterrupt - class Test_Diary(TestCase): '''Conducts unit tests for diary methods''' @@ -42,13 +19,11 @@ def tearDown(self) -> None: os.remove(self.filename) return super().tearDown() + @expectedFailure def test_init(self): diary = Diary(self.filename, version='3.2') self.assertEqual(diary.entries, [], 'Expected blank list of diary entries on init') self.assertEqual(diary.file, self.filename, 'Filename not saved into file attribute on init') - self.assertEqual(diary.printdate, False, 'printdate expected to be False') - self.assertEqual(diary.typespeed, 1.5, 'Typespeed mismatch') - self.assertEqual(diary.sessionStopWord, 'bye', 'stop Word mismatch') self.assertEqual(diary.version, '3.2', 'version mismatch') def test_blank_init(self): @@ -57,65 +32,114 @@ def test_blank_init(self): def test_add_brand_new(self): diary = Diary(self.filename) nowTime = datetime.now() - diary.add(Entry('hello\n', nowTime, [0.1 for _ in range(5)])) + diary.add(Entry('hello\n', nowTime, [0.1 for _ in range(6)])) f = open(self.filename, 'r').read() - self.assertEqual(f, 'diary v'+diary.version+' github.com/madhaven/diary\n\n' + nowTime.ctime() + 'hello\n[0.1, 0.1, 0.1, 0.1, 0.1]', 'Brand New entry format error') + self.assertEqual(f, 'diary v'+diary.version+' github.com/madhaven/diary\n\n' + nowTime.ctime() + 'hello\n[0.1, 0.1, 0.1, 0.1, 0.1, 0.1]\n', 'Brand New entry format error') def test_add_entry(self): diary = Diary(self.filename) nowTime = datetime.now() diary.add(Entry('hello', nowTime, [0.1 for _ in range(5)])) - nowTime2 = datetime.now() - diary.add(Entry('olleh', nowTime2, [0.2 for _ in range(5)])) - expected = 'diary v%s github.com/madhaven/diary\n\n%shello[0.1, 0.1, 0.1, 0.1, 0.1]\n\n%solleh[0.2, 0.2, 0.2, 0.2, 0.2]'%( - diary.version, nowTime.ctime(), nowTime2.ctime()) + nowt2 = datetime.now() + diary.add(Entry('olleh', nowt2, [0.2 for _ in range(5)])) + expected = 'diary v%s github.com/madhaven/diary\n\n%shello[0.1, 0.1, 0.1, 0.1, 0.1]\n\n%solleh[0.2, 0.2, 0.2, 0.2, 0.2]\n'%( + diary.version, nowTime.ctime(), nowt2.ctime()) fileContents = open(self.filename, 'r').read() self.assertEqual(fileContents, expected, 'Next entry format error') - def test_record(self): - time = datetime.now() - ctime = time.ctime() - diary = Diary(self.filename, stopWord='bye') - testEntryMaker = EntryTest(2, 'bye', 'bleh', time) - diary.record(testEntryMaker) - - expected = '%s\n\n%sbleh\n[0.1, 0.1, 0.1, 0.1, 0.1]\n\n%sbleh\n[0.1, 0.1, 0.1, 0.1, 0.1]\n\n%sbye\n[0.1, 0.1, 0.1, 0.1]'%(diary.headerFormat%diary.version, ctime, ctime, ctime) - fileContents = open(self.filename, 'r').read() - - self.assertEqual(fileContents, expected) - - def test_record_plenty(self): + def test_add_plenty(self): n = 1000 time = datetime.now() ctime = time.ctime() - diary = Diary(self.filename, stopWord='bye') - testEntryMaker = EntryTest(n, 'bye', 'bleh', time) - diary.record(testEntryMaker) + diary = Diary(self.filename) + diary.add(*[Entry('bleh\n', time, [.1]*5)]*n) expected = diary.headerFormat%diary.version for _ in range(n): - expected += '\n\n%sbleh\n[0.1, 0.1, 0.1, 0.1, 0.1]'%ctime - expected += '\n\n%sbye\n[0.1, 0.1, 0.1, 0.1]'%ctime - fileContents = open(self.filename, 'r').read() + expected += '\n%sbleh\n[0.1, 0.1, 0.1, 0.1, 0.1]\n'%ctime - self.assertEqual(fileContents, expected) + fileContents = open(self.filename, 'r').read() + self.assertEqual(fileContents, expected, 'Diary saved an unexpected output') - # def test_record_keyboard_interrupt(self): + def test_load(self): + f = open(self.filename, 'w') + f.write('diary v3.2 github.com/madhaven/diary\n\nSun Sep 11 19:05:01 2022hello \n[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]\n\nSun Sep 11 19:05:03 2022testing\n[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]\n\nSun Sep 11 19:05:04 2022bye\n[0.1, 0.1, 0.1, 0.1]\n') + f.close() - def test_read_empty_args(self): diary = Diary(self.filename) - self.assertEqual(diary.read([]), None) - - def test_read(self): + diary.load() + self.assertListEqual(diary.entries, [ + Entry('hello \n', datetime.strptime('Sun Sep 11 19:05:01 2022', '%a %b %d %H:%M:%S %Y'), [0.1 for _ in range(7)]), + Entry('testing\n', datetime.strptime('Sun Sep 11 19:05:03 2022', '%a %b %d %H:%M:%S %Y'), [0.1 for _ in range(8)]), + Entry('bye\n', datetime.strptime('Sun Sep 11 19:05:04 2022', '%a %b %d %H:%M:%S %Y'), [0.1 for _ in range(4)]), + ]) + + @skip + def test_load_versions(self): pass + + def test_filter_empty_args(self): + diary = Diary(self.filename) + e = Entry('bleh\n', datetime.now(), [0.1]*5) + diary.add(e) + self.assertEqual(diary.filter() == [e], True, msg='Diary should return None if no args are supplied') - def test_load(self): - pass + def test_filter_year_alone(self): + diary = Diary(self.filename) + t1, t2, t3 = datetime.now(), datetime.now(), datetime.now() + t1 -= timedelta(days=-30) + t2 -= timedelta(days=-70) + t3 -= timedelta(days=-1000) + e1, e2, e3 = Entry('bleh\n', t1, [0.1]*5), Entry('bleh\n', t2, [0.1]*5), Entry('bleh\n', t3, [0.1]*5) + diary.add(e1, e2, e3) + self.assertListEqual([e1, e2], diary.filter(year=t1.year), msg='expected entries of a year were not returned') + + def test_filter_month_alone(self): + diary = Diary(self.filename) + t1, t2, t3 = datetime.now(), datetime.now(), datetime.now() + t1 -= timedelta(days=-365) + t3 -= timedelta(days=-1000) + e1, e2, e3 = Entry('bleh\n', t1, [0.1]*5), Entry('bleh\n', t2, [0.1]*5), Entry('bleh\n', t3, [0.1]*5) + diary.add(e1, e2, e3) + self.assertListEqual([e1, e2], diary.filter(month=t1.month), msg='expected entries of a month were not returned') + + def test_filter_date_alone(self): + diary = Diary(self.filename) + t1, t2, t3 = datetime(2015, 5, 7), datetime(2016, 7, 7), datetime(2018, 2, 8) + e1, e2, e3 = Entry('bleh\n', t1, [0.1]*5), Entry('bleh\n', t2, [0.1]*5), Entry('bleh\n', t3, [0.1]*5) + diary.add(e1, e2, e3) + self.assertListEqual([e1, e2], diary.filter(day=t1.day), msg='expected entries of a month were not returned') + + def test_filter_year_and_month(self): + diary = Diary(self.filename) + t1, t2, t3, t4 = datetime(2015, 5, 7), datetime(2015, 5, 9), datetime(2018, 5, 8), datetime(2015, 8, 1) + e1, e2, e3, e4 = Entry('bleh\n', t1, [0.1]*5), Entry('bleh\n', t2, [0.1]*5), Entry('bleh\n', t3, [0.1]*5), Entry('bleh\n', t4, [0.1]*5) + diary.add(e1, e2, e3, e4) + self.assertListEqual([e1, e2], diary.filter(year=t1.year, month=t1.month), msg='expected entries of a month were not returned') + + def test_filter_year_and_date(self): + diary = Diary(self.filename) + t1, t2, t3, t4 = datetime(2015, 5, 7), datetime(2015, 9, 7), datetime(2018, 5, 7), datetime(2015, 8, 6) + e1, e2, e3, e4 = Entry('bleh\n', t1, [0.1]*5), Entry('bleh\n', t2, [0.1]*5), Entry('bleh\n', t3, [0.1]*5), Entry('bleh\n', t4, [0.1]*5) + diary.add(e1, e2, e3, e4) + self.assertListEqual([e1, e2], diary.filter(year=t1.year, day=t1.day), msg='expected entries of a month were not returned') + + def test_filter_month_and_date(self): + diary = Diary(self.filename) + t1, t2, t3, t4 = datetime(2015, 5, 7), datetime(2018, 5, 7), datetime(2019, 5, 1), datetime(2015, 8, 7) + e1, e2, e3, e4 = Entry('bleh\n', t1, [0.1]*5), Entry('bleh\n', t2, [0.1]*5), Entry('bleh\n', t3, [0.1]*5), Entry('bleh\n', t4, [0.1]*5) + diary.add(e1, e2, e3, e4) + self.assertListEqual([e1, e2], diary.filter(month=t1.month, day=t1.day), msg='expected entries of a month were not returned') + @skip + def test_filter_unrecognized_month(self): + pass + def test_search(self): pass + @skip def test_export(self): pass \ No newline at end of file diff --git a/tests/test_DiaryController.py b/tests/test_DiaryController.py new file mode 100644 index 0000000..1ef5cb4 --- /dev/null +++ b/tests/test_DiaryController.py @@ -0,0 +1,37 @@ +import os +from unittest import TestCase, skip + +from diary import Diary, DiaryController + +class Test_DiaryController(TestCase): + '''unit tests for DiaryController''' + + def setUp(self) -> None: + self.filename = 'test.txt' + if os.path.isfile(self.filename): + os.remove(self.filename) + return super().setUp() + + def tearDown(self) -> None: + if os.path.isfile(self.filename): + os.remove(self.filename) + return super().tearDown() + + def test_blank_init(self): + self.assertRaises(TypeError, DiaryController, msg='blank filename error not raised') + + def test_init(self): + diary = Diary(self.filename) + controller = DiaryController(diary, 'bye', 1.5) + self.assertEqual(controller.diary.file, 'test.txt', msg='filename not initialized') + self.assertEqual(controller.typespeed, 1.5, msg='Typespeed mismatch') + self.assertEqual(controller.stopWord, 'bye', msg='stop Word mismatch') + self.assertEqual(diary.printdate, False, 'printdate expected to be False') + + @skip + def test_record_keyboard_interrupt(self): + pass + + @skip + def test_unknown_month_interrupt(self): + pass \ No newline at end of file diff --git a/tests/test_Entry.py b/tests/test_Entry.py index d3b28c5..19c38d2 100644 --- a/tests/test_Entry.py +++ b/tests/test_Entry.py @@ -1,17 +1,32 @@ -from unittest import TestCase -from diary import Diary, Entry +from datetime import datetime +from unittest import TestCase, skip +from diary import Entry class TestEntry(TestCase): '''conducts unit tests for entry methods''' def test_init(self): - pass + t = datetime.now() + entry = Entry('hello\n', t, [0.1 for _ in range(6)]) + self.assertEqual(entry.intervals, [0.1 for _ in range(6)], msg="intervals should match") + self.assertEqual(entry.printdate, False, msg='Printdate should match') + self.assertEqual(entry.time, t,msg='Entry time should match') + self.assertEqual(Entry().text, '', msg='Blank Entry text should be blank') + self.assertEqual(Entry().intervals, [], msg='Blank Entry intervals should be blank') + self.assertEqual(len(entry.text), len(entry.intervals), msg='Intervals should correspond to text in the entry') def test_str(self): - pass + entry = Entry('hello\b\b\b\bmy name is x') + self.assertEqual(str(entry), 'hmy name is x', msg='string conversion does not match') + + def test_str_with_backspace(self): + entry = Entry('x\b\b\b\b\bhello') + self.assertEqual(str(entry), 'hello', msg='string conversion does not match') + @skip def test_record(self): pass + @skip def test_print(self): pass \ No newline at end of file