mirror of
https://github.com/tailormade-eu/obsidian-task-export-plugin.git
synced 2026-07-22 06:41:42 +00:00
feat: Enhance CSV export by including folder hierarchy and refining task parsing
This commit is contained in:
parent
ea5ea0f4a1
commit
01a5d99d0b
4 changed files with 30 additions and 15 deletions
|
|
@ -28,7 +28,8 @@ export class CsvWriter {
|
|||
|
||||
// Build header row based on max level depth
|
||||
if (includeHeader) {
|
||||
csv += `CustomerName${this.delimiter}ProjectName`;
|
||||
// Emit CustomerName, then Level columns (first level may be project name or a subfolder), then Task
|
||||
csv += `CustomerName`;
|
||||
for (let i = 1; i <= maxLevelDepth; i++) {
|
||||
csv += `${this.delimiter}Level${i}`;
|
||||
}
|
||||
|
|
@ -38,9 +39,7 @@ export class CsvWriter {
|
|||
// Write data rows
|
||||
for (const task of tasks) {
|
||||
csv += this.escapeField(task.customerName);
|
||||
csv += this.delimiter;
|
||||
csv += this.escapeField(task.projectName);
|
||||
|
||||
|
||||
if (compressLevels) {
|
||||
// Compressed mode: only output non-empty levels, skipping empty slots
|
||||
const nonEmptyLevels = task.levels.filter(l => l !== '');
|
||||
|
|
@ -59,7 +58,6 @@ export class CsvWriter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
csv += this.delimiter;
|
||||
csv += this.escapeField(task.task);
|
||||
csv += '\n';
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ export class TaskExporter {
|
|||
|
||||
// Process each file
|
||||
for (const file of customerFiles) {
|
||||
// Extract customer name and project name from file path
|
||||
const { customerName, projectName } = this.extractCustomerAndProject(file, normalizedPath);
|
||||
// Extract customer name, project name and folder segments from file path
|
||||
const { customerName, projectName, folderSegments } = this.extractCustomerAndProject(file, normalizedPath);
|
||||
|
||||
// Skip files in hidden directories
|
||||
if (customerName.startsWith('.')) {
|
||||
|
|
@ -57,7 +57,8 @@ export class TaskExporter {
|
|||
|
||||
try {
|
||||
const content = await this.app.vault.read(file);
|
||||
const tasks = this.parser.parseFile(content, customerName, projectName);
|
||||
// Pass folderSegments into parser so levels can include folder hierarchy
|
||||
const tasks = this.parser.parseFile(content, customerName, projectName, folderSegments);
|
||||
|
||||
if (tasks.length > 0) {
|
||||
allTasks.push(...tasks);
|
||||
|
|
@ -96,7 +97,7 @@ export class TaskExporter {
|
|||
* Customer name is the first folder under customersFolder.
|
||||
* Project name is the filename without extension.
|
||||
*/
|
||||
private extractCustomerAndProject(file: TFile, customersFolder: string): { customerName: string, projectName: string } {
|
||||
private extractCustomerAndProject(file: TFile, customersFolder: string): { customerName: string, projectName: string, folderSegments: string[] } {
|
||||
// Remove the customers folder prefix from the path
|
||||
let relativePath = file.path.substring(customersFolder.length + 1);
|
||||
|
||||
|
|
@ -108,7 +109,11 @@ export class TaskExporter {
|
|||
|
||||
// Project name is the filename without extension
|
||||
const projectName = file.basename;
|
||||
|
||||
return { customerName, projectName };
|
||||
|
||||
// Folder segments are the intermediate path parts between the customer folder and the file name
|
||||
// parts = [ customer, sub1, sub2, ..., filename ] so folderSegments should be parts.slice(1, parts.length - 1)
|
||||
const folderSegments = parts.length > 2 ? parts.slice(1, parts.length - 1) : [];
|
||||
|
||||
return { customerName, projectName, folderSegments };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export class MarkdownParser {
|
|||
/**
|
||||
* Parses a markdown file and extracts all outstanding tasks.
|
||||
*/
|
||||
parseFile(content: string, customerName: string, projectName: string): TaskItem[] {
|
||||
parseFile(content: string, customerName: string, projectName: string, folderSegments: string[] = []): TaskItem[] {
|
||||
const tasks: TaskItem[] = [];
|
||||
const lines = content.split('\n');
|
||||
|
||||
|
|
@ -91,9 +91,19 @@ export class MarkdownParser {
|
|||
levels: []
|
||||
};
|
||||
|
||||
// Add document headers - keep all levels including empty ones for proper structure
|
||||
// Build levels with folder segments first, then document headers
|
||||
const levels: string[] = [];
|
||||
|
||||
|
||||
// Insert folder segments (subfolders under the customer folder)
|
||||
if (folderSegments && folderSegments.length > 0) {
|
||||
levels.push(...folderSegments);
|
||||
}
|
||||
|
||||
// Insert the project name as a level after folder segments (so it appears after subfolders)
|
||||
if (projectName && projectName.length > 0) {
|
||||
levels.push(projectName);
|
||||
}
|
||||
|
||||
// Copy all header levels (including empty ones) to preserve hierarchy
|
||||
levels.push(...headers);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ export const DEFAULT_SETTINGS: TaskExportSettings = {
|
|||
export interface TaskItem {
|
||||
customerName: string;
|
||||
projectName: string;
|
||||
levels: string[]; // Hierarchical headers and parent tasks
|
||||
// `levels` contains folder segments (subfolders under the customer),
|
||||
// followed by document header levels (##, ###, ...), then parent task texts.
|
||||
levels: string[];
|
||||
task: string; // The actual task text
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue