locations API now supports CSV

closes #72
This commit is contained in:
Jan-Piet Mens 2015-11-14 13:36:00 +01:00
parent dd846a3860
commit e7f368ef04
5 changed files with 135 additions and 107 deletions

View File

@ -487,7 +487,7 @@ List users. If _user_ is specified, lists that user's devices. If both _user_ an
#### `locations`
Here comes the actual data. This lists users' locations and requires both _user_ and _device_. Output format is JSON unless a different _format_ is given (`json`, `geojson`, and `linestring` are supported).
Here comes the actual data. This lists users' locations and requires both _user_ and _device_. Output format is JSON unless a different _format_ is given (`csv`, `json`, `geojson`, `xml`, and `linestring` are supported).
In order to limit the number of records returned, use _limit_ which causes a reverse search through the `.rec` files; this can be used to find the last N positions.

22
http.c
View File

@ -270,10 +270,24 @@ static void emit_xml_line(char *line, void *param)
mg_printf_data(conn, "\n");
}
static void emit_csv_line(char *line, void *param)
{
struct mg_connection *conn = (struct mg_connection *)param;
mg_printf_data(conn, line);
}
static int xml_response(struct mg_connection *conn, JsonNode *obj)
{
/* TODO: support fields? */
xml_output(obj, CSV, NULL, emit_xml_line, conn);
xml_output(obj, XML, NULL, emit_xml_line, conn);
json_delete(obj);
return (MG_TRUE);
}
static int csv_response(struct mg_connection *conn, JsonNode *obj)
{
csv_output(obj, CSV, NULL, emit_csv_line, conn);
json_delete(obj);
return (MG_TRUE);
@ -376,6 +390,8 @@ static int dispatch(struct mg_connection *conn, const char *uri)
otype = JSON;
else if (!strcmp(buf, "linestring"))
otype = LINESTRING;
else if (!strcmp(buf, "csv"))
otype = CSV;
else if (!strcmp(buf, "xml"))
otype = XML;
else {
@ -440,6 +456,8 @@ static int dispatch(struct mg_connection *conn, const char *uri)
if (otype == JSON) {
return (json_response(conn, obj));
} else if (otype == CSV) {
return (csv_response(conn, obj));
} else if (otype == XML) {
return (xml_response(conn, obj));
} else if (otype == LINESTRING) {

106
ocat.c
View File

@ -32,36 +32,6 @@
#include "misc.h"
#include "version.h"
#define STRINGCOLUMN(x) (!strcmp(x, "addr") || !strcmp(x, "locality"))
/*
* Print the value in a single JSON node. If string, easy. If number account for
* what we call 'integer' types which shouldn't be printed as floats.
*/
static void print_one(JsonNode *j, JsonNode *inttypes)
{
/* Check if the value should be an "integer" (ie not float) */
if (j->tag == JSON_NUMBER) {
if (json_find_member(inttypes, j->key)) {
printf("%.lf", j->number_);
} else {
printf("%lf", j->number_);
}
} else if (j->tag == JSON_STRING) {
char *quote = "";
if (STRINGCOLUMN(j->key)) {
quote = "\"";
}
printf("%s%s%s", quote, j->string_, quote);
} else if (j->tag == JSON_BOOL) {
printf("%s", (j->bool_) ? "true" : "false");
} else if (j->tag == JSON_NULL) {
printf("null");
}
}
static void print_xml_line(char *line, void *param)
{
FILE *fp = (FILE *)param;
@ -69,78 +39,6 @@ static void print_xml_line(char *line, void *param)
fprintf(fp, "%s\n", line);
}
static void csv_title(JsonNode *node, char *column)
{
char *quote = "";
if (STRINGCOLUMN(column)) {
quote = "\"";
}
printf("%s%s%s%c", quote, column, quote, (node->next) ? ',' : '\n');
}
/*
* Output location data as CSV. If `fields' is not NULL, it's a JSON
* array of JSON elment names which should be printed instead of the
* default ALL.
*/
void csv_output(JsonNode *json, output_type otype, JsonNode *fields)
{
JsonNode *node, *inttypes;
JsonNode *arr, *one, *j;
short virgin = 1;
/* Prime the inttypes object with types we consider "integer" */
inttypes = json_mkobject();
json_append_member(inttypes, "batt", json_mkbool(1));
json_append_member(inttypes, "vel", json_mkbool(1));
json_append_member(inttypes, "cog", json_mkbool(1));
json_append_member(inttypes, "tst", json_mkbool(1));
json_append_member(inttypes, "alt", json_mkbool(1));
json_append_member(inttypes, "dist", json_mkbool(1));
json_append_member(inttypes, "trip", json_mkbool(1));
arr = json_find_member(json, "locations");
json_foreach(one, arr) {
/* Headings */
if (virgin) {
virgin = !virgin;
if (fields) {
json_foreach(node, fields) {
csv_title(node, node->string_);
}
} else {
json_foreach(node, one) {
if (node->key)
csv_title(node, node->key);
}
}
}
/* Now the values */
if (fields) {
json_foreach(node, fields) {
if ((j = json_find_member(one, node->string_)) != NULL) {
print_one(j, inttypes);
printf("%c", node->next ? ',' : '\n');
} else {
/* specified field not in JSON for this row */
printf("%c", node->next ? ',' : '\n');
}
}
} else {
json_foreach(j, one) {
print_one(j, inttypes);
printf("%c", j->next ? ',' : '\n');
}
}
}
json_delete(inttypes);
}
void usage(char *prog)
{
printf("Usage: %s [options..] [file ...]\n", prog);
@ -446,7 +344,7 @@ int main(int argc, char **argv)
JsonNode *o = json_mkobject();
json_append_member(o, "locations", user_array);
csv_output(o, CSV, fields);
csv_output(o, CSV, fields, print_xml_line, xmlp);
json_delete(o);
} else if (otype == XML) {
@ -570,7 +468,7 @@ int main(int argc, char **argv)
}
} else if (otype == CSV) {
csv_output(obj, CSV, fields);
csv_output(obj, CSV, fields, print_xml_line, xmlp);
} else if (otype == XML) {
xml_output(obj, XML, fields, print_xml_line, xmlp);
} else if (otype == RAW || otype == RAWPAYLOAD) {

111
storage.c
View File

@ -1282,6 +1282,117 @@ void xml_output(JsonNode *json, output_type otype, JsonNode *fields, void (*func
json_delete(inttypes);
}
#define STRINGCOLUMN(x) (!strcmp(x, "addr") || !strcmp(x, "locality"))
/*
* Print the value in a single JSON node. If string, easy. If number account for
* what we call 'integer' types which shouldn't be printed as floats.
*/
static void print_one(UT_string *line, JsonNode *j, JsonNode *inttypes, void (func)(char *line, void *param), void *param)
{
/* Check if the value should be an "integer" (ie not float) */
if (j->tag == JSON_NUMBER) {
if (json_find_member(inttypes, j->key)) {
utstring_printf(line, "%.lf", j->number_);
} else {
utstring_printf(line, "%lf", j->number_);
}
} else if (j->tag == JSON_STRING) {
char *quote = "";
if (STRINGCOLUMN(j->key)) {
quote = "\"";
}
utstring_printf(line, "%s%s%s", quote, j->string_, quote);
} else if (j->tag == JSON_BOOL) {
utstring_printf(line, "%s", (j->bool_) ? "true" : "false");
} else if (j->tag == JSON_NULL) {
utstring_printf(line, "null");
}
}
static void csv_title(UT_string *line, JsonNode *node, char *column)
{
char *quote = "";
if (STRINGCOLUMN(column)) {
quote = "\"";
}
utstring_printf(line, "%s%s%s%c", quote, column, quote, (node->next) ? ',' : '\n');
}
/*
* Output location data as CSV. If `fields' is not NULL, it's a JSON
* array of JSON elment names which should be printed instead of the
* default ALL.
*/
void csv_output(JsonNode *json, output_type otype, JsonNode *fields, void (*func)(char *s, void *param), void *param)
{
JsonNode *node, *inttypes;
JsonNode *arr, *one, *j;
short virgin = 1;
static UT_string *line = NULL;
utstring_renew(line);
/* Prime the inttypes object with types we consider "integer" */
inttypes = json_mkobject();
json_append_member(inttypes, "batt", json_mkbool(1));
json_append_member(inttypes, "vel", json_mkbool(1));
json_append_member(inttypes, "cog", json_mkbool(1));
json_append_member(inttypes, "tst", json_mkbool(1));
json_append_member(inttypes, "alt", json_mkbool(1));
json_append_member(inttypes, "dist", json_mkbool(1));
json_append_member(inttypes, "trip", json_mkbool(1));
arr = json_find_member(json, "locations");
json_foreach(one, arr) {
/* Headings */
if (virgin) {
virgin = !virgin;
if (fields) {
json_foreach(node, fields) {
csv_title(line, node, node->string_);
}
} else {
json_foreach(node, one) {
if (node->key)
csv_title(line, node, node->key);
}
}
func(UB(line), param);
utstring_renew(line);
}
/* Now the values */
if (fields) {
json_foreach(node, fields) {
if ((j = json_find_member(one, node->string_)) != NULL) {
print_one(line, j, inttypes, func, param);
utstring_printf(line, "%c", node->next ? ',' : '\n');
} else {
/* specified field not in JSON for this row */
utstring_printf(line, "%c", node->next ? ',' : '\n');
}
}
func(UB(line), param);
utstring_renew(line);
} else {
json_foreach(j, one) {
print_one(line, j, inttypes, func, param);
utstring_printf(line, "%c", j->next ? ',' : '\n');
}
func(UB(line), param);
utstring_renew(line);
}
}
json_delete(inttypes);
}
char *storage_userphoto(char *username)
{
static char path[BUFSIZ];

View File

@ -48,6 +48,7 @@ void storage_init(int revgeo);
void storage_gcache_dump(char *lmdbname);
void storage_gcache_load(char *lmdbname);
void xml_output(JsonNode *json, output_type otype, JsonNode *fields, void (*func)(char *s, void *param), void *param);
void csv_output(JsonNode *json, output_type otype, JsonNode *fields, void (*func)(char *s, void *param), void *param);
char *storage_userphoto(char *username);
#endif