def test_reset(setup): runner = CliRunner() # Reset habit that does not exist on record result = runner.invoke(habiter, ['reset', '--yes', 't0']) assert result.exit_code == 1 # Reset habit that exists on record runner.invoke(habiter, ['add', 't0']) with SQLiteDataFileOperations() as fo: # Reset habit that exists on record fo.cur.execute('UPDATE habit SET curr_tally = 5, ' 'total_tally = 30, num_of_trials = 4, ' 'wait_period = 1, is_active = True, ' 'prev_tally = 1 ' 'WHERE habit_name="t0"') runner.invoke(habiter, ['reset', '--yes', 't0']) with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT * FROM habit WHERE habit_name="t0"') row = fo.cur.fetchone() assert row['curr_tally'] == 0 assert row['total_tally'] == 0 assert row['num_of_trials'] == 0 assert row['wait_period'] == 0 assert row['is_active'] == 0 assert row['prev_tally'] is None
def test_add(setup): runner = CliRunner() # Normal behavior result = runner.invoke(habiter, ['add', 't0', 't1']) assert result.exit_code == 0 with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT habit_name FROM habit') rows = fo.cur.fetchall() assert rows[0]['habit_name'] in ['t0', 't1'] assert rows[1]['habit_name'] in ['t0', 't1'] # Add habit that already exists on record runner.invoke(habiter, ['add', 't2']) result = runner.invoke(habiter, ['add', 't2']) assert result.exit_code == 1 with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT habit_name FROM habit ' 'WHERE habit_name = "t2"') rows = fo.cur.fetchall() assert len(rows) < 2 # Abnormal behavior (habit name is duplicated) result = runner.invoke(habiter, ['add', 't3', 't3']) assert result.exit_code == 0 with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT habit_name FROM habit ' 'WHERE habit_name = "t3"') rows = fo.cur.fetchall() assert len(rows) == 1
def add(habits): existing_habit_detected = False # Cast to set to remove possible duplicates habits = set(habits) with SQLiteDataFileOperations() as fo: # TODO: Instead of invoking a query on each habit_name, try to use one query for all habits for habit_name in habits: fo.cur.execute( 'SELECT curr_tally, prev_tally, is_active, ' 'last_updated, num_of_trials FROM habit WHERE ' 'habit_name=?', (habit_name, )) row = fo.cur.fetchone() if row is not None: existing_habit_detected = True echo_failure(f"Habit \"{habit_name}\" already exists.") continue curr_time = datetime.now().strftime(HAB_DATE_FORMAT) fo.cur.execute( 'INSERT INTO habit (habit_name, curr_tally, ' 'total_tally, num_of_trials, wait_period, is_active, ' 'last_updated, date_added) ' 'VALUES(?, 0, 0, 0, 0, False, ?, ?)', (habit_name, curr_time, curr_time)) echo_success(f"Habit \"{habit_name}\" has been added.") if existing_habit_detected: sys.exit(1)
def list(habits, verbose): nonExistingHabitDetected = False data = [] habits = set(habits) # Cast to set to remove possible duplicates with SQLiteDataFileOperations() as fo: if len(habits) != 0: for habit_name in habits: fo.cur.execute('SELECT * FROM habit WHERE ' 'habit_name=?', (habit_name,)) row = fo.cur.fetchone() if row is None: nonExistingHabitDetected = True echo_failure(f"Habit \"{habit_name}\" does not exist.") continue data.append(row) else: data = fo.cur.execute('SELECT * FROM habit').fetchall() meta_data = fo.cur.execute('SELECT last_logged FROM meta_info') \ .fetchone() if not verbose: print("Habit\n-------------------") for habit in data: print(habit["habit_name"]) print("-------------------") else: curr_day = datetime.strptime(meta_data["last_logged"], HAB_DATE_FORMAT).date() print("Habit + Attributes\t\t\tValue") print("-------------------\t\t\t-----") for habit in data: print_verbose(habit, curr_day) print("-------------------\t\t\t-----") echo_info("Note: More data captured = increased statistical accuracy!\n") if nonExistingHabitDetected: sys.exit(1)
def reset(habits): non_existing_habit_detected = False # Cast to set to remove possible duplicates habits = set(habits) with SQLiteDataFileOperations() as fo: for habit_name in habits: fo.cur.execute('SELECT habit_id FROM habit WHERE habit_name=?', (habit_name, )) row = fo.cur.fetchone() if row is None: echo_failure(f"No habit with the name \"{habit_name}\".") non_existing_habit_detected = True else: fo.cur.execute( 'UPDATE habit SET curr_tally = 0, ' 'total_tally = 0, num_of_trials = 0, ' 'wait_period = 0, is_active = False, ' 'last_updated = ?, prev_tally = NULL ' 'WHERE habit_id=?', (datetime.now().strftime(HAB_DATE_FORMAT), row['habit_id'])) echo_success(f"Habit \"{habit_name}\" has been reset.") if non_existing_habit_detected: sys.exit(1)
def main(): dir_path = user_data_dir(HAB_NAME, HAB_AUTHOR) data_file_creator = SQLiteDataFileCreator(dir_path, HAB_F_RECORD_NAME) data_file_creator.create() data_file_path = data_file_creator.get_data_f_path() # Set the data file path that all classes using this can have access to SQLiteDataFileOperations().set_f_path(data_file_path) data_file_updater = SQLiteDataFileUpdater(data_file_path) data_file_updater.update() cli.habiter()
def test_tally(setup): runner = CliRunner() # Tally habit that does not exist on record result = runner.invoke(habiter, ['tally', 't0']) assert result.exit_code == 1 # Tally habit that does exist on record runner.invoke(habiter, ['add', 't0']) runner.invoke(habiter, ['tally', 't0']) with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT curr_tally FROM habit WHERE habit_name="t0"') row = fo.cur.fetchone() assert row['curr_tally'] == 1 # Tally existing habit using '-n/--num' option runner.invoke(habiter, ['add', 't1']) runner.invoke(habiter, ['tally', '-n', 2, 't1']) runner.invoke(habiter, ['tally', '--num', 2, 't1']) with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT curr_tally FROM habit WHERE habit_name="t1"') row = fo.cur.fetchone() assert row['curr_tally'] == 4 # Mark habit as active using '-z/--zero' option runner.invoke(habiter, ['add', 't2']) runner.invoke(habiter, ['tally', '-z', 't2']) runner.invoke(habiter, ['tally', '--zero', 't2']) with SQLiteDataFileOperations() as fo: # When a habit is added, it will by default be considered inactive # until the end user makes a tally, so we are testing to see if this # option marks it as active fo.cur.execute('SELECT is_active FROM habit WHERE habit_name="t2"') row = fo.cur.fetchone() assert row['is_active'] # Use the '-z/--zero' option' but the habit has already been active result = runner.invoke(habiter, ['tally', '-z', 't1']) assert result.exit_code == 1 # evaluates to a true expression
def test_remove(setup): runner = CliRunner() # Remove habit from an empty record result = runner.invoke(habiter, ['remove', '--yes', 't0']) assert result.exit_code == 1 # Remove habit that exists on record runner.invoke(habiter, ['add', 't0']) runner.invoke(habiter, ['remove', '--yes', 't0']) with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT habit_name FROM habit WHERE habit_name="t0"') rows = fo.cur.fetchall() assert len(rows) == 0
def export(self) -> None: with SQLiteDataFileOperations(HAB_TRACE_FPATH) as fo: # Retrieve all rows from database fo.cur.execute('SELECT * FROM habit') to_dir_path = os.path.join(self.export_dir, f'{self.export_name}.csv') with open(to_dir_path, 'w') as csvfile: csv_writer = csv.writer(csvfile) # Retrieve columns # 'i[0]' because description returns a 7-tuple for each column csv_writer.writerow(i[0] for i in fo.cur.description) # Retrieve rows csv_writer.writerows(fo.cur)
def update(self) -> None: with SQLiteDataFileOperations() as fo: fo.cur.execute('SELECT * FROM meta_info') # There should be a single row from the meta_info table row = fo.cur.fetchone() logged_time = datetime.strptime(row['last_logged'], HAB_DATE_FORMAT).date() curr_time = datetime.now().date() # Check if habit data has already been updated if logged_time >= curr_time: return echo_info('Last accessed: {}\n'.format(row['last_logged'])) # Update row fo.cur.execute( 'UPDATE meta_info SET version=?, ' 'last_logged=? WHERE meta_id=?', (__version__, datetime.now().strftime(HAB_DATE_FORMAT), row['meta_id'])) # Retrieve all habit data with active column set to 'True' fo.cur.execute('SELECT curr_tally, prev_tally, is_active, ' 'last_updated, num_of_trials, habit_name, habit_id ' 'FROM habit WHERE is_active=True') data = fo.cur.fetchall() for habit in data: habit_date = datetime \ .strptime(habit["last_updated"], HAB_DATE_FORMAT) \ .date() # TODO: implementation really inefficient but sqlite3 is challenging to use if habit_date < curr_time: prev_tally = 0 num_of_trials = habit['num_of_trials'] + 1 is_active = False curr_tally = 0 fo.cur.execute( 'UPDATE habit SET ' 'prev_tally = ?, num_of_trials = ?, ' 'is_active = ?, curr_tally = ? ' 'WHERE habit_id = ?', (prev_tally, num_of_trials, is_active, curr_tally, habit['habit_id']))
def remove(habits): non_existing_habit_detected = False # Cast to set to remove possible duplicates habits = set(habits) with SQLiteDataFileOperations() as fo: for habit_name in habits: fo.cur.execute('SELECT habit_id FROM habit WHERE habit_name=?', (habit_name, )) row = fo.cur.fetchone() if row is None: echo_failure(f"No habit with the name \"{habit_name}\".") non_existing_habit_detected = True else: fo.cur.execute('DELETE FROM habit WHERE habit_id=?', (row['habit_id'], )) echo_success(f"Habit \"{habit_name}\" has been deleted.") if non_existing_habit_detected: sys.exit(1)
def setup(get_init_data_f_path): SQLiteDataFileOperations.set_f_path(get_init_data_f_path) return SQLiteDataFileOperations.get_f_path()