commit 074e9c6c7b8abab17a0c9178b4031c31210183ed
parent 879ba90362fefcfcda2b1898ac1e7be930060311
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Mon, 11 Mar 2024 01:18:34 -0700
Add export page
This page exports timesheets to TSV. By default times are serialized
to the ISO 8601 format (i.e. "2002-10-02T13:00:00Z"), but I also
made it possible to output times as seconds since the UNIX epoch.
Diffstat:
6 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
@@ -5,7 +5,7 @@ lddflags = ${LDDFLAGS} -static -L/usr/local/lib -lkcgi -lkcgihtml -lz -lsqlbox -
prefix = /var/www/htdocs/${name}.primus.lan
pages = pages/index.c pages/404.c pages/login.c pages/logout.c \
- pages/account.c pages/main.c
+ pages/account.c pages/main.c pages/export.c
hdrsrc = page.c html.c user.c stmt.c key.c times.c
src = ${hdrsrc} ${pages} pages/util.c
hdr = ${hdrsrc:.c=.h} util.h pages/util.h pages/pages.h
diff --git a/key.c b/key.c
@@ -39,5 +39,6 @@ struct kvalid keys[] = {
[KeyHash] = { kvalid_stringne, "hash" },
[KeyCreate] = { kvalid_stringne, "create" },
[KeyDelete] = { kvalid_stringne, "delete" },
- [KeyTime] = { kvalid_stringne, "time" }
+ [KeyTime] = { kvalid_stringne, "time" },
+ [KeyFormat] = { kvalid_stringne, "format" }
};
diff --git a/key.h b/key.h
@@ -5,6 +5,7 @@ enum http_key {
KeyCreate, /* Whether to create a new user in login page */
KeyDelete, /* Delete account in account page */
KeyTime, /* one of start, startbreak, endbreak, end */
+ KeyFormat, /* currently either tsv;epoch or ;epoch for /export */
KeyMax
};
diff --git a/pages/export.c b/pages/export.c
@@ -0,0 +1,90 @@
+#define const
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "../times.h"
+
+/* useful for scripting and such */
+enum kcgi_err
+serialize_epoch(struct pagedata *pd, time_t time)
+{
+ return khttp_printf(&pd->req, "%lld", time);
+}
+
+enum kcgi_err
+serialize_ustr(struct pagedata *pd, time_t time)
+{
+ char buf[64];
+
+ if (!khttp_epoch2ustr(time, buf, sizeof(buf)))
+ return KCGI_SYSTEM;
+ return khttp_puts(&pd->req, buf);
+}
+
+enum kcgi_err
+pageexport(struct pagedata *pd)
+{
+ enum kcgi_err status;
+ time_t *times;
+ size_t len;
+ size_t a, i;
+ int b;
+ char *tmp;
+ enum kcgi_err (*serialize)(struct pagedata *, time_t) = serialize_ustr;
+
+ if (!pd->user)
+ return errorpage(pd, KHTTP_401);
+
+ if (pd->req.fieldmap[KeyFormat]) {
+ /*
+ * format.timeformat
+ * i.e. csv.epoch
+ */
+ tmp = pd->req.fieldmap[KeyFormat]->parsed.s;
+ if (strcmp(tmp, ".epoch") != 0 && strcmp(tmp, "tsv.epoch") != 0)
+ return errorpage(pd, KHTTP_415);
+ serialize = serialize_epoch;
+ }
+
+ times = gettimes(pd, pd->user->hash, &len);
+ if (!times)
+ return errorpage(pd, KHTTP_500);
+
+ if ((status = khttp_head(&pd->req, kresps[KRESP_STATUS],
+ "%s", khttps[KHTTP_200])) != KCGI_OK ||
+ (status = khttp_head(&pd->req, kresps[KRESP_CONTENT_TYPE],
+ "text/tsv")) != KCGI_OK ||
+ (status = khttp_head(&pd->req, kresps[KRESP_CONTENT_DISPOSITION],
+ "attachment; filename=\"timekeeper-times.tsv\"")) != KCGI_OK ||
+ (status = khttp_body(&pd->req)) != KCGI_OK) {
+ free(times);
+ return status;
+ }
+
+ for (a = 0; a < len / 4; ++a) {
+ for (b = 0; b < 4; ++b) {
+ if (b && (status = khttp_putc(&pd->req, '\t')) != KCGI_OK) {
+ free(times);
+ return status;
+ }
+
+ i = (a * 4) + b;
+ if (times[i]) {
+ status = serialize(pd, times[i]);
+ if (status != KCGI_OK) {
+ free(times);
+ return status;
+ }
+ }
+ }
+ if ((status = khttp_putc(&pd->req, '\n')) != KCGI_OK) {
+ free(times);
+ return status;
+ }
+ }
+
+ free(times);
+ return KCGI_OK;
+}
diff --git a/pages/pages.h b/pages/pages.h
@@ -4,6 +4,7 @@ enum Page {
PageLogout,
PageAccount,
PageMain,
+ PageExport,
Page404,
PageMax
};
@@ -13,4 +14,5 @@ enum kcgi_err pageindex(struct pagedata *pd);
enum kcgi_err pagelogin(struct pagedata *pd);
enum kcgi_err pagelogout(struct pagedata *pd);
enum kcgi_err pageaccount(struct pagedata *pd);
+enum kcgi_err pageexport(struct pagedata *pd);
enum kcgi_err pagemain(struct pagedata *pd);
diff --git a/timekeeper.c b/timekeeper.c
@@ -34,6 +34,7 @@ static char *pages[] = {
[PageLogout] = "logout",
[PageAccount] = "account",
[PageMain] = "main",
+ [PageExport] = "export",
[Page404] = "404"
};
@@ -67,6 +68,7 @@ main(void)
[PageLogout] = pagelogout,
[PageAccount] = pageaccount,
[PageMain] = pagemain,
+ [PageExport] = pageexport,
[Page404] = page404
};