From 5be4f37c14992b5af3e921c3c201ba29d1ff7699 Mon Sep 17 00:00:00 2001 From: Zachariah Chow Date: Thu, 16 Apr 2020 00:32:13 +0800 Subject: [PATCH 1/2] remodelled cli-todo to use psql --- index.js | 158 +++++++++++++++++++++++++++++++++++++++++------ package.json | 26 ++++++++ todo-table.sql | 9 +++ util/get-date.js | 3 + 4 files changed, 176 insertions(+), 20 deletions(-) create mode 100644 package.json create mode 100644 todo-table.sql create mode 100644 util/get-date.js diff --git a/index.js b/index.js index 3907d3b5..ee3509ed 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,9 @@ -console.log("works!!", process.argv[2]); - const pg = require('pg'); +const timeStampUtil = require('./util/get-date.js') + const configs = { - user: 'akira', + user: 'zachariah', host: '127.0.0.1', database: 'todo', port: 5432, @@ -11,26 +11,144 @@ const configs = { const client = new pg.Client(configs); -let queryDoneCallback = (err, result) => { - if (err) { - console.log("query error", err.message); - } else { - console.log("result", result.rows ); - } - client.end(); -}; +//Async/Await IIFE: --> Async/Await connect --> Async/Await query +(async () => { + try { -let clientConnectionCallback = (err) => { + await client.connect(() => { + console.log('connected'); + }); - if( err ){ - console.log( "error", err.message ); - } + //Async Query, takes callback to manipulate response when displaying data on CLI (see 'show' and 'showmeta' userArgs below) + const todoQuery = async (queryText, queryValues, cb) => { + try { + const res = await client.query(queryText, queryValues); + if (cb) cb(res); - let text = "INSERT INTO todo (name) VALUES ($1) RETURNING id"; + } catch (e) { + console.log('Error! ' + e.message); - const values = ["hello"]; + } + } + // - client.query(text, values, queryDoneCallback); -}; + //CLI display for show / showmeta arguments + const listTitle = + ` + ╔═╗┌─┐┌─┐┬ ┬┌─┐ ╔╦╗┌─┐ ╔╦╗┌─┐ ╦ ┬┌─┐┌┬┐ + ╔═╝├─┤│ ├─┤└─┐ ║ │ │───║║│ │ ║ │└─┐ │ + ╚═╝┴ ┴└─┘┴ ┴└─┘ ╩ └─┘ ═╩╝└─┘ ╩═╝┴└─┘ ┴ + ` + // + + const userArgs = process.argv.slice(2); + + if (!userArgs[0]) { + + console.log(`Enter "add", "clear", "clearall", "crossoff", "show", or "showmeta" followed by appropriate values as arguments`); + + } else { + + console.log(userArgs); + + let queryT; + let queryV; + + switch (userArgs[0].toLowerCase()) { + + case 'add': + + if (userArgs[1]) { + + queryT = 'INSERT INTO to_do_items(time_stamp, item) VALUES ($1, $2) RETURNING *'; + + queryV = [timeStampUtil.getTimeStamp(), userArgs[1]]; + + todoQuery(queryT, queryV); + + } + break; + + case 'clear': + + queryT = `DELETE FROM to_do_items WHERE item = '${userArgs[1]}' RETURNING *`; + console.log(queryT); + + todoQuery(queryT); + + break; + + case 'clearall': + + queryT = 'DELETE FROM to_do_items RETURNING *'; + + todoQuery(queryT); + + break; -client.connect(clientConnectionCallback); + case 'crossoff': + + queryT = `UPDATE to_do_items SET done = TRUE, updated_time_stamp ='${timeStampUtil.getTimeStamp()}' WHERE item = '${userArgs[1]}' RETURNING *`; + + + todoQuery(queryT); + + break; + + case 'show': + + queryT = 'SELECT id, item, done FROM to_do_items'; + + todoQuery(queryT, "", (res) => { + + console.log(`${listTitle}\n\n`); + + res.rows.forEach(obj => { + + let isItemDone; + + if (obj.done) { + isItemDone = '[X]'; + } else { + isItemDone = '[ ]'; + } + + + console.log(`${res.rows.indexOf(obj) + 1}. ${isItemDone} - ${obj.item}`); + }) + }); + + break; + + case 'showmeta': + + queryT = 'SELECT * FROM to_do_items'; + + todoQuery(queryT, "", (res) => { + res.rows.forEach(obj => { + + console.log(`${listTitle}\n\n`); + + let isItemDone; + let markedDoneInfo = ""; + + if (obj.done) { + isItemDone = '[X]'; + markedDoneInfo = `\nMarked done on ${obj["updated_time_stamp"]}` + } else { + isItemDone = '[ ]'; + } + + console.log(`${res.rows.indexOf(obj) + 1}. ${isItemDone} - ${obj.item}\n Item added to list on ${obj["time_stamp"]}${markedDoneInfo}`); + }) + }); + + break; + } + } + + + } catch (e) { + console.log(e.message); + } +})(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..ef326e79 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "cli-todo-sql", + "version": "1.0.0", + "description": "![https://i.giphy.com/media/26ufnwz3wDUli7GU0/giphy.webp](https://i.giphy.com/media/26ufnwz3wDUli7GU0/giphy.webp)", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/zachariahchow/cli-todo-sql.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/zachariahchow/cli-todo-sql/issues" + }, + "homepage": "https://github.com/zachariahchow/cli-todo-sql#readme", + "devDependencies": { + "nodemon": "^2.0.3" + }, + "dependencies": { + "jsonfile": "^6.0.1", + "pg": "^8.0.2" + } +} \ No newline at end of file diff --git a/todo-table.sql b/todo-table.sql new file mode 100644 index 00000000..b75ca067 --- /dev/null +++ b/todo-table.sql @@ -0,0 +1,9 @@ +/*psql -d todo -U zachariah -f todo-table.sql*/ + +CREATE TABLE IF NOT EXISTS to_do_items ( + id serial PRIMARY KEY, + time_stamp VARCHAR (30) NOT NULL, + updated_time_stamp VARCHAR (30), + item VARCHAR (50) NOT NULL, + done boolean +); \ No newline at end of file diff --git a/util/get-date.js b/util/get-date.js new file mode 100644 index 00000000..a86fec39 --- /dev/null +++ b/util/get-date.js @@ -0,0 +1,3 @@ +module.exports.getTimeStamp = () => { + return new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().slice(0, 19).replace('T', ' '); +}; \ No newline at end of file From 1d60b91a79dd73ce79075dcf8d627f2ad2919122 Mon Sep 17 00:00:00 2001 From: Zachariah Chow Date: Thu, 16 Apr 2020 02:29:31 +0800 Subject: [PATCH 2/2] added archive, unarchive, get average completed time --- index.js | 83 +++++++++++++++++++++++++++++++++++++----------- package.json | 3 +- todo-table.sql | 3 +- util/get-date.js | 6 +++- 4 files changed, 73 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index ee3509ed..44b27aef 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ const pg = require('pg'); -const timeStampUtil = require('./util/get-date.js') +const timeStampUtil = require('./util/get-date.js'); const configs = { user: 'zachariah', @@ -11,7 +11,7 @@ const configs = { const client = new pg.Client(configs); -//Async/Await IIFE: --> Async/Await connect --> Async/Await query +//Async/Await IIFE: --> connect --> query (async () => { try { @@ -19,7 +19,7 @@ const client = new pg.Client(configs); console.log('connected'); }); - //Async Query, takes callback to manipulate response when displaying data on CLI (see 'show' and 'showmeta' userArgs below) + //Query, takes callback to manipulate response when displaying data on CLI (see 'show' and 'showmeta' userArgs below) const todoQuery = async (queryText, queryValues, cb) => { try { const res = await client.query(queryText, queryValues); @@ -27,7 +27,6 @@ const client = new pg.Client(configs); } catch (e) { console.log('Error! ' + e.message); - } } // @@ -45,7 +44,7 @@ const client = new pg.Client(configs); if (!userArgs[0]) { - console.log(`Enter "add", "clear", "clearall", "crossoff", "show", or "showmeta" followed by appropriate values as arguments`); + console.log(`Enter "add", "clear", "clearall", "crossoff", "show", "showmeta", "archive" or "unarchive" followed by appropriate values as arguments`); } else { @@ -90,14 +89,13 @@ const client = new pg.Client(configs); queryT = `UPDATE to_do_items SET done = TRUE, updated_time_stamp ='${timeStampUtil.getTimeStamp()}' WHERE item = '${userArgs[1]}' RETURNING *`; - todoQuery(queryT); break; case 'show': - queryT = 'SELECT id, item, done FROM to_do_items'; + queryT = 'SELECT id, item, done, archived FROM to_do_items'; todoQuery(queryT, "", (res) => { @@ -107,14 +105,10 @@ const client = new pg.Client(configs); let isItemDone; - if (obj.done) { - isItemDone = '[X]'; - } else { - isItemDone = '[ ]'; - } - + obj.done ? isItemDone = '[X]' : isItemDone = '[ ]'; - console.log(`${res.rows.indexOf(obj) + 1}. ${isItemDone} - ${obj.item}`); + if (!obj.archived) + console.log(`${res.rows.indexOf(obj) + 1}. ${isItemDone} - ${obj.item}`); }) }); @@ -125,30 +119,81 @@ const client = new pg.Client(configs); queryT = 'SELECT * FROM to_do_items'; todoQuery(queryT, "", (res) => { - res.rows.forEach(obj => { - console.log(`${listTitle}\n\n`); + console.log(`${listTitle}\n\n`); + + res.rows.forEach(obj => { let isItemDone; let markedDoneInfo = ""; if (obj.done) { isItemDone = '[X]'; - markedDoneInfo = `\nMarked done on ${obj["updated_time_stamp"]}` + markedDoneInfo = `\nMarked done on ${obj["updated_time_stamp"]}`; } else { isItemDone = '[ ]'; } - console.log(`${res.rows.indexOf(obj) + 1}. ${isItemDone} - ${obj.item}\n Item added to list on ${obj["time_stamp"]}${markedDoneInfo}`); + if (!obj.archived) + console.log(`${res.rows.indexOf(obj) + 1}. ${isItemDone} - ${obj.item}\n Item added to list on ${obj["time_stamp"]}${markedDoneInfo}`); }) }); + case 'archive': + + queryT = `UPDATE to_do_items SET archived = TRUE WHERE item = '${userArgs[1]}' RETURNING *`; + + todoQuery(queryT); + + break; + + case 'unarchive': + + queryT = `UPDATE to_do_items SET archived = FALSE WHERE item = '${userArgs[1]}' RETURNING *`; + + todoQuery(queryT); + + break; + + case 'stats-complete-time': + + queryT = `SELECT time_stamp, updated_time_stamp FROM to_do_items`; + + todoQuery(queryT, "", (res) => { + + //**To get average time completed based on crossoff timestamp and creation timestamp. To consider refactoring to simpler function as presently, am calling reduce twice. To consider storing timestamps in format more conducive for JS manipulation. + + let timeArray = [] + + const avgTotalMinutes = res.rows + .reduce((arr, obj) => { + + let timeDiff; + + if (obj["updated_time_stamp"]) { + timeDiff = Math.abs(new Date(obj["updated_time_stamp"].replace(/-/g, '/')) - new Date(obj["time_stamp"].replace(/-/g, '/'))); + + } + + if (timeDiff) arr.push(timeDiff); + return arr; + + }, timeArray) + + .reduce((sum, time) => { + return sum + time / timeArray.length / 60000; + }, 0) + + console.log('The average time taken to complete a task is ' + avgTotalMinutes.toFixed(2) + ' minutes'); + }); + break; + } } - } catch (e) { console.log(e.message); } + })(); \ No newline at end of file diff --git a/package.json b/package.json index ef326e79..5816b6bc 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "![https://i.giphy.com/media/26ufnwz3wDUli7GU0/giphy.webp](https://i.giphy.com/media/26ufnwz3wDUli7GU0/giphy.webp)", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node index.js" }, "repository": { "type": "git", diff --git a/todo-table.sql b/todo-table.sql index b75ca067..8956ddda 100644 --- a/todo-table.sql +++ b/todo-table.sql @@ -5,5 +5,6 @@ CREATE TABLE IF NOT EXISTS to_do_items ( time_stamp VARCHAR (30) NOT NULL, updated_time_stamp VARCHAR (30), item VARCHAR (50) NOT NULL, - done boolean + done boolean, + archived boolean ); \ No newline at end of file diff --git a/util/get-date.js b/util/get-date.js index a86fec39..00b8c8a3 100644 --- a/util/get-date.js +++ b/util/get-date.js @@ -1,3 +1,7 @@ module.exports.getTimeStamp = () => { - return new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().slice(0, 19).replace('T', ' '); + return new Date(Date.now() - new Date() + .getTimezoneOffset() * 60000) + .toISOString() + .slice(0, 19) + .replace('T', ' '); }; \ No newline at end of file