#include #include #include "sqlite3.h" #include "x_string.h" #include "x_curses.h" #include "dodo.h" // FOR DEBUG int callback(void *NotUsed, int argc, char **argv, char **azColName){ int i; for(i=0; iselected_columns, task->table, task->status); rc = sqlite3_prepare_v2(db, sql_query, -1, out_stmt, NULL); if (checksqlerr(rc, "prepare broken in gen_sql_select_stmt")){ return -1; } return 0; } int checksqlerr(int rc, char *errmsg){ if( rc!=SQLITE_OK ){ fprintf(stderr, "rc = %d\n", rc); fprintf(stderr, "SQL error: %s\n", errmsg); //sqlite3_free(errmsg); return -1; } return 0; } int opendb(sqlite3 **db, char* filename){ int rc = 0; rc = sqlite3_open(filename, db); if ( rc != 0 ){ fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(*db)); sqlite3_close(*db); return(rc); } return(rc); } void display_task_list_heading(char* heading){ int width = (FIXED_ID_WIDTH + FIXED_TITLE_WIDTH + FIXED_DATE_WIDTH + x_strlen(heading) ); int width_div_2 = width / 2; printf("%s%*s", X_BOLD, width_div_2, heading); printf("%*s", width_div_2 - x_strlen(heading), ""); printf(" "); } void display_column_heading(const char* str){ int width = 0; if ( x_strcmp(str, "id") == 0 ){ width = FIXED_ID_WIDTH; } else if ( x_strcmp(str, "title") == 0 ){ width = FIXED_TITLE_WIDTH; } else if ( x_strcmp(str, "due_date") == 0 ){ width = FIXED_DATE_WIDTH; } else{ width = FIXED_MAX_WIDTH; } printf("%s%s%-*.*s%s", X_BOLD, X_UNDL, width, width, str, X_RST); } void display_column_headings_for_all_task_lists(){ int i = 0; char* str; for ( i = 0; i < NUM_TASK_LISTS; i++){ str = "id"; display_column_heading(str); str = "title"; display_column_heading(str); str = "due_date"; display_column_heading(str); printf(" "); } printf("\n"); } // Print Heading void display_heading(){ char * str; printf("\n"); str = "Today"; display_task_list_heading(str); str = "Backlog"; display_task_list_heading(str); str = "Blocked"; display_task_list_heading(str); printf("\n"); display_column_headings_for_all_task_lists(); } // pass in the args and return the title and due date // due date passed as NULL if for delete // TODO input validation for strings implement in strings! // TODO maybe a Abstract Sytnax Tree for parsing int parse_args(int argc, char** argv, filtered_tasks* task){ if ( argc > 2 ){ x_strcpy(task->title, argv[2], ARG_MAX); } if ( argc > 3 ){ if ( x_strcmp(argv[1], "update") ){ x_strcpy(task->due_date, argv[3], ARG_MAX); return 0; } else{ x_strcpy(task->new_title, argv[3], ARG_MAX); } } if ( argc > 4 ){ x_strcpy(task->due_date, argv[4], ARG_MAX); } if ( argc > 5 ){ x_strcpy(task->project_tag, argv[5], ARG_MAX); } return 0; } // Get number of tasks from tasks table give status int get_num_rows(sqlite3 *db, char* table, char* status){ filtered_tasks* task = malloc(sizeof(filtered_tasks)); x_strcpy(task->table, table, ARG_MAX); x_strcpy(task->status, status, ARG_MAX); x_strcpy(task->selected_columns, "COUNT(*)", ARG_MAX); sqlite3_stmt *out_stmt; int rc = 0; if ( gen_sql_select_stmt(db, &out_stmt, task) == 1 ){ return -1; } free(task); while ( rc = sqlite3_step(out_stmt) == SQLITE_ROW ){ return sqlite3_column_int(out_stmt, 0); } return rc; } // Print with FIXED_WIDTH int print_fixed_width(const unsigned char* str, int width){ if (str){ printf("%-*.*s", width, width - FIXED_WHITESPACE, str); }else{ printf("%-*.*s", width, width, ""); } } int display_task_list(int start_col, sqlite3 *db, filtered_tasks* task){ static int max_rows = -1; int rc = 0; int i = 0; int num_rows = -1; int num_cols = 0; int fixed_width = 0; char* errmsg; const unsigned char* col_val; const unsigned char* col_name; sqlite3_stmt* out_stmt; // Get the number of rows in current task list num_rows = get_num_rows(db, task->table, task->status); // Then keep track of the furthest down we go if (num_rows > max_rows){ max_rows = num_rows; } // Generate the sql statement by giving the columns, table, and current task list wanted if ( gen_sql_select_stmt(db, &out_stmt, task) ){ return -1; } // TODO: prob should be a func begin // Start col for the current task list X_goright(start_col); // while there is still rows available while ( rc = sqlite3_step(out_stmt) == SQLITE_ROW ){ // for each column print the column num_cols = sqlite3_column_count(out_stmt); for (i = 0; i < num_cols; i++){ col_val = sqlite3_column_text(out_stmt, i); col_name = sqlite3_column_name(out_stmt, i); if ( x_strcmp(col_name, "title") == 0 ){ fixed_width = FIXED_TITLE_WIDTH; } else if ( x_strcmp(col_name, "active_id") == 0 ){ fixed_width = FIXED_ID_WIDTH; } else if ( x_strcmp(col_name, "due_date") == 0 ){ fixed_width = FIXED_DATE_WIDTH; } print_fixed_width(col_val, fixed_width); } // move down one and over to the start of the current task column printf("\n"); // end X_goright(start_col); } // if while loop broke and rc returned an error if ( rc == SQLITE_ERROR ){ X_godown(max_rows); checksqlerr(rc, "step broken in display_task_list"); return -1; } // Once the task list is completely printed // reset to the top of the task lists if (num_rows > 0){ X_goup(num_rows); } // if it is the last task list move down one past the longest list if ( x_strcmp(task->status, "blocked") == 0){ X_godown(max_rows); printf("\n"); } return 0; } // Print kanban table // All lists with task name and due date int view_tasks(sqlite3 *db){ // Set the table and cols to be printed filtered_tasks* tasks = malloc(sizeof(filtered_tasks)); // TODO check that malloc is ok x_strcpy(tasks->table, "tasks", ARG_MAX); x_strcpy(tasks->selected_columns, "active_id, title, due_date", ARG_MAX); // Print Heading display_heading(); // Print "today" tasks x_strcpy(tasks->status, "today", ARG_MAX); if ( display_task_list(TODAY_COL_START, db, tasks) ){ free(tasks); return -1; } // Print "backlog" tasks x_strcpy(tasks->status, "backlog", ARG_MAX); if ( display_task_list(BACKLOG_COL_START, db, tasks) ){ free(tasks); return -1; } // Print "blocked" tasks x_strcpy(tasks->status, "blocked", ARG_MAX); if ( display_task_list(BLOCKED_COL_START, db, tasks) ){ free(tasks); return -1; } free(tasks); return 0; } // TODO: the way this ensures that we are only passing in // valid inputs is stupid and ugly FIX int add_new_task(sqlite3 *db, int argc, char** argv){ int rc = 0; char values[ARG_MAX]; sqlite3_stmt* out_stmt; filtered_tasks* task = malloc(sizeof(filtered_tasks)); x_strcpy(task->table, "tasks", ARG_MAX); task->title[0] = '\0'; task->due_date[0] = '\0'; parse_args(argc, argv, task); if ( task->title[0] != '\0' ){ if ( task->due_date[0] != '\0' ){ x_strcpy(task->selected_columns, "(title, due_date)", ARG_MAX); snprintf(values, 100, "('%s', '%s')", task->title, task->due_date); }else{ x_strcpy(task->selected_columns, "(title)", ARG_MAX); snprintf(values, 100, "('%s')", task->title); } } if ( gen_sql_insert_stmt(db, &out_stmt, task->table, task->selected_columns, values) ){ return -1; } if ( ( rc = sqlite3_step(out_stmt) ) == SQLITE_DONE){ return 0; } checksqlerr(rc, "broken in add_new_task"); return -1; } int update_task_status(sqlite3 *db, int argc, char** argv){ int rc = 0; sqlite3_stmt* out_stmt; filtered_tasks* task = malloc(sizeof(filtered_tasks)); x_strcpy(task->table, "tasks", ARG_MAX); // TODO: this is not just title but also active ID so fix this parse_args(argc, argv, task); if ( gen_sql_update_stmt(db, &out_stmt, task->table, argv[1], task->title) ){ free(task); return -1; } if ( ( rc = sqlite3_step(out_stmt) ) == SQLITE_DONE){ free(task); return 0; } checksqlerr(rc, "broken in update_task_status"); free(task); return -1; } int complete_task(sqlite3 *db, int argc, char** argv){ int rc = 0; sqlite3_stmt* out_stmt; filtered_tasks* task = malloc(sizeof(filtered_tasks)); x_strcpy(task->table, "tasks", ARG_MAX); parse_args(argc, argv, task); if ( gen_sql_update_stmt(db, &out_stmt, task->table, NULL, task->title) ){ free(task); return -1; } if ( ( rc = sqlite3_step(out_stmt) ) == SQLITE_DONE){ free(task); return 0; } checksqlerr(rc, "broken in complete_task"); free(task); return -1; } // TODO this can definatly be made into one func with all the other updates int update_task(sqlite3 *db, int argc, char** argv){ int rc = 0; sqlite3_stmt* out_stmt; filtered_tasks* task = malloc(sizeof(filtered_tasks)); task->new_title[0] = '\0'; task->due_date[0] = '\0'; x_strcpy(task->table, "tasks", ARG_MAX); parse_args(argc, argv, task); if ( gen_sql_update_stmt_v2(db, &out_stmt, task->table, task->new_title, task->due_date, task->project_tag, task->title) ){ free(task); return -1; } if ( ( rc = sqlite3_step(out_stmt) ) == SQLITE_DONE){ free(task); return 0; } checksqlerr(rc, "broken in complete_task"); free(task); return -1; } int del_task(sqlite3 *db, int argc, char** argv){ int rc = 0; sqlite3_stmt* out_stmt; filtered_tasks* task = malloc(sizeof(filtered_tasks)); x_strcpy(task->table, "tasks", ARG_MAX); parse_args(argc, argv, task); if ( gen_sql_delete_stmt(db, &out_stmt, task->table, task->title) ){ free(task); return -1; } if ( ( rc = sqlite3_step(out_stmt) ) == SQLITE_DONE){ free(task); return 0; } checksqlerr(rc, "broken in del_task"); free(task); return -1; }