mirror of
https://github.com/michaeldv/pit.git
synced 2025-12-10 00:15:35 +00:00
326 lines
11 KiB
C
326 lines
11 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include "pit.h"
|
|
|
|
static int task_find_current(int id, PTask *ppt)
|
|
{
|
|
if (id) {
|
|
*ppt = (PTask)pit_table_find(tasks, id);
|
|
if (!*ppt) die("could not find task %d", id);
|
|
} else {
|
|
*ppt = (PTask)pit_table_current(tasks);
|
|
if (!*ppt) die("could not find current task");
|
|
}
|
|
return *ppt ? (*(PTask *)ppt)->id : 0;
|
|
}
|
|
|
|
static void task_log_create(PTask pt, POptions po)
|
|
{
|
|
Action a = { pt->project_id, pt->id, 0 };
|
|
|
|
sprintf(a.message, "created task %d: %s (status: %s, priority: %s", pt->id, po->task.name, po->task.status, po->task.priority);
|
|
if (po->task.date > 0) sprintf(a.message + strlen(a.message), ", date: %s", format_date(po->task.date));
|
|
if (po->task.time > 0) sprintf(a.message + strlen(a.message), ", time: %s", format_time(po->task.time));
|
|
sprintf(a.message + strlen(a.message), ", project: %d)", pt->project_id);
|
|
pit_action(&a);
|
|
}
|
|
|
|
static void task_log_update(PTask pt, POptions po)
|
|
{
|
|
Action a = { pt->project_id, pt->id, 0 };
|
|
bool empty = TRUE;
|
|
|
|
sprintf(a.message, "updated task %d:", pt->id);
|
|
if (po->task.name) {
|
|
sprintf(a.message + strlen(a.message), " (name: %s", po->task.name);
|
|
empty = FALSE;
|
|
} else {
|
|
sprintf(a.message + strlen(a.message), " %s (", pt->name);
|
|
}
|
|
if (po->task.status) {
|
|
sprintf(a.message + strlen(a.message), "%sstatus: %s", (empty ? "" : ", "), po->task.status);
|
|
empty = FALSE;
|
|
}
|
|
if (po->task.priority) {
|
|
sprintf(a.message + strlen(a.message), "%spriority: %s", (empty ? "" : ", "), po->task.priority);
|
|
empty = FALSE;
|
|
}
|
|
if (po->task.date) {
|
|
if (po->task.date < 0) {
|
|
sprintf(a.message + strlen(a.message), "%sdate: none", (empty ? "" : ", "));
|
|
} else {
|
|
sprintf(a.message + strlen(a.message), "%sdate: %s", (empty ? "" : ", "), format_date(po->task.date));
|
|
}
|
|
empty = FALSE;
|
|
}
|
|
if (po->task.time) {
|
|
if (po->task.time < 0) {
|
|
sprintf(a.message + strlen(a.message), "%stime: none", (empty ? "" : ", "));
|
|
} else {
|
|
sprintf(a.message + strlen(a.message), "%stime: %s", (empty ? "" : ", "), format_time(po->task.time));
|
|
}
|
|
empty = FALSE;
|
|
}
|
|
strcat(a.message, ")");
|
|
pit_action(&a);
|
|
}
|
|
|
|
static void task_log_delete(int project_id, int id, char *name, int number_of_notes)
|
|
{
|
|
Action a = { project_id, id, 0 };
|
|
|
|
sprintf(a.message, "deleted task %d: %s", id, name);
|
|
if (number_of_notes > 0) {
|
|
sprintf(a.message + strlen(a.message), " with %d note%s", number_of_notes, (number_of_notes == 1 ? "" : "s"));
|
|
}
|
|
sprintf(a.message + strlen(a.message), " (project: %d)", project_id);
|
|
pit_action(&a);
|
|
}
|
|
|
|
static void task_show(int id)
|
|
{
|
|
PTask pt;
|
|
|
|
pit_db_load();
|
|
id = task_find_current(id, &pt);
|
|
|
|
if (pt) {
|
|
/* printf("The task was created on %s, last updated on %s\n", format_timestamp(pt->created_at), format_timestamp(pt->updated_at)); */
|
|
printf("* %d: (%s) %s (project: %d, status: %s, priority: %s", pt->id, pt->username, pt->name, pt->project_id, pt->status, pt->priority);
|
|
if (pt->date) printf(", date: %s", format_date(pt->date));
|
|
if (pt->time) printf(", time: %s", format_time(pt->time));
|
|
printf(", %d note%s)\n", pt->number_of_notes, pt->number_of_notes != 1 ? "s" : "");
|
|
pit_table_mark(tasks, pt->id);
|
|
if (pt->number_of_notes > 0)
|
|
pit_note_list(pt);
|
|
pit_db_save();
|
|
} else {
|
|
die("could not find the task");
|
|
}
|
|
}
|
|
|
|
static void task_create(POptions po)
|
|
{
|
|
pit_db_load();
|
|
PProject pp = (PProject)pit_table_current(projects);
|
|
|
|
if (!pp) {
|
|
die("no project selected");
|
|
} else {
|
|
Task t = { 0 }, *pt;
|
|
|
|
t.project_id = pp->id;
|
|
if (!po->task.status) po->task.status = "open";
|
|
if (!po->task.priority) po->task.priority = "normal";
|
|
strncpy(t.name, po->task.name, sizeof(t.name) - 1);
|
|
strncpy(t.status, po->task.status, sizeof(t.status) - 1);
|
|
strncpy(t.priority, po->task.priority, sizeof(t.priority) - 1);
|
|
strncpy(t.username, current_user(), sizeof(t.username) - 1);
|
|
t.date = max(0, po->task.date);
|
|
t.time = max(0, po->task.time);
|
|
|
|
pt = (PTask)pit_table_insert(tasks, (char *)&t);
|
|
pit_table_mark(tasks, pt->id);
|
|
pp->number_of_tasks++;
|
|
task_log_create(pt, po);
|
|
pit_db_save();
|
|
}
|
|
}
|
|
|
|
static void task_update(int id, POptions po)
|
|
{
|
|
PTask pt;
|
|
|
|
pit_db_load();
|
|
id = task_find_current(id, &pt);
|
|
|
|
if (po->task.name) strncpy(pt->name, po->task.name, sizeof(pt->name) - 1);
|
|
if (po->task.status) strncpy(pt->status, po->task.status, sizeof(pt->status) - 1);
|
|
if (po->task.priority) strncpy(pt->priority, po->task.priority, sizeof(pt->priority) - 1);
|
|
if (po->task.date) pt->date = max(0, po->task.date);
|
|
if (po->task.time) pt->time = max(0, po->task.time);
|
|
strncpy(pt->username, current_user(), sizeof(pt->username) - 1);
|
|
pit_table_mark(tasks, pt->id);
|
|
|
|
task_log_update(pt, po);
|
|
pit_db_save();
|
|
}
|
|
|
|
static void task_parse_options(char **arg, POptions po)
|
|
{
|
|
while(*++arg) {
|
|
switch(pit_arg_option(arg)) {
|
|
case 'n':
|
|
po->task.name = pit_arg_string(++arg, "task name");
|
|
break;
|
|
case 's':
|
|
po->task.status = pit_arg_string(++arg, "task status");
|
|
break;
|
|
case 'p':
|
|
po->task.priority = pit_arg_string(++arg, "task priority");
|
|
break;
|
|
case 'd':
|
|
po->task.date = pit_arg_date(++arg, "task date");
|
|
break;
|
|
case 'D':
|
|
po->task.date_max = pit_arg_date(++arg, "task end date");
|
|
break;
|
|
case 't':
|
|
po->task.time = pit_arg_time(++arg, "task time");
|
|
break;
|
|
case 'T':
|
|
po->task.time_max = pit_arg_time(++arg, "task max time");
|
|
break;
|
|
default:
|
|
die("invalid task option: %s", *arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Display a list of tasks based on any combination of name, status, priority,
|
|
** date, time. If the project is set the search results get scoped by the project.
|
|
*/
|
|
void pit_task_list(POptions po, PProject pp)
|
|
{
|
|
if (!tasks) pit_db_load();
|
|
|
|
if (tasks->number_of_records > 0) {
|
|
PPager ppager = pit_pager_initialize(PAGER_TASK, (pp ? 4 : 0), tasks->number_of_records);
|
|
if (!pp) pp = (PProject)pit_table_current(projects);
|
|
|
|
for_each_task(pt) {
|
|
if ((pp && pt->project_id != pp->id) ||
|
|
(po && ((po->task.name && !stristr(pt->name, po->task.name)) ||
|
|
(po->task.status && !stristr(pt->status, po->task.status)) ||
|
|
(po->task.priority && !stristr(pt->priority, po->task.priority)) ||
|
|
(po->task.date && pt->date < po->task.date) ||
|
|
(po->task.date_max && pt->date > po->task.date_max) ||
|
|
(po->task.time && pt->time < po->task.time) ||
|
|
(po->task.time_max && pt->time > po->task.time_max))
|
|
)) continue;
|
|
pit_pager_print(ppager, (char *)pt);
|
|
}
|
|
pit_pager_flush(ppager);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** 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(int id, PProject pp)
|
|
{
|
|
PTask pt;
|
|
bool standalone = (pp == NULL);
|
|
|
|
if (standalone) pit_db_load();
|
|
id = task_find_current(id, &pt);
|
|
if (standalone) pp = (PProject)pit_table_find(projects, pt->project_id);
|
|
|
|
if (pp) {
|
|
/*
|
|
** Preserve task name and number_of_notes before deleting the task since
|
|
** we need these for logging.
|
|
*/
|
|
char *deleted_name = str2str(pt->name);
|
|
int deleted_number_of_notes = pt->number_of_notes;
|
|
/*
|
|
** Delete task notes if any.
|
|
*/
|
|
if (pt->number_of_notes > 0) {
|
|
for_each_note(pn) {
|
|
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. */
|
|
}
|
|
}
|
|
}
|
|
pt = (PTask)pit_table_delete(tasks, id);
|
|
if (pt) {
|
|
pit_table_mark(tasks, 0); /* TODO: find better current task candidate. */
|
|
task_log_delete(pp->id, 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 %d", id);
|
|
}
|
|
} else {
|
|
die("could not find project for task %d", id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** CREATING TASKS:
|
|
** pit task -c name [-s status] [-p priority] [-d date] [-t time]
|
|
**
|
|
** EDITING TASKS:
|
|
** pit task -e [number] [-n name] [-s status] [-p priority] [-d date] [-t time]
|
|
**
|
|
** DELETING TASKS:
|
|
** pit task -d [number]
|
|
**
|
|
** VIEWING TASK:
|
|
** pit task [[-q] number]
|
|
**
|
|
** LISTING TASKS:
|
|
** pit task -q [number | [-n name] [-s status] [-p priority] [-d date] [-D date] [-t time] [-T time]]
|
|
*/
|
|
void pit_task(char *argv[])
|
|
{
|
|
int number = 0;
|
|
char **arg = &argv[1];
|
|
Options opt = {{ 0 }};
|
|
|
|
if (!*arg) {
|
|
pit_task_list(&opt, NULL); /* List all tasks for current project. */
|
|
} else { /* pit task [number] */
|
|
number = pit_arg_number(arg, NULL);
|
|
if (number) {
|
|
task_show(number);
|
|
} else {
|
|
switch(pit_arg_option(arg)) {
|
|
case 'c': /* pit task -c name [-s status] [-p priority] [-d date] [-t time] */
|
|
opt.task.name = pit_arg_string(++arg, "task name");
|
|
task_parse_options(arg, &opt);
|
|
task_create(&opt);
|
|
break;
|
|
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, &opt);
|
|
if (is_zero((char *)&opt.task, sizeof(opt.task))) {
|
|
die("nothing to update");
|
|
} else {
|
|
task_update(number, &opt);
|
|
}
|
|
break;
|
|
case 'd': /* pit task -d [number] */
|
|
number = pit_arg_number(++arg, NULL);
|
|
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]] */
|
|
opt.task.id = pit_arg_number(++arg, NULL);
|
|
if (opt.task.id) {
|
|
task_show(opt.task.id);
|
|
} else {
|
|
task_parse_options(--arg, &opt);
|
|
if (is_zero((char *)&opt.task, sizeof(opt.task))) {
|
|
task_show(0); /* Show current task if any. */
|
|
} else {
|
|
pit_task_list(&opt, NULL);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
die("invalid task option: %s", *arg);
|
|
}
|
|
}
|
|
}
|
|
}
|