diff --git a/examples/node-js-import-cards-with-excel/Import_Cards_From_Xlsx.js b/examples/node-js-import-cards-with-excel/Import_Cards_From_Xlsx.js new file mode 100644 index 0000000..1a529f6 --- /dev/null +++ b/examples/node-js-import-cards-with-excel/Import_Cards_From_Xlsx.js @@ -0,0 +1,151 @@ +const xlsx = require('node-xlsx'); +const request = require('request'); +const prompt = require('prompt-sync')(); + +const access_Token = prompt("Please provide your API access token: "); +const workspace_ID = prompt("Please provide the Workspace/Project ID where the Cards should appear in: "); +const file_Path = prompt("Please provide the full path to the xlsx file: "); +const work_Sheets = xlsx.parse(file_Path); + +var failed_ImportedCards = []; + +// Check how many Sheets there are in the xlsx file. +if (work_Sheets.length > 1) { + console.warn("Found more than one sheet in the xlsx file."); + sheet_Name = prompt("Please provide the sheet name that the Cards are in: "); + handle_SheetInformation(get_SheetInformation(sheet_Name)); +} else { + handle_SheetInformation(work_Sheets[0]); +} + +function get_SheetInformation(sheetName) { + for (let i = 0; i < work_Sheets.length; i++){ + if ((work_Sheets[i].name).toLocaleLowerCase() == (sheetName).toLocaleLowerCase()) { + return work_Sheets[i] + } + } +} +function handle_SheetInformation(sheet) { + if (sheet.data[0].length < 2) { + console.warn("Missing some attributes in the headers / top row of the sheet:", sheet.name); + console.warn("Checking if it is the Board ID attribute..."); + } + if (sheet.data[0].length > 13) { + console.error("Too many attributes in the headers / top row of the sheet:", sheet.name); + console.error("Found: "+sheet.data[0].length + " attributes, should be: 14"); + return false; + } + let userMappings = []; + // Get the user headers in the xlsx file. + for (let i = 0; i < sheet.data[0].length; i++) { + // Check if one of the columns in the headers / top row is empty. + if (sheet.data[0][i] == undefined) { + console.error("The headers / top row has an empty column in position: ",i+1); + return false; + } + userValue = sheet.data[0][i].toLowerCase(); + userMappings[userValue] = i; + } + + // Remove the headers from the sheet data array so we only get the data for the Cards. + sheet.data.splice(0,1); + + // Check for empty rows for each card that should be imported from the file. + let filtered_sheet_data = []; + sheet.data.forEach(Row => { + if (!(Row.length == 0)) { + filtered_sheet_data.push(Row); + } + }); + + // Check if the user has Board ID in the headers / top row. + if (userMappings["board id"] == undefined) { + console.error("No 'board id' header found in the xlsx file."); + } + post_CreateCards(filtered_sheet_data,userMappings); + +} +async function post_CreateCards(card_Information, userMappings) { + /* + card_Information[y] = row + card_Information[y][x] = in row y column x + */ + let userMappings_keys = Object.keys(userMappings); + let allUser_Cards = []; + // Each row + for (let i = 0; i < card_Information.length; i++) { + let card = {} + // Each column in that row + for (let j = 0; j < card_Information[i].length; j++) { + userMappings_keys.forEach(Key => { + if (j == userMappings[Key]) { + card[Key] = card_Information[i][j]; + } + }); + } + allUser_Cards.push(card); + } + for (const Card of allUser_Cards) { + // Check to see if the Card should be blocked. + if (Card["blocked reason"] == undefined || Card["blocked reason"].length == 0 ) { + // If the Card should not be blocked then set the attribute "is blocked" to 0 (false). + Card["is blocked"] = 0; + } else { + // If the Card should be blocked then set the attribute "is blocked" to 1 (true). + Card["is blocked"] = 1; + } + // Create the array for the checklist + let Card_CheckList = []; + if (Card["checklist"] != undefined) { + Card_CheckList = Card["checklist"].split("##"); + } + + userMappings_keys.forEach(Key => { + if (Card[Key] == undefined) { + delete Card[Key]; + } + }); + + let full_Card = { + assignee_id: Card["assignee"], + block_reason: Card["blocked reason"], + board_id: Card["board id"], + checklist: Card_CheckList, + column_id: Card["column"], + description: Card["description"], + due_date: Card["due date"], + estimate: Card["points"], + estimated_time: Card["estimated time"], + is_blocked: Card["is blocked"], + label_id: Card["label"], + planlet_id: Card["activity"], + title: Card["title"], + } + await send_Card(full_Card); + } + if (failed_ImportedCards.length > 0) { + console.log(); + console.error("These Cards failed: "); + console.log(failed_ImportedCards); + } +} + +async function send_Card(Card) { + return new Promise(Information => { + request.post("https://api.projectplace.com/1/projects/"+workspace_ID+"/cards/create-new", { + auth: { + "bearer": access_Token.toString(), + "Content-Type": "application/json", + }, + json: Card, + }, function(error, response, body) { + console.log(response.statusCode,response.statusMessage); + if (response.statusCode != 200) { + Card["Error"] = body + failed_ImportedCards.push(Card); + console.error(body); + } + Information(body); + }); + }); +} \ No newline at end of file diff --git a/examples/node-js-import-cards-with-excel/nodejs_cards.xlsx b/examples/node-js-import-cards-with-excel/nodejs_cards.xlsx new file mode 100644 index 0000000..517d58a Binary files /dev/null and b/examples/node-js-import-cards-with-excel/nodejs_cards.xlsx differ diff --git a/examples/node-js-import-cards-with-excel/package.json b/examples/node-js-import-cards-with-excel/package.json new file mode 100644 index 0000000..ec4cbe1 --- /dev/null +++ b/examples/node-js-import-cards-with-excel/package.json @@ -0,0 +1,13 @@ +{ + "name": "js-import-cards-with-excel", + "version": "1.0.0", + "description": "import cards to projectplace from a excel file", + "main": "Import_Cards_From_Xlsx.js", + "author": "", + "license": "ISC", + "dependencies": { + "node-xlsx": "^0.21.0", + "prompt-sync": "^4.2.0", + "request": "^2.88.2" + } +} diff --git a/examples/node-js-import-cards-with-excel/readme.md b/examples/node-js-import-cards-with-excel/readme.md new file mode 100644 index 0000000..b5d0f3a --- /dev/null +++ b/examples/node-js-import-cards-with-excel/readme.md @@ -0,0 +1,39 @@ +# Upload Cards from Xlsx file + +This NodeJS script reads the provided Xlsx file and creates Cards to the correct Board/Activity. +The script requires a Oauth2 token in order to make the API calls to create the Cards, +it will assume that you have already created one. + +#### 1. Install requirements +See the `package.json` file for the needed third-party libraries. + +#### 2. Run the script +``` +node Import_Cards_From_Xlsx.js +``` + +#### 3. Provide the necessary information +* The script will ask first for your access token (Oauth2 token). +* Then it will ask for the Workspace ID of where Board that you have provided in the Xlsx is located. +* Lastly the script will ask the exact path for the Xlsx file. + +##### Note file name location +If the Xlsx file is in `c:/User` folder, then you should provide the path as: `c:/user/FILENAME.xlsx`. + +##### See the test Xlsx file named: ```nodejs_cards.xlsx``` + +The following headers are possible to use in your Excel-file: +Headers that will be used: **Due date, Description, Assignee, Title, Label, Points, Estimated time, +Blocked reason, Column, Checklist, Co-assignees, Activity, Board id.** + +If you have more headers that are not on the list above, then they will be ignored. + +***The headers does not have to be arranged in any specific order for the script to work.*** + + +1. The script will get all the headers from the Xlsx file. +2. Create a JSON object for each of the Cards. +3. Lastly it will send the JSON object to the API endpoint: ```/cards/create-new``` - once for every + card +4. If any of the Cards failed to import then you will see those Cards in the console once the script is done. +