From be3e1a40c794ea4da0e884ac616c353fb6cd136a Mon Sep 17 00:00:00 2001 From: mihay42 Date: Sat, 10 Sep 2022 14:22:55 -0700 Subject: [PATCH 1/7] Cleanup 1-Remove stale files & insert copyrights --- cli/doc_company.js | 264 ------------------------------------ cli/mr_backup.js | 8 ++ src/report/companies.js | 8 ++ src/report/company.js | 271 ------------------------------------- src/report/interactions.js | 144 ++------------------ src/report/themes.js | 127 ----------------- 6 files changed, 24 insertions(+), 798 deletions(-) delete mode 100755 cli/doc_company.js delete mode 100644 src/report/company.js delete mode 100644 src/report/themes.js diff --git a/cli/doc_company.js b/cli/doc_company.js deleted file mode 100755 index 2f84133..0000000 --- a/cli/doc_company.js +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env node - -// Import required modules -import { Companies, Interactions, Studies } from '../src/api/highLevel.js' -import Firmographics from '../src/report/company.js' -import Utilities from '../src/report/common.js' -import program from 'commander' -import ConfigParser from 'configparser' -import * as fs from "fs" -import docx from 'docx' -import AWS from 'aws-sdk' -import zip from 'adm-zip' -import boxPlot from 'box-plot' - - -// ______ __ __ __ __ ______ ______ __ ______ __ __ ______ -// /\ ___\ /\ \/\ \ /\ "-.\ \ /\ ___\ /\__ _\ /\ \ /\ __ \ /\ "-.\ \ /\ ___\ -// \ \ __\ \ \ \_\ \ \ \ \-. \ \ \ \____ \/_/\ \/ \ \ \ \ \ \/\ \ \ \ \-. \ \ \___ \ -// \ \_\ \ \_____\ \ \_\\"\_\ \ \_____\ \ \_\ \ \_\ \ \_____\ \ \_\\"\_\ \/\_____\ -// \/_/ \/_____/ \/_/ \/_/ \/_____/ \/_/ \/_/ \/_____/ \/_/ \/_/ \/_____/ - -// Parse the cli options -function parseCLIArgs() { - // Define commandline options - program - .version('0.7.5') - .description('A CLI to generate a document report for mediumroast.io Company objects.') - program - .requiredOption('-n --name ', 'The name of the company to construct a report for.') - .option('-s --substudy ', 'The GUID for the substudy to include in the report.') - .option('-r --report_dir ', 'Directory to write the report to', 'Documents') - .option('-w --work_dir ', 'Directory to use for creating a ZIP package', '~/Documents') - .option('-p --package', 'Create a package with interactions') - .option('-z --zip', 'Create a ZIP archive with report and interactions') - .option('-s --server ', 'Specify the server URL', 'http://mr-01:3000') - .option('-t --server_type ', 'Specify the server type as [json || mr_server]', 'json') - .option('-a --author_company ', 'Specify the company the report is for') - .option('-c --config_file ', 'Path to the configuration file', '.mr_config') - program.parse(process.argv) - const options = program.opts() - return options -} - -// Filter interactions by the GUID of the Company -function filterObjects(objects, guid) { - let myObjects = [] - for (const object in objects) { - const allCompanies = Object.values(objects[object].linkedCompanies) - if (allCompanies.includes(guid)) { - myObjects.push(objects[object]) - } - } - return myObjects -} - -function rankTags (tags, ranges) { - let finalTags = {} - for (const tag in tags) { - // Rank the tag score using the ranges derived from box plots - // if > Q3 then the ranking is high - // if in between Q2 and Q3 then the ranking is medium - // if < Q3 then the ranking is low - let rank = null - if (tags[tag] > ranges.upperQuartile) { - rank = 'High' - } else if (tags[tag] < ranges.lowerQuartile) { - rank = 'Low' - } else if (ranges.lowerQuartile <= tags[tag] <= ranges.upperQuartile) { - rank = 'Medium' - } - - finalTags[tag] = { - score: tags[tag], // Math.round(tags[tag]), - rank: rank - } - - } - return finalTags -} - -// ______ ______ __ __ ______ __ ______ -// /\ ___\ /\ __ \ /\ "-.\ \ /\ ___\ /\ \ /\ ___\ -// \ \ \____ \ \ \/\ \ \ \ \-. \ \ \ __\ \ \ \ \ \ \__ \ -// \ \_____\ \ \_____\ \ \_\\"\_\ \ \_\ \ \_\ \ \_____\ -// \/_____/ \/_____/ \/_/ \/_/ \/_/ \/_/ \/_____/ - -// Get the configuration objects -const opts = parseCLIArgs() // CLI arguments and options -const config = new ConfigParser() // Config file -config.read(process.env.HOME + '/' + opts.config_file) - -// Set the server type -let serverType = null -config.hasKey('DEFAULT', 'server_type') ? serverType = config.get('DEFAULT', 'server_type') : serverType = opts.server_type - -// Set the server url -let mrServer = null -config.hasKey('DEFAULT', 'server') ? mrServer = config.get('DEFAULT', 'server') : mrServer = opts.server - -// Set the working directory -let workDir = null -config.hasKey('DEFAULT', 'working_dir') ? workDir = config.get('DEFAULT', 'working_dir') : workDir = opts.work_dir - -// Set the output directory -let outputDir = null -config.hasKey('DEFAULT', 'output_dir') ? outputDir = process.env.HOME + '/' + config.get('DEFAULT', 'output_dir') : outputDir = process.env.HOME + '/' + opts.output_dir - -// Set the author company -let authorCompany = null -config.hasKey('DEFAULT', 'company') ? authorCompany = config.get('DEFAULT', 'company') : outputDir = opts.author_company - -// Set up the S3 credentials and download protocol -const s3Server = config.get('s3_credentials', 'server') -const s3User = config.get('s3_credentials', 'user') -const s3APIkey = config.get('s3_credentials', 'api_key') -const s3Source = config.get('s3_credentials', 'source') -const s3Region = config.get('s3_credentials', 'region') -const s3Protocol = 's3' -const localProtocol = 'file' -const httpProtocol = 'http' - -// Determine if we need to create a zip package or not -let createPackage = null -opts.package ? createPackage = true : createPackage = false - -// Determine if we need to create a zip package or not -let createArchive = null -opts.zip ? createArchive = true : createArchive = false - -// Obtain the GUID for the needed substudy -let addedSubstudy = null -opts.substudy ? addedSubstudy = opts.substudy : addedSubstudy = false - -// Set up the control objects -const companyCtl = new Companies(mrServer, serverType) -const interactionCtl = new Interactions(mrServer, serverType) -const studyCtl = new Studies(mrServer, serverType) -const docCtl = new Utilities( - config.get('document_settings', 'font_type'), - parseInt(config.get('document_settings', 'font_size')), - parseInt(config.get('document_settings', 'title_font_size')), - config.get('document_settings', 'title_font_color') -) -const s3Ctl = new AWS.S3({ - accessKeyId: s3User , - secretAccessKey: s3APIkey , - endpoint: s3Server , - s3ForcePathStyle: true, // needed with minio? - signatureVersion: 'v4', - region: s3Region // S3 won't work without the region setting -}) - -// Get the company in question and all interactions -const company = await companyCtl.getByName(opts.name) -const interactions = filterObjects(await interactionCtl.getAll(), company[0].GUID) - -// Get the relevant study and substudy -const [studyName, substudyId] = opts.substudy.split(':') -const studies = await studyCtl.getByName(studyName) -const substudy = studies[0].substudies[substudyId] -const ranges = boxPlot(Object.values(substudy.keyThemes.summary_theme.tags)) -const quotes = (substudy.keyThemeQuotes.summary) -const tags = rankTags(substudy.keyThemes.summary_theme.tags, ranges) - -// simple function for safe directory creation -function safeMakedir(name) { - try { - if (!fs.existsSync(name)) { - fs.mkdirSync(name) - } - } catch (err) { - console.error(err) - } -} - -// create a ZIP package -async function createZIPArchive -(outputFile, sourceDirectory) { - try { - const zipPackage = new zip(); - zipPackage.addLocalFolder(sourceDirectory); - zipPackage.writeZip(outputFile); - console.log(`Created ${outputFile} successfully`); - } catch (e) { - console.log(`Something went wrong. ${e}`); - } -} - -function writeReport (docObj, fileName) { - docx.Packer.toBuffer(docObj).then((buffer) => { - fs.writeFileSync(fileName, buffer) - }) -} - -// Download the objects -async function downloadInteractions (interactions, directory) { - for (const interaction in interactions) { - const objWithPath = interactions[interaction].url.split('://').pop() - const myObj = objWithPath.split('/').pop() - const myParams = {Bucket: s3Source, Key: myObj} - const myFile = fs.createWriteStream(directory + myObj) - s3Ctl.getObject(myParams). - on('httpData', function(chunk) { myFile.write(chunk) }). - on('httpDone', function() { myFile.end() }). - send() - } -} - -// __ __ ______ __ __ __ ______ __ __ -// /\ "-./ \ /\ __ \ /\ \ /\ "-.\ \ /\ ___\ /\ \ /\ \ -// \ \ \-./\ \ \ \ __ \ \ \ \ \ \ \-. \ \ \ \____ \ \ \____ \ \ \ -// \ \_\ \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_____\ \ \_____\ \ \_\ -// \/_/ \/_/ \/_/\/_/ \/_/ \/_/ \/_/ \/_____/ \/_____/ \/_/ - -const outputFile = outputDir + '/' + company[0].companyName -const outputDocFileName = outputFile + '.docx' - - -// Set key properties for the document -const creator = 'mediumroast.io barista robot' -const title = company[0].companyName + ' Company Report' -const description = 'A report snapshot including firmographics and interactions for: ' - + company[0].companyName - -// Get the first page for the company that includes firmographics -const companyData = new Firmographics(company, interactions, 'Interactions/', tags, quotes) - -let doc = new docx.Document ({ - creator: creator, - company: authorCompany, - title: title, - description: description, - styles: {default: docCtl.styling.default}, - numbering: docCtl.styling.numbering, - sections: [{ - properties: {}, - children: companyData.companyDoc, - }], -}) - -// If needed create the zip package -if (createPackage) { - - const fileName = 'Company Report.docx' - const interactionsDir = 'Interactions/' - - // As needed create the working directory - const workingDirectory = workDir + '/' + company[0].companyName + '/' - safeMakedir(workingDirectory) - safeMakedir(workingDirectory + interactionsDir) - - // Write the report to the working directory - writeReport(doc, workingDirectory + fileName) - - // Download the objects - await downloadInteractions(interactions, workingDirectory + interactionsDir) - -} else if (createArchive) { - const outputPackage = outputFile + '.zip' - // Create the zip package - await createZIPArchive(outputPackage, workDir + '/' + company[0].companyName) -} else { - writeReport(doc, outputDocFileName) -} \ No newline at end of file diff --git a/cli/mr_backup.js b/cli/mr_backup.js index 7321095..bb0bc8c 100644 --- a/cli/mr_backup.js +++ b/cli/mr_backup.js @@ -1,5 +1,13 @@ #!/usr/bin/env node +/** + * A CLI utility to backup and restore data from the mediumroast.io + * @author Michael Hay + * @file mr_backup.js + * @copyright 2022 Mediumroast, Inc. All rights reserved. + * @license Apache-2.0 + */ + // Import required modules import { Auth, Companies, Interactions, Studies, Users } from '../src/api/mrServer.js' import CLI from '../src/helpers.js' diff --git a/src/report/companies.js b/src/report/companies.js index f1099d1..9f90043 100644 --- a/src/report/companies.js +++ b/src/report/companies.js @@ -1,3 +1,11 @@ +/** + * Two classes to create sections and documents for company objects in mediumroast.io + * @author Michael Hay + * @file companies.js + * @copyright 2022 Mediumroast, Inc. All rights reserved. + * @license Apache-2.0 + */ + // Import required modules import docx from 'docx' import boxPlot from 'box-plot' diff --git a/src/report/company.js b/src/report/company.js deleted file mode 100644 index fb9242c..0000000 --- a/src/report/company.js +++ /dev/null @@ -1,271 +0,0 @@ -// Import required modules -import docx from 'docx' -import References from './interactions.js' -import KeyThemes from './themes.js' - -class Firmographics { - // Consider a switch between HTML and DOCX - // NOTE This may not be needed for the HTML version more thinking needed - constructor(company, interactions, protocol, themes, quotes) { - // Decode the regions - const regions = { - AMER: 'Americas', - EMEA: 'Europe, Middle East and Africa', - APAC: 'Asia Pacific and Japan' - } - - // Set the company Type - company[0].stockSymbol === 'Unknown' && company[0].cik === 'Unknown' ? - this.companyType = 'Private' : - this.companyType = 'Public' - - this.company = company[0] - this.region = regions[company[0].region] - this.font = 'Avenir Next' // We need to pass this in from the config file - this.fontSize = 10 // We need to pass this in from the config file - this.fontFactor = 1.5 - this.interactions = interactions - this.protocol = protocol ? protocol : false - this.themes = themes ? themes : false - this.quotes = quotes ? quotes : false - this.companyDoc = this.doc() - } - - // Define the CIK and link it to an EDGAR search if available - stockSymbolRow () { - if (this.company.stockSymbol === 'Unknown') { - return this.basicRow('Stock Symbol', this.company.stockSymbol) - } else { - const baseURL = 'https://www.bing.com/search?q=' - return this.urlRow('Stock Symbol', this.company.stockSymbol, baseURL + this.company.stockSymbol) - } - } - - // Define the CIK and link it to an EDGAR search if available - cikRow () { - if (this.company.cik === 'Unknown') { - return this.basicRow('CIK', this.company.cik) - } else { - const baseURL = 'https://www.sec.gov/edgar/search/#/ciks=' - return this.urlRow('CIK', this.company.cik, baseURL + this.company.cik) - } - } - - // Create the website row - urlRow(category, name, link) { - // define the link to the target URL - const myUrl = new docx.ExternalHyperlink({ - children: [ - new docx.TextRun({ - text: name, - style: 'Hyperlink', - font: this.font, - size: this.fontFactor * this.fontSize - }) - ], - link: link - }) - - // return the row - return new docx.TableRow({ - children: [ - new docx.TableCell({ - width: { - size: 20, - type: docx.WidthType.PERCENTAGE - }, - children: [this.makeParagraph(category, this.fontFactor * this.fontSize, true)] - }), - new docx.TableCell({ - width: { - size: 80, - type: docx.WidthType.PERCENTAGE - }, - children: [new docx.Paragraph({children:[myUrl]})] - }) - ] - }) - } - - // Basic row to produce a name/value pair - basicRow (name, data) { - // return the row - return new docx.TableRow({ - children: [ - new docx.TableCell({ - width: { - size: 20, - type: docx.WidthType.PERCENTAGE - }, - children: [this.makeParagraph(name, this.fontFactor * this.fontSize, true)] - }), - new docx.TableCell({ - width: { - size: 80, - type: docx.WidthType.PERCENTAGE - }, - children: [this.makeParagraph(data, this.fontFactor * this.fontSize)] - }) - ] - }) - } - - // Create the table for the doc - docTable() { - // Define the address string - const addressBits = [ - this.company.streetAddress, - this.company.city, - this.company.stateProvince, - this.company.zipPostal, - this.company.country - ] - const addressBaseUrl = 'https://www.google.com/maps/place/' - let addressSearch = "" - for (const element in addressBits) { - let tmpString = addressBits[element] - tmpString = tmpString.replace(' ', '+') - addressSearch+='+' + tmpString - } - const addressUrl = addressBaseUrl + encodeURIComponent(addressSearch) - const addressString = addressBits[0] + ', ' + - addressBits[1] + ', ' + addressBits[2] + ' ' + addressBits[3] + ', ' + - addressBits[4] - - const patentString = this.company.companyName + ' Patent Search' - const patentURL = 'https://patents.google.com/?assignee=' + this.company.companyName - - const newsString = this.company.companyName + ' Company News' - const newsURL = 'https://news.google.com/search?q=' + this.company.companyName - - // define the table with firmographics - const myTable = new docx.Table({ - columnWidths: [20, 80], - rows: [ - this.basicRow('Name', this.company.companyName), - this.basicRow('Description', this.company.description), - this.urlRow('Website', this.company.url, this.company.url), - this.basicRow('Role', this.company.role), - this.basicRow('Industry', this.company.industry), - this.urlRow('Patents', patentString, patentURL), - this.urlRow('News', newsString, newsURL), - this.urlRow('Location', addressString, addressUrl), - this.basicRow('Region', this.region), - this.basicRow('Phone', this.company.phone), - this.basicRow('Type', this.companyType), - this.stockSymbolRow(), - this.cikRow(), - this.basicRow('No. Interactions', String(this.company.totalInteractions)), - this.basicRow('No. Studies', String(this.company.totalStudies)), - // TODO Add Rows and maybe URLs for current interaction and study - // NOTE this requires the URL for the mr_backend - ], - width: { - size: 100, - type: docx.WidthType.PERCENTAGE - } - }) - - return myTable - } - - // For a section of prose create a paragraph - makeParagraph (paragraph, size, bold) { - return new docx.Paragraph({ - children: [ - new docx.TextRun({ - text: paragraph, - font: this.font, - size: size ? size : 20, - bold: bold ? bold : false - }) - ] - }) - } - - // Create a title of heading style 1 - makeTitle(title) { - return new docx.Paragraph({ - text: title, - heading: docx.HeadingLevel.HEADING_1 - }) - } - - makeActions () { - // TODO inherit bullet and numbering styles in doc_company - let actionArray = [] - for (const action in this.company.document.Action) { - if (action === 'text') { continue } - const protoAction = this.company.document.Action[action] - const [actionText, actionStatus] = protoAction.split('|') - // console.log(actionText, actionStatus) - actionArray.push( - new docx.Paragraph({ - text: actionText, - numbering: { - reference: 'number-styles', - level: 0 - } - }), - new docx.Paragraph({ - text: actionStatus, - numbering: { - reference: 'number-styles', - level: 1 - } - }) - ) - } - return actionArray - } - - // Create a page break - pageBreak() { - return new docx.Paragraph({ - children: [ - new docx.PageBreak() - ] - }) - } - - // Generate a page with all company firmographics - doc() { - - // Create the references from supplied interactions - const refCtl = new References( - this.interactions, - this.company.companyName, - 'company', - this.protocol) - const myReferences = refCtl.makeDocx() - - // Create summary themes from supplied themes if needed - let myThemes = [] - if (this.themes) { - const themeCtl = new KeyThemes('summary', this.themes, this.quotes, this.interactions) - myThemes = themeCtl.makeDocx() - } - - return [].concat( - [this.makeTitle('Introduction'), // Intro title - this.makeParagraph(this.company.document.Introduction), // Introduction paragraph - this.makeTitle('Purpose'), // Intro title - this.makeParagraph(this.company.document.Purpose), // Purpose paragraph - this.makeTitle('Actions'), // Actions title - this.makeParagraph(this.company.document.Action.text)], - ...this.makeActions(), - [this.pageBreak(), // Add a page break - this.makeTitle('Firmographics'), // Firmographics title - this.docTable(), // Table containing firmographics - ...myThemes, // Section for the summary theme if available - this.pageBreak(), - this.makeTitle('References')], - ...myReferences - ) - } - - - -} - -export default Firmographics \ No newline at end of file diff --git a/src/report/interactions.js b/src/report/interactions.js index cbd6036..ff81d1c 100644 --- a/src/report/interactions.js +++ b/src/report/interactions.js @@ -1,7 +1,14 @@ +/** + * Two classes to create sections and documents for interaction objects in mediumroast.io + * @author Michael Hay + * @file interactions.js + * @copyright 2022 Mediumroast, Inc. All rights reserved. + * @license Apache-2.0 + */ + // Import required modules import docx from 'docx' import boxPlot from 'box-plot' - import Utilities from './common.js' import { CompanySection } from './companies.js' @@ -16,147 +23,12 @@ class InteractionSection { this.interactions = interactions this.characterLimit = characterLimit - // TODO anything that is commented out can likely be removed - // this.introduction = 'The mediumroast.io system has automatically generated this section.' + - // ' It includes key metadata from each interaction associated to the object ' + objectName + - // '. If this report document is produced as a package, instead of standalone, then the' + - // ' hyperlinks are active and will link to documents on the local folder after the' + - // ' package is opened.' this.objectName = objectName this.objectType = objectType - // TODO anything that is commented out can likely be removed - // this.font = 'Avenir Next' // We need to pass this in from the config file this.fontSize = 10 // We need to pass this in from the config file - // this.protocol = protocol - // this.protoDoc = this.createRefs() this.util = new Utilities() } - // Create the entire section as a proto document to be fed to a format like docx, ..., html. - // createRefs() { - // let protoDoc = { - // intro: this.introduction, - // references: {} - // } - // for (const item in this.interactions) { - // protoDoc.references[this.interactions[item].interactionName] = this.createRef(this.interactions[item]) - // } - // return protoDoc - // } - - // // Create an individual reference from an interaction - // createRef(interaction, dateKey = 'date', timeKey = 'time', httpType = 'http') { - // // NOTE need to think about the URL and how to properly unpack it - // // example, swap to from X to http and then preserve it as a - // // part of the protoDoc - - // // Decode the date - // const myDate = interaction[dateKey] - // const [year, month, day] = [myDate.substring(0, 4), myDate.substring(4, 6), myDate.substring(6, 8)] - - // // Decode the time - // const myTime = interaction[timeKey] - // const [hour, min] = [myTime.substr(0, 2), myTime.substr(2, 4)] - - // // Detect the repository type and replace it with http - // // NOTE This is setup to create a local package with source links in a local working directory - // const repoType = interaction.url.split('://')[0] - // let myURL = interaction.url.split('://').pop() - // myURL = this.protocol + myURL.split('/').pop() - - // // Create the reference - // let reference = { - // type: interaction.interactionType, // TODO There is a bug here in the ingestion - // abstract: interaction.abstract.substr(0, this.characterLimit) + '...', - // date: year + '-' + month + '-' + day, - // time: hour + ':' + min, - // url: myURL, - // repo: repoType, - // guid: interaction.GUID - // } - // // Set the object type and name - // reference[this.objectType] = this.objectName - - // return reference - // } - - // // Create a paragraph - // makeParagraph(paragraph, size, bold) { - // return new docx.Paragraph({ - // children: [ - // new docx.TextRun({ - // text: paragraph, - // font: this.font, - // size: size ? size : 20, - // bold: bold ? bold : false, - // }) - // ] - // }) - // } - - // // Create a title of heading style 2 - // makeTitle(title, ident) { - // return new docx.Paragraph({ - // text: text, - // heading: docx.HeadingLevel.HEADING_2 - // }) - // } - - // // Create a text run - // makeTextrun(text) { - // return new docx.TextRun({ - // text: text, - // font: this.font, - // size: 1.5 * this.fontSize, - // }) - // } - - // makeURL(name, link) { - // return new docx.ExternalHyperlink({ - // children: [ - // new docx.TextRun({ - // text: name, - // style: 'Hyperlink', - // font: this.font, - // size: 1.5 * this.fontSize - // }) - // ], - // link: link - // }) - // } - - // // Return the proto document as a docx formatted section - // makeDocx() { - // const excerptAnchor = this.util.makeInternalHyperLink('Summary Excerpts', 'summary_excerpts') - // let finaldoc = [this.makeParagraph(this.protoDoc.intro)] - - // for (const myReference in this.protoDoc.references) { - // // String(this.protoDoc.references[myReference].guid) - // finaldoc.push(this.util.makeBookmark2(myReference, String(this.protoDoc.references[myReference].guid).substring(0, 40))) - // finaldoc.push(this.makeParagraph( - // this.protoDoc.references[myReference].abstract, - // 1.5 * this.fontSize)) - // const permaLink = this.makeURL( - // 'Document link', - // this.protoDoc.references[myReference].url) - - // finaldoc.push( - // new docx.Paragraph({ - // children: [ - // this.makeTextrun('[ '), - // permaLink, - // this.makeTextrun(' | Date: ' + this.protoDoc.references[myReference].date + ' | '), - // this.makeTextrun('Time: ' + this.protoDoc.references[myReference].time + ' | '), - // this.makeTextrun('Type: ' + this.protoDoc.references[myReference].type + ' | '), - // excerptAnchor, - // this.makeTextrun(' ]'), - // ] - // }) - // ) - // } - // return finaldoc - // } - // Generate the descriptions for interactions makeDescriptions () { // TODO create bookmark with the right kind of heading diff --git a/src/report/themes.js b/src/report/themes.js deleted file mode 100644 index 9343338..0000000 --- a/src/report/themes.js +++ /dev/null @@ -1,127 +0,0 @@ -// Import modules -import docx from 'docx' -import Utilities from './common.js' - -class KeyThemes { - constructor (themeType, themes, quotes, interactions) { - this.type = themeType - this.themes = themes ? themes : false - this.quotes = quotes ? quotes : false - this.interactions = interactions ? interactions : false - this.font = 'Avenir Next' // We need to pass this in from the config file - this.fontSize = 10 // We need to pass this in from the config file - this.fontFactor = 1.5 - this.util = new Utilities() - this.introduction = 'The mediumroast.io system has automatically generated this section. ' + - 'If this section is for a summary theme, then two sections are included: ' + - '1. Information for the summary theme including key words, score and rank. ' + - '2. Excerpts from the interactions within the sub-study and links to each interaction reference.' - } - - basicThemeRow (theme, score, rank, bold) { - // return the row - return new docx.TableRow({ - children: [ - new docx.TableCell({ - width: { - size: 60, - type: docx.WidthType.PERCENTAGE, - font: this.font, - }, - children: [this.util.makeParagraph(theme, this.fontFactor * this.fontSize, bold ? true : false)] - }), - new docx.TableCell({ - width: { - size: 20, - type: docx.WidthType.PERCENTAGE, - font: this.font, - }, - children: [this.util.makeParagraph(score, this.fontFactor * this.fontSize, bold ? true : false)] - }), - new docx.TableCell({ - width: { - size: 20, - type: docx.WidthType.PERCENTAGE, - font: this.font, - }, - children: [this.util.makeParagraph(rank, this.fontFactor * this.fontSize, bold ? true : false)] - }), - ] - }) - } - - // Create the table for the doc - summaryThemeTable(themes) { - let myRows = [this.basicThemeRow('Topic Keywords', 'Score', 'Rank', true)] - for (const theme in themes) { - myRows.push(this.basicThemeRow(theme, themes[theme].score.toFixed(2), themes[theme].rank)) - } - // define the table with the summary theme information - const myTable = new docx.Table({ - columnWidths: [60, 20, 20], - rows: myRows, - width: { - size: 100, - type: docx.WidthType.PERCENTAGE - } - }) - - return myTable - } - - getInteractionName (guid, interactions) { - for (const interaction in interactions) { - if (String(guid) === String(interactions[interaction].GUID)) { - return interactions[interaction].interactionName - } else { - continue - } - } - return 'Unknown Interaction Name' - } - - // Create the theme quotes/excerpts for the doc - summaryQuotes(quotes, interactions) { - let myQuotes = [] - for (const quote in quotes) { - const myInteraction = this.getInteractionName(quote, interactions) - myQuotes.push( - this.util.makeParagraph('"' + quotes[quote].quotes[0] + '"', this.fontFactor * this.fontSize, false, 0), - new docx.Paragraph({ - children: [ - this.util.makeTextrun('Source: '), - // TODO open a GitHub issue for this behavior: - // A bookmark can only handle a substring from 0:40, and it will automatically - // truncate to that length. However when there is an internal hyperlink there is - // no similar truncation. This leads to the internal hyperlink having an incorrect - // reference. - // NOTE move the substring into the method to create an internal hyperlink - this.util.makeInternalHyperLink(myInteraction, String(quote).substring(0,40)), - this.util.makeTextrun('', 2), - ], - }) - ) - } - return myQuotes - } - - makeDocx () { - if (this.type === 'summary') { - const excerpts = this.summaryQuotes(this.quotes, this.interactions) - return [ - this.util.pageBreak(), - this.util.makeHeading1('Summary Theme: Table and Excerpts'), - this.util.makeParagraph(this.introduction, 0), - this.util.makeHeading2('Summary Theme Table'), - this.summaryThemeTable(this.themes), - this.util.makeBookmark2('Summary Theme Excerpts', 'summary_excerpts'), - ...excerpts - ] - } else { - return [] - } - - } -} - -export default KeyThemes \ No newline at end of file From ee2ea888d3185197e4f501002c702ab25f019ce6 Mon Sep 17 00:00:00 2001 From: mihay42 Date: Sat, 10 Sep 2022 20:12:46 -0700 Subject: [PATCH 2/7] Cleaned and commented report/interactions.js --- cli/interaction.js | 2 +- src/api/mrServer.js | 1 + src/api/scaffold.js | 1 + src/report/common.js | 8 +++ src/report/companies.js | 4 +- src/report/interactions.js | 122 +++++++++++++++++++------------------ 6 files changed, 75 insertions(+), 63 deletions(-) diff --git a/cli/interaction.js b/cli/interaction.js index c35d900..b1d39a9 100755 --- a/cli/interaction.js +++ b/cli/interaction.js @@ -91,7 +91,7 @@ if (myArgs.report) { } // Create the document - const [report_success, report_stat, report_result] = await docController.makeDocx(fileName, myArgs.package) + const [report_success, report_stat, report_result] = await docController.makeDOCX(fileName, myArgs.package) // Create the package and cleanup as needed if (myArgs.package) { diff --git a/src/api/mrServer.js b/src/api/mrServer.js index b5ba309..381633d 100644 --- a/src/api/mrServer.js +++ b/src/api/mrServer.js @@ -4,6 +4,7 @@ * @file mrServer.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @version 1.0.0 */ // Import required modules diff --git a/src/api/scaffold.js b/src/api/scaffold.js index 125515e..a15bcba 100644 --- a/src/api/scaffold.js +++ b/src/api/scaffold.js @@ -4,6 +4,7 @@ * @file scaffold.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @version 1.0.0 */ // Import required modules diff --git a/src/report/common.js b/src/report/common.js index e0098ce..6b51710 100644 --- a/src/report/common.js +++ b/src/report/common.js @@ -604,6 +604,14 @@ class Utilities { return myTable } + + // Create an introductory section + makeIntro (introText) { + return [ + this.makeHeading1('Introduction'), + this.makeParagraph(introText) + ] + } } export default Utilities \ No newline at end of file diff --git a/src/report/companies.js b/src/report/companies.js index 9f90043..e6cb092 100644 --- a/src/report/companies.js +++ b/src/report/companies.js @@ -275,11 +275,11 @@ class CompanyStandalone { this.util.topicTable(this.topics), this.util.makeHeadingBookmark1('Interaction Summaries', 'interaction_summaries') ], - ...interactionSection.makeDescriptions(), + ...interactionSection.makeDescriptionsDOCX(), [ this.util.pageBreak(), this.util.makeHeading1('References') ], - ...interactionSection.makeReferences(isPackage) + ...interactionSection.makeReferencesDOCX(isPackage) ) // Construct the document diff --git a/src/report/interactions.js b/src/report/interactions.js index ff81d1c..b395e67 100644 --- a/src/report/interactions.js +++ b/src/report/interactions.js @@ -4,17 +4,31 @@ * @file interactions.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @version 1.0.0 */ // Import required modules import docx from 'docx' -import boxPlot from 'box-plot' import Utilities from './common.js' import { CompanySection } from './companies.js' - +/** + * A high level class to create sections for an Interaction report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. These sections are designed to be consumed + * by a wrapping document which could be for any one of the mediumroast objects. + * + * To operate this class the constructor should be passed an array of interaction + * objects, the name of the object that is using this class, and the type of object + * that is calling this class. + * + * From there two methods can be called: + * 1. makeDescriptionsDOCX() + * 2. makeReferencesDOCX() + * @class + */ class InteractionSection { - constructor(interactions, objectName, objectType, characterLimit = 1000) { + constructor(interactions, objectName, objectType) { // NOTE creation of a ZIP package is something we likely need some workspace for // since the documents should be downloaded and then archived. Therefore, @@ -22,25 +36,31 @@ class InteractionSection { // we will need some server side logic to make this happen. this.interactions = interactions - this.characterLimit = characterLimit this.objectName = objectName this.objectType = objectType this.fontSize = 10 // We need to pass this in from the config file this.util = new Utilities() } - // Generate the descriptions for interactions - makeDescriptions () { - // TODO create bookmark with the right kind of heading + /** + * Generate the descriptions for interactions in the DOCX format + * @param {Returns} result An array containing a section description and a table of interaction descriptions + */ + makeDescriptionsDOCX () { + // Set the number of interactions for use later const noInteractions = this.interactions.length + + // Create the header row for the descriptions let myRows = [this.util.descriptionRow('Id', 'Description', true)] - // TODO ids should be hyperlinks to the actual interaction which is interaction_ + // Loop over the interactions and pull out the interaction ids and descriptions for (const interaction in this.interactions) { myRows.push(this.util.descriptionRow( + // Create the internal hyperlink for the interaction reference this.util.makeInternalHyperLink( this.interactions[interaction].id, 'interaction_' + String(this.interactions[interaction].id) ), + // Pull in the description this.interactions[interaction].description ) ) @@ -56,6 +76,7 @@ class InteractionSection { } }) + // Return the results as an array return [ this.util.makeParagraph( 'This section contains descriptions for the ' + noInteractions + ' interactions associated to the ' + @@ -66,15 +87,19 @@ class InteractionSection { ] } - // Create the references for calling programs - makeReferences(isPackage, independent=false) { + /** + * Create the references for calling programs in the DOCX format + * @param {Boolean} isPackage When set to true links are set up for connecting to interaction documents + * @param {Returns} result An array containing a section description and a table of interaction references + */ + makeReferencesDOCX(isPackage) { // Link this back to the descriptions section const descriptionsLink = this.util.makeInternalHyperLink( 'Back to Interaction Summaries', 'interaction_summaries' ) - // Create the array for the references with the introduction + // Create the array for the references starting with the introduction let references = [ this.util.makeParagraph( 'The mediumroast.io system has automatically generated this section.' + @@ -85,7 +110,9 @@ class InteractionSection { ) ] + // Loop over all interactions for (const interaction in this.interactions) { + // Create the link to the underlying interaction document const objWithPath = this.interactions[interaction].url.split('://').pop() const myObj = objWithPath.split('/').pop() @@ -97,7 +124,7 @@ class InteractionSection { // Depending upon if this is a package or not create the metadata strip with/without document link let metadataStrip = null if(isPackage) { - // Package version of the strip + // isPackage version of the strip metadataStrip = new docx.Paragraph({ spacing: { before: 100, @@ -115,7 +142,7 @@ class InteractionSection { ] }) } else { - // Non package version of the strip + // Non isPackage version of the strip metadataStrip = new docx.Paragraph({ spacing: { before: 100, @@ -161,10 +188,20 @@ class InteractionSection { // Return the built up references return references } - - } +/** + * A high level class to create a complete document for an Interaction report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. + * + * To operate this class the constructor should be passed a single interaction + * object, the associated company, document creator and authoring company. + * + * From there one method can be called: + * 1. makeDOCX() + * @class + */ class InteractionStandalone { constructor(interaction, company, creator, authorCompany) { this.creator = creator @@ -180,48 +217,10 @@ class InteractionStandalone { ' package is opened.' this.abstract = interaction.abstract this.util = new Utilities() - // TODO test rankTags in common.js - this.topics = this.rankTags(this.interaction.topics) - // this.topics = this.util.rankTags(this.interaction.topics) - } - - // TODO remove this and rely on the one in utils - rankTags (tags) { - const ranges = boxPlot(Object.values(this.interaction.topics)) - let finalTags = {} - for (const tag in tags) { - // Rank the tag score using the ranges derived from box plots - // if > Q3 then the ranking is high - // if in between Q2 and Q3 then the ranking is medium - // if < Q3 then the ranking is low - let rank = null - if (tags[tag] > ranges.upperQuartile) { - rank = 'High' - } else if (tags[tag] < ranges.lowerQuartile) { - rank = 'Low' - } else if (ranges.lowerQuartile <= tags[tag] <= ranges.upperQuartile) { - rank = 'Medium' - } - - finalTags[tag] = { - score: tags[tag], // Math.round(tags[tag]), - rank: rank - } - - } - return finalTags + this.topics = this.util.rankTags(this.interaction.topics) } - // TODO consider moving this to common - makeIntro () { - const myIntro = [ - this.util.makeHeading1('Introduction'), - this.util.makeParagraph(this.introduction) - ] - return myIntro - } - - metadataTable (isPackage) { + metadataTableDOCX (isPackage) { // Switch the name row if depending upon if this is a package or not const objWithPath = this.interaction.url.split('://').pop() const myObj = objWithPath.split('/').pop() @@ -230,7 +229,6 @@ class InteractionStandalone { nameRow = this.util.urlRow('Interaction Name', this.interaction.name, './interactions/' + myObj) : nameRow = this.util.basicRow('Interaction Name', this.interaction.name) - const myTable = new docx.Table({ columnWidths: [20, 80], rows: [ @@ -248,8 +246,12 @@ class InteractionStandalone { return myTable } - // Create the document - async makeDocx(fileName, isPackage) { + /** + * Create the DOCX document for a single interaction which includes a company section + * @param {String} fileName Full path to the file name, if no file name is supplied a default is assumed + * @param {Boolean} isPackage When set to true links are set up for connecting to interaction documents + */ + async makeDOCX(fileName, isPackage) { // If fileName isn't specified create a default fileName = fileName ? fileName : process.env.HOME + '/Documents/' + this.interaction.name.replace(/ /g,"_") + '.docx' @@ -258,10 +260,10 @@ class InteractionStandalone { // Set up the default options for the document const myDocument = [].concat( - this.makeIntro(), + this.util.makeIntro(this.introduction), [ this.util.makeHeading1('Interaction Detail'), - this.metadataTable(isPackage), + this.metadataTableDOCX(isPackage), this.util.makeHeading1('Topics'), this.util.topicTable(this.topics), this.util.makeHeading1('Abstract'), From 4017ba36b4ec1d210d262baedd3aa7ca42826cc5 Mon Sep 17 00:00:00 2001 From: mihay42 Date: Sat, 10 Sep 2022 20:58:02 -0700 Subject: [PATCH 3/7] Cleanup 2: Commented & cleaned report/companies.js --- cli/company.js | 2 +- src/report/companies.js | 71 +++++++++++++++++++++++++++++--------- src/report/interactions.js | 35 +++++++++++++++---- 3 files changed, 84 insertions(+), 24 deletions(-) diff --git a/cli/company.js b/cli/company.js index 0c57ae1..ff50c95 100755 --- a/cli/company.js +++ b/cli/company.js @@ -97,7 +97,7 @@ if (myArgs.report) { } // Create the document - const [report_success, report_stat, report_result] = await docController.makeDocx(fileName, myArgs.package) + const [report_success, report_stat, report_result] = await docController.makeDOCX(fileName, myArgs.package) // Create the package and cleanup as needed if (myArgs.package) { diff --git a/src/report/companies.js b/src/report/companies.js index e6cb092..bd14710 100644 --- a/src/report/companies.js +++ b/src/report/companies.js @@ -4,6 +4,7 @@ * @file companies.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @version 1.0.0 */ // Import required modules @@ -12,7 +13,25 @@ import boxPlot from 'box-plot' import Utilities from './common.js' import { InteractionSection } from './interactions.js' +/** + * A high level class to create sections for a Company report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. These sections are designed to be consumed + * by a wrapping document which could be for any one of the mediumroast objects. + * + * To operate this class the constructor should be passed a single company object. + * + * From there two methods can be called: + * 1. makeFirmographicsDOCX() + * 2. makeComparisonsDOCX() + * @class + */ class CompanySection { + /** + * @constructor + * To operate this class the constructor should be passed a single company object. + * @param {Object} company - The company object to generate the section(s) for + */ constructor(company) { this.company = company this.company.stock_symbol === 'Unknown' && this.company.cik === 'Unknown' ? @@ -87,7 +106,12 @@ class CompanySection { } } - makeFirmographics() { + /** + * @function makeFirmographicsDOCX + * Create a table containing key information for the company in question + * @returns {Object} A docx table is return to the caller + */ + makeFirmographicsDOCX() { const noInteractions = String(Object.keys(this.company.linked_interactions).length) const noStudies = String(Object.keys(this.company.linked_studies).length) const myTable = new docx.Table({ @@ -165,7 +189,13 @@ class CompanySection { return [finalComparisons, rankPicker] } - makeComparison(comparisons) { + /** + * @function makeComparisonDOCX + * Generate the comparisons section for the document from the company in question + * @param {Object} comparisons - the object containing the comparisons for the company in question + * @returns {Array} An array containing an introduction to this section and the table with the comparisons + */ + makeComparisonDOCX(comparisons) { // Transform the comparisons into something that is usable for display const [myComparison, picks] = this.rankComparisons(comparisons) @@ -212,6 +242,18 @@ class CompanySection { } } +/** + * A high level class to create a complete document for a Company report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. + * + * To operate this class the constructor should be passed a single company + * object, the associated array of interactions, document creator and authoring company. + * + * From there one method can be called: + * 1. makeDOCX() + * @class + */ class CompanyStandalone { constructor(company, interactions, creator, authorCompany) { this.objectType = 'Company' @@ -232,17 +274,14 @@ class CompanyStandalone { this.noInteractions = String(Object.keys(this.company.linked_interactions).length) } - // TODO Move to common.js - makeIntro () { - const myIntro = [ - this.util.makeHeading1('Introduction'), - this.util.makeParagraph(this.introduction) - ] - return myIntro - } - - - async makeDocx(fileName, isPackage) { + /** + * @async + * @function makeDocx + * @param {String} fileName - Full path to the file name, if no file name is supplied a default is assumed + * @param {Boolean} isPackage - When set to true links are set up for connecting to interaction documents + * @returns {Array} The result of the writeReport function that is an Array + */ + async makeDOCX(fileName, isPackage) { // If fileName isn't specified create a default fileName = fileName ? fileName : process.env.HOME + '/Documents/' + this.company.name.replace(/ /g,"_") + '.docx' @@ -259,13 +298,13 @@ class CompanyStandalone { // Set up the default options for the document const myDocument = [].concat( - this.makeIntro(), + this.util.makeIntro(this.introduction), [ this.util.makeHeading1('Company Detail'), - companySection.makeFirmographics(), + companySection.makeFirmographicsDOCX(), this.util.makeHeading1('Comparison') ], - companySection.makeComparison(this.comparison), + companySection.makeComparisonDOCX(this.comparison), [ this.util.makeHeading1('Topics'), this.util.makeParagraph( 'The following topics were automatically generated from all ' + diff --git a/src/report/interactions.js b/src/report/interactions.js index b395e67..8551a66 100644 --- a/src/report/interactions.js +++ b/src/report/interactions.js @@ -28,6 +28,13 @@ import { CompanySection } from './companies.js' * @class */ class InteractionSection { + /** + * @constructor + * Construct the InteractionSection object + * @param {Array} interactions - a complete array of interactions ranging from 1:N + * @param {String} objectName - the name of the object calling this class + * @param {String} objectType - the type of object calling this class + */ constructor(interactions, objectName, objectType) { // NOTE creation of a ZIP package is something we likely need some workspace for @@ -43,8 +50,9 @@ class InteractionSection { } /** - * Generate the descriptions for interactions in the DOCX format - * @param {Returns} result An array containing a section description and a table of interaction descriptions + * @function makeDescriptionsDOCX + * Make the descriptions for interactions in the DOCX format + * @returns {Array} An array containing a section description and a table of interaction descriptions */ makeDescriptionsDOCX () { // Set the number of interactions for use later @@ -88,9 +96,10 @@ class InteractionSection { } /** + * @function makeReferencesDOCX * Create the references for calling programs in the DOCX format - * @param {Boolean} isPackage When set to true links are set up for connecting to interaction documents - * @param {Returns} result An array containing a section description and a table of interaction references + * @param {Boolean} isPackage - When set to true links are set up for connecting to interaction documents + * @returns {Array} An array containing a section description and a table of interaction references */ makeReferencesDOCX(isPackage) { // Link this back to the descriptions section @@ -203,6 +212,15 @@ class InteractionSection { * @class */ class InteractionStandalone { + /** + * @constructor + * The constructor should be passed a single interaction + * object, the associated company, document creator and authoring company. + * @param {Object} interaction - The interaction in question to process + * @param {Object} company - The company associated to the interaction + * @param {String} creator - A string defining the creator for this document + * @param {String} authorCompany - A string containing the company who authored the document + */ constructor(interaction, company, creator, authorCompany) { this.creator = creator this.authorCompany = authorCompany @@ -247,9 +265,12 @@ class InteractionStandalone { } /** + * @async + * @function makeDOCX * Create the DOCX document for a single interaction which includes a company section - * @param {String} fileName Full path to the file name, if no file name is supplied a default is assumed - * @param {Boolean} isPackage When set to true links are set up for connecting to interaction documents + * @param {String} fileName - Full path to the file name, if no file name is supplied a default is assumed + * @param {Boolean} isPackage - When set to true links are set up for connecting to interaction documents + * @returns {Array} The result of the writeReport function that is an Array */ async makeDOCX(fileName, isPackage) { // If fileName isn't specified create a default @@ -269,7 +290,7 @@ class InteractionStandalone { this.util.makeHeading1('Abstract'), this.util.makeParagraph(this.abstract), this.util.makeHeading1('Company Detail'), - companySection.makeFirmographics() + companySection.makeFirmographicsDOCX() ]) // Construct the document From 965030b8a5247cae56a6c2c59d938ca0cbfe20d1 Mon Sep 17 00:00:00 2001 From: mihay42 Date: Sun, 11 Sep 2022 20:41:59 -0700 Subject: [PATCH 4/7] Cleanup 3: cli.js, helpers.js & their impl --- cli/company.js | 13 +- cli/interaction.js | 15 ++- src/cli.js | 234 ++++++++++++++++++++++++++++++++++++ src/helpers.js | 260 ++++++++++------------------------------ src/report/companies.js | 1 + 5 files changed, 316 insertions(+), 207 deletions(-) create mode 100644 src/cli.js diff --git a/cli/company.js b/cli/company.js index ff50c95..3abcb14 100755 --- a/cli/company.js +++ b/cli/company.js @@ -6,24 +6,29 @@ * @file company.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @version 2.0.0 */ // Import required modules import { Auth, Companies, Interactions } from '../src/api/mrServer.js' -import { CLI } from '../src/helpers.js' +import { Utilities } from '../src/helpers.js' +import { CLIUtilities } from '../src/cli.js' import { CompanyStandalone } from '../src/report/companies.js' // Globals const objectType = 'Companies' // Construct the CLI object -const myCLI = new CLI ( +const myCLI = new CLIUtilities ( '2.0', 'company', 'Command line interface for mediumroast.io Company objects.', objectType ) +// Construct the Utilities object +const utils = new Utilities(objectType) + // Create the environmental settings const myArgs = myCLI.parseCLIArgs() const myConfig = myCLI.getConfig(myArgs.conf_file) @@ -74,7 +79,7 @@ if (myArgs.report) { if(myArgs.package) { // Create the working directory - const [dir_success, dir_msg, dir_res] = myCLI.safeMakedir(baseDir + '/interactions') + const [dir_success, dir_msg, dir_res] = utils.safeMakedir(baseDir + '/interactions') // If the directory creations was successful download the interaction if(dir_success) { @@ -88,7 +93,7 @@ if (myArgs.report) { access points, but the tradeoff would be that caffeine would need to run on a system with file system access to these objects. */ - await myCLI.s3DownloadObjs(interactions, myEnv, baseDir + '/interactions') + await utils.s3DownloadObjs(interactions, myEnv, baseDir + '/interactions') // Else error out and exit } else { console.error('ERROR (%d): ' + dir_msg, -1) diff --git a/cli/interaction.js b/cli/interaction.js index b1d39a9..224ce75 100755 --- a/cli/interaction.js +++ b/cli/interaction.js @@ -6,23 +6,26 @@ * @file interactions.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @version 2.0.0 */ // Import required modules import { Auth, Interactions, Companies } from '../src/api/mrServer.js' -import { CLI } from '../src/helpers.js' +import { CLIUtilities } from '../src/cli.js' +import { Utilities } from '../src/helpers.js' import { InteractionStandalone } from '../src/report/interactions.js' // Globals const objectType = 'Interactions' // Construct the CLI object -const myCLI = new CLI( +const myCLI = new CLIUtilities( '2.0', 'interaction', 'Command line interface for mediumroast.io Interaction objects.', objectType ) +const utils = new Utilities(objectType) // Create the environmental settings const myArgs = myCLI.parseCLIArgs() @@ -68,7 +71,7 @@ if (myArgs.report) { if(myArgs.package) { // Create the working directory - const [dir_success, dir_msg, dir_res] = myCLI.safeMakedir(baseDir + '/interactions') + const [dir_success, dir_msg, dir_res] = utils.safeMakedir(baseDir + '/interactions') // If the directory creations was successful download the interaction if(dir_success) { @@ -82,7 +85,7 @@ if (myArgs.report) { access points, but the tradeoff would be that caffeine would need to run on a system with file system access to these objects. */ - await myCLI.s3DownloadObjs(int_results, myEnv, baseDir + '/interactions') + await utils.s3DownloadObjs(int_results, myEnv, baseDir + '/interactions') // Else error out and exit } else { console.error('ERROR (%d): ' + dir_msg, -1) @@ -95,13 +98,13 @@ if (myArgs.report) { // Create the package and cleanup as needed if (myArgs.package) { - const [package_success, package_stat, package_result] = await myCLI.createZIPArchive( + const [package_success, package_stat, package_result] = await utils.createZIPArchive( myEnv.outputDir + '/' + baseName + '.zip', baseDir ) if (package_success) { console.log(package_stat) - myCLI.rmDir(baseDir) + utils.rmDir(baseDir) process.exit(0) } else { console.error(package_stat, -1) diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 0000000..442300c --- /dev/null +++ b/src/cli.js @@ -0,0 +1,234 @@ +/** + * A class used to build CLIs for accessing and reporting on mediumroast.io objects + * @author Michael Hay + * @file cli.js + * @copyright 2022 Mediumroast, Inc. All rights reserved. + * @license Apache-2.0 + * @version 1.0.0 + */ + +// Import required modules +import program from 'commander' +import ConfigParser from 'configparser' +import Table from 'cli-table' +import Parser from 'json2csv' +import * as XLSX from 'xlsx' +import { Utilities } from './helpers.js' + +/** + * A class to create consistent CLI operations for mediumroast.io objects like + * interactions, studies, companies and users. The functions within help with environmental + * settings, command line switches and output formatting. + * @class + */ +class CLIUtilities { + /** + * @constructor + * Construct a CLI object with key parameters + * @param {String} version - the version for the CLI + * @param {String} name - name for the CLI + * @param {String} description - a description for the CLI + * @param {String} objectType - the type of objects the CLI manages + */ + constructor(version, name, description, objectType) { + this.version = version + this.name = name + this.description = description + this.objectType = objectType + this.utils = new Utilities(objectType) + } + + /** + * @function parseCLIArgs + * Consistently parse the CLI for options and switches + * @returns {Object} - an object containing all CLI options and switches + */ + parseCLIArgs() { + // Define commandline options + program + .name(this.name) + .version(this.version) + .description(this.description) + + program + // System command line switches + .requiredOption( + '-c --conf_file ', + 'Path to the configuration file', + process.env.HOME + '/.mediumroast/config.ini' + ) + .option( + '-r --rest_server ', + 'The URL of the target mediumroast.io server', + 'http://cherokee.from-ca.com:46767' + ) + .option( + '-a --api_key ', + 'The API key needed to talk to the mediumroast.io server' + ) + .option( + '-u --user ', + 'Your user name for the mediumroast.io server' + ) + .option( + '-s --secret ', + 'Your user secret or password for the mediumroast.io server' + ) + .option( + '-o --output ', + 'Select output type: table, json, xls or csv. xls & csv will save to a file.', + 'table', + 'json', + 'xls', + 'csv' + ) + + // Operational command line switches + .option( + '--find_by_name ', + 'Find an individual Interaction by name' + ) + .option( + '--find_by_id ', + 'Find an individual Interaction by ID' + ) + .option( + '--find_by_x ', + 'Find object by an arbitrary attribute as specified by JSON (ex \'{\"zip_postal\":\"92131\"}\')' + ) + .option( + '--create ', + 'Add objects to the backend by specifying a JSON file' + ) + .option( + '--update ', + 'Update an object from the backend by specifying the object\'s id and value to update in JSON' + ) + .option( + '--delete ', + 'Delete an object from the backend by specifying the object\'s id' + ) + .option( + '--report ', + 'Create an MS word document for an object by specifying the object\'s id' + ) + .option( + '--package', + 'An additional switch used with --report to generate a ZIP package that includes the interaction' + ) + + program.parse(process.argv) + return program.opts() + } + + /** + * @function getConfig + * Using the confFile argument read, parse and return the contents of a configuration file + * @param {String} confFile - a fully qualified path to the configuration file + * @returns {Object} The object containing the parsed configuration file results + */ + getConfig(confFile) { + const config = new ConfigParser() + config.read(confFile) + return config + } + + + /** + * @function getEnv + * With the CLI arguments as the priority create an environmentals object to be used in the CLI + * @param {Object} cliArgs - should contain the results of parseCLIArgs() above + * @param {Object} config - should contain the results of getConfig() above + * @returns {Object} after merging cliArgs and config an Object containing the final environmental settings + */ + getEnv(cliArgs, config) { + let env = { + "restServer": null, + "apiKey": null, + "user": null, + "secret": null, + "workDir": null, + "outputDir": null, + "s3Server": null, + "s3User": null, + "s3APIKey": null, + "s3Region": null, + "s3Source": null + } + + // With the cli options as the priority set up the environment for the cli + cliArgs.rest_server ? env.restServer = cliArgs.rest_server : env.restServer = config.get('DEFAULT', 'rest_server') + cliArgs.api_key ? env.apiKey = cliArgs.api_key : env.apiKey = config.get('DEFAULT', 'api_key') + cliArgs.user ? env.user = cliArgs.user : env.user = config.get('DEFAULT', 'user') + cliArgs.secret ? env.secret = cliArgs.secret : env.secret = config.get('DEFAULT', 'secret') + + // Set up additional parameters from config file + env.workDir = config.get('DEFAULT', 'working_dir') + env.outputDir = process.env.HOME + '/' + config.get('document_settings', 'output_dir') + env.s3Server = config.get('s3_settings', 'server') + env.s3User = config.get('s3_settings', 'user') + env.s3Region = config.get('s3_settings', 'region') + env.s3APIKey = config.get('s3_settings', 'api_key') + env.s3Source = config.get('s3_settings', 'source') + + // Return the environmental settings needed for the CLI to operate + return env + } + + + /** + * @function outputCLI + * An output router enabling users to pick their output format of choice for a CLI + * @param {String} outputType Type of output to produce/route to: table, json, csv, xls + * @param {Object} results Data objects to be output + * @param {Object} env Environmental variables from the CLI + * @param {String} objType The object type: Interactions, Studies or Companies + */ + outputCLI(outputType, results, env, objType) { + // Emit the output as per the cli options + if (outputType === 'table') { + this.outputTable(results) + } else if (outputType === 'json') { + console.dir(results) + } else if (outputType === 'csv') { + this.outputCSV(results, env) + } else if (outputType === 'xls') { + this.outputXLS(results, env, objType) + } + } + + outputTable(objects) { + let table = new Table({ + head: ['Id', 'Name', 'Description'], + colWidths: [5, 40, 90] + }) + + for (const myObj in objects) { + table.push([ + objects[myObj].id, + objects[myObj].name, + objects[myObj].description + ]) + } + console.log(table.toString()) + } + + outputCSV(objects, env) { + const fileName = 'Mr_' + this.objectType + '.csv' + const myFile = env['outputDir'] + '/' + fileName + const csv = Parser.parse(objects) + this.utils.saveTextFile(myFile, csv) + } + + // TODO add error checking via try catch + outputXLS(objects, env) { + const fileName = 'Mr_' + this.objectType + '.xlsx' + const myFile = env['outputDir'] + '/' + fileName + const mySheet = XLSX.utils.json_to_sheet(objects) + const myWorkbook = XLSX.utils.book_new() + XLSX.utils.book_append_sheet(myWorkbook, mySheet, this.objectType) + XLSX.writeFile(myWorkbook, myFile) + } +} + +export { CLIUtilities } \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index fe7e537..2dd3a6d 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -4,203 +4,35 @@ * @file helpers.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @version 2.0.0 */ // Import required modules import * as fs from 'fs' -import program from 'commander' -import ConfigParser from 'configparser' -import Table from 'cli-table' -import Parser from 'json2csv' -import * as XLSX from 'xlsx' import zip from 'adm-zip' import AWS from 'aws-sdk' - -class CLI { - constructor(version, name, description, objectType) { - this.version = version - this.name = name - this.description = description - this.objectType = objectType - } - - // Parse the cli options - parseCLIArgs() { - // Define commandline options - program - .name(this.name) - .version(this.version) - .description(this.description) - - program - // System command line switches - .requiredOption( - '-c --conf_file ', - 'Path to the configuration file', - process.env.HOME + '/.mediumroast/config.ini' - ) - .option( - '-r --rest_server ', - 'The URL of the target mediumroast.io server', - 'http://cherokee.from-ca.com:46767' - ) - .option( - '-a --api_key ', - 'The API key needed to talk to the mediumroast.io server' - ) - .option( - '-u --user ', - 'Your user name for the mediumroast.io server' - ) - .option( - '-s --secret ', - 'Your user secret or password for the mediumroast.io server' - ) - .option( - '-o --output ', - 'Select output type: table, json, xls or csv. xls & csv will save to a file.', - 'table', - 'json', - 'xls', - 'csv' - ) - - // Operational command line switches - .option( - '--find_by_name ', - 'Find an individual Interaction by name' - ) - .option( - '--find_by_id ', - 'Find an individual Interaction by ID' - ) - .option( - '--find_by_x ', - 'Find object by an arbitrary attribute as specified by JSON (ex \'{\"zip_postal\":\"92131\"}\')' - ) - .option( - '--create ', - 'Add objects to the backend by specifying a JSON file' - ) - .option( - '--update ', - 'Update an object from the backend by specifying the object\'s id and value to update in JSON' - ) - .option( - '--delete ', - 'Delete an object from the backend by specifying the object\'s id' - ) - .option( - '--report ', - 'Create an MS word document for an object by specifying the object\'s id' - ) - .option( - '--package', - 'An additional switch used with --report to generate a ZIP package that includes the interaction' - ) - - program.parse(process.argv) - return program.opts() - } - - getConfig(confFile) { - const config = new ConfigParser() - config.read(confFile) - return config - } - - getEnv(cliArgs, config) { - let env = { - "restServer": null, - "apiKey": null, - "user": null, - "secret": null, - "workDir": null, - "outputDir": null, - "s3Server": null, - "s3User": null, - "s3APIKey": null, - "s3Region": null, - "s3Source": null - } - - // With the cli options as the priority set up the environment for the cli - cliArgs.rest_server ? env.restServer = cliArgs.rest_server : env.restServer = config.get('DEFAULT', 'rest_server') - cliArgs.api_key ? env.apiKey = cliArgs.api_key : env.apiKey = config.get('DEFAULT', 'api_key') - cliArgs.user ? env.user = cliArgs.user : env.user = config.get('DEFAULT', 'user') - cliArgs.secret ? env.secret = cliArgs.secret : env.secret = config.get('DEFAULT', 'secret') - - // Set up additional parameters from config file - env.workDir = config.get('DEFAULT', 'working_dir') - env.outputDir = process.env.HOME + '/' + config.get('document_settings', 'output_dir') - env.s3Server = config.get('s3_settings', 'server') - env.s3User = config.get('s3_settings', 'user') - env.s3Region = config.get('s3_settings', 'region') - env.s3APIKey = config.get('s3_settings', 'api_key') - env.s3Source = config.get('s3_settings', 'source') - - // Return the environmental settings needed for the CLI to operate - return env - } - - +/** + * A class to enable consistent functionality for basic operations like writing files, + * downloading from S3, reading files, creating ZIP archives, etc. + * @class + */ +class Utilities { /** - * An output router enabling users to pick their output format of choice for a CLI - * @param {String} outputType Type of output to produce/route to: table, json, csv, xls - * @param {Object} results Data objects to be output - * @param {Object} env Environmental variables from the CLI - * @param {String} objType The object type: Interactions, Studies or Companies + * @constructor + * Largely reserved for future use a basic constructor to create the object + * @param {String} objectType - The type of object constructing this object */ - outputCLI(outputType, results, env, objType) { - // Emit the output as per the cli options - if (outputType === 'table') { - this.outputTable(results) - } else if (outputType === 'json') { - console.dir(results) - } else if (outputType === 'csv') { - this.outputCSV(results, env) - } else if (outputType === 'xls') { - this.outputXLS(results, env, objType) - } - } - - outputTable(objects) { - let table = new Table({ - head: ['Id', 'Name', 'Description'], - colWidths: [5, 40, 90] - }) - - for (const myObj in objects) { - table.push([ - objects[myObj].id, - objects[myObj].name, - objects[myObj].description - ]) - } - console.log(table.toString()) + constructor(objectType) { + this.objectType = objectType ? objectType : null } - outputCSV(objects, env) { - const fileName = 'Mr_' + this.objectType + '.csv' - const myFile = env['outputDir'] + '/' + fileName - // const parser = new Parser() - const csv = Parser.parse(objects) - console.log(csv) - this.saveTextFile(myFile, csv) - } - - // TODO add error checking via try catch - outputXLS(objects, env) { - const fileName = 'Mr_' + this.objectType + '.xlsx' - const myFile = env['outputDir'] + '/' + fileName - const mySheet = XLSX.utils.json_to_sheet(objects) - const myWorkbook = XLSX.utils.book_new() - XLSX.utils.book_append_sheet(myWorkbook, mySheet, this.objectType) - XLSX.writeFile(myWorkbook, myFile) - } - - + /** + * @function saveTextFile + * Save textual data to a file + * @param {String} fileName - full path to the file and the file name to save to + * @param {String} content - the string content to save to a file which could be JSON, XML, TXT, etc. + */ saveTextFile(fileName, content) { fs.writeFileSync(fileName, content, err => { if (err) { @@ -211,6 +43,12 @@ class CLI { }) } + /** + * @function readTextFile + * Safely read a text file of any kind + * @param {String} fileName - name of the file to read + * @returns {Array} containing the status of the read operation, status message and data read + */ readTextFile(fileName) { try { const fileData = fs.readFileSync(fileName, 'utf8') @@ -220,21 +58,31 @@ class CLI { } } - // simple function for safe directory creation - safeMakedir(name) { + /** + * @function safeMakedir + * Resursively and safely create a directory + * @param {String} dirName - full path to the directory to create + * @returns {Array} containing the status of the create operation, status message and null + */ + safeMakedir(dirName) { try { - if (!fs.existsSync(name)) { - fs.mkdirSync(name, { recursive: true }) - return [true, 'Created directory [' + name + ']', null] + if (!fs.existsSync(dirName)) { + fs.mkdirSync(dirName, { recursive: true }) + return [true, 'Created directory [' + dirName + ']', null] } else { - return [true, 'Directory [' + name + '] exists did not create.', null] + return [true, 'Directory [' + dirName + '] exists did not create.', null] } } catch (err) { - return [false, 'Did not create directory [' + name + '] because: ' + err, null] + return [false, 'Did not create directory [' + dirName + '] because: ' + err, null] } } - // Recursively remove a directory + /** + * @function rmDir + * Recursively remove a directory + * @param {String} dirName - full path to the parent directory to revmove + * @returns {Array} containing the status of the create operation, status message and null + */ rmDir(dirName) { try { fs.rmdirSync(dirName, {recursive: true}) @@ -245,6 +93,13 @@ class CLI { } // create a ZIP package + /** + * @function createZIPArchive + * Create a ZIP package from a source directory + * @param {String} outputFile - the name, including the full path name, of the target ZIP package + * @param {Sting} sourceDirectory - the full path to directory where the ZIP package will be stored + * @returns + */ async createZIPArchive(outputFile, sourceDirectory) { try { const zipPackage = new zip() @@ -256,7 +111,12 @@ class CLI { } } - // Extract a ZIP package + /** + * @function extractZIPArchive + * Extract objects from a ZIP package into a target directory + * @param {String} inputFile - the ZIP file name, including the full path, to be extracted + * @param {String} targetDirectory - the location for the ZIP package to be extracted to + */ async extractZIPArchive(inputFile, targetDirectory) { try { const zipPackage = new zip(inputFile) @@ -267,7 +127,13 @@ class CLI { } } - // Download the objects + /** + * @function s3DownloadObjs + * From an S3 bucket download the document associated to each interaction + * @param {Array} interactions - an array of interaction objects + * @param {Object} env - the environmental settings to use for accessing the S3 endpoint + * @param {String} targetDirectory - the target location for downloading the objects to + */ async s3DownloadObjs (interactions, env, targetDirectory) { const s3Ctl = new AWS.S3({ accessKeyId: env.s3User , @@ -289,4 +155,4 @@ class CLI { } -export { CLI } \ No newline at end of file +export { Utilities } \ No newline at end of file diff --git a/src/report/companies.js b/src/report/companies.js index bd14710..a23963e 100644 --- a/src/report/companies.js +++ b/src/report/companies.js @@ -11,6 +11,7 @@ import docx from 'docx' import boxPlot from 'box-plot' import Utilities from './common.js' +import { CLIUtilities } from '../cli.js' import { InteractionSection } from './interactions.js' /** From d165b29910b10eb6c2ec20501c22152f90def6fe Mon Sep 17 00:00:00 2001 From: mihay42 Date: Mon, 12 Sep 2022 20:14:11 -0700 Subject: [PATCH 5/7] Cleanup 4: mrServer, scaffold, various --- cli/study.js | 5 ++ cli/users.js | 4 ++ package.json | 4 +- rollup.config.js | 2 +- src/api/mrServer.js | 143 ++++++++++++++++++++++++++++++++++++++++++-- src/api/scaffold.js | 20 +++++-- 6 files changed, 164 insertions(+), 14 deletions(-) diff --git a/cli/study.js b/cli/study.js index ac33313..98818bc 100755 --- a/cli/study.js +++ b/cli/study.js @@ -6,8 +6,13 @@ * @file study.js * @copyright 2022 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 + * @verstion 2.0.0 */ +// TODO: This needs to be reimplemented using the right structure as the other CLIs +console.log('NOTICE: This CLI is presently a work in progress and will not operate, exiting.') +process.exit(0) + // Import required modules import { Auth, Studies } from '../src/api/mrServer.js' import { CLI } from '../src/helpers.js' diff --git a/cli/users.js b/cli/users.js index 6f727f9..70e2bf0 100755 --- a/cli/users.js +++ b/cli/users.js @@ -1,5 +1,9 @@ #!/usr/bin/env node +// TODO: This needs to be reimplemented using the right structure as the other CLIs +console.log('NOTICE: This CLI is presently a work in progress and will not operate, exiting.') +process.exit(0) + // Import required modules import {Users} from '../src/api/highLevel.js' import program from 'commander' diff --git a/package.json b/package.json index e472ed4..a0cf2a8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mediumroast_js", "version": "0.3.0", - "description": "A Javascript SDK to interaction with the mediumroast.io application, end user command line interfaces and a a series of CLIs for building company, study and interaction reports.", + "description": "A Javascript SDK to interact with the mediumroast.io application including command line interfaces.", "main": "src/api/mrServer", "typings": "dist/index", "scripts": { @@ -14,7 +14,7 @@ "company": "cli/company.js", "interaction": "cli/interaction.js", "users": "cli/users.js", - "doc_company": "cli/doc_company.js" + "mr_backup": "cli/mr_backup.js" }, "type": "module", "keywords": [ diff --git a/rollup.config.js b/rollup.config.js index 662cacd..227ab76 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,7 +7,7 @@ export default [ { input: "src/index.js", // your entry point output: { - name: "mediumroast", // package name + name: "mediumroast_js", // package name file: pkg.browser, format: "umd", }, diff --git a/src/api/mrServer.js b/src/api/mrServer.js index 381633d..e080be8 100644 --- a/src/api/mrServer.js +++ b/src/api/mrServer.js @@ -10,7 +10,22 @@ // Import required modules import mrRest from './scaffold.js' +/** + * An implementation for authenticating into the mediumroast.io application. The present + * development is very simple and largely a placeholder. After the object is constructed + * the user would issue a login to generate the credential for usage in the API to talk to the + * mediumroast.io application and gather create, read, deleted and update various objects. + * @class + */ class Auth { + /** + * @constructor + * Stores key information in memory to login to the mediumroast.io application + * @param {String} restServer - the full URL and TCP/IP port for the mediumroast.io application + * @param {String} apiKey - the API key for the mediumroast.io application + * @param {String} user - your username for the mediumroast.io application + * @param {String} secret - your secret for the mediumroast.io application + */ constructor(restServer, apiKey, user, secret) { this.apiKey = apiKey this.user = user @@ -18,6 +33,11 @@ class Auth { this.restServer = restServer } + /** + * @function login + * Initiate a login to the mediumroast.io application + * @returns {Object} the credential object needed to perform API calls to mediumroast.io + */ login() { return { 'apiKey': this.apiKey, @@ -27,12 +47,33 @@ class Auth { } } + /** + * @function logout + * While not yet implemented meant to enable a logout of a session for the mediumroast.io + * @returns {Boolean} true for logout at this time + */ logout() { return true } } - +/** + * This class contains all of the core operations which make it easier to interact with + * the mediumroast.io application. Access to the RESTful endpoints is wrapped in a series + * of Javascript functions to enable SDK users to not have to manage the details. + * + * Discrete objects are subclasses of this baseObjects class and are defined below. These + * subclasses specify additional information like the objType, etc. and as needed can + * implement their own additional functions as needed. + * @class + */ class baseObjects { + /** + * @constructor + * Store and setup key variables needed to operate the SDK + * @param {Object} credential - the credential object returned from Auth.login() + * @param {String} objType - the type of object for the API session which could be users, studies, interactions or companies + * @param {String} apiVersion - the version of the API + */ constructor(credential, objType, apiVersion = 'v1') { this.cred = credential this.rest = new mrRest(credential) @@ -40,68 +81,158 @@ class baseObjects { this.apiVersion = apiVersion } + /** + * @async + * @function getAll + * Get all objects from the mediumroast.io application + * @param {String} endpoint - defaults to getall and is combined with credential and version info + * @returns {Array} the results from the called function mrRest class + */ async getAll(endpoint='getall') { const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint return this.rest.getObj(fullEndpoint) } + /** + * @async + * @function findByName + * Find all objects by name from the mediumroast.io application + * @param {String} name - the name of the object to find + * @param {String} endpoint - defaults to findbyx and is combined with credential and version info + * @returns {Array} the results from the called function mrRest class + */ async findByName(name, endpoint='findbyx') { const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint const my_obj = {findByX: 'name', xEquals: name} return this.rest.postObj(fullEndpoint, my_obj) } - // TODO change to findById - // NOTE this needs to change in the backend implementation too + /** + * @async + * @function findById + * Find all objects by id from the mediumroast.io application + * @param {String} id - the id of the object to find + * @param {String} endpoint - defaults to findbyx and is combined with credential and version info + * @returns {Array} the results from the called function mrRest class + */ async findById(id, endpoint='findbyx') { const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint const my_obj = {findByX: "id", xEquals: id} return this.rest.postObj(fullEndpoint, my_obj) } - // TODO change to findByX - // NOTE this needs to change in the backend implementation too + /** + * @async + * @function findByX + * Find all objects by attribute and value pair from the mediumroast.io application + * @param {String} attribute - the attribute used to find objects + * @param {String} value - the value for the defined attribute + * @param {String} endpoint - defaults to findbyx and is combined with credential and version info + * @returns {Array} the results from the called function mrRest class + */ async findByX(attribute, value, endpoint='findbyx') { const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint const my_obj = {findByX: attribute, xEquals: value} return this.rest.postObj(fullEndpoint, my_obj) } + /** + * @async + * @function createObj + * Create objects in the mediumroast.io application + * @param {Object} obj - the object to create in the backend + * @param {String} endpoint - defaults to findbyx and is combined with credential and version info + * @returns {Array} the results from the called function mrRest class + */ async createObj(obj, endpoint='register') { const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint return this.rest.postObj(fullEndpoint, obj) } - + + /** + * @async + * @function updateObj + * Update an object in the mediumroast.io application + * @param {Object} obj - the object to update in the backend which includes the id and, the attribute and value to be updated + * @param {String} endpoint - defaults to findbyx and is combined with credential and version info + * @returns {Array} the results from the called function mrRest class + */ async updateObj(obj, endpoint='update') { const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint return this.rest.postObj(fullEndpoint, obj) } + /** + * @async + * @function deleteObj + * Delete an object in the mediumroast.io application + * @param {String} id - the object to be deleted in the mediumroast.io application + * @param {String} endpoint - defaults to findbyx and is combined with credential and version info + * @returns {Array} the results from the called function mrRest class + * @todo implment when available in the backend + */ async deleteObj(id, endpoint) { const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint return false } } +// YOU ARE HERE THE CLASS DEFINITIONS NEED TO BE UPDATED +/** +* A subclass of baseObjects to manage mediumroast.io application User objects. + * @class + */ class Users extends baseObjects { + /** + * @constructor + * Construct the user objects + * @param {Object} credential - the credential object returned from Auth.login() + */ constructor (credential) { super(credential, 'users') } } +/** + * A subclass of baseObjects to manage mediumroast.io application Study objects. + * @class + */ class Studies extends baseObjects { + /** + * @constructor + * Construct the study objects + * @param {Object} credential - the credential object returned from Auth.login() + */ constructor (credential) { super(credential, 'studies') } } +/** + * A subclass of baseObjects to manage mediumroast.io application Company objects. + * @class + */ class Companies extends baseObjects { + /** + * @constructor + * Construct the company objects + * @param {Object} credential - the credential object returned from Auth.login() + */ constructor (credential) { super(credential, 'companies') } } +/** + * A subclass of baseObjects to manage mediumroast.io application Interaction objects. + * @class + */ class Interactions extends baseObjects { + /** + * @constructor + * Construct the interaction objects + * @param {Object} credential - the credential object returned from Auth.login() + */ constructor (credential) { super(credential, 'interactions') } diff --git a/src/api/scaffold.js b/src/api/scaffold.js index a15bcba..db7d447 100644 --- a/src/api/scaffold.js +++ b/src/api/scaffold.js @@ -18,6 +18,11 @@ import axios from "axios" * @class */ class mrRest { + /** + * @constructor + * Construct the object to interact with the mediumroast.io application + * @param {Object} credential - contains key items needed to interact with the mediumroast.io application + */ constructor(credential) { this.user = credential.user this.secret = credential.secret @@ -26,9 +31,10 @@ class mrRest { } /** + * @function getObj * Get an object using endpoint only. - * @param {String} endpoint The full URL to the RESTful target - * @param {Returns} result An array starting with a boolean success/failure and resulting data + * @param {String} endpoint - The full URL to the RESTful target + * @returns {Array} An array starting with a boolean success/failure and resulting data */ async getObj(endpoint) { const myURL = this.restServer + endpoint @@ -50,10 +56,11 @@ class mrRest { } /** + * @function postObj * Post an object using endpoint and a Javascript object. * @param {String} endpoint The full URL to the RESTful target * @param {Object} obj Data objects for input - * @param {Returns} result An array starting with a boolean success/failure and resulting data + * @returns {Array} An array starting with a boolean success/failure and resulting data */ async postObj(endpoint, obj) { const myURL = this.restServer + endpoint @@ -72,10 +79,11 @@ class mrRest { } /** + * @function patchObj * Patch an object using endpoint and a Javascript object. * @param {String} endpoint The full URL to the RESTful target * @param {Object} obj Data objects for input - * @param {Returns} result An array starting with a boolean success/failure and resulting data + * @returns {Array} An array starting with a boolean success/failure and resulting data * @todo This may not be needed for the final implementation, verify with the backend */ async patchObj(endpoint, obj) { @@ -96,10 +104,12 @@ class mrRest { } /** + * @function deleteObj * Delete an object using endpoint and a Javascript object. * @param {String} endpoint The full URL to the RESTful target * @param {Object} obj Data objects for input - * @param {Returns} result An array starting with a boolean success/failure and resulting data + * @returns {Array} An array starting with a boolean success/failure and resulting data + * @todo this isn't yet implemented in the backend verification is needed */ async deleteObj(endpoint, obj) { const myURL = this.restServer + endpoint From 0683a4ed8aaa906ac8076986b50faa69b991c692 Mon Sep 17 00:00:00 2001 From: mihay42 Date: Wed, 14 Sep 2022 06:34:56 -0700 Subject: [PATCH 6/7] Cleanup 5: common.js and docstrings overall --- src/api/mrServer.js | 78 ++++++--------- src/api/scaffold.js | 19 ++-- src/cli.js | 20 ++-- src/helpers.js | 32 +++---- src/report/common.js | 191 ++++++++++++++++++++++++++++++++----- src/report/companies.js | 53 +++++----- src/report/interactions.js | 50 +++------- 7 files changed, 270 insertions(+), 173 deletions(-) diff --git a/src/api/mrServer.js b/src/api/mrServer.js index e080be8..c241615 100644 --- a/src/api/mrServer.js +++ b/src/api/mrServer.js @@ -10,21 +10,18 @@ // Import required modules import mrRest from './scaffold.js' -/** - * An implementation for authenticating into the mediumroast.io application. The present - * development is very simple and largely a placeholder. After the object is constructed - * the user would issue a login to generate the credential for usage in the API to talk to the - * mediumroast.io application and gather create, read, deleted and update various objects. - * @class - */ class Auth { /** + * The present development is very simple and largely a placeholder. After the object is constructed + * the user would issue a login to generate the credential for usage in the API to talk to the + * mediumroast.io application and gather create, read, deleted and update various objects. * @constructor - * Stores key information in memory to login to the mediumroast.io application + * @classdesc An implementation for authenticating into the mediumroast.io application. * @param {String} restServer - the full URL and TCP/IP port for the mediumroast.io application * @param {String} apiKey - the API key for the mediumroast.io application * @param {String} user - your username for the mediumroast.io application * @param {String} secret - your secret for the mediumroast.io application + * @todo Evolve as the backend improves authentication and authorization */ constructor(restServer, apiKey, user, secret) { this.apiKey = apiKey @@ -35,7 +32,7 @@ class Auth { /** * @function login - * Initiate a login to the mediumroast.io application + * @description Initiate a login to the mediumroast.io application * @returns {Object} the credential object needed to perform API calls to mediumroast.io */ login() { @@ -49,27 +46,25 @@ class Auth { /** * @function logout - * While not yet implemented meant to enable a logout of a session for the mediumroast.io + * @description While not yet implemented meant to enable a logout of a session for the mediumroast.io * @returns {Boolean} true for logout at this time */ logout() { return true } } -/** - * This class contains all of the core operations which make it easier to interact with - * the mediumroast.io application. Access to the RESTful endpoints is wrapped in a series - * of Javascript functions to enable SDK users to not have to manage the details. - * - * Discrete objects are subclasses of this baseObjects class and are defined below. These - * subclasses specify additional information like the objType, etc. and as needed can - * implement their own additional functions as needed. - * @class - */ + class baseObjects { /** + * This class contains all of the core operations which make it easier to interact with + * the mediumroast.io application. Access to the RESTful endpoints is wrapped in a series + * of Javascript functions to enable SDK users to not have to manage the details. + * + * Discrete objects are subclasses of this baseObjects class and are defined below. These + * subclasses specify additional information like the objType, etc. and as needed can + * implement their own additional functions as needed. * @constructor - * Store and setup key variables needed to operate the SDK + * @classdesc Store and setup key variables needed to operate the SDK * @param {Object} credential - the credential object returned from Auth.login() * @param {String} objType - the type of object for the API session which could be users, studies, interactions or companies * @param {String} apiVersion - the version of the API @@ -84,7 +79,7 @@ class baseObjects { /** * @async * @function getAll - * Get all objects from the mediumroast.io application + * @description Get all objects from the mediumroast.io application * @param {String} endpoint - defaults to getall and is combined with credential and version info * @returns {Array} the results from the called function mrRest class */ @@ -96,7 +91,7 @@ class baseObjects { /** * @async * @function findByName - * Find all objects by name from the mediumroast.io application + * @description Find all objects by name from the mediumroast.io application * @param {String} name - the name of the object to find * @param {String} endpoint - defaults to findbyx and is combined with credential and version info * @returns {Array} the results from the called function mrRest class @@ -110,7 +105,7 @@ class baseObjects { /** * @async * @function findById - * Find all objects by id from the mediumroast.io application + * @description Find all objects by id from the mediumroast.io application * @param {String} id - the id of the object to find * @param {String} endpoint - defaults to findbyx and is combined with credential and version info * @returns {Array} the results from the called function mrRest class @@ -124,7 +119,7 @@ class baseObjects { /** * @async * @function findByX - * Find all objects by attribute and value pair from the mediumroast.io application + * @description Find all objects by attribute and value pair from the mediumroast.io application * @param {String} attribute - the attribute used to find objects * @param {String} value - the value for the defined attribute * @param {String} endpoint - defaults to findbyx and is combined with credential and version info @@ -139,7 +134,7 @@ class baseObjects { /** * @async * @function createObj - * Create objects in the mediumroast.io application + * @description Create objects in the mediumroast.io application * @param {Object} obj - the object to create in the backend * @param {String} endpoint - defaults to findbyx and is combined with credential and version info * @returns {Array} the results from the called function mrRest class @@ -152,7 +147,7 @@ class baseObjects { /** * @async * @function updateObj - * Update an object in the mediumroast.io application + * @description Update an object in the mediumroast.io application * @param {Object} obj - the object to update in the backend which includes the id and, the attribute and value to be updated * @param {String} endpoint - defaults to findbyx and is combined with credential and version info * @returns {Array} the results from the called function mrRest class @@ -165,7 +160,7 @@ class baseObjects { /** * @async * @function deleteObj - * Delete an object in the mediumroast.io application + * @description Delete an object in the mediumroast.io application * @param {String} id - the object to be deleted in the mediumroast.io application * @param {String} endpoint - defaults to findbyx and is combined with credential and version info * @returns {Array} the results from the called function mrRest class @@ -177,15 +172,11 @@ class baseObjects { } } -// YOU ARE HERE THE CLASS DEFINITIONS NEED TO BE UPDATED -/** -* A subclass of baseObjects to manage mediumroast.io application User objects. - * @class - */ + class Users extends baseObjects { /** * @constructor - * Construct the user objects + * @classdesc A subclass of baseObjects that construct the user objects * @param {Object} credential - the credential object returned from Auth.login() */ constructor (credential) { @@ -193,14 +184,10 @@ class Users extends baseObjects { } } -/** - * A subclass of baseObjects to manage mediumroast.io application Study objects. - * @class - */ class Studies extends baseObjects { /** * @constructor - * Construct the study objects + * @classdesc A subclass of baseObjects that construct the study objects * @param {Object} credential - the credential object returned from Auth.login() */ constructor (credential) { @@ -208,14 +195,10 @@ class Studies extends baseObjects { } } -/** - * A subclass of baseObjects to manage mediumroast.io application Company objects. - * @class - */ class Companies extends baseObjects { /** * @constructor - * Construct the company objects + * @classdesc A subclass of baseObjects that construct the company objects * @param {Object} credential - the credential object returned from Auth.login() */ constructor (credential) { @@ -223,14 +206,11 @@ class Companies extends baseObjects { } } -/** - * A subclass of baseObjects to manage mediumroast.io application Interaction objects. - * @class - */ + class Interactions extends baseObjects { /** * @constructor - * Construct the interaction objects + * @classdesc A subclass of baseObjects that construct the interaction objects * @param {Object} credential - the credential object returned from Auth.login() */ constructor (credential) { diff --git a/src/api/scaffold.js b/src/api/scaffold.js index db7d447..fc210d9 100644 --- a/src/api/scaffold.js +++ b/src/api/scaffold.js @@ -11,16 +11,17 @@ import axios from "axios" /** - * Simple and safe wrappers around axios to make RESTful API to mediuroast.io. - * The credential object, passed when this object is created, should include all relevant items - * needed to authenticate a client. This can include appropriate JWT tokens, user identifiers, - * passwords, etc. At a minimum the restServer and an apiKey are needed to connect. + * @class */ class mrRest { /** + * Simple and safe wrappers around axios to make RESTful API to mediuroast.io. + * The credential object, passed when this object is created, should include all relevant items + * needed to authenticate a client. This can include appropriate JWT tokens, user identifiers, + * passwords, etc. At a minimum the restServer and an apiKey are needed to connect. * @constructor - * Construct the object to interact with the mediumroast.io application + * @classdesc Construct the object to interact with the mediumroast.io application * @param {Object} credential - contains key items needed to interact with the mediumroast.io application */ constructor(credential) { @@ -32,7 +33,7 @@ class mrRest { /** * @function getObj - * Get an object using endpoint only. + * @description Get an object using endpoint only. * @param {String} endpoint - The full URL to the RESTful target * @returns {Array} An array starting with a boolean success/failure and resulting data */ @@ -57,7 +58,7 @@ class mrRest { /** * @function postObj - * Post an object using endpoint and a Javascript object. + * @description Post an object using endpoint and a Javascript object. * @param {String} endpoint The full URL to the RESTful target * @param {Object} obj Data objects for input * @returns {Array} An array starting with a boolean success/failure and resulting data @@ -80,7 +81,7 @@ class mrRest { /** * @function patchObj - * Patch an object using endpoint and a Javascript object. + * @description Patch an object using endpoint and a Javascript object. * @param {String} endpoint The full URL to the RESTful target * @param {Object} obj Data objects for input * @returns {Array} An array starting with a boolean success/failure and resulting data @@ -105,7 +106,7 @@ class mrRest { /** * @function deleteObj - * Delete an object using endpoint and a Javascript object. + * @description Delete an object using endpoint and a Javascript object. * @param {String} endpoint The full URL to the RESTful target * @param {Object} obj Data objects for input * @returns {Array} An array starting with a boolean success/failure and resulting data diff --git a/src/cli.js b/src/cli.js index 442300c..3900a9f 100644 --- a/src/cli.js +++ b/src/cli.js @@ -15,16 +15,14 @@ import Parser from 'json2csv' import * as XLSX from 'xlsx' import { Utilities } from './helpers.js' -/** - * A class to create consistent CLI operations for mediumroast.io objects like - * interactions, studies, companies and users. The functions within help with environmental - * settings, command line switches and output formatting. - * @class - */ + class CLIUtilities { /** + * A class to create consistent CLI operations for mediumroast.io objects like + * interactions, studies, companies and users. The functions within help with environmental + * settings, command line switches and output formatting. * @constructor - * Construct a CLI object with key parameters + * @classdesc Construct a CLI object with key parameters * @param {String} version - the version for the CLI * @param {String} name - name for the CLI * @param {String} description - a description for the CLI @@ -40,7 +38,7 @@ class CLIUtilities { /** * @function parseCLIArgs - * Consistently parse the CLI for options and switches + * @description Consistently parse the CLI for options and switches * @returns {Object} - an object containing all CLI options and switches */ parseCLIArgs() { @@ -123,7 +121,7 @@ class CLIUtilities { /** * @function getConfig - * Using the confFile argument read, parse and return the contents of a configuration file + * @description Using the confFile argument read, parse and return the contents of a configuration file * @param {String} confFile - a fully qualified path to the configuration file * @returns {Object} The object containing the parsed configuration file results */ @@ -136,7 +134,7 @@ class CLIUtilities { /** * @function getEnv - * With the CLI arguments as the priority create an environmentals object to be used in the CLI + * @description With the CLI arguments as the priority create an environmentals object to be used in the CLI * @param {Object} cliArgs - should contain the results of parseCLIArgs() above * @param {Object} config - should contain the results of getConfig() above * @returns {Object} after merging cliArgs and config an Object containing the final environmental settings @@ -178,7 +176,7 @@ class CLIUtilities { /** * @function outputCLI - * An output router enabling users to pick their output format of choice for a CLI + * @description An output router enabling users to pick their output format of choice for a CLI * @param {String} outputType Type of output to produce/route to: table, json, csv, xls * @param {Object} results Data objects to be output * @param {Object} env Environmental variables from the CLI diff --git a/src/helpers.js b/src/helpers.js index 2dd3a6d..886a599 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -12,15 +12,13 @@ import * as fs from 'fs' import zip from 'adm-zip' import AWS from 'aws-sdk' -/** - * A class to enable consistent functionality for basic operations like writing files, - * downloading from S3, reading files, creating ZIP archives, etc. - * @class - */ + class Utilities { /** + * A class to enable consistent functionality for basic operations like writing files, + * downloading from S3, reading files, creating ZIP archives, etc. * @constructor - * Largely reserved for future use a basic constructor to create the object + * @classdesc Largely reserved for future use a basic constructor to create the object * @param {String} objectType - The type of object constructing this object */ constructor(objectType) { @@ -29,9 +27,10 @@ class Utilities { /** * @function saveTextFile - * Save textual data to a file + * @description Save textual data to a file * @param {String} fileName - full path to the file and the file name to save to * @param {String} content - the string content to save to a file which could be JSON, XML, TXT, etc. + * @returns {Array} containing the status of the save operation, status message and null/error */ saveTextFile(fileName, content) { fs.writeFileSync(fileName, content, err => { @@ -45,7 +44,7 @@ class Utilities { /** * @function readTextFile - * Safely read a text file of any kind + * @description Safely read a text file of any kind * @param {String} fileName - name of the file to read * @returns {Array} containing the status of the read operation, status message and data read */ @@ -60,9 +59,9 @@ class Utilities { /** * @function safeMakedir - * Resursively and safely create a directory + * @description Resursively and safely create a directory * @param {String} dirName - full path to the directory to create - * @returns {Array} containing the status of the create operation, status message and null + * @returns {Array} containing the status of the mkdir operation, status message and null */ safeMakedir(dirName) { try { @@ -79,9 +78,9 @@ class Utilities { /** * @function rmDir - * Recursively remove a directory + * @description Recursively remove a directory * @param {String} dirName - full path to the parent directory to revmove - * @returns {Array} containing the status of the create operation, status message and null + * @returns {Array} containing the status of the rmdir operation, status message and null */ rmDir(dirName) { try { @@ -95,10 +94,10 @@ class Utilities { // create a ZIP package /** * @function createZIPArchive - * Create a ZIP package from a source directory + * @description Create a ZIP package from a source directory * @param {String} outputFile - the name, including the full path name, of the target ZIP package * @param {Sting} sourceDirectory - the full path to directory where the ZIP package will be stored - * @returns + * @returns {Array} containing the status of the create operation, status message and null */ async createZIPArchive(outputFile, sourceDirectory) { try { @@ -113,7 +112,7 @@ class Utilities { /** * @function extractZIPArchive - * Extract objects from a ZIP package into a target directory + * @description Extract objects from a ZIP package into a target directory * @param {String} inputFile - the ZIP file name, including the full path, to be extracted * @param {String} targetDirectory - the location for the ZIP package to be extracted to */ @@ -129,10 +128,11 @@ class Utilities { /** * @function s3DownloadObjs - * From an S3 bucket download the document associated to each interaction + * @description From an S3 bucket download the document associated to each interaction * @param {Array} interactions - an array of interaction objects * @param {Object} env - the environmental settings to use for accessing the S3 endpoint * @param {String} targetDirectory - the target location for downloading the objects to + * @todo As the implementation grows this function will likely need be put into a separate class */ async s3DownloadObjs (interactions, env, targetDirectory) { const s3Ctl = new AWS.S3({ diff --git a/src/report/common.js b/src/report/common.js index 6b51710..5b2aa26 100644 --- a/src/report/common.js +++ b/src/report/common.js @@ -15,7 +15,22 @@ import boxPlot from 'box-plot' // TODO Change class names to: GenericUtilities, DOCXUtilities and HTMLUtilities // TODO rankTags belongs to GenericUtilities -class Utilities { +class DOCXUtilities { + /** + * To make machine authoring of a Microsoft DOCX file consistent and easier this class has been + * developed. Key functions are available that better describe the intent of the operation + * by name which makes it simpler author a document instead of sweating the details. + * Further through trial and error the idiosyncrasies of the imported docx module + * have been worked out so that the developer doesn't have to accidently find out and struggle + * with document generation. + * @constructor + * @classdesc Core utilities for generating elements in a Microsoft word DOCX file + * @param {String} font + * @param {Float} fontSize + * @param {Float} textFontSize + * @param {String} textFontColor + * @todo when we get to HTML report generation for the front end we will rename this class and create a new one for HTML + */ constructor (font, fontSize, textFontSize, textFontColor) { this.font = font ? font : 'Avenir Next' this.size = fontSize ? fontSize : 11 @@ -233,7 +248,13 @@ class Utilities { } } - // Create a bullet for a bit of prose + /** + * @function makeBullet + * @description Create a bullet for a bit of prose + * @param {String} text - text/prose for the bullet + * @param {Integer} level - the level of nesting for the bullet + * @returns + */ makeBullet(text, level=0) { return new docx.Paragraph({ text: text, @@ -244,7 +265,15 @@ class Utilities { }) } - // For a section of prose create a paragraph + /** + * @function makeParagraph + * @description For a section of prose create a paragraph + * @param {String} paragraph - text/prose for the paragraph + * @param {Integer} size - font size for the paragrah + * @param {Boolean} bold - a boolean value for determining if the text should be bolded + * @param {Integer} spaceAfter - an integer 1 or 0 to determine if there should be space after this element + * @returns {Object} a docx paragraph object + */ makeParagraph (paragraph, size, bold, spaceAfter) { return new docx.Paragraph({ children: [ @@ -259,7 +288,14 @@ class Utilities { }) } - // Create a text run with or without space after + // + /** + * @function makeTextrun + * @description Create a text run with or without space after + * @param {String} text - text/prose for the textrun + * @param {Integer} spaceAfter - an integer 1 or 0 to determine if there should be space after this element + * @returns {Object} a docx textrun object + */ makeTextrun(text, spaceAfter=false) { const myFontSize = 16 if (spaceAfter) { @@ -278,7 +314,11 @@ class Utilities { } } - // Create a page break + /** + * @function pageBreak + * @description Create a page break + * @returns {Object} a docx paragraph object with a PageBreak + */ pageBreak() { return new docx.Paragraph({ children: [ @@ -287,7 +327,12 @@ class Utilities { }) } - // Create a text of heading style 1 + /** + * @function makeHeading1 + * @description Create a text of heading style 1 + * @param {String} text - text/prose for the function + * @returns {Object} a new paragraph as a heading + */ makeHeading1(text) { return new docx.Paragraph({ text: text, @@ -295,7 +340,12 @@ class Utilities { }) } - // Create a text of heading style 2 + /** + * @function makeHeading2 + * @description Create a text of heading style 2 + * @param {String} text - text/prose for the function + * @returns {Object} a new paragraph as a heading + */ makeHeading2(text) { return new docx.Paragraph({ text: text, @@ -303,7 +353,13 @@ class Utilities { }) } - // Create a text of heading style 2 + + /** + * @function makeHeading3 + * @description Create a text of heading style 3 + * @param {String} text - text/prose for the function + * @returns {Object} a new paragraph as a heading + */ makeHeading3(text) { return new docx.Paragraph({ text: text, @@ -311,7 +367,13 @@ class Utilities { }) } - // Create an external hyperlink + /** + * @function makeExternalHyperLink + * @description Create an external hyperlink + * @param {String} text - text/prose for the function + * @param {String} link - the URL for the hyperlink + * @returns {Object} a new docx ExternalHyperlink object + */ makeExternalHyperLink(text, link) { return new docx.ExternalHyperlink({ children: [ @@ -326,7 +388,13 @@ class Utilities { }) } - // Create an internal hyperlink + /** + * @function makeInternalHyperLink + * @description Create an external hyperlink + * @param {String} text - text/prose for the function + * @param {String} link - the URL for the hyperlink within the document + * @returns {Object} a new docx InternalHyperlink object + */ makeInternalHyperLink(text, link) { return new docx.InternalHyperlink({ children: [ @@ -341,8 +409,14 @@ class Utilities { }) } - // Create a bookmark needed to create an internal hyperlink - // TODO at some point test this + /** + * @function makeBookmark + * @description Create a target within a document to link to with an internal hyperlink + * @param {String} text - text/prose for the function + * @param {String} ident - the unique name of the bookmark + * @returns {Object} a new docx paragraph object with a bookmark + * @todo test and revise this function as it may need to be a textrun which can be embedded in something else + */ makeBookmark(text, ident) { return new docx.Paragraph({ children: [ @@ -356,7 +430,14 @@ class Utilities { }) } - // Create a bookmark needed to create an internal hyperlink + /** + * @function makeHeadingBookmark1 + * @description Create a target within a document to link to with an internal hyperlink of heading 1 + * @param {String} text - text/prose for the function + * @param {String} ident - the unique name of the bookmark + * @returns {Object} a new docx paragraph object with a bookmark at the heading level 1 + * @todo could we generalize this function and make the heading level a parameter in the future? + */ makeHeadingBookmark1(text, ident) { return new docx.Paragraph({ heading: docx.HeadingLevel.HEADING_1, @@ -371,7 +452,13 @@ class Utilities { }) } - // Create a bookmark needed to create an internal hyperlink + /** + * @function makeHeadingBookmark2 + * @description Create a target within a document to link to with an internal hyperlink of heading 2 + * @param {String} text - text/prose for the function + * @param {String} ident - the unique name of the bookmark + * @returns {Object} a new docx paragraph object with a bookmark at the heading level 2 + */ makeHeadingBookmark2(text, ident) { return new docx.Paragraph({ heading: docx.HeadingLevel.HEADING_2, @@ -388,7 +475,13 @@ class Utilities { - // Basic table row to produce a name/value pair + /** + * @function basicRow + * @description Basic table row to produce a name/value pair table with 2 columns + * @param {String} name - text/prose for the cell + * @param {String} data - text/prose for the cell + * @returns {Object} a new docx TableRow object + */ basicRow (name, data) { // return the row return new docx.TableRow({ @@ -411,7 +504,13 @@ class Utilities { }) } - // Create rows for object ids and object descriptions + /** + * @function descriptionRow + * @description Description table row to produce a name/value pair table with 2 columns + * @param {String} id - text/prose for the cell + * @param {String} description - text/prose for the cell + * @returns {Object} a new docx TableRow object + */ descriptionRow(id, description, bold=false) { // return the row return new docx.TableRow({ @@ -434,7 +533,14 @@ class Utilities { }) } - // Create the rows with URLs/links + /** + * @function urlRow + * @description Hyperlink table row to produce a name/value pair table with 2 columns and an external hyperlink + * @param {String} category - text/prose for the first column + * @param {String} name - text/prose for the hyperlink in the second column + * @param {String} link - the URL for the hyperlink + * @returns {Object} a new docx TableRow object with an external hyperlink + */ urlRow(category, name, link) { // define the link to the target URL const myUrl = new docx.ExternalHyperlink({ @@ -470,6 +576,15 @@ class Utilities { }) } + /** + * @function basicTopicRow + * @description Create a 3 column row for displaying topics which are the results of term extraction + * @param {String} theme - text/prose for the theme in col 1 + * @param {Float} score - the numerical score for the term in col 2 + * @param {String} rank - a textual description of the relative priority for the term in col 3 + * @param {Boolean} bold - whether or not to make the text/prose bold typically used for header row + * @returns {Object} a new 3 column docx TableRow object + */ basicTopicRow (theme, score, rank, bold) { const myFontSize = 16 // return the row @@ -503,7 +618,16 @@ class Utilities { }) } - // Build a comparisons row + /** + * @function basicComparisonRow + * @description Create a 4 column row for displaying comparisons which are the results of similarity comparisons + * @param {String} company - text/prose for the company in col 1 + * @param {String} role - the role of the company in col 2 + * @param {Float} score - the numerical score for the term in col 3 + * @param {String} rank - a textual description of the relative priority for the company in col 4 + * @param {Boolean} bold - whether or not to make the text/prose bold typically used for header row + * @returns {Object} a new 4 column docx TableRow object + */ basicComparisonRow (company, role, score, rank, bold) { const myFontSize = 16 // return the row @@ -547,7 +671,14 @@ class Utilities { - // Write the report to storage + /** + * @async + * @function writeReport + * @description safely write a DOCX report to a desired location + * @param {Object} docObj - a complete and error free document object that is ready to be saved + * @param {String} fileName - the file name for the DOCX object + * @returns {Array} an array containing if the save operation succeeded, the message, and null + */ async writeReport (docObj, fileName) { try { await docx.Packer.toBuffer(docObj).then((buffer) => { @@ -559,7 +690,12 @@ class Utilities { } } - // Rank supplied topics and return an object that can be rendered + /** + * @function rankTags + * @description Rank supplied topics and return an object that can be rendered + * @param {Object} tags - the tags from the source object to be ranked + * @returns {Object} the final tags which now have ranking and are suitable for a basicTopicRow + */ rankTags (tags) { const ranges = boxPlot(Object.values(tags)) let finalTags = {} @@ -586,7 +722,12 @@ class Utilities { return finalTags } - // Create a table for topics + /** + * @function topicTable + * @description A higher level function that calls basicTopicRow to create a complete table + * @param {Object} topics - the result of rankTags + * @returns {Object} a complete docx table that includes topics + */ topicTable(topics) { let myRows = [this.basicTopicRow('Keywords', 'Score', 'Rank', true)] for (const topic in topics) { @@ -606,6 +747,12 @@ class Utilities { } // Create an introductory section + /** + * @function makeIntro + * @description Creates a complete document with a heading of level 1 + * @param {String} introText - text/prose for the introduction + * @returns {Object} a complete introduction with heading level 1 and a paragraph + */ makeIntro (introText) { return [ this.makeHeading1('Introduction'), @@ -614,4 +761,4 @@ class Utilities { } } -export default Utilities \ No newline at end of file +export default DOCXUtilities \ No newline at end of file diff --git a/src/report/companies.js b/src/report/companies.js index a23963e..b6c055c 100644 --- a/src/report/companies.js +++ b/src/report/companies.js @@ -10,27 +10,17 @@ // Import required modules import docx from 'docx' import boxPlot from 'box-plot' -import Utilities from './common.js' -import { CLIUtilities } from '../cli.js' +import DOCXUtilities from './common.js' import { InteractionSection } from './interactions.js' -/** - * A high level class to create sections for a Company report using either - * Microsoft DOCX format or eventually HTML format. Right now the only available - * implementation is for the DOCX format. These sections are designed to be consumed - * by a wrapping document which could be for any one of the mediumroast objects. - * - * To operate this class the constructor should be passed a single company object. - * - * From there two methods can be called: - * 1. makeFirmographicsDOCX() - * 2. makeComparisonsDOCX() - * @class - */ class CompanySection { /** + * A high level class to create sections for a Company report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. These sections are designed to be consumed + * by a wrapping document which could be for any one of the mediumroast objects. * @constructor - * To operate this class the constructor should be passed a single company object. + * @classdesc To operate this class the constructor should be passed a single company object. * @param {Object} company - The company object to generate the section(s) for */ constructor(company) { @@ -38,7 +28,7 @@ class CompanySection { this.company.stock_symbol === 'Unknown' && this.company.cik === 'Unknown' ? this.companyType = 'Private' : this.companyType = 'Public' - this.util = new Utilities() + this.util = new DOCXUtilities() } // Create a URL on Google maps to search for the address @@ -109,7 +99,7 @@ class CompanySection { /** * @function makeFirmographicsDOCX - * Create a table containing key information for the company in question + * @description Create a table containing key information for the company in question * @returns {Object} A docx table is return to the caller */ makeFirmographicsDOCX() { @@ -192,7 +182,7 @@ class CompanySection { /** * @function makeComparisonDOCX - * Generate the comparisons section for the document from the company in question + * @description Generate the comparisons section for the document from the company in question * @param {Object} comparisons - the object containing the comparisons for the company in question * @returns {Array} An array containing an introduction to this section and the table with the comparisons */ @@ -243,19 +233,19 @@ class CompanySection { } } -/** - * A high level class to create a complete document for a Company report using either - * Microsoft DOCX format or eventually HTML format. Right now the only available - * implementation is for the DOCX format. - * - * To operate this class the constructor should be passed a single company - * object, the associated array of interactions, document creator and authoring company. - * - * From there one method can be called: - * 1. makeDOCX() - * @class - */ + class CompanyStandalone { + /** + * A high level class to create a complete document for a Company report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. + * @constructor + * @classdesc Create a full and standlaone report document for a company + * @param {*} company + * @param {*} interactions + * @param {*} creator + * @param {*} authorCompany + */ constructor(company, interactions, creator, authorCompany) { this.objectType = 'Company' this.creator = creator @@ -278,6 +268,7 @@ class CompanyStandalone { /** * @async * @function makeDocx + * @description Generate and save a DOCX report for a Company object * @param {String} fileName - Full path to the file name, if no file name is supplied a default is assumed * @param {Boolean} isPackage - When set to true links are set up for connecting to interaction documents * @returns {Array} The result of the writeReport function that is an Array diff --git a/src/report/interactions.js b/src/report/interactions.js index 8551a66..2d5cd3a 100644 --- a/src/report/interactions.js +++ b/src/report/interactions.js @@ -9,28 +9,18 @@ // Import required modules import docx from 'docx' -import Utilities from './common.js' +import DOCXUtilities from './common.js' import { CompanySection } from './companies.js' -/** - * A high level class to create sections for an Interaction report using either - * Microsoft DOCX format or eventually HTML format. Right now the only available - * implementation is for the DOCX format. These sections are designed to be consumed - * by a wrapping document which could be for any one of the mediumroast objects. - * - * To operate this class the constructor should be passed an array of interaction - * objects, the name of the object that is using this class, and the type of object - * that is calling this class. - * - * From there two methods can be called: - * 1. makeDescriptionsDOCX() - * 2. makeReferencesDOCX() - * @class - */ + class InteractionSection { /** + * A high level class to create sections for an Interaction report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. These sections are designed to be consumed + * by a wrapping document which could be for any one of the mediumroast objects. * @constructor - * Construct the InteractionSection object + * @classdesc Construct the InteractionSection object * @param {Array} interactions - a complete array of interactions ranging from 1:N * @param {String} objectName - the name of the object calling this class * @param {String} objectType - the type of object calling this class @@ -46,12 +36,12 @@ class InteractionSection { this.objectName = objectName this.objectType = objectType this.fontSize = 10 // We need to pass this in from the config file - this.util = new Utilities() + this.util = new DOCXUtilities() } /** * @function makeDescriptionsDOCX - * Make the descriptions for interactions in the DOCX format + * @description Make the descriptions for interactions in the DOCX format * @returns {Array} An array containing a section description and a table of interaction descriptions */ makeDescriptionsDOCX () { @@ -97,7 +87,7 @@ class InteractionSection { /** * @function makeReferencesDOCX - * Create the references for calling programs in the DOCX format + * @description Create the references for calling programs in the DOCX format * @param {Boolean} isPackage - When set to true links are set up for connecting to interaction documents * @returns {Array} An array containing a section description and a table of interaction references */ @@ -199,23 +189,13 @@ class InteractionSection { } } -/** - * A high level class to create a complete document for an Interaction report using either - * Microsoft DOCX format or eventually HTML format. Right now the only available - * implementation is for the DOCX format. - * - * To operate this class the constructor should be passed a single interaction - * object, the associated company, document creator and authoring company. - * - * From there one method can be called: - * 1. makeDOCX() - * @class - */ class InteractionStandalone { /** + * A high level class to create a complete document for an Interaction report using either + * Microsoft DOCX format or eventually HTML format. Right now the only available + * implementation is for the DOCX format. * @constructor - * The constructor should be passed a single interaction - * object, the associated company, document creator and authoring company. + * @classdesc Create a full and standlaone report document for an interaction * @param {Object} interaction - The interaction in question to process * @param {Object} company - The company associated to the interaction * @param {String} creator - A string defining the creator for this document @@ -267,7 +247,7 @@ class InteractionStandalone { /** * @async * @function makeDOCX - * Create the DOCX document for a single interaction which includes a company section + * @description Create the DOCX document for a single interaction which includes a company section * @param {String} fileName - Full path to the file name, if no file name is supplied a default is assumed * @param {Boolean} isPackage - When set to true links are set up for connecting to interaction documents * @returns {Array} The result of the writeReport function that is an Array From 1fb8bbbc826bf5f11721e6bad81c037a8a972c35 Mon Sep 17 00:00:00 2001 From: mihay42 Date: Thu, 15 Sep 2022 20:44:27 -0700 Subject: [PATCH 7/7] Cleanup 6: Doc cleanup and readme progress --- README.md | 270 +++---------------------------------- src/api/scaffold.js | 5 +- src/report/common.js | 2 +- src/report/companies.js | 10 +- src/report/interactions.js | 2 +- 5 files changed, 24 insertions(+), 265 deletions(-) diff --git a/README.md b/README.md index 4b380e2..83a7534 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ # Node.js Javascript SDK for the mediumroast.io -This is the Node.js Javascript SDK, Software Development Kit, for the mediumroast.io. We actually use this SDK for our own developments of `web_ui`, `cli` and `text_ui` directly. We do this to ensure that our developers have a first class experience with the mediumroast.io with an always up to date version of the SDK. +This is the Node.js Javascript SDK, Software Development Kit, for the mediumroast.io. The SDK is comprised of several things: +1. A wrapper atop the backend's RESTful APIs for Interaction, Study, Company and User objects. +2. A high level written to make it easier to work with the node.js docx package and generated Microsoft Word reports. +3. Core Command Line Interface (CLI) utilities for mediumroast.io companies, studies, interactions and user objects. +4. Helper CLI utilities to setup your mediumroast.io environment and backup objects. +5. Example data to use with the CLIs to add objects to the mediumroast.io. + +We actually use this SDK for our own developments directly. We do this to ensure that our developers have a first class experience for the mediumroast.io with an always up to date version of the SDK. + +# Installation and Configuration Steps via NPM + # Installation and Configuration Steps for Developers The following steps are important if you are developing or extending the Node.js Javascript SDK. If you're not a developer then the current state of the package is not for you. Please wait until we exit the alpha for the mediumroast.io. @@ -7,266 +17,18 @@ The following steps are important if you are developing or extending the Node.js ## Cloning the repository for Developers Assuming `git` is installed and your credentials are set up to talk to the mediumroast.io set of repositories it should be possible to do the following as a user on the system: 1. `mkdir ~/dev;cd ~/dev` -2. `git clone git@github.com:mediumroast/mr_sdk.git` -This will create an `mr_sdk` directory in `~/dev/` and allow you to proceed to the following steps for installation. +2. `git clone git@github.com:mediumroast/mediumroast_js.git` +This will create an `mediumroast_js` directory in `~/dev/` and allow you to proceed to the following steps for installation. ## Installation for Developers, Early Adopters and Testers For developers the following steps are suggested to perform a local installation. 1. Install the [Local Package Publisher](https://www.npmjs.com/package/local-package-publisher) as per the instructions on the package page. We recommend that you install the package globally so that you don't have to worry about it being in the right place. If you choose to install globally you should run `sudo npm install -g local-package-publisher`. -2. Assuming that you've clone the repo into `~/dev` enter the appropriate directory `cd ~/dev/mr_sdk/javascript`. +2. Assuming that you've clone the repo into `~/dev` enter the appropriate directory `cd ~/dev/mediumroast_js`. 3. Install the package globally with `sudo local-package-publisher -p` -4. Run `npm link mediumroast` in target project to consume the library. - -## Structure of the repository -The following structure is available for the Node.js Javascript SDK. -``` -mr_sdk/ - javascript/ - cli/ - list_companies.js - list_studies.js - list_interactions.js - list_users.js - src/ - helpers.js - api/ - highLevel.js - jsonServer.js - spec/ - README.md - LICENSE - package.json - rollup.config.js -``` -# Future Work -As mentioned earlier in the documentation the current state of this package is alpha. As such we're in pretty heavy development with the high potential for things being broken. After we graduate from alpha we anticipate the following things to likely occur or be completed. -- Separation of the package out of the `mr_sdk` and into separate `python`, `javascript` and `cli` packages. -- For this Node.js Javascript package it will be published on the `npm` repository. -- For the Python package it will be published on `PyPi` or similar. -- Implementation of a test suite to perform object adds, updates, reads, and deletes to confirm basic operation. -- Implementation of `put`, `patch`, and `delete` operations to perform full object functions. -- Creation of correlating `cli` commands or a `tui` for updates, deletes and adds. -- Swap out the temporary backend for the final `mr_server` backend. - -# The CLI, Command Line Interface -The following example CLI wrappers have been built that wrap the sample API implementation and other elements in the SDK. As appropriate example outputs are also included in the documentation. -## list_companies -``` -Usage: list_companies [options] - -A CLI for mediumroast.io Company objects, without options: list all Companies. - -Options: - -V, --version output the version number - -g --get_guids List all Companies by Name - -n --get_names List all Companies by GUID - -m --get_map List all Companies by {Name:GUID} - --get_by_name Get an individual Company by name - --get_by_guid Get an individual Company by GUID - -s --server Specify the server URL (default: "http://mr-01:3000") - -t --server_type Specify the server type as [json || mr_server] (default: "json") - -c --config_file Path to the configuration file (default: "~/.mr_config") - -h, --help display help for command -``` -### Example output -``` -list_companies --get_by_guid=70340e90b851a6f1e4dd725cfa22b533d0e3eecd457a1e23fe51be56d9a75d76 -[ - { - companyName: 'JP Morgan Chase', - industry: 'Finance, Insurance, And Real Estate | Major Group 60: Depository Institutions | National Commercial Banks', - role: 'User', - url: 'Unknown', - streetAddress: 'Unknown', - city: 'San Francisco', - stateProvince: 'California', - country: 'US', - region: 'AMER', - phone: 'Unknown', - simpleDesc: 'JPMorgan Chase & Co. is an American multinational investment bank and financial services holding company headquartered in New York City.', - cik: 'Unknown', - stockSymbol: 'Unknown', - Recent10kURL: 'Unknown', - Recent10qURL: 'Unknown', - zipPostal: 'Unknown', - linkedStudies: { - 'Caffeine Customer Insights': 'dccd4e2568bfd5733d3ef9f3f8dad09ea0da0e87325ccafeac1abfd0c52c23ff', - 'Customer Insights': 'f3eae874b1fba924e81d5963a2bc7752ab8d2acd906bb2944f6243f163a6bf23' - }, - linkedInteractions: { - '202108091407-Caffeine Customer Insights-JP Morgan Chase': '3206b19fa72cde9970ceb3792b1f2ea54afa69c7d80adc74d07fb9f699db860e', - '202108091407-Customer Insights-JP Morgan Chase': 'd579da261a38581f3e3b5c800d7e956f3f22570cf8a71dfd5e4e45f45a7ea3dd' - }, - longitude: -122.41963999999996, - latitude: 37.777120000000025, - notes: { '1': [Object] }, - GUID: '70340e90b851a6f1e4dd725cfa22b533d0e3eecd457a1e23fe51be56d9a75d76', - id: '70340e90b851a6f1e4dd725cfa22b533d0e3eecd457a1e23fe51be56d9a75d76', - totalInteractions: 2, - totalStudies: 2 - } -] -``` -## list_interactions -``` -Usage: list_interactions [options] +4. Run `npm link mediumroast_js` in target project to consume the library. -A CLI for mediumroast.io Interaction objects, without options: list all Interactions. -Options: - -V, --version output the version number - -g --get_guids List all Interactions by Name - -n --get_names List all Interactions by GUID - -m --get_map List all Interactions by {Name:GUID} - --get_by_name Get an individual Interaction by name - --get_by_guid Get an individual Interaction by GUID - -s --server Specify the server URL (default: "http://mr-01:3000") - -t --server_type Specify the server type as [json || mr_server] (default: "json") - -c --config_file Path to the configuration file (default: "~/.mr_config") - -h, --help display help for command -``` -### Example output -``` -list_interactions --get_by_guid=729fbd901aaa7114ea46df0b8736fc599d8d7b94f5f9c74375ebd684c4d349cf -[ - { - interactionName: '202108111451-Customer Insights-Self Employed', - time: '1451', - date: '20210811', - state: 'summarized', - simpleDesc: 'Learn from Self Employed, either in person or digitally, key points and inputs related to the study Customer Insights', - contactAddress: 'Unknown', - contactZipPostal: 'Unknown', - contactPhone: 'Unknown', - contactLinkedIn: 'Unknown', - contactEmail: 'Unknown', - contactTwitter: 'Unknown', - contactName: 'Unknown', - public: false, - abstract: "Audio le Wow there's some new boys that says. Nice so it is a. It's a one man shop one man. 00:058 Speaker 2 In that era and then following people on Twi er that are interested in this type of game to see what excites them. You're trying to get insights on on you know Twi er or talking to people that may already have the consoles or they may have them haven't used them and maybe they would like to go back to that por on right? Alright so of course I cannot ask you what well this is a good ques on so over me do you feel that your company Is losing or maintaining its as the essence of your product? And I'm being serious about this kind of thing. Uhm a ec vely what I've what I did was decided I wanted to go a er. So I had to build you know I'm doing a pla orm or Mario Castlevania type game so I had to build you know physics and collision detec on and you know all that kind of stu from the ground up. My rst objec ve was to make a. List of all. And and by the me you're done you go back and look at your original your original goal and you're like how did we even get here? Do you ever felt that product management or PM UM were asked to put together a road map without having customer insight? So it's kind of a hard ques on to answer 'cause. No you know you know the three right right? I think for some context I feel like the problem got worse over me. So you you have a plan for when you're going to deliver stu but you probably didn't do. Do we do a great job of saying yeah we know we're going to need to allocate such so much percent of our of our me to. Alright cool this is pre y good evidence.", - interactionType: 'Interview', - status: 'Canceled', - linkedStudies: { - 'Customer Insights': 'f3eae874b1fba924e81d5963a2bc7752ab8d2acd906bb2944f6243f163a6bf23' - }, - linkedCompanies: { - 'Self Employed': '7c916f300b766bab9c9652f1e83617d0e3488a810b3a9388573905c52f924f84' - }, - longitude: -80.83795999999995, - latitude: 35.222860000000026, - url: 's3://mr-02:9000/mediumroastinc/202108111451-AMER-US-North Carolina-Charlotte-Entertainment-Customer Insights-Self Employed-Interview.pdf', - thumbnail: 's3://mr-02:9000/mediumroastinc/thumb_202108111451-AMER-US-North Carolina-Charlotte-Entertainment-Customer Insights-Self Employed-Interview.pdf.png', - notes: { '1': [Object] }, - GUID: '729fbd901aaa7114ea46df0b8736fc599d8d7b94f5f9c74375ebd684c4d349cf', - id: '729fbd901aaa7114ea46df0b8736fc599d8d7b94f5f9c74375ebd684c4d349cf', - totalStudies: 1, - totalCompanies: 1 - } -] -``` -## list_studies -``` -Usage: list_studies [options] +# The mediumroast.io CLI (Command Line Interface) -A CLI for mediumroast.io Study objects, without options: list all Studies. -Options: - -V, --version output the version number - -g --get_guids List all Studies by Name - -n --get_names List all Studies by GUID - -m --get_map List all Studies by {Name:GUID} - --get_substudies List all Studies and their substudies - --get_by_name Get an individual Study by name - --get_by_guid Get an individual Study by GUID - -s --server Specify the server URL (default: "http://mr-01:3000") - -t --server_type Specify the server type as [json || mr_server] (default: "json") - -c --config_file Path to the configuration file (default: "~/.mr_config") - -h, --help display help for command -``` -### Example output -``` -list_studies --get_by_guid=f3eae874b1fba924e81d5963a2bc7752ab8d2acd906bb2944f6243f163a6bf23 -[ - { - studyName: 'Customer Insights', - description: 'Work to realize a SaaS intently focused on revealing and solving problems for Customer and\n' + - 'Competitive interactions which can make Customer Success and Product Management disciplines stronger.', - linkedCompanies: { - HDS: 'b61e57adaaa834f2a560be1b8d1b62c0ebbfd68cb3d33917185bc1792063a677', - Aha: '6dbfa33b06706033931b0154210fbcb5fafb995315eccfbd8bc5b12d5e5569f7', - VMware: 'd9069370a7dc4fff8ef20bf01915df2568bb26dee54c54f209664c089843f0e3', - Hitachi: '567922c545773872eaab4b5e3466e0a60756d98148849662be330ff336b56659', - Google: '392dfcd311a276b9aca48dfa52b5985aa5546c3157c3288452bbc00f2af8048a', - Microsoft: '6ffc56686598cbac08864b9218f43e203b22f258d00ab7dc3e4a1aa29819f982', - Amazon: 'df6cb4f48281f2612c6ca5e9d89f85a684c39e3aa25ab648ba91db60368dfe8d', - 'Providence Health and Services': 'ca78cf0b874c9139b0316c1803408553202069186297a8bec7bfea862168032b', - eBay: 'ed128e1b2763deba9a49583d91a1dafd979f2453fed920339fe98e7d30e66aa7', - 'JP Morgan Chase': '70340e90b851a6f1e4dd725cfa22b533d0e3eecd457a1e23fe51be56d9a75d76', - 'Self Employed': '7c916f300b766bab9c9652f1e83617d0e3488a810b3a9388573905c52f924f84' - }, - totalCompanies: 11, - linkedInteractions: { - '201402240930-Customer Insights-HDS': '37c5b453fdc2a4074958c3c41f02c2491f9961eafbcdc30a354b586e379a94f4', - '201402241004-Customer Insights-HDS': 'e216d44c6b934beebf1cf58a27030233c3e5e9ea8e5fc457c3003f99dc54efe7', - '201402250831-Customer Insights-HDS': 'bd5b40cb911347abb82bc95c59105e7ea1d3ac48248f0644aa348ab5399bd4d3', - '201402250915-Customer Insights-HDS': '57bf64ca80c491324e917cbeda55d6fe7494c9c8c9e09033a9648e6d4e1cd640', - '201402270900-Customer Insights-HDS': 'af98040145afe692ae78b806323289ad5fddb99c5f45d453d8fc5dde2292a8a9', - '201402271515-Customer Insights-HDS': '0928f03c7b34f90333eacd162fe8ba1dc748639d1cd361613e70de6253222322', - '201402281310-Customer Insights-HDS': '76d41347eba7ddf45aaa7ccf9e40d204e3b0f643ede6d3fc4d212af8c0ffa7de', - '201403101041-Customer Insights-HDS': 'c1eaca9b8de83997b81c2143479b78b5b705c878ef3f1610257278691fdf95d4', - '201403110945-Customer Insights-HDS': '1c992ed623c0ce6ec01470a3438720c39198c0153dcc38be24429a42137dd3be', - '201403111306-Customer Insights-HDS': '1f15ec408b0857662152566408118b580734684f1e7dca5a6d7a77f84cffb041', - '201403121034-Customer Insights-HDS': '60d72cc0a3f0837a81edeec80b8673895ab076d52753d4707115d6f81a239883', - '201403130801-Customer Insights-HDS': '43ac78317d2c402abe0b9afd942e1e742e4029ea3a85679bde2cb740f3c697db', - '201403140702-Customer Insights-HDS': '1fe08d24f23e303d70f73b438b33556df190ee7c2edb076879f9a15f676ee558', - '201912151800-Customer Insights-Aha': 'd42ba72ab7317dea495dbef9fd7a4b18e220316689059d5ce87134a68033ed6a', - '201912151900-Customer Insights-Aha': '2975c26e6ac058eaa6617949e4be59c14ed8da69d49168f2b7e11eedc7ff689c', - '201912152000-Customer Insights-Aha': '03d225af95bc8d9ae1af795cae25b1f98afd886d3772e7b56b1899642fdbf8fc', - '201912161800-Customer Insights-Aha': '203719812f4d2b7d986f134c81c7e01ff0b7ec0a454351ac020b3fc23ee7209f', - '201912161900-Customer Insights-Aha': '9c9703bd9ea5aa58fbec75da5b7a67848a2c69efb34d6b82bdd43f4dca700c1a', - '201912162000-Customer Insights-Aha': '440a968c8ec519d046a2064e4c1704307499f013eca6fae639cebdc6a1d31080', - '201912171800-Customer Insights-Aha': 'cf22d4ac4c07fa502fdf507237396cc919b99ca6d5e20ede53fc0c65c3657886', - '201912171900-Customer Insights-Aha': 'adcdc8c0c4e27ef6617cf61927253473986103662e9b1a0c7e3d0b4c93de41f2', - '201912172000-Customer Insights-Aha': '02e5fbd561ae1ab679b793644c5bcf50fb26163623e35c2ff1a76f44f1022a46', - '201912181800-Customer Insights-Aha': '29581d790d073ab20b1d7814f6c777944b601d038a59a5a9553f47eb29aa20c7', - '201912181900-Customer Insights-Aha': '2599cac8d57cc536b8f46b55f4b90a265af000faf09785aebf37303179dbc61b', - '201912182000-Customer Insights-Aha': '70cd002264a60361fcea43bfeb5bca9b6cfc05364b377530581a7317cbad69e4', - '201912191800-Customer Insights-Aha': 'a0ec3c225c1b5a6df8444955c56d458b6920a91df7417a4a8b19babfd223401d', - '201912191900-Customer Insights-Aha': '167c7dc72ed236c70c165e510de251d635d20116922068e8dc4579d6277e2725', - '201912192000-Customer Insights-Aha': 'c812a6f111acfb353ba944f3c536334a1c15fb2663c3dd62ca6e0f78208eaf7f', - '201912201800-Customer Insights-Aha': '02e4f0587d9426d4d9aa26d88eff656b35058c250ee58703ecb88e187bc6ca31', - '201912201900-Customer Insights-Aha': '7f0b7da04a4919c7bc4a2e676fb916e0cc1465deaeadc9d74008baf28268c74c', - '201912202000-Customer Insights-Aha': 'fe57ff2b168359e14950b6f46c45a891494b90651adf784096ecd7d4fdd0f003', - '201912211800-Customer Insights-Aha': 'cdb15ae893120080c7cef65f23c0a7f0ed16942077a6b3af7be1f2c6fc8fe63a', - '201912211900-Customer Insights-Aha': '60438bac13b7fab03a9d48062b1764ffb627acec94403c0eec79185d6771b448', - '201912212000-Customer Insights-Aha': '1255026e832da09862cf55623ea0a78d795aa10a12522860e7720c6e3664de16', - '201912212100-Customer Insights-Aha': 'a1049f53de0929252fd6b655be1da37ae9ed27fe44f2814b5f0f9e102b7cabd3', - '202107091400-Customer Insights-VMware': 'f99a403173fed358fc7d6937ba3d646412e248fabd6b20280a9099121e0ca121', - '202107231300-Customer Insights-Hitachi': 'd63337f0967ef5c84953b9ca0b2023cf90da3dcc401baf91fc43687826bd6c99', - '202107281900-Customer Insights-Google': 'c9a9094844a42394ee61b5e35572701a128815fc01047282e53d315134ce9eac', - '202107301345-Customer Insights-Microsoft': '13c0455ce917a0ced3047ac93b52b649c0d17ae124eb8a6067142579b178cd2f', - '202108031722-Customer Insights-Amazon': '1f5252dbab6cdebc669d3c6e306f73c4f0562dbadda7299e795f78d9334ee6a1', - '202108041019-Customer Insights-Providence Health and Services': '7490db9c62b688cffa865569d6bc5504bbc2dc8ac5ddb5919b21e0a5f65eb15c', - '202108051509-Customer Insights-eBay': '0aa63c14ca41f7f422f569f5dab55c2674d83d9c43a18013fcbb641130ef1e1b', - '202108091407-Customer Insights-JP Morgan Chase': 'd579da261a38581f3e3b5c800d7e956f3f22570cf8a71dfd5e4e45f45a7ea3dd', - '202108111451-Customer Insights-Self Employed': '729fbd901aaa7114ea46df0b8736fc599d8d7b94f5f9c74375ebd684c4d349cf' - }, - totalInteractions: 44, - substudies: { '1': [Object], '2': [Object], default: [Object] }, - document: { - Introduction: 'This Customer Insights study includes two phases separated by 5 years. The first phase was performed as as a part of a 2014 research project that emphasized A/B testing for a customer study indexing application.\n' + - 'While the second phase, conducted in late 2019, both uncovered new themes and validated key ideas surfaced in the first phase. In the second phase the emphasis was to investigate a single competitor/partner candidate, Aha!, to\n' + - 'to determine if the key themes, detected within the first phase, had or had not been already addressed. While details are accounted for in the Opportunities section, the conclusion is that the themes still largely remain unsolved\n' + - 'by companies who build tools for product management, project management, and program management disciplines. Further, research continues to test both the user experience and refine elements of these\n' + - 'key themes with product managers at companies like Ring Central, Google, Chaos Search, and so on.', - Opportunity: [Object], - Action: [Object] - }, - public: false, - groups: 'users:studyadmin', - GUID: 'f3eae874b1fba924e81d5963a2bc7752ab8d2acd906bb2944f6243f163a6bf23', - id: 'f3eae874b1fba924e81d5963a2bc7752ab8d2acd906bb2944f6243f163a6bf23' - } -] -``` diff --git a/src/api/scaffold.js b/src/api/scaffold.js index fc210d9..06175b8 100644 --- a/src/api/scaffold.js +++ b/src/api/scaffold.js @@ -10,10 +10,7 @@ // Import required modules import axios from "axios" -/** - - * @class - */ + class mrRest { /** * Simple and safe wrappers around axios to make RESTful API to mediuroast.io. diff --git a/src/report/common.js b/src/report/common.js index 5b2aa26..2723a5d 100644 --- a/src/report/common.js +++ b/src/report/common.js @@ -253,7 +253,7 @@ class DOCXUtilities { * @description Create a bullet for a bit of prose * @param {String} text - text/prose for the bullet * @param {Integer} level - the level of nesting for the bullet - * @returns + * @returns {Object} new docx paragraph object as a bullet */ makeBullet(text, level=0) { return new docx.Paragraph({ diff --git a/src/report/companies.js b/src/report/companies.js index b6c055c..19c4b7e 100644 --- a/src/report/companies.js +++ b/src/report/companies.js @@ -241,10 +241,10 @@ class CompanyStandalone { * implementation is for the DOCX format. * @constructor * @classdesc Create a full and standlaone report document for a company - * @param {*} company - * @param {*} interactions - * @param {*} creator - * @param {*} authorCompany + * @param {Object} company - the company object to be reported on + * @param {Array} interactions - the interactions associated to the company + * @param {String} creator - the author of the report + * @param {*} authorCompany - the company of the report author */ constructor(company, interactions, creator, authorCompany) { this.objectType = 'Company' @@ -259,7 +259,7 @@ class CompanyStandalone { ' If this report document is produced as a package, instead of standalone, then the' + ' hyperlinks are active and will link to documents on the local folder after the' + ' package is opened.' - this.util = new Utilities() + this.util = new DOCXUtilities() this.topics = this.util.rankTags(this.company.topics) this.comparison = company.comparison, this.noInteractions = String(Object.keys(this.company.linked_interactions).length) diff --git a/src/report/interactions.js b/src/report/interactions.js index 2d5cd3a..1e52afb 100644 --- a/src/report/interactions.js +++ b/src/report/interactions.js @@ -214,7 +214,7 @@ class InteractionStandalone { ' hyperlinks are active and will link to documents on the local folder after the' + ' package is opened.' this.abstract = interaction.abstract - this.util = new Utilities() + this.util = new DOCXUtilities() this.topics = this.util.rankTags(this.interaction.topics) }