Implement CSV download for all parameters
This commit is contained in:
1 parent
4016f3acfd
commit
d27247e265
4 files changed
+61
-2
No files matched your search
@@ -115,6 +115,15 @@ const component = {
|
|||||||
|
|
||||||
return m(
|
return m(
|
||||||
".all-parameters",
|
".all-parameters",
|
||||||
|
m(
|
||||||
|
"a.download-csv",
|
||||||
|
{
|
||||||
|
href: `/api/devices/${device["DeviceID.ID"].value[0]}.csv`,
|
||||||
|
download: "",
|
||||||
|
style: "float: right;"
|
||||||
|
},
|
||||||
|
"Download"
|
||||||
|
),
|
||||||
search,
|
search,
|
||||||
m(".parameter-list", m("table", m("tbody", rows)))
|
m(".parameter-list", m("table", m("tbody", rows)))
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -528,7 +528,6 @@ span.na {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.all-parameters {
|
.all-parameters {
|
||||||
font-family: monospace;
|
|
||||||
& > input {
|
& > input {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
@@ -537,6 +536,7 @@ span.na {
|
|||||||
}
|
}
|
||||||
|
|
||||||
& > .parameter-list {
|
& > .parameter-list {
|
||||||
|
font-family: monospace;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,53 @@ const resources = {
|
|||||||
tasks: 0
|
tasks: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
router.get(`/devices/:id.csv`, async ctx => {
|
||||||
|
const log = {
|
||||||
|
message: "Query device (CSV)",
|
||||||
|
context: ctx,
|
||||||
|
id: ctx.params.id
|
||||||
|
};
|
||||||
|
|
||||||
|
const filter = ["=", ["PARAM", RESOURCE_IDS.devices], ctx.params.id];
|
||||||
|
|
||||||
|
if (!ctx.state.authorizer.hasAccess("devices", 2)) {
|
||||||
|
logUnauthorizedWarning(log);
|
||||||
|
return void (ctx.status = 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await db.query("devices", filter);
|
||||||
|
if (!res[0]) return void (ctx.status = 404);
|
||||||
|
|
||||||
|
ctx.type = "text/csv";
|
||||||
|
ctx.attachment(
|
||||||
|
`device-${ctx.params.id}-${new Date()
|
||||||
|
.toISOString()
|
||||||
|
.replace(/[:.]/g, "")}.csv`
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.body = new stream.PassThrough();
|
||||||
|
ctx.body.write(
|
||||||
|
"Parameter,Object,Object timestamp,Writable,Writable timestamp,Value,Value type,Value timestamp\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const k of Object.keys(res[0]).sort()) {
|
||||||
|
const p = res[0][k];
|
||||||
|
const row = [
|
||||||
|
k,
|
||||||
|
p.object,
|
||||||
|
p.objectTimestamp,
|
||||||
|
p.writable,
|
||||||
|
p.writableTimestamp,
|
||||||
|
p.value != null ? `"${p.value[0].toString().replace(/"/g, '""')}"` : "",
|
||||||
|
p.value != null ? p.value[1] : "",
|
||||||
|
p.valueTimestamp
|
||||||
|
];
|
||||||
|
ctx.body.write(row.map(r => (r != null ? r : "")).join(",") + "\n");
|
||||||
|
}
|
||||||
|
ctx.body.end();
|
||||||
|
logger.accessInfo(log);
|
||||||
|
});
|
||||||
|
|
||||||
for (const [resource, flags] of Object.entries(resources)) {
|
for (const [resource, flags] of Object.entries(resources)) {
|
||||||
router.head(`/${resource}`, async (ctx, next) => {
|
router.head(`/${resource}`, async (ctx, next) => {
|
||||||
let filter = true;
|
let filter = true;
|
||||||
|
|||||||
@@ -330,7 +330,10 @@ function flattenDevice(device) {
|
|||||||
|
|
||||||
const attrs = {};
|
const attrs = {};
|
||||||
if (tree["_value"] != null) {
|
if (tree["_value"] != null) {
|
||||||
attrs["value"] = [tree["_value"], tree["_type"]];
|
attrs["value"] = [
|
||||||
|
tree["_value"] instanceof Date ? +tree["_value"] : tree["_value"],
|
||||||
|
tree["_type"]
|
||||||
|
];
|
||||||
attrs["valueTimestamp"] = +(tree["_timestamp"] || childrenTimestamp);
|
attrs["valueTimestamp"] = +(tree["_timestamp"] || childrenTimestamp);
|
||||||
attrs["object"] = false;
|
attrs["object"] = false;
|
||||||
attrs["objectTimestamp"] = childrenTimestamp;
|
attrs["objectTimestamp"] = childrenTimestamp;
|
||||||
|
|||||||
Reference in new issue
Block a user