find dates works
This commit is contained in:
parent
8bef6d4d6a
commit
bd538bcee8
|
@ -6,5 +6,8 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node app.js"
|
"start": "node app.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"moment": "^2.29.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,30 @@
|
||||||
* convertir un fichier .org vers des données structurées en json
|
* convertir un fichier .org vers des données structurées en json
|
||||||
* @type {*}
|
* @type {*}
|
||||||
*/
|
*/
|
||||||
// const fileToParse = process.argv[0]
|
|
||||||
import fs from 'node-fs';
|
import fs from 'node-fs';
|
||||||
const sourceFileName = 'demo.org'
|
import moment from 'moment';
|
||||||
const sourceFilePath = './sources/'+sourceFileName;
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* initialize configs
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
const sourceFileName = 'demo_more.org'
|
||||||
|
const sourceFilePath = './sources/' + sourceFileName;
|
||||||
|
|
||||||
|
let headers = []
|
||||||
|
let tasksObjectsForJsonExport = []
|
||||||
|
let headersByKind = {}
|
||||||
|
let writeJsonAfterParse = false;
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
* fetch the source orgmode file to read its contents
|
||||||
|
*************************************************************/
|
||||||
|
|
||||||
console.log('parse some org file', sourceFilePath)
|
console.log('parse some org file', sourceFilePath)
|
||||||
if (!sourceFilePath) {
|
if (!sourceFilePath) {
|
||||||
console.error('pas de fichier à ouvrir')
|
console.error('pas de fichier à ouvrir')
|
||||||
}
|
}
|
||||||
let headers = []
|
|
||||||
let tasksObjectsForJsonExport = []
|
|
||||||
let headersByKind = {}
|
|
||||||
|
|
||||||
|
|
||||||
fs.stat(sourceFilePath, function (err, stat) {
|
fs.stat(sourceFilePath, function (err, stat) {
|
||||||
if (err == null) {
|
if (err == null) {
|
||||||
console.log(`File ${sourceFilePath} exists`);
|
console.log(`File ${sourceFilePath} exists`);
|
||||||
|
@ -28,26 +38,41 @@ fs.stat(sourceFilePath, function (err, stat) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* search elements
|
||||||
|
*********************/
|
||||||
|
let stateKeywordList = ['SOMEDAY', 'NEXT', 'TODO', 'CANCELLED', 'DONE', 'WAITING'];
|
||||||
|
let dateKeywordList = ['CREATED', 'SCHEDULED', 'DEADLINE', 'CLOSED','Refiled'];
|
||||||
|
let sectionKeywordList = ['PROPERTIES', 'LOGBOOK', 'END'];
|
||||||
|
let propertiesSection = {}
|
||||||
|
let logBookSection = {}
|
||||||
|
|
||||||
let keyword = 'SOMEDAY';
|
let headerKeywordSearch = '[' + stateKeywordList.join('|') + ']'
|
||||||
let keywordList = ['SOMEDAY', 'NEXT','TODO', 'CANCELLED','DONE', 'WAITING'];
|
/**
|
||||||
|
* task object example
|
||||||
|
* @type {{level: string, header: string, dates: {CREATED: string, DONE: string, REFILED: string}, state: string, content: string, properties: {}, tags: [], tagsInherited: []}}
|
||||||
|
*/
|
||||||
let task = {
|
let task = {
|
||||||
header : "",
|
header: "",
|
||||||
level : "",
|
level: "",
|
||||||
content : "",
|
content: "",
|
||||||
state : "",
|
state: "",
|
||||||
tags : [],
|
tags: [],
|
||||||
tagsInherited : [],
|
tagsInherited: [],
|
||||||
dates : {
|
dates: {},
|
||||||
'CREATED':'',
|
logbook: {},
|
||||||
'REFILED':'',
|
properties: {},
|
||||||
'DONE':'',
|
|
||||||
},
|
|
||||||
properties : {},
|
|
||||||
}
|
}
|
||||||
let currentTask = Object.create(task);
|
// init first task object as empty clone
|
||||||
|
let currentTask = {...task};
|
||||||
|
let isHeader = false;
|
||||||
|
let isProperty = false;
|
||||||
|
let isLogbook = false;
|
||||||
|
let isFirst = true;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* loop to parse all
|
||||||
|
*********************/
|
||||||
fs.readFile(sourceFilePath, 'utf8', function (err, data) {
|
fs.readFile(sourceFilePath, 'utf8', function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return console.log(err);
|
return console.log(err);
|
||||||
|
@ -61,54 +86,125 @@ fs.readFile(sourceFilePath, 'utf8', function (err, data) {
|
||||||
everyline.forEach((line) => {
|
everyline.forEach((line) => {
|
||||||
|
|
||||||
|
|
||||||
|
// gérer la création d'objets définissant les tâches et leurs propriétés
|
||||||
if (line.match(/^\*+? /)) {
|
if (line.match(/^\*+? /)) {
|
||||||
// add last task to export list
|
// add last task to export list
|
||||||
tasksObjectsForJsonExport.push(currentTask)
|
if (!isFirst) {
|
||||||
|
tasksObjectsForJsonExport.push(currentTask)
|
||||||
|
|
||||||
|
console.log('currentTask.dates', currentTask.dates)
|
||||||
|
currentTask = {...task};
|
||||||
|
} else {
|
||||||
|
isFirst = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isHeader = true;
|
||||||
|
// compter les étoiles pour trouver le niveau du header
|
||||||
|
currentTask.level = line.match(/\*/g)?.length
|
||||||
|
|
||||||
// create a new task
|
// create a new task
|
||||||
currentTask = Object.create(task);
|
|
||||||
|
|
||||||
|
line = line.replace('*', '')
|
||||||
|
line = line.replace(stateKeywordList, [].fill('', 0, stateKeywordList.length))
|
||||||
|
|
||||||
headers.push(line)
|
headers.push(line)
|
||||||
currentTask.header = line;
|
currentTask.header = line;
|
||||||
keywordList.forEach(keyword => {
|
stateKeywordList.forEach(keyword => {
|
||||||
lookForKeywordInLine(line, keyword)
|
let keywordIsFound = lineHasKeyword(line, keyword)
|
||||||
|
|
||||||
if(line.indexOf('* '+keyword) !== -1){
|
if (keywordIsFound) {
|
||||||
currentTask.state = keyword
|
currentTask.state = keyword
|
||||||
}
|
}
|
||||||
// compter les étoiles pour trouver le niveau du header
|
|
||||||
currentTask.level = line.match(/\*/g).length
|
|
||||||
let tagsFound = line.match(/\:(.*)\:/g)
|
|
||||||
console.log('tagsFound', tagsFound)
|
|
||||||
if(tagsFound){
|
|
||||||
tagsFound = tagsFound[0];
|
|
||||||
console.log('tagsFound', tagsFound)
|
|
||||||
tagsFound = tagsFound.split(':').filter(item => item.length )
|
|
||||||
currentTask.tags = tagsFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
// TODO gérer la création d'objets définissant les tâches et leurs propriétés
|
|
||||||
|
|
||||||
|
|
||||||
|
// trouver les tags
|
||||||
|
let tagsFound = line.match(/\:(.*)\:/g)
|
||||||
|
if (tagsFound) {
|
||||||
|
tagsFound = tagsFound[0];
|
||||||
|
console.log('tagsFound', tagsFound)
|
||||||
|
tagsFound = tagsFound.split(':').filter(item => item.length)
|
||||||
|
currentTask.tags = tagsFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fin des recherches dans la ligne de Header
|
||||||
|
} else {
|
||||||
|
isHeader = false;
|
||||||
}
|
}
|
||||||
|
// examen des lignes de corps de tâche, ou de corps de section suite au header.
|
||||||
|
|
||||||
|
// classer les dates de création, cloture, et de logbook
|
||||||
|
let dateFound = searchDate(line)
|
||||||
|
if(dateFound){
|
||||||
|
|
||||||
|
dateKeywordList.forEach(keyword => {
|
||||||
|
if (lineHasSubstring(line, keyword)) {
|
||||||
|
if (!currentTask.dates[keyword]) {
|
||||||
|
currentTask.dates[keyword] = '';
|
||||||
|
}
|
||||||
|
currentTask.dates[keyword] = new Date(dateFound[0]);
|
||||||
|
} else {
|
||||||
|
// console.log('keyword', keyword)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ajouter le corps complet de la section après le header
|
||||||
|
if (line.length && !isHeader) {
|
||||||
|
|
||||||
|
let cleanedLine = line.replace(/\s\s/g, ' ')
|
||||||
|
cleanedLine = line.replace(/ {2,}/g, ' ')
|
||||||
|
console.log('line', cleanedLine)
|
||||||
|
currentTask.corpus += `
|
||||||
|
` + cleanedLine;
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
// ajouter la dernière tâche parsée
|
||||||
|
tasksObjectsForJsonExport.push(currentTask)
|
||||||
|
|
||||||
console.log('headers', headers)
|
console.log('headers', headers)
|
||||||
console.log(" parsing fini")
|
console.log(" parsing fini")
|
||||||
console.log('nombre de lignes', everyline.length)
|
stateKeywordList.forEach(keyword => console.log('nombre de headers', keyword, headersByKind[keyword]?.length))
|
||||||
console.log('nombre de headers', headers.length)
|
|
||||||
keywordList.forEach(keyword => console.log('nombre de headers',keyword, headersByKind[keyword]?.length))
|
|
||||||
|
|
||||||
console.log('tasksObjectsForJsonExport', tasksObjectsForJsonExport)
|
|
||||||
// writeJsonFile('export_'+sourceFileName +'.json' , JSON.stringify(tasksObjectsForJsonExport));
|
const jsonContent = {
|
||||||
|
statistics: {
|
||||||
|
lines_count: everyline.length,
|
||||||
|
headers_count: headers.length,
|
||||||
|
},
|
||||||
|
meta_data: {
|
||||||
|
author: '@tykayn@mastodon.Cipherbliss.com',
|
||||||
|
generated_at: new Date(),
|
||||||
|
generated_from_file: sourceFilePath + sourceFileName,
|
||||||
|
sources: 'https://forge.chapril.org/tykayn/org-report-stats.git'
|
||||||
|
},
|
||||||
|
tasks_list: tasksObjectsForJsonExport
|
||||||
|
}
|
||||||
|
// console.log('tasksObjectsForJsonExport', jsonContent)
|
||||||
|
|
||||||
|
if (writeJsonAfterParse) {
|
||||||
|
writeJsonFile('export_' + sourceFileName + '.json', JSON.stringify(jsonContent));
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
})
|
})
|
||||||
|
|
||||||
function lookForKeywordInLine(line, keyword='TODO') {
|
function lineHasKeyword(line, keyword = 'TODO') {
|
||||||
|
|
||||||
|
let isFound = (line.indexOf('* ' + keyword) !== -1)
|
||||||
if ( line.indexOf('* '+keyword) !== -1) {
|
if (isFound) {
|
||||||
createNewHeaderKind(keyword)
|
createNewHeaderKind(keyword)
|
||||||
headersByKind[keyword].push(line);
|
headersByKind[keyword].push(line);
|
||||||
}
|
}
|
||||||
|
return isFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lineHasSubstring(line, keyword) {
|
||||||
|
|
||||||
|
return (line.indexOf(keyword) !== -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewHeaderKind(keyword) {
|
function createNewHeaderKind(keyword) {
|
||||||
|
@ -117,9 +213,45 @@ function createNewHeaderKind(keyword) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* chercher des dates et heures au format
|
||||||
|
* YYYY-MM-DD HH:II:SS
|
||||||
|
*
|
||||||
|
* @param line
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
function searchDate(line) {
|
||||||
|
// return line.match(/[(\d{4}\-\d{2}\-\d{2} ?\d{2}?\:?\d{2}?\:?\d{2}?)(\d{4}\-\d{2}\-\d{2})]/)
|
||||||
|
let simpleDay = line.match(/\d{4}\-\d{2}\-\d{2} \w{3}?\.?/)
|
||||||
|
let simpleDayHour = line.match(/\d{4}\-\d{2}\-\d{2} \w{3}?\.? \d{2}\:\d{2}/)
|
||||||
|
let simpleDayHourSec = line.match(/\d{4}\-\d{2}\-\d{2} \w{3}?\.? \d{2}\:\d{2}\:\d{2}/)
|
||||||
|
|
||||||
|
if(simpleDayHourSec){
|
||||||
|
return simpleDayHourSec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(simpleDayHour){
|
||||||
|
return simpleDayHour;
|
||||||
|
}
|
||||||
|
if(simpleDay){
|
||||||
|
return simpleDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* afin de trouver la première date liée à une tâche parmi celles mentionnées, il faut comparer les dates
|
||||||
|
* @param date1
|
||||||
|
* @param date2
|
||||||
|
*/
|
||||||
|
function compareDatesAndKeepOldest(date1, date2) {
|
||||||
|
date1 = moment(date1)
|
||||||
|
date2 = moment(date2)
|
||||||
|
}
|
||||||
|
|
||||||
function writeJsonFile(fileName, fileContent) {
|
function writeJsonFile(fileName, fileContent) {
|
||||||
console.log('write file ', fileName);
|
console.log('write file ', fileName);
|
||||||
|
|
||||||
return fs.writeFile(
|
return fs.writeFile(
|
||||||
`./output/${fileName}`,
|
`./output/${fileName}`,
|
||||||
fileContent,
|
fileContent,
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
* coucou la démo de fichier org
|
|
||||||
* voilà un 2e header
|
|
||||||
* TODO faire une démo
|
* TODO faire une démo
|
||||||
* NEXT écrire le fichier de démo [1/3] :demo:
|
DEADLINE: <2023-02-28 mar. 12:30>
|
||||||
** SOMEDAY oh un sous header, niveau 2 :subtil:
|
** NEXT écrire le fichier de démo [1/3] :demo:
|
||||||
** CANCELLED un truc pas fait au final
|
CREATED: <2023-02-28 mar.>
|
||||||
** DONE et voilà ça c'est fait :projet_fini:ARCHIVE:
|
|
||||||
CLOSED : [2023-03-04 15:00]
|
|
||||||
* pas mal hein
|
|
||||||
c'est un texte de description
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
* coucou la démo de fichier org
|
||||||
|
* voilà un 2e header
|
||||||
|
* TODO faire une démo
|
||||||
|
DEADLINE: <2023-02-28 mar.>
|
||||||
|
* NEXT écrire le fichier de démo [1/3] :demo:
|
||||||
|
CREATED: <2023-02-28 mar.>
|
||||||
|
SCHEDULED: <2023-02-28 mar.>
|
||||||
|
DEADLINE: <2023-02-28 mar.>
|
||||||
|
** SOMEDAY oh un sous header, niveau 2 avec logbook et propriétés :subtil:
|
||||||
|
CREATED: <2023-02-28 mar.>
|
||||||
|
|
||||||
|
:PROPERTIES:
|
||||||
|
:CREATED: [2023-02-28 11:36:23]
|
||||||
|
:END:
|
||||||
|
:LOGBOOK:
|
||||||
|
- State "TODO" from "SOMEDAY" [2023-02-28 mar. 12:12]
|
||||||
|
- Refiled on [2023-02-28 mar. 11:50]
|
||||||
|
:END:
|
||||||
|
** CANCELLED un truc pas fait au final
|
||||||
|
** DONE et voilà ça c'est fait :projet_fini:ARCHIVE:
|
||||||
|
CLOSED : [2023-03-04 15:00]
|
||||||
|
* pas mal hein
|
||||||
|
c'est un texte de description
|
|
@ -1052,6 +1052,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"moment@npm:^2.29.4":
|
||||||
|
version: 2.29.4
|
||||||
|
resolution: "moment@npm:2.29.4"
|
||||||
|
checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ms@npm:2.1.2":
|
"ms@npm:2.1.2":
|
||||||
version: 2.1.2
|
version: 2.1.2
|
||||||
resolution: "ms@npm:2.1.2"
|
resolution: "ms@npm:2.1.2"
|
||||||
|
@ -1268,6 +1275,7 @@ __metadata:
|
||||||
resolution: "root-workspace-0b6124@workspace:."
|
resolution: "root-workspace-0b6124@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
d3: ^7.8.2
|
d3: ^7.8.2
|
||||||
|
moment: ^2.29.4
|
||||||
node-fs: ^0.1.7
|
node-fs: ^0.1.7
|
||||||
nodemon: ^2.0.19
|
nodemon: ^2.0.19
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
|
|
Loading…
Reference in New Issue