cleanup done, more verbose comments added

This commit is contained in:
Patrick Lipka 2021-12-17 12:05:13 +01:00
parent bcaa73fa79
commit 06b9f25893
8 changed files with 61 additions and 13 deletions

View File

@ -11,23 +11,27 @@ Project::Project(std::string name){
Project::active_task = NULL; Project::active_task = NULL;
} }
// adds task to project
void Project::add_task(Task task){ void Project::add_task(Task task){
tasks.push_back(task); tasks.push_back(task);
set_active_task(num_tasks); set_active_task(num_tasks);
num_tasks++; num_tasks++;
} }
// removes task from project by task id (position in tasks vector)
void Project::remove_task(int id){ void Project::remove_task(int id){
tasks.erase(tasks.begin()+id); tasks.erase(tasks.begin()+id);
set_active_task(std::max(id-1,0)); set_active_task(std::max(id-1,0));
num_tasks--; num_tasks--;
} }
// sets active task pointer and id by task id (position in tasks vector)
void Project::set_active_task(int id){ void Project::set_active_task(int id){
active_task_id = id; active_task_id = id;
active_task = &tasks[id]; active_task = &tasks[id];
} }
// returns pointer to task with name task_name if present in tasks vector
Task* Project::find_task_by_name(std::string task_name){ Task* Project::find_task_by_name(std::string task_name){
for (int i=0; i<num_tasks; i++){ for (int i=0; i<num_tasks; i++){
if(tasks[i].name == task_name){ if(tasks[i].name == task_name){
@ -37,6 +41,7 @@ Task* Project::find_task_by_name(std::string task_name){
return NULL; return NULL;
} }
// returns id of task with name task_name if present in tasks vector
int Project::find_task_id_by_name(std::string task_name){ int Project::find_task_id_by_name(std::string task_name){
for (int i=0; i<num_tasks; i++){ for (int i=0; i<num_tasks; i++){
if(tasks[i].name == task_name){ if(tasks[i].name == task_name){
@ -46,6 +51,7 @@ int Project::find_task_id_by_name(std::string task_name){
return -1; return -1;
} }
// returns sum of task work times in project
int Project::get_total_work_time(){ int Project::get_total_work_time(){
int wtime_proj = 0; int wtime_proj = 0;
for (int i=0; i<num_tasks; i++){ for (int i=0; i<num_tasks; i++){
@ -62,29 +68,31 @@ ProjectList::ProjectList(std::string month){
ProjectList::num_projects = 0; ProjectList::num_projects = 0;
} }
// adds project to project list
void ProjectList::add_project(Project proj){ void ProjectList::add_project(Project proj){
projects.push_back(proj); projects.push_back(proj);
set_active_project(num_projects); set_active_project(num_projects);
num_projects++; num_projects++;
} }
// removes project from proects list by id (position in projects vector)
void ProjectList::remove_project(int id){ void ProjectList::remove_project(int id){
projects.erase(projects.begin()+id); projects.erase(projects.begin()+id);
set_active_project(std::max(id-1,0)); set_active_project(std::max(id-1,0));
num_projects--; num_projects--;
} }
// sets active project pointer and id by id (position in projects vector)
void ProjectList::set_active_project(int id){ void ProjectList::set_active_project(int id){
active_project_id = id; active_project_id = id;
active_project = &projects[id]; active_project = &projects[id];
} }
// serializes project list and writes it to binary file
void ProjectList::save(std::string file_name){ void ProjectList::save(std::string file_name){
std::ofstream of(file_name, std::ios::binary); std::ofstream of(file_name, std::ios::binary);
if (!of){ if (!of){
std::cout << "Could not open file " << file_name << " for writing!" << std::endl; std::cout << "Could not open file " << file_name << " for writing!" << std::endl;
// exit(1);
} }
of.write((char*) &num_projects, sizeof(int)); of.write((char*) &num_projects, sizeof(int));
of.write((char*) &active_project_id, sizeof(int)); of.write((char*) &active_project_id, sizeof(int));
@ -104,6 +112,7 @@ void ProjectList::save(std::string file_name){
of.close(); of.close();
} }
// reads project list from binary file
void ProjectList::load(std::string file_name, bool ignore_worktimes){ void ProjectList::load(std::string file_name, bool ignore_worktimes){
std::ifstream inf(file_name, std::ios::binary); std::ifstream inf(file_name, std::ios::binary);
int active; int active;
@ -111,11 +120,16 @@ void ProjectList::load(std::string file_name, bool ignore_worktimes){
std::cout << "Could not open file " << file_name << " for reading!" << std::endl; std::cout << "Could not open file " << file_name << " for reading!" << std::endl;
} }
inf.read((char*) &num_projects,sizeof(int)); inf.read((char*) &num_projects,sizeof(int));
// save copy of num_projects to control loop and apply later as
// num_projects will get bigger when adding projects to the project list
int number_of_projects = num_projects; int number_of_projects = num_projects;
inf.read((char*) &active, sizeof(int)); inf.read((char*) &active, sizeof(int));
int np = num_projects; // build active task array to set active tasks once
int *active_task_arr = new int[np]; // project list is set up to get the poiters right
for (int i=0; i<np;i++){ int *active_task_arr = new int[number_of_projects];
// read project properties
for (int i=0; i<number_of_projects; i++){
size_t len; size_t len;
std::string proj_name; std::string proj_name;
int active_t; int active_t;
@ -130,6 +144,7 @@ void ProjectList::load(std::string file_name, bool ignore_worktimes){
int number_of_tasks = proj.num_tasks; int number_of_tasks = proj.num_tasks;
inf.read((char*) &active_t, sizeof(int)); inf.read((char*) &active_t, sizeof(int));
int nt = proj.num_tasks; int nt = proj.num_tasks;
// read task properties
for (int j=0; j<nt; j++){ for (int j=0; j<nt; j++){
std::string task_name; std::string task_name;
size_t task_name_len; size_t task_name_len;
@ -151,7 +166,7 @@ void ProjectList::load(std::string file_name, bool ignore_worktimes){
add_project(proj); add_project(proj);
} }
set_active_project(active); set_active_project(active);
for (int i=0; i<np ; i++){ for (int i=0; i<number_of_projects ; i++){
projects[i].set_active_task(active_task_arr[i]); projects[i].set_active_task(active_task_arr[i]);
} }
delete [] active_task_arr; delete [] active_task_arr;
@ -159,6 +174,7 @@ void ProjectList::load(std::string file_name, bool ignore_worktimes){
num_projects = number_of_projects; num_projects = number_of_projects;
} }
// returns pointer to project with name proj_name if present in projects vector
Project* ProjectList::find_project_by_name(std::string proj_name){ Project* ProjectList::find_project_by_name(std::string proj_name){
for (int i=0; i<num_projects; i++){ for (int i=0; i<num_projects; i++){
if(projects[i].name == proj_name){ if(projects[i].name == proj_name){
@ -168,6 +184,7 @@ Project* ProjectList::find_project_by_name(std::string proj_name){
return NULL; return NULL;
} }
// returns id of project with name proj_name is present in projects vector
int ProjectList::find_project_id_by_name(std::string proj_name){ int ProjectList::find_project_id_by_name(std::string proj_name){
for (int i=0; i<num_projects; i++){ for (int i=0; i<num_projects; i++){
if(projects[i].name == proj_name){ if(projects[i].name == proj_name){

View File

@ -1,6 +1,7 @@
#include <string> #include <string>
#include "string_utils.h" #include "string_utils.h"
// replaces white spaces by underscores, used for readline completion
std::string space_to_underscore(std::string str){ std::string space_to_underscore(std::string str){
for(size_t i = 0; i < str.length(); i++){ for(size_t i = 0; i < str.length(); i++){
if(str[i] == ' ') str[i] = '_'; if(str[i] == ' ') str[i] = '_';
@ -8,6 +9,7 @@ std::string space_to_underscore(std::string str){
return str; return str;
} }
// replaces underscores by white space, used to translate readline completion to real names
std::string underscore_to_space(std::string str){ std::string underscore_to_space(std::string str){
for(size_t i = 0; i < str.length(); i++){ for(size_t i = 0; i < str.length(); i++){
if(str[i] == '_') str[i] = ' '; if(str[i] == '_') str[i] = ' ';
@ -15,6 +17,7 @@ std::string underscore_to_space(std::string str){
return str; return str;
} }
// remove trailing and leading white spaces from string
std::string trim(const std::string& str){ std::string trim(const std::string& str){
const char* WhiteSpace = " "; const char* WhiteSpace = " ";
std::size_t start = str.find_first_not_of(WhiteSpace); std::size_t start = str.find_first_not_of(WhiteSpace);
@ -22,6 +25,7 @@ std::string trim(const std::string& str){
return start == end ? std::string() : str.substr(start, end - start + 1); return start == end ? std::string() : str.substr(start, end - start + 1);
} }
// check whether string is a number
bool is_num(const std::string &str){ bool is_num(const std::string &str){
for (const char &c : str){ for (const char &c : str){
if (std::isdigit(c) == 0) return false; if (std::isdigit(c) == 0) return false;

View File

@ -7,6 +7,7 @@ Task::Task(std::string name){
Task::work_time = 0; Task::work_time = 0;
} }
// add working time, also used to remove working time with negative input values
void Task::add_time(int seconds){ void Task::add_time(int seconds){
work_time = std::max(work_time+seconds,0); work_time = std::max(work_time+seconds,0);
} }

View File

@ -6,7 +6,10 @@
int sigint; int sigint;
int tracking; int tracking;
// signal handler to execute when receiving SIGINT (CTRL-C)
void handler(int signum){ void handler(int signum){
// while tracking: end tracking and continue execution
// else: exit program
if (tracking){ if (tracking){
sigint = 1; sigint = 1;
std::cout << std::endl; std::cout << std::endl;
@ -15,10 +18,12 @@ void handler(int signum){
} }
} }
// track time for active task in project
void track(Project *proj){ void track(Project *proj){
int worktime = 0; int worktime = 0;
int work_h, work_m, work_s; int work_h, work_m, work_s;
time_t start; time_t start;
// clear terminal
system("clear"); system("clear");
time(&start); time(&start);
tracking = 1; tracking = 1;

View File

@ -13,16 +13,20 @@
#include "tt.h" #include "tt.h"
#include "util.h" #include "util.h"
// glovbal variables:
std::string user_name, tracking_dir, config_file; std::string user_name, tracking_dir, config_file;
int main(){ int main(){
// connect SIGINT signal (CTRL-C) to signal handler from track.h to use it to stop tracking of projects // connect SIGINT signal (CTRL-C) to signal handler from track.h to use it to stop tracking of projects
signal(SIGINT, handler); signal(SIGINT, handler);
tracking = 0; tracking = 0;
// PREFIX set by preprocessor, see Makefile
std::string prefix=STRING(PREFIX); std::string prefix=STRING(PREFIX);
config_file = prefix+"/etc/tt.conf"; config_file = prefix+"/etc/tt.conf";
parse_config_file(config_file, &user_name, &tracking_dir); parse_config_file(config_file, &user_name, &tracking_dir);
// initialize project list for current month
ProjectList proj_list(get_date()); ProjectList proj_list(get_date());
// if available: load project list for current month, else: start with list from last month // if available: load project list for current month, else: start with list from last month
@ -41,6 +45,7 @@ int main(){
proj_list.load(last_proj_file,true); proj_list.load(last_proj_file,true);
} }
// add command names and names of project list to the autocomplete names
init_autocomplete(&proj_list); init_autocomplete(&proj_list);
// use GNU readline for auto completion and history when parsing command input // use GNU readline for auto completion and history when parsing command input

View File

@ -2,6 +2,8 @@
#define TT_H #define TT_H
#include <string> #include <string>
// global variables:
extern std::string user_name; extern std::string user_name;
extern std::string tracking_dir; extern std::string tracking_dir;
extern std::string config_file; extern std::string config_file;

View File

@ -15,8 +15,7 @@
#include "tt.h" #include "tt.h"
#include "util.h" #include "util.h"
// Uses GNU readline library for autocompletion and command history // names of available commands
std::string command_names[num_commands]={ std::string command_names[num_commands]={
"np", "np",
"nt", "nt",
@ -32,12 +31,15 @@ std::string command_names[num_commands]={
"save" "save"
}; };
std::vector<std::string> autocomplete_names; std::vector<std::string> autocomplete_names; // global variable
// completion function for readline
char **tt_name_completion(const char* text, int start, int end){ char **tt_name_completion(const char* text, int start, int end){
rl_attempted_completion_over = 1; rl_attempted_completion_over = 1;
return rl_completion_matches(text, tt_name_generator); return rl_completion_matches(text, tt_name_generator);
} }
// search for input in auto complete names to attempt completion and give options
char *tt_name_generator(const char *text, int state){ char *tt_name_generator(const char *text, int state){
static std::vector<std::string>::const_iterator it; static std::vector<std::string>::const_iterator it;
if (state == 0) it=begin(autocomplete_names); if (state == 0) it=begin(autocomplete_names);
@ -51,6 +53,7 @@ char *tt_name_generator(const char *text, int state){
return NULL; return NULL;
} }
// add command and project/task names to auto complete list
void init_autocomplete(ProjectList *proj_list){ void init_autocomplete(ProjectList *proj_list){
// add command names to auto complete list // add command names to auto complete list
for (int i=0; i<num_commands; i++){ for (int i=0; i<num_commands; i++){
@ -68,6 +71,7 @@ void init_autocomplete(ProjectList *proj_list){
} }
} }
// parse user input and call appropriate commad_* functions
void parse_input(std::string input, ProjectList *proj_list){ void parse_input(std::string input, ProjectList *proj_list){
input = trim(input); input = trim(input);
size_t command_end = input.find(" "); size_t command_end = input.find(" ");
@ -159,7 +163,6 @@ void parse_input(std::string input, ProjectList *proj_list){
}else{ }else{
std::cout << "remove time: please specify time to remove and task name [optional]." << std::endl; std::cout << "remove time: please specify time to remove and task name [optional]." << std::endl;
} }
} }
} }
}else if (command == "report"){ }else if (command == "report"){
@ -262,6 +265,7 @@ void command_rm(std::string input, ProjectList *proj_list){
} }
bool del = false; bool del = false;
if (place_of_slash == std::string::npos && id >= 0){ if (place_of_slash == std::string::npos && id >= 0){
// do not let the user remove projects by accident
std::cout << "Do you really want to remove project " << underscore_to_space(proj_name) << "? [y|n]" << std::endl; std::cout << "Do you really want to remove project " << underscore_to_space(proj_name) << "? [y|n]" << std::endl;
std::string input; std::string input;
while(1){ while(1){
@ -302,6 +306,7 @@ void command_rm(std::string input, ProjectList *proj_list){
int task_id = proj_list->active_project->find_task_id_by_name(trim(underscore_to_space(task_name))); int task_id = proj_list->active_project->find_task_id_by_name(trim(underscore_to_space(task_name)));
if (task_id >=0){ if (task_id >=0){
del = false; del = false;
// do not let the user remove tasks by accident
std::cout << "Do you really want to remove task " << underscore_to_space(proj->name) << "/" << underscore_to_space(task_name) << "? [y|n]" << std::endl; std::cout << "Do you really want to remove task " << underscore_to_space(proj->name) << "/" << underscore_to_space(task_name) << "? [y|n]" << std::endl;
std::string input; std::string input;
while(1){ while(1){
@ -383,6 +388,7 @@ void command_st(std::string input, ProjectList *proj_list){
int proj_id = proj_list->find_project_id_by_name(trim(underscore_to_space(proj_name))); int proj_id = proj_list->find_project_id_by_name(trim(underscore_to_space(proj_name)));
Project *proj = proj_list->find_project_by_name(trim(underscore_to_space(proj_name))); Project *proj = proj_list->find_project_by_name(trim(underscore_to_space(proj_name)));
if (place_of_slash == std::string::npos){ if (place_of_slash == std::string::npos){
// task in same project
proj = proj_list->active_project; proj = proj_list->active_project;
proj_id = proj_list->find_project_id_by_name(trim(underscore_to_space(proj_list->active_project->name))); proj_id = proj_list->find_project_id_by_name(trim(underscore_to_space(proj_list->active_project->name)));
} }
@ -540,6 +546,7 @@ void command_rt(std::string input, int wtime, ProjectList *proj_list){
} }
} }
// print monthly report
void command_report(std::string date_str, ProjectList* proj_list){ void command_report(std::string date_str, ProjectList* proj_list){
date_str = trim(date_str); date_str = trim(date_str);
size_t place_of_minus = date_str.find("-"); size_t place_of_minus = date_str.find("-");
@ -561,6 +568,7 @@ void command_report(std::string date_str, ProjectList* proj_list){
float wtime_task = proj->tasks[j].work_time / 3600.0; float wtime_task = proj->tasks[j].work_time / 3600.0;
if(wtime_task >= 0.01) std::cout << "--- " << proj->tasks[j].name << ": " << std::fixed << std::setprecision(2) << wtime_task << std::endl; if(wtime_task >= 0.01) std::cout << "--- " << proj->tasks[j].name << ": " << std::fixed << std::setprecision(2) << wtime_task << std::endl;
} }
// format: *.xx hours
std::cout << "Total: " << std::fixed << std::setprecision(2) << wtime_proj << std::endl << std::endl; std::cout << "Total: " << std::fixed << std::setprecision(2) << wtime_proj << std::endl << std::endl;
} }
} }
@ -580,6 +588,7 @@ void command_report(std::string date_str, ProjectList* proj_list){
float wtime_task = proj->tasks[j].work_time / 3600.0; float wtime_task = proj->tasks[j].work_time / 3600.0;
if(wtime_task >= 0.01) std::cout << "--- " << proj->tasks[j].name << ": " << std::fixed << std::setprecision(2) << wtime_task << std::endl; if(wtime_task >= 0.01) std::cout << "--- " << proj->tasks[j].name << ": " << std::fixed << std::setprecision(2) << wtime_task << std::endl;
} }
// format: *.xx hours
std::cout << "Total: " << std::fixed << std::setprecision(2) << wtime_proj << std::endl << std::endl; std::cout << "Total: " << std::fixed << std::setprecision(2) << wtime_proj << std::endl << std::endl;
} }
} }
@ -589,7 +598,8 @@ void command_report(std::string date_str, ProjectList* proj_list){
} }
} }
// save project list to file - POSIX only // save project list to file, uses system("mkdir") to create diretory
// might be replaced by C++ filesystem API int the future
void command_save(ProjectList *proj_list){ void command_save(ProjectList *proj_list){
std::string date_str = get_date(); std::string date_str = get_date();
std::string file_name = tracking_dir+"/"+date_str; std::string file_name = tracking_dir+"/"+date_str;

View File

@ -2,6 +2,7 @@
#include <iostream> #include <iostream>
#include "util.h" #include "util.h"
// returns string in format "yyyy-mm"
std::string get_date(){ std::string get_date(){
time_t t = time(NULL); time_t t = time(NULL);
tm *date = localtime(&t); tm *date = localtime(&t);
@ -17,12 +18,14 @@ std::string get_date(){
return date_str; return date_str;
} }
// returns date string for previous month
std::string get_last_date(){ std::string get_last_date(){
time_t t = time(NULL); time_t t = time(NULL);
tm *date = localtime(&t); tm *date = localtime(&t);
int month = date->tm_mon; int month = date->tm_mon;
int year = date->tm_year+1900; int year = date->tm_year+1900;
// in case of January:
if(month == 0){ if(month == 0){
month = 12; month = 12;
year--; year--;
@ -38,6 +41,7 @@ std::string get_last_date(){
return date_str; return date_str;
} }
// basic parsing of configureation file to get user name and tracking directory
void parse_config_file(std::string file_name, std::string *user_name, std::string *tracking_dir){ void parse_config_file(std::string file_name, std::string *user_name, std::string *tracking_dir){
std::ifstream f(file_name); std::ifstream f(file_name);
if (!f){ if (!f){