1st version

This commit is contained in:
STEINNI
2025-11-04 07:36:02 +00:00
commit 2723c34a5d
177 changed files with 53167 additions and 0 deletions

122
Jira_helper/converter.js Normal file
View File

@@ -0,0 +1,122 @@
import fs from 'fs'
import yargs from 'yargs/yargs'
import { hideBin } from 'yargs/helpers'
import { parse } from 'csv-parse/sync'
const argv = yargs(hideBin(process.argv)).command('Converter', 'Checks CSV for missig stuff, then converts CSV to Jiracula import file.', {})
.options({
'file': {
description: 'CSV file',
alias: 'f',
type: 'string',
demandOption: true,
},
'month': {
description: 'Month number to extract',
alias: 'm',
type: 'number',
demandOption: true,
}
}).help().version('1.0').argv
const colors = {
red: "\x1b[31m",
green: "\x1b[32m",
yellow: "\x1b[33m",
blue: "\x1b[34m",
magenta: "\x1b[35m",
cyan: "\x1b[36m",
bold: "\x1b[1m"
}
// Regex patterns
const durationPattern = /(\d+(?:[.,]\d+)?)\s*(h|H)/
const codePattern = /(SMEIMKT[- ]?\d+)/
// Read CSV
if(!fs.existsSync(argv.file)) {
console.error(`${colors.red}Cannot find this file !?`)
process.exit(1)
}
const csv = fs.readFileSync(argv.file, 'utf8')
const records = parse(csv, {
columns: false,
skip_empty_lines: true
})
// Skip header
const [header, ...rows] = records
// Transform
let results = []
for(let row of rows) {
if (row.length<3) continue
const date = row[0]
const tasksStr = row[2]
if (!tasksStr) continue
if(date.substring(3,5)!=String(argv.month).padStart(2, '0')) continue
const tasks = tasksStr.split('+').map(t => t.trim())
for (let t of tasks) {
const durationMatch = durationPattern.exec(t)
let duration = durationMatch ? durationMatch[1] : null
const codeMatch = codePattern.exec(t)
const code = codeMatch ? codeMatch[1].replace(' ', '-') : null
results.push({
Date: date.trim(),
TaskCode: code,
Duration: parseFloat(duration) || 0,
Description: t
})
}
}
let errs=0, tot=0, timePerDate = {}
results.forEach(r => {
if(!r.TaskCode){
errs++
} else if(!r.Duration){
errs++
} else {
if(r.Date in timePerDate) timePerDate[r.Date] += r.Duration
else timePerDate[r.Date] = r.Duration
}
console.log(
`${colors.cyan}${r.Date} ` +
`${(r.TaskCode) ? colors.yellow+r.TaskCode : colors.red+'Missing Code'} ` +
`${(r.Duration) ? colors.yellow+r.Duration : colors.red+'Missing Duration'} ` +
`${colors.green}${r.Description}`
)
tot++
})
if(errs>0){
console.log(`\n${colors.red}${errs} parsing errors on ${tot} tasks.${colors.green}\n`)
} else {
console.log(`\n${colors.green}${tot} task entries, no parsing errors.\n`)
}
const badDailyHours = {}
let billable = 0
for(let dat in timePerDate){
if((timePerDate[dat] != 8) && (timePerDate[dat] != 4)) badDailyHours[dat] = timePerDate[dat]
else billable += (timePerDate[dat]/8)
}
if(Object.keys(badDailyHours).length>0){
console.log(`${colors.red}Some dates don't have 8H...\n`)
for(let dat in badDailyHours){
console.log(`${colors.red} ${dat}: ${badDailyHours[dat]}H`)
}
} else {
console.log(`\n${colors.green}Ready to export to Jiracula !`)
console.log(`Total billable days : ${billable}\n`)
const outfile = argv.file.substring(0, argv.file.lastIndexOf('.'))+'.jrcl.json'
fs.writeFileSync(outfile, JSON.stringify(results, null, 2), 'utf8')
}