More work on projects and cascading deletes

This commit is contained in:
Mike Dvorkin
2010-08-07 19:24:35 -07:00
parent 24c793fce5
commit 452d3afc9a
6 changed files with 285 additions and 83 deletions

View File

@@ -3,6 +3,11 @@
#include <stdio.h>
#include "pit.h"
void pit_note_delete(ulong id, PTask pt)
{
}
void pit_note(char *argv[])
{
puts("pit: note is not implemented yet");

View File

@@ -53,6 +53,9 @@ void pit_db_load();
void pit_db_save();
void pit_db_initialize();
void pit_action(ulong id, char *subject, char *message);
void pit_task_delete(ulong id, PProject pp);
void pit_note_delete(ulong id, PTask pt);
/* Argument parsing helpers */
int pit_arg_is_option(char **arg);
@@ -65,6 +68,7 @@ time_t pit_arg_time(char **arg, char *required);
/* Misc utilities */
void die(char *msg, ...);
void perish(char *prefix);
char *str2str(char *str);
char *mem2str(char *mem, int len);
char *current_user();
char *home_dir(char *username, int len);

View File

@@ -3,20 +3,36 @@
#include <stdio.h>
#include "pit.h"
static int already_exist(char *name)
{
PProject pp;
static void project_list(char *name, char *status);
static void project_show(ulong id);
static void project_create(char *name, char *status);
static void project_update(ulong id, char *name, char *status);
static void project_delete(ulong id);
static bool project_already_exist(char *name);
static void project_find_current(PProject *ppp, ulong *pid);
static void project_log_create(PProject pp, char *name, char *status);
static void project_log_update(PProject pp, char *name, char *status);
static void project_log_delete(ulong id, char *name, ulong number_of_tasks);
static void project_parse_options(char **arg, char **name, char **status);
pit_db_load();
for_each_project(pp) {
if (!strcmp(pp->name, name)) {
return 1;
}
}
return 0;
}
/*
** CREATING PROJECTS:
** pit project -c name [-s status]
**
** EDITING PROJECTS:
** pit project -e [number] [-n name] [-s status]
**
** DELETING PROJECTS:
** pit project -d [number]
**
** VIEWING PROJECT:
** pit project [[-q] number]
**
** LISTING PROJECTS:
** pit project -q [number | [-n name] [-s status]]
*/
static void list_projects()
static void project_list(char *name, char *status)
{
PProject pp;
PPager ppager;
@@ -31,11 +47,11 @@ static void list_projects()
}
}
static void create_project(char *name, char *status)
static void project_create(char *name, char *status)
{
pit_db_load();
if (already_exist(name)) {
if (project_already_exist(name)) {
die("project with the same name already exists");
} else {
Project p, *pp;
@@ -55,12 +71,12 @@ static void create_project(char *name, char *status)
}
}
static int show_project(ulong number)
static void project_show(ulong id)
{
PProject pp;
pit_db_load();
pp = (PProject)pit_table_find(projects, number);
pp = (PProject)pit_table_find(projects, id);
if (pp) {
printf("%lu: %s (%s, %lu task%s)\n",
pp->id, pp->name, pp->status, pp->number_of_tasks, (pp->number_of_tasks != 1 ? "s" : ""));
@@ -77,56 +93,192 @@ static int show_project(ulong number)
} else {
die("could not find the project");
}
return 1;
}
static int delete_project(unsigned long number)
static void project_update(ulong id, char *name, char *status)
{
PProject pp;
printf("deleting project %lu\n", number);
pit_db_load();
pp = (PProject)pit_table_delete(projects, number);
project_find_current(&pp, &id);
if (name) strncpy(pp->name, name, sizeof(pp->name) - 1);
if (status) strncpy(pp->status, status, sizeof(pp->status) - 1);
pit_table_mark(projects, pp->id);
project_log_update(pp, name, status);
pit_db_save();
}
static void project_delete(ulong id)
{
PProject pp;
PTask pt;
char *deleted_name;
ulong deleted_number_of_tasks;
pit_db_load();
project_find_current(&pp, &id);
/*
** Delete project tasks.
*/
if (pp->number_of_tasks > 0) {
for_each_task(pt) {
if (pt->project_id == id) {
pit_task_delete(pt->id, pp);
--pt; /* Make the task pointer stay since it now points to the next task. */
}
}
}
/*
** Ready to delete the project itself. But first preserve the
** name and number of tasks since we need these bits for logging.
*/
deleted_name = str2str(pp->name);
deleted_number_of_tasks = pp->number_of_tasks;
pp = (PProject)pit_table_delete(projects, id);
if (pp) {
pit_table_mark(projects, 0);
pit_table_mark(projects, 0); /* TODO: find better current project candidate. */
project_log_delete(id, deleted_name, deleted_number_of_tasks);
pit_db_save();
} else {
die("could not delete the project");
}
return 1;
}
static bool project_already_exist(char *name)
{
PProject pp;
pit_db_load();
for_each_project(pp) {
if (!strcmp(pp->name, name)) {
return TRUE;
}
}
return FALSE;
}
static void project_find_current(PProject *ppp, ulong *pid)
{
if (*pid) {
*ppp = (PProject)pit_table_find(projects, *pid);
if (!*ppp) die("could not find project %lu", *pid);
} else {
*ppp = (PProject)pit_table_current(projects);
if (!*ppp) die("could not find current project");
else *pid = (*(PProject *)ppp)->id;
}
}
static void project_log_create(PProject pp, char *name, char *status)
{
char str[256];
sprintf(str, "created project %lu: %s (status: %s)", pp->id, name, status);
puts(str);
pit_action(pp->id, "project", str);
}
static void project_log_update(PProject pp, char *name, char *status)
{
char str[256];
bool empty = TRUE;
sprintf(str, "updated project %lu:", pp->id);
if (name) {
sprintf(str + strlen(str), " (name: %s", name);
empty = FALSE;
} else {
sprintf(str + strlen(str), " %s (", pp->name);
}
if (status) {
sprintf(str + strlen(str), "%sstatus: %s)", (empty ? "" : ", "), status);
}
puts(str);
pit_action(pp->id, "project", str);
}
static void project_log_delete(ulong id, char *name, ulong number_of_tasks)
{
char str[256];
sprintf(str, "deleted project %lu: %s", id, name);
if (number_of_tasks > 0) {
sprintf(str + strlen(str), " with %lu task%s", number_of_tasks, (number_of_tasks == 1 ? "" : "s"));
}
puts(str);
pit_action(id, "project", str);
}
static void project_parse_options(char **arg, char **name, char **status)
{
while(*++arg) {
switch(pit_arg_option(arg)) {
case 's':
*status = pit_arg_string(++arg, "project status");
break;
case 'n':
if (name) {
*name = pit_arg_string(++arg, "project name");
break;
} /* else fall though */
default:
die("invalid project option: %s", *arg);
}
}
}
void pit_project(char *argv[])
{
char **arg = &argv[1];
unsigned long number;
char *name = NULL, *status = NULL;
ulong number = 0L;
if (!*arg) {
list_projects();
} else if (!strcmp(*arg, "-c")) {
if (!*++arg) {
die("missing project name");
project_list(name, status); /* Show all projects. */
} else { /* pit project [number] */
number = pit_arg_number(arg, NULL);
if (number) {
project_show(number);
} else {
create_project(*arg, *(arg + 1));
}
} else if (!strcmp(*arg, "-d")) {
if (!*++arg) {
die("missing project number");
} else {
number = atoi(*arg);
if (!number) {
die("invalid project number");
} else {
delete_project(number);
switch(pit_arg_option(arg)) {
case 'c': /* pit project -c name [-s status] */
name = pit_arg_string(++arg, "project name");
project_parse_options(arg, NULL, &status);
project_create(name, status);
break;
case 'e': /* pit project -e [number] [-n name] [-s status] */
number = pit_arg_number(++arg, NULL);
if (!number) --arg;
project_parse_options(arg, &name, &status);
if (!name && !status) {
die("nothing to update");
} else {
project_update(number, name, status);
}
break;
case 'd': /* pit project -d [number] */
number = pit_arg_number(++arg, NULL);
project_delete(number);
break;
case 'q': /* pit project -q [number | [-n name] [-s status]] */
number = pit_arg_number(++arg, NULL);
if (number) {
project_show(number);
} else {
project_parse_options(--arg, &name, &status);
if (!name && !status) {
project_show(0); /* Show current project if any. */
} else {
project_list(name, status);
}
}
break;
default:
die("invalid project option: %s", *arg);
}
}
/* } else if (!strcmp(*arg, "-e")) { TODO: Edit */
} else {
number = atoi(*arg);
if (!number) {
die("invalid project parameters");
} else {
show_project(number);
}
}
}

View File

@@ -107,7 +107,7 @@ uchar *pit_table_find(PTable pt, ulong id) {
** if the record was not found.
*/
uchar *pit_table_delete(PTable pt, ulong id) {
// TODO: retrn NULL (or raise?) if table doesn't have id.
// TODO: return NULL (or die?) if table doesn't have id.
register uchar *pr = (uchar *)pit_table_find(pt, id);
if (pr) {

View File

@@ -4,13 +4,13 @@
#include "pit.h"
static void task_list(char *name, char *status, char *priority, time_t date, time_t time);
static void task_show(ulong number);
static void task_show(ulong id);
static void task_create(char *name, char *status, char *priority, time_t date, time_t time);
static void task_update(ulong number, char *name, char *status, char *priority, time_t date, time_t time);
static void task_delete(ulong number);
static void task_update(ulong id, char *name, char *status, char *priority, time_t date, time_t time);
static void task_find_current(PTask *ppt, ulong *pid);
static void task_log_create(PTask pt, char *name, char *status, char *priority, time_t date, time_t time);
static void task_log_update(PTask pt, char *name, char *status, char *priority, time_t date, time_t time);
static void task_log_delete(PTask pt);
static void task_log_delete(ulong id, char *name, ulong number_of_notes);
static void task_parse_options(char **arg, char **name, char **status, char **priority, time_t *date, time_t *time);
/*
@@ -27,7 +27,7 @@ static void task_parse_options(char **arg, char **name, char **status, char **pr
** pit task [[-q] number]
**
** LISTING TASKS:
** pit task -q [-s status] [-p priority] [-d date] [-t time]
** pit task -q [number | [-n name] [-s status] [-p priority] [-d date] [-t time]]
*/
static void task_list(char *name, char *status, char *priority, time_t date, time_t time)
@@ -50,9 +50,9 @@ static void task_list(char *name, char *status, char *priority, time_t date, tim
}
}
static void task_show(ulong number)
static void task_show(ulong id)
{
printf("task_show(%lu)\n", number);
printf("task_show(%lu)\n", id);
}
static void task_create(char *name, char *status, char *priority, time_t date, time_t time)
@@ -87,18 +87,13 @@ static void task_create(char *name, char *status, char *priority, time_t date, t
}
}
static void task_update(ulong number, char *name, char *status, char *priority, time_t date, time_t time)
static void task_update(ulong id, char *name, char *status, char *priority, time_t date, time_t time)
{
PTask pt;
pit_db_load();
if (number) {
pt = (PTask)pit_table_find(tasks, number);
if (!pt) die("could not find task %lu", number);
} else {
pt = (PTask)pit_table_current(tasks);
if (!pt) die("could not find current task");
}
task_find_current(&pt, &id);
if (name) strncpy(pt->name, name, sizeof(pt->name) - 1);
if (status) strncpy(pt->status, status, sizeof(pt->status) - 1);
if (priority) strncpy(pt->priority, priority, sizeof(pt->priority) - 1);
@@ -110,26 +105,68 @@ static void task_update(ulong number, char *name, char *status, char *priority,
pit_db_save();
}
static void task_delete(ulong number)
/*
** A task could be deleted as standalone entity or as part of cascading project
** delete. In later case we're going to have 'pp' set and the database loaded.
*/
void pit_task_delete(ulong id, PProject pp)
{
PTask pt;
PNote pn;
bool standalone = (pp == NULL);
pit_db_load();
pt = (PTask)pit_table_delete(tasks, number);
if (pt) {
pit_table_mark(tasks, 0);
if (standalone) {
pit_db_load();
pp = (PProject)pit_table_find(projects, pt->project_id);
}
task_find_current(&pt, &id);
if (pp) {
/*
** First delete task notes if any.
*/
if (pt->number_of_notes > 0) {
PNote pn;
for_each_note(pn) {
if (pn->task_id == pt->id) {
pit_table_delete(notes, pn->id);
if (pn->task_id == id) {
pit_note_delete(pn->id, pt);
--pn; /* Make the note pointer stay since it now points to the next note. */
}
}
}
task_log_delete(pt);
pit_db_save();
/*
** Preserve task name and number_of_notes before deleting the task since
** we need these for logging.
*/
char *deleted_name = str2str(pt->name);
ulong deleted_number_of_notes = pt->number_of_notes;
pt = (PTask)pit_table_delete(tasks, id);
if (pt) {
pit_table_mark(tasks, 0); /* TODO: find better current task candidate. */
task_log_delete(id, deleted_name, deleted_number_of_notes);
if (standalone) {
pp->number_of_tasks--;
pit_db_save();
}
free(deleted_name);
} else {
free(deleted_name);
die("could not delete task %lu", id);
}
} else {
die("could not delete task %lu", number);
die("could not find project for task %lu", id);
}
}
static void task_find_current(PTask *ppt, ulong *pid)
{
if (*pid) {
*ppt = (PTask)pit_table_find(tasks, *pid);
if (!*ppt) die("could not find task %lu", *pid);
} else {
*ppt = (PTask)pit_table_current(tasks);
if (!*ppt) die("could not find current task");
else *pid = (*(PTask *)ppt)->id;
}
}
@@ -175,16 +212,16 @@ static void task_log_update(PTask pt, char *name, char *status, char *priority,
pit_action(pt->id, "task", str);
}
static void task_log_delete(PTask pt)
static void task_log_delete(ulong id, char *name, ulong number_of_notes)
{
char str[256];
sprintf(str, "deleted task %lu: %s", pt->id, pt->name);
if (pt->number_of_notes > 0) {
sprintf(str + strlen(str), " with %lu note%s", pt->number_of_notes, (pt->number_of_notes == 1 ? "" : "s"));
sprintf(str, "deleted task %lu: %s", id, name);
if (number_of_notes > 0) {
sprintf(str + strlen(str), " with %lu note%s", number_of_notes, (number_of_notes == 1 ? "" : "s"));
}
puts(str);
pit_action(pt->id, "task", str);
pit_action(id, "task", str);
}
static void task_parse_options(char **arg, char **name, char **status, char **priority, time_t *date, time_t *time)
@@ -223,7 +260,7 @@ void pit_task(char *argv[])
ulong number = 0L;
if (!*arg) {
task_list(NULL, NULL, NULL, 0, 0); /* List all tasks (with default paramaters). */
task_list(name, status, priority, date, time); /* List all tasks (i.e. use default paramaters). */
} else { /* pit task [number] */
number = pit_arg_number(arg, NULL);
if (number) {
@@ -235,7 +272,7 @@ void pit_task(char *argv[])
task_parse_options(arg, NULL, &status, &priority, &date, &time);
task_create(name, status, priority, date, time);
break;
case 'e': /* pit task -e [number] [-n name] [-s status] [-p priority] [-d date] [-t time]*/
case 'e': /* pit task -e [number] [-n name] [-s status] [-p priority] [-d date] [-t time] */
number = pit_arg_number(++arg, NULL);
if (!number) --arg;
task_parse_options(arg, &name, &status, &priority, &date, &time);
@@ -247,7 +284,7 @@ void pit_task(char *argv[])
break;
case 'd': /* pit task -d [number] */
number = pit_arg_number(++arg, NULL);
task_delete(number);
pit_task_delete(number, NULL); /* Delete the task, but keep its project. */
break;
case 'q': /* pit task -q [number | [-n name] [-s status] [-p priority] [-d date] [-t time]] */
number = pit_arg_number(++arg, NULL);
@@ -256,7 +293,7 @@ void pit_task(char *argv[])
} else {
task_parse_options(--arg, &name, &status, &priority, &date, &time);
if (!name && !status && !priority && !date && !time) {
task_show(0);
task_show(0); /* Show current task if any. */
} else {
task_list(name, status, priority, date, time);
}

View File

@@ -6,6 +6,10 @@
#include <sys/types.h>
#include "pit.h"
char *str2str(char *str) {
return strcpy(malloc(strlen(str) + 1), str); /* Cheap strdup() */
}
char *mem2str(char *mem, int len) {
char *str = malloc(len + 1);
memcpy(str, mem, len);