4
\$\begingroup\$

What this tool does update skills for agents on a Contact Center Cluster (Cisco UCCX). The tool reads a csv file called agents.csv from the current working directory. This file has a list of agents and their skills. The skills are updated using a REST API.

Note that UCCX allows a maximum of 400 agents, so that's why I use the sync API for reading a file and csv because I don't think it's going to be performance impacting.

This is the line where the agent skills are updated:

 await axios.put(ccxURI, xml, config);

It is done sequentially. I originally wanted to do it concurrently using Promise.all but for some reason the UCCX server responds with a 500 error. So for now I'm doing it one at a time.

Code is below:

"use strict";
const ora = require("ora");
let throbber;
const inquirer = require("inquirer");
const prompts = [
 {
 name: "IP",
 type: "input",
 message: "Enter IP address of UCCX publisher: ",
 },
 {
 name: "user",
 type: "input",
 message: "Enter username: ",
 },
 {
 name: "pass",
 type: "password",
 message: "Enter password: ",
 },
];
const axios = require("axios");
const https = require("https");
const fs = require("fs");
// csv sync api is used as there can be at most 400 agents on the cluster
// hence it should not be performance impacting
const parse = require("csv-parse/lib/sync");
const parseOptions = {
 comment: "#",
 skip_empty_lines: true,
 trim: true
};
// csv file won"t exceed 1MB so readFileSync should be OK
const csv = fs.readFileSync("agents.csv", "utf-8");
const parser = require("xml2json");
(async () => {
 console.log("Please verify agents.csv is in the current working directory\n");
 const answers = await inquirer.prompt(prompts);
 const IP = answers.IP;
 const config = {
 auth: {
 username: answers.user,
 password: answers.pass
 },
 httpsAgent: new https.Agent({ 
 rejectUnauthorized: false
 }),
 headers: {"Content-Type": "application/xml"}
 };
 const XMLtemplate = `
 <resource>
 <self></self>
 <userID></userID>
 <firstName></firstName>
 <lastName></lastName>
 <extension></extension>
 <alias></alias>
 <resourceGroup></resourceGroup>
 <skillMap>
 <skillCompetency></skillCompetency>
 </skillMap>
 <autoAvailable>true</autoAvailable>
 <type></type>
 <team name="Default">
 <refURL>${IP}/adminapi/team/1</refURL>
 </team>
 <primarySupervisorOf/>
 <secondarySupervisorOf/>
 </resource>`;
 const resourceGroupMapping = {};
 const resourceGroupURI = `https://${IP}/adminapi/resourceGroup`;
 const resourceGroupList = await axios.get(resourceGroupURI, config); 
 throbber = ora("Updating").start();
 for(const resourceGroup of resourceGroupList.data.resourceGroup) {
 resourceGroupMapping[resourceGroup.name] = resourceGroup.id;
 }
 const skillMapping = {};
 const skillURI = `https://${IP}/adminapi/skill`; 
 const skillList = await axios.get(skillURI, config);
 for(const skill of skillList.data.skill) {
 skillMapping[skill.skillName] = skill.skillId;
 }
 const teamMapping = {};
 const teamURI = `https://${IP}/adminapi/team`;
 const teamList = await axios.get(teamURI, config); 
 for(const team of teamList.data.team) {
 teamMapping[team.teamname] = team.teamId;
 }
 // sample csv
 // #id,firstName,lastName,extension,resourceGroup,team,skill1,competence1,skill2,competence2,skill3,competence3,skill4,competence4 ... 
 // Adrian_A,Adrian,Aldana,1008,1,Saturday-Lending,Lending,Pre_Approved_Loans,9,HELOC,6,Auto_and_Consumer_Loans,4,Loan_Status,7
 const records = parse(csv, parseOptions);
 const recordLength = records.length;
 const XMLOptions = {
 object: true,
 reversible: true,
 trim: true
 };
 for(let index = 0; index < recordLength; index++) {
 let skillsAndCompetency;
 let jsonObject;
 let xml;
 let ccxURI;
 jsonObject = parser.toJson(XMLtemplate, XMLOptions);
 jsonObject.resource.skillMap.skillCompetency = [];
 jsonObject.resource.self = { "$t" : `https://${IP}/adminapi/resource/${records[index][0]}` };
 jsonObject.resource.userID = { "$t" : `${records[index][0]}` };
 jsonObject.resource.firstName = { "$t" : `${records[index][1]}` };
 jsonObject.resource.lastName = { "$t" : `${records[index][2]}` };
 jsonObject.resource.extension = { "$t" : `${records[index][3]}` };
 // include resource type
 jsonObject.resource.type = { "$t" : `${records[index][4]}` };
 // update resource group
 if(records[index][5]) {
 jsonObject.resource.resourceGroup.name = `${records[index][5]}`;
 jsonObject.resource.resourceGroup.refURL = { "$t" : `https://${IP}/adminapi/resourceGroup/${resourceGroupMapping[records[index][5]]}` };
 }
 // update team
 if(records[index][6] !== "Default") {
 jsonObject.resource.team.name = records[index][6];
 jsonObject.resource.team.refURL = { "$t" : `https://${IP}/adminapi/team/${teamMapping[records[index][6]]}` };
 }
 // update all skills and competency 
 for(let j = 7; j < records[index].length; j += 2) {
 skillsAndCompetency = { 
 competencelevel : "",
 skillNameUriPair : { 
 name: "",
 refURL : ""
 }
 };
 skillsAndCompetency.competencelevel = { "$t" : `${records[index][j + 1]}` };
 skillsAndCompetency.skillNameUriPair.name = `${records[index][j]}`;
 skillsAndCompetency.skillNameUriPair.refURL = { "$t" : `https://${IP}/adminapi/skill/${skillMapping[records[index][j]]}` };
 jsonObject.resource.skillMap.skillCompetency.push(skillsAndCompetency);
 }
 // xml payload 
 xml = `<?xml version="1.0" encoding="UTF-8"?>` + parser.toXml(JSON.stringify(jsonObject));
 // update skills for the agents
 ccxURI = `https://${IP}/adminapi/resource/${records[index][0]}`;
 // if a row in the csv is incorrect, continue processing subsequent rows
 try {
 await axios.put(ccxURI, xml, config);
 } catch (error) {
 throbber.stop();
 console.log(`\nCould not assign skills to ${records[index][0]}\n${error.response.data.apiError[0].errorMessage}\n`);
 throbber.start();
 }
 }
})()
 .catch(error => console.log(error.stack))
 .then( () => throbber.stop());
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Jun 4, 2019 at 3:00
\$\endgroup\$
0

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.