diff --git a/server/out.csv b/server/out.csv new file mode 100644 index 0000000..777a0d2 --- /dev/null +++ b/server/out.csv @@ -0,0 +1,2 @@ +hello,foo +"x,y","a,b" diff --git a/server/package-lock.json b/server/package-lock.json index 8a5e3ab..c51b29c 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -126,7 +126,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -1560,6 +1559,16 @@ "cssom": "0.3.x" } }, + "csv-write-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/csv-write-stream/-/csv-write-stream-2.0.0.tgz", + "integrity": "sha1-/C2iGkjW6l+MF/3jnPuRHk8CkrA=", + "requires": { + "argparse": "^1.0.7", + "generate-object-property": "^1.0.0", + "ndjson": "^1.3.0" + } + }, "d": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", @@ -3270,6 +3279,14 @@ } } }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "requires": { + "is-property": "^1.0.0" + } + }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -3674,6 +3691,11 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -4495,8 +4517,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "0.5.1", @@ -4972,6 +4993,24 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ndjson": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-1.5.0.tgz", + "integrity": "sha1-rmA7NrE0vOw0e0UkIrC/mNWDLsg=", + "requires": { + "json-stringify-safe": "^5.0.1", + "minimist": "^1.2.0", + "split2": "^2.1.0", + "through2": "^2.0.3" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -6124,8 +6163,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.13.1", diff --git a/server/package.json b/server/package.json index 7ff0b02..8012e74 100644 --- a/server/package.json +++ b/server/package.json @@ -28,6 +28,7 @@ "config": "^1.25.1", "cors": "^2.8.3", "credential": "^2.0.0", + "csv-write-stream": "^2.0.0", "deepmerge": "^2.1.0", "express": "^4.15.2", "gridfs-stream": "jlyonsmith/gridfs-stream", diff --git a/server/src/api/routes/TeamRoutes.js b/server/src/api/routes/TeamRoutes.js index b141300..d795e68 100644 --- a/server/src/api/routes/TeamRoutes.js +++ b/server/src/api/routes/TeamRoutes.js @@ -4,6 +4,7 @@ import autobind from "autobind-decorator" import zlib from "zlib" import { Readable } from "stream" import { catchAll, BaseRoutes } from "." +import csvWriter from "csv-write-stream" @autobind export class TeamRoutes extends BaseRoutes { @@ -29,30 +30,50 @@ export class TeamRoutes extends BaseRoutes { throw createError.Forbidden() } + res.writeHead(200, { + "Content-Type": "application/gzip", + "Content-Disposition": 'attachment; filename="team-data.csv.gzip"', + }) + const gzip = zlib.createGzip() + const writeable = csvWriter({ + headers: [ + "Team Name", + "Start Time", + "Activity Status", + "Activity Created", + "WorkItem Ticket", + "WorkItem Type", + "Activity Resolution", + "Activity Notes", + ], + }) + + writeable.pipe(gzip).pipe(res) + const Team = this.db.Team + const WorkItem = this.db.WorkItem const Activity = this.db.Activity let teams = await Team.find({}).exec() - teams = teams.map((doc) => doc.toObject({ versionKey: false })) - for (let team of teams) { let activities = await Activity.find({ team: team._id }).exec() - team.activities = activities.map((doc) => - doc.toObject({ versionKey: false }) - ) + for (let activity of activities) { + const workItem = await WorkItem.findById(activity.workItem).exec() + + writeable.write([ + team.name, + team.start, + activity.status, + activity.createdAt, + workItem.ticketNumber, + workItem.workItemType, + activity.resolution, + activity.notes, + ]) + } } - const gzip = zlib.createGzip() - let readable = new Readable() - - readable.push(JSON.stringify(teams, null, " ")) - readable.push(null) - - res.writeHead(200, { - "Content-Type": "application/gzip", - "Content-Disposition": 'attachment; filename="team-scores.json.gzip"', - }) - readable.pipe(gzip).pipe(res) + writeable.end() } }