Skip to content

Commit

Permalink
feature(terminal): Make all report tasks optional
Browse files Browse the repository at this point in the history
  • Loading branch information
seantiz committed Nov 14, 2024
1 parent 66c4216 commit 69b56a9
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 81 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## 0.8.4 (2024-11-12)
## 0.8.5 (2024-11-14)

* Creating files is now optional for every possible report file. Only DOT, SVG and CSV are coupled together and TSV is still coupled with posting to the Github Projects API.

## 0.8.4 (2024-11-14)

* We now bundle hpcc-js/wasm-graphviz into Dryfold just to make it simpler. No more need for a separate install of Graphviz.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dryfold-cli",
"version": "0.8.4",
"version": "0.8.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
80 changes: 41 additions & 39 deletions src/export/complexityReport.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
import path from 'path'
import fs from 'fs'
import type { ComplexityValues } from "../schema";
import { formatEstimatedTime, generateMethodList } from "./utils";
import type { ComplexityValues } from "../schema"
import { formatEstimatedTime, generateMethodList } from "./utils"

export function printComplexityReport(moduleMap: Map<string, ComplexityValues>) {
const styling = '../src/export/styles/complexity.css';
let html = `
try {
const styling = '../src/export/styles/complexity.css'
let html = `
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="${styling}">
</head>
<body>
<div class="container">
<h1>Complexity Analysis Report</h1>`;
<h1>Complexity Analysis Report</h1>`

let totalEstimatedTime = {
hours: 0,
minutes: 0
};
let totalEstimatedTime = {
hours: 0,
minutes: 0
}

for (const [file, data] of moduleMap) {
if (data.complexity?.estimatedTime) {
const { metrics, complexityScore, estimatedTime, methods } = data.complexity;
for (const [file, data] of moduleMap) {
if (data.complexity?.estimatedTime) {
const { metrics, complexityScore, estimatedTime, methods } = data.complexity

// Update total time
totalEstimatedTime.hours += estimatedTime?.hours || 0;
totalEstimatedTime.minutes += estimatedTime?.minutes || 0;
// Update total time
totalEstimatedTime.hours += estimatedTime?.hours || 0
totalEstimatedTime.minutes += estimatedTime?.minutes || 0

if (totalEstimatedTime.minutes >= 60) {
totalEstimatedTime.hours += Math.floor(totalEstimatedTime.minutes / 60);
totalEstimatedTime.minutes = totalEstimatedTime.minutes % 60;
}
if (totalEstimatedTime.minutes >= 60) {
totalEstimatedTime.hours += Math.floor(totalEstimatedTime.minutes / 60)
totalEstimatedTime.minutes = totalEstimatedTime.minutes % 60
}

html += `
html += `
<div class="file-card">
<h2>${path.basename(file)}</h2>
<div class="metrics">
<div class="metric">Lines: ${metrics.loc}</div>
<div class="metric">Complexity: ${
metrics.conditionals + metrics.loops || 'N/A'
}</div>
<div class="metric">Complexity: ${metrics.conditionals + metrics.loops || 'N/A'
}</div>
<div class="metric">Functions: ${metrics.functions}</div>
<div class="metric">Score: ${complexityScore.toFixed(2)}</div>
<div class="metric">Est. Time: ${formatEstimatedTime(estimatedTime)}</div>
</div>`
if(methods.localFunctions.length > 0 || methods.callbacks.length > 0) {
html += `
if (methods.localFunctions.length > 0 || methods.callbacks.length > 0) {
html += `
<div>
<h3>Methods Used</h3>
${generateMethodList(methods)}
</div>`;
}
}
</div>`
}
}

html += `</div>`;
}
html += `</div>`
}

html += `
html += `
<div class="summary">
<h2>Project Summary</h2>
<div class="metric">
Expand All @@ -67,16 +67,18 @@ export function printComplexityReport(moduleMap: Map<string, ComplexityValues>)
<div class="metric">
<strong>Approximately:</strong>
${Math.ceil(
(totalEstimatedTime.hours + totalEstimatedTime.minutes / 60) / 40
)} work weeks
(totalEstimatedTime.hours + totalEstimatedTime.minutes / 60) / 40
)} work weeks
</div>
</div>
</body>
</html>`;
</html>`

if(!fs.existsSync('./allreports'))
fs.mkdirSync('./allreports', { recursive: true} )
if (!fs.existsSync('./allreports'))
fs.mkdirSync('./allreports', { recursive: true })

fs.writeFileSync('./allreports/complexity_report.html', html);
console.log(`Report generated: ${path.resolve('./allreports/complexity_report.html')}`);
}
fs.writeFileSync('./allreports/complexity_report.html', html)
} catch (error) {
throw error
}
}
2 changes: 0 additions & 2 deletions src/export/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ export async function generateDataViz(moduleMap: Map<string, DesignValues>) {
const svg = graphviz.layout(dot, "svg", "fdp");
await fs.promises.writeFile('./allreports/dependencies.svg', svg);
dotToCSV(dotFilePath)

console.log('Successfully generated graph and CSV files');
} catch (error) {
throw error;
}
Expand Down
45 changes: 24 additions & 21 deletions src/export/featureReport.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
import fs from 'fs';
import path from 'path';
import type { DesignValues} from '../schema';
import { generateCards, generateLayerSummary } from './utils';
import fs from 'fs'
import path from 'path'
import type { DesignValues } from '../schema'
import { generateCards, generateLayerSummary } from './utils'
import { createDot } from './core'

export function printFeatureReport(moduleMap: Map<string, DesignValues>) {
const { unknownLayers } = createDot(moduleMap);
const styling = '../src/export/styles/features.css';
try {
const { unknownLayers } = createDot(moduleMap)
const styling = '../src/export/styles/features.css'

let svg = '';
const svgFromData = './allreports/dependencies.svg';
if (fs.existsSync(svgFromData)) {
svg = fs.readFileSync(svgFromData, 'utf-8');
} else {
svg = "No renderable content"
}
let svg = ''
const svgFromData = './allreports/dependencies.svg'
if (fs.existsSync(svgFromData)) {
svg = fs.readFileSync(svgFromData, 'utf-8')
} else {
svg = "No renderable content"
}

const unknownLayersSection = unknownLayers.length ? `
const unknownLayersSection = unknownLayers.length ? `
<div class="warning-section">
<h2>⚠️ Unclassified Modules</h2>
<p>The following ${unknownLayers.length} modules need architectural classification:</p>
<ul>
${unknownLayers.map(name => `<li>${name}</li>`).join('\n')}
</ul>
</div>
` : '';
` : ''

const html = `
const html = `
<!DOCTYPE html>
<html>
<head>
Expand All @@ -51,11 +52,13 @@ export function printFeatureReport(moduleMap: Map<string, DesignValues>) {
</div>
</body>
</html>
`;
`

if(!fs.existsSync('./allreports'))
fs.mkdirSync('./allreports', { recursive: true} )
if (!fs.existsSync('./allreports'))
fs.mkdirSync('./allreports', { recursive: true })

fs.writeFileSync('./allreports/features_report.html', html);
console.log(`Report generated: ${path.resolve('./allreports/features_report.html')}`);
fs.writeFileSync('./allreports/features_report.html', html)
} catch (error) {
throw error
}
}
3 changes: 0 additions & 3 deletions src/export/githubProject.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import fs from 'fs'
import path from 'path'
import type { DesignValues } from "../schema"

export function generateGHProject(moduleMap: Map<string, DesignValues>) {
// Original functionality starts here
if(!fs.existsSync('./allreports')) {
fs.mkdirSync('./allreports', { recursive: true })
}
Expand All @@ -25,5 +23,4 @@ export function generateGHProject(moduleMap: Map<string, DesignValues>) {
}

fs.writeFileSync('./allreports/module_tasks.tsv', tsvContent)
console.log(`Tasks preadsheet generated: ${path.resolve('./allreports/module_tasks.tsv')}`)
}
79 changes: 65 additions & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import fs from 'fs'
import path from 'path'
import { exec } from 'child_process'
import { createInterface } from 'readline/promises'
import { analyseCppFile, findDesign } from './analyse'
import fs from 'fs';
import path from 'path';
import { exec } from 'child_process';
import { createInterface } from 'readline/promises';
import { analyseCppFile, findDesign } from './analyse';
import {
printComplexityReport,
printFeatureReport,
generateDataViz,
generateGHProject,
generateKanriJSON } from './export'
generateKanriJSON
} from './export';

async function main() {
const errors: Error[] = [];
const terminal = createInterface({
input: process.stdin,
output: process.stdout
Expand All @@ -25,16 +27,63 @@ async function main() {
}

const codebaseComplexity = walkDirectory(entryPoint)
printComplexityReport(codebaseComplexity)

const kanriCards = generateKanriJSON(codebaseComplexity)
fs.writeFileSync('./allreports/kanri_tasks.json', JSON.stringify(kanriCards, null, 2));
console.log('\nPlease make your choices below (y/n) to all:\n')
const yesToComplexity = (await terminal.question('Create complexity report? : ')).trim().toLowerCase() === 'y'
const yesToDataViz = (await terminal.question('Create data viz files - DOT, SVG & CSV? : ')).trim().toLowerCase() === 'y'

if (!yesToDataViz) {
console.log('\nNOTE: You chose not to create an SVG. If you choose to create a Feature Report, it won\'t have an SVG map embedded in the report.\n')
}

const yesToFeatures = (await terminal.question('Create feature report ? : ')).trim().toLowerCase() === 'y'
const yesToJSON = (await terminal.question('Create JSON for Kanri cards? : ')).trim().toLowerCase() === 'y'

if (yesToComplexity) {
try {
printComplexityReport(codebaseComplexity);
} catch (error) {
errors.push(error as Error);
}
}

const codebaseDesign = findDesign(codebaseComplexity);

if (yesToDataViz) {
try {
await generateDataViz(codebaseDesign);
} catch (error) {
errors.push(error as Error);
}
}

if (yesToFeatures) {
try {
printFeatureReport(codebaseDesign);
} catch (error) {
errors.push(error as Error);
}
}

if (yesToJSON) {
try {
const kanriCards = generateKanriJSON(codebaseComplexity);
fs.writeFileSync('./allreports/kanri_tasks.json', JSON.stringify(kanriCards, null, 2));
} catch (error) {
errors.push(error as Error);
}
}

if (errors.length > 0) {
console.log('\nWarning: Created with errors. All successfully created files are in dryfold-cli/allreports folder.');
console.log('Error details:');
errors.forEach((error, index) => {
console.error(`${index + 1}. ${error.message}`);
});
} else {
console.log('\nDone! All reports can be found in the dryfold-cli/allreports folder');
}

const codebaseDesign = findDesign(codebaseComplexity)
// AST removed from the map from here on
await generateDataViz(codebaseDesign)
printFeatureReport(codebaseDesign)
generateGHProject(codebaseDesign)

const postGH = (await terminal.question('\n\nWould you like to create a GitHub project for these tasks? (y/n): ')).trim().toLowerCase()

Expand All @@ -50,6 +99,7 @@ async function main() {
}

try {
generateGHProject(codebaseDesign)
const projectName = (await terminal.question('Please name your new project: ')).trim()
console.log(`Creating ${projectName} on Github. Please wait...`)

Expand Down Expand Up @@ -84,6 +134,7 @@ async function main() {
}
}


function walkDirectory(entryPoint: string) {
const moduleMap = new Map()

Expand Down

0 comments on commit 69b56a9

Please sign in to comment.