Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for parsing tasks in callouts #76

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 91 additions & 3 deletions src/cacheOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,29 @@ export class CacheOperation {
}
}


updateMovedTaskToCacheByID(taskId: string, updatedContent: Object)
{
const savedTasks = this.plugin.settings.todoistTasksData.tasks;
const task = savedTasks.find(obj => obj.id === taskId);
//console.log('found existing task: ' + JSON.stringify(task, null, 4))
if(task)
{
if(updatedContent.sectionId !== undefined)
{
task.sectionId = updatedContent.sectionId
}
if(updatedContent.projectId !== undefined)
{
task.projectId = updatedContent.projectId
}
if(updatedContent.parentId !== undefined)
{
task.parentId = updatedContent.parentId
}
//console.log('modified task: ' + JSON.stringify(task, null, 4))
}
}
//due 的结构 {date: "2025-02-25",isRecurring: false,lang: "en",string: "2025-02-25"}


Expand Down Expand Up @@ -402,6 +425,23 @@ export class CacheOperation {
}
}

// section names can be non-unique across projects.
// this we need the project id for the lookup here.
getSectionIdByNameFromCache(projectId:string, sectionName:string) {
try {
//console.log('lookig for section: ' + sectionName + 'of project' + projectId)
const savedSections = this.plugin.settings.todoistTasksData.sections
//console.log('sections are: ' + JSON.stringify(savedSections))
const targetSection = savedSections.find(obj => obj.name === sectionName && obj.projectId === projectId);
//console.log('got target section: ' + targetSection)
const sectionId = targetSection ? targetSection.id : null;
return(sectionId)
} catch (error) {
console.error(`Error finding section from Cache file: ${error}`);
return(false)
}
}



getProjectNameByIdFromCache(projectId:string) {
Expand All @@ -415,6 +455,20 @@ export class CacheOperation {
return(false)
}
}


//reverse lookup of sections is unique.
getSectionNameByIdFromCache(sectionId:string) {
try {
const savedSections = this.plugin.settings.todoistTasksData.sections
const targetSection = savedSections.find(obj => obj.id === sectionId);
const sectionName = targetSection ? targetSection.name : null;
return(sectionName)
} catch (error) {
console.error(`Error finding section from Cache file: ${error}`);
return(false)
}
}



Expand All @@ -426,20 +480,54 @@ export class CacheOperation {
if(!projects){
return false
}


// reset cached sections
this.plugin.settings.todoistTasksData.sections = [];

// we can flatten the list of sections here, as we just need the lookup
// from names to sections.
// Additionally, sections contain their parent projects as a property
for(const project of projects)
{
//console.log('saving project' + project.name)
this.saveSectionsToCache(project.id)
}
//save to json
this.plugin.settings.todoistTasksData.projects = projects

return true

}catch(error){
return false
console.log(`error downloading projects: ${error}`)
return false

}

}

// save project sections to json file: This is required, as we need a lookup
// from section ids to section names.
// The design of the plugin avoids live requests to the REST API.
async saveSectionsToCache(projectId: string) {
try{
//get projects
const sections = await this.plugin.todoistRestAPI.GetAllSections(projectId)
if(!sections){
return false
}

//save to json
for(const section of sections){
this.plugin.settings.todoistTasksData.sections.push(section)
}

return true

}catch(error){
console.log(`error downloading sections: ${error}`)
return false

}
}

async updateRenamedFilePath(oldpath:string,newpath:string){
try{
Expand Down
16 changes: 15 additions & 1 deletion src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface UltimateTodoistSyncSettings {
enableFullVaultSync: boolean;
statistics: any;
debugMode:boolean;
sectionPrefix:string;
}


Expand All @@ -29,11 +30,12 @@ export const DEFAULT_SETTINGS: UltimateTodoistSyncSettings = {
apiInitialized:false,
defaultProjectName:"Inbox",
automaticSynchronizationInterval: 300, //default aync interval 300s
todoistTasksData:{"projects":[],"tasks":[],"events":[]},
todoistTasksData:{"projects":[],"sections":[],"tasks":[],"events":[]},
fileMetadata:{},
enableFullVaultSync:false,
statistics:{},
debugMode:false,
sectionPrefix:"",
//mySetting: 'default',
//todoistTasksFilePath: 'todoistTasks.json'

Expand Down Expand Up @@ -167,7 +169,19 @@ export class UltimateTodoistSyncSettingTab extends PluginSettingTab {

)

new Setting(containerEl)
.setName('Section Tag Prefix')
.setDesc('Provides the option to set a prefix for identifying tags that should move a task to a project section (e.g. "sec-"). Can be empty.')
.addText((text) =>
text
.setValue(this.plugin.settings.sectionPrefix)
.onChange(async (value) => {
this.plugin.settings.sectionPrefix = value;
this.plugin.settings.apiInitialized = false;
//
})

)

new Setting(containerEl)
.setName('Full Vault Sync')
Expand Down
53 changes: 44 additions & 9 deletions src/syncModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,9 @@ export class TodoistSync {
//tag or labels 是否修改
const tagsModified = !this.plugin.taskParser.taskTagCompare(lineTask,savedTask)
//project 是否修改
const projectModified = !(await this.plugin.taskParser.taskProjectCompare(lineTask,savedTask))
const projectModified = !await(this.plugin.taskParser.taskProjectCompare(lineTask,savedTask))
//section
const sectionModified = !await(this.plugin.taskParser.taskSectionCompare(lineTask, savedTask))
//status 是否修改
const statusModified = !this.plugin.taskParser.taskStatusCompare(lineTask,savedTask)
//due date 是否修改
Expand All @@ -379,6 +381,7 @@ export class TodoistSync {
let contentChanged= false;
let tagsChanged = false;
let projectChanged = false;
let sectionChanged = false;
let statusChanged = false;
let dueDateChanged = false;
let parentIdChanged = false;
Expand Down Expand Up @@ -413,16 +416,30 @@ export class TodoistSync {

//todoist Rest api 没有 move task to new project的功能
if (projectModified) {
//console.log(`Project id modified for task ${lineTask_todoist_id}`)
//updatedContent.projectId = lineTask.projectId
//projectChanged = false;
console.log(`Project id modified for task ${lineTask_todoist_id}`)
updatedContent.projectId = lineTask.projectId
projectChanged = true;
}

//todoist Rest api 没有修改 parent id 的借口
if (parentIdModified) {
//console.log(`Parnet id modified for task ${lineTask_todoist_id}`)
//updatedContent.parentId = lineTask.parentId
//parentIdChanged = false;
console.log(`Parent id modified for task ${lineTask_todoist_id}`)
updatedContent.parentId = lineTask.parentId
parentIdChanged = true;
}

if(sectionModified)
{
//console.log(`Section modified for task ${lineTask_todoist_id}. Changed to ${lineTask.sectionId}`)
updatedContent.sectionId = lineTask.sectionId
//if sectionId is updated, but empty, we move the task to "No Section".
//for this, we keep the section id empty, but provide the project id:
//see https://developer.todoist.com/sync/v9/#move-an-item
if(!lineTask.sectionId) {
delete updatedContent.sectionId
updatedContent.projectId = lineTask.projectId
}
sectionChanged = true;
}

if (priorityModified) {
Expand All @@ -432,14 +449,29 @@ export class TodoistSync {
}


if (contentChanged || tagsChanged ||dueDateChanged ||projectChanged || parentIdChanged || priorityChanged) {
if (contentChanged || tagsChanged ||dueDateChanged || priorityChanged) {
//console.log("task content was modified");
//console.log(updatedContent)
const updatedTask = await this.plugin.todoistRestAPI.UpdateTask(lineTask.todoist_id.toString(),updatedContent)
updatedTask.path = filepath
this.plugin.cacheOperation.updateTaskToCacheByID(updatedTask);

}

if(projectChanged || sectionChanged || parentIdChanged)
{
// update task in cache and move the task.
// according to https://github.com/Doist/todoist-api-python/issues/8
// tasks cannot be moved by the REST API, at least for now.
// hence, moveTask triggers a POST request to the Sync API.

//console.log('ProjectId, ParentId and/or SectionId have been modified.')
//console.log('Moving Task ' + lineTask.todoist_id?.toString())
//console.log('content is: ' + JSON.stringify(updatedContent, null, 4))
this.plugin.cacheOperation.updateMovedTaskToCacheByID(lineTask.todoist_id.toString(), updatedContent)
await this.plugin.todoistSyncAPI.moveTask(lineTask.todoist_id.toString(), updatedContent)
}

if (statusModified) {
console.log(`Status modified for task ${lineTask_todoist_id}`)
if (lineTask.isCompleted === true) {
Expand All @@ -457,7 +489,7 @@ export class TodoistSync {



if (contentChanged || statusChanged ||dueDateChanged ||tagsChanged || projectChanged || priorityChanged) {
if (contentChanged || statusChanged ||dueDateChanged ||tagsChanged || projectChanged || sectionChanged || priorityChanged) {
console.log(lineTask)
console.log(savedTask)
//`Task ${lastLineTaskTodoistId} was modified`
Expand All @@ -479,6 +511,9 @@ export class TodoistSync {
if (projectChanged) {
message += " Project was changed.";
}
if (sectionChanged) {
message += " Section was changed.";
}
if (priorityChanged) {
message += " Priority was changed.";
}
Expand Down
48 changes: 39 additions & 9 deletions src/taskParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const keywords = {
};

const REGEX = {
TODOIST_TAG: new RegExp(`^[\\s]*[-] \\[[x ]\\] [\\s\\S]*${keywords.TODOIST_TAG}[\\s\\S]*$`, "i"),
TODOIST_TAG: new RegExp(`^[\\s|>]*[-] \\[[x ]\\] [\\s\\S]*${keywords.TODOIST_TAG}[\\s\\S]*$`, "i"),
TODOIST_ID: /\[todoist_id::\s*\d+\]/,
TODOIST_ID_NUM:/\[todoist_id::\s*(.*?)\]/,
TODOIST_LINK:/\[link\]\(.*?\)/,
Expand All @@ -62,14 +62,14 @@ const REGEX = {
REMOVE_SPACE: /^\s+|\s+$/g,
REMOVE_DATE: new RegExp(`(${keywords.DUE_DATE})\\s?\\d{4}-\\d{2}-\\d{2}`),
REMOVE_INLINE_METADATA: /%%\[\w+::\s*\w+\]%%/,
REMOVE_CHECKBOX: /^(-|\*)\s+\[(x|X| )\]\s/,
REMOVE_CHECKBOX_WITH_INDENTATION: /^([ \t]*)?(-|\*)\s+\[(x|X| )\]\s/,
REMOVE_CHECKBOX: /^(>*\s*)(-|\*)\s+\[(x|X| )\]\s/,
REMOVE_CHECKBOX_WITH_INDENTATION: /^(>*)([ \t]*)?(-|\*)\s+\[(x|X| )\]\s/,
REMOVE_TODOIST_LINK: /\[link\]\(.*?\)/,
},
ALL_TAGS: /#[\w\u4e00-\u9fa5-]+/g,
TASK_CHECKBOX_CHECKED: /- \[(x|X)\] /,
TASK_INDENTATION: /^(\s{2,}|\t)(-|\*)\s+\[(x|X| )\]/,
TAB_INDENTATION: /^(\t+)/,
TASK_INDENTATION: /^>*(\s{2,}|\t)(-|\*)\s+\[(x|X| )\]/,
TAB_INDENTATION: /^>*[ ]*(\t+)/,
TASK_PRIORITY: /\s!!([1-4])\s/,
BLANK_LINE: /^\s*$/,
TODOIST_EVENT_DATE: /(\d{4})-(\d{2})-(\d{2})/
Expand Down Expand Up @@ -149,6 +149,7 @@ export class TaskParser {

let projectId = this.plugin.cacheOperation.getDefaultProjectIdForFilepath(filepath as string)
let projectName = this.plugin.cacheOperation.getProjectNameByIdFromCache(projectId)
let sectionId = null;

if(hasParent){
projectId = parentTaskObject.projectId
Expand All @@ -157,19 +158,40 @@ export class TaskParser {
if(!hasParent){
//匹配 tag 和 peoject
for (const label of labels){

//check for tags matching todoist projects

//console.log(label)
let labelName = label.replace(/#/g, "");
let labelName = label.replace(/#/g, "")
//console.log(labelName)
let hasProjectId = this.plugin.cacheOperation.getProjectIdByNameFromCache(labelName)
if(!hasProjectId){
continue
}
projectName = labelName
//console.log(`project is ${projectName} ${label}`)
console.log(`project is ${projectName} ${label}`)
projectId = hasProjectId
break
}

//its probably easier to first look for the projectID and THEN find the section.
for(const label of labels)
{
//check for tags matching todoist project sections
let sectionName = label

if(this.plugin.settings.sectionPrefix != "") {
const needle = `/(${this.plugin.settings.sectionPrefix})/`
let sectionRegEx = label.match(needle)
if(sectionRegEx){

sectionName = label.replace(needle, "")
}
}
let hasSectionId = this.plugin.cacheOperation.getSectionIdByNameFromCache(projectId, sectionName)
if(hasSectionId){
sectionId = hasSectionId
}
}
}


Expand All @@ -185,6 +207,7 @@ export class TaskParser {

const todoistTask = {
projectId: projectId,
sectionId: sectionId || null,
content: content || '',
parentId: parentId || null,
dueDate: dueDate || '',
Expand Down Expand Up @@ -379,14 +402,21 @@ export class TaskParser {


//task project id compare
async taskProjectCompare(lineTask:Object,todoistTask:Object) {
taskProjectCompare(lineTask:Object,todoistTask:Object) {
//project 是否修改
//console.log(dataviewTaskProjectId)
//console.log(todoistTask.projectId)
return(lineTask.projectId === todoistTask.projectId)
}


taskSectionCompare(lineTask:Object,todoistTask:Object) {
//project 是否修改
//console.log("Comparing sections")
//console.log("new: " + lineTask.sectionId + " was: " + todoistTask.sectionId)
return(lineTask.sectionId === todoistTask.sectionId)
}

//判断任务是否缩进
isIndentedTask(text:string) {
return(REGEX.TASK_INDENTATION.test(text));
Expand Down
Loading