danielo515 / obsidian-modal-form

Define forms for filling data that you will be able to open from anywhere you can run JS
https://danielorodriguez.com/obsidian-modal-form/
MIT License
211 stars 18 forks source link

[BUG] Getting a list from dataview #312

Closed Fertion closed 2 months ago

Fertion commented 2 months ago

Hello, this is probably not a bug but rather my mistake, and I don't know where else to seek advice.

I'm trying to generate a list of notes obtained from dataview. Since the list will be quite large, I've added sorting by usage frequency. As a result, I ended up with the following dataview query.

let accountCount = {};

async function getAllAccounts() {
    let pages = dv.pages("#Financial/Accounts");
    return pages.map(p => p.file.name).sort();
}

async function processPages() {
    let thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    for (let page of dv.pages().where(page => page.chronicle_date).values) {
        let chronicleDate = new Date(page.chronicle_date);

        if (chronicleDate > thirtyDaysAgo) {
            let content = await dv.io.load(page.file.path);
            let lines = content.split("\n");
            let foundFinancialSection = false;

            for (let line of lines) {
                if (line.trim() === "# Financial Accounting") {
                    foundFinancialSection = true;
                } else if (foundFinancialSection && line.trim().startsWith("💱:")) {
                    let [fromAccount, toAccount, amount] = line.slice(3).split("|").map(s => s.trim());

                    if (accountCount[fromAccount]) {
                        accountCount[fromAccount] += 1;
                    } else {
                        accountCount[fromAccount] = 1;
                    }
                }
            }
        }
    }

    let allAccounts = await getAllAccounts();

    for (let account of allAccounts) {
        if (!accountCount[account]) {
            accountCount[account] = 0;
        }
    }

    let sortedAccounts = Object.entries(accountCount)
        .sort((a, b) => b[1] - a[1])
        .map(([account, count]) => account);

    dv.list(sortedAccounts);
}

processPages();

For example, in my test vault, it gives the following response:

But when I try to use this query, I get the response "the dataview query did not return an array." I can't seem to fix it. Could you please tell me what I'm doing wrong?

danielo515 commented 2 months ago

try adding a return to the bottom of your processPages function:

return dv.list(sortedAccounts);
danielo515 commented 2 months ago

Ok, that is not going to work because the dataview is meant for simple things. Like simple expressions that evaluate to a value. It is not meant to be a full code environment where you can write whatever you want. That said, there are always workarounds:

Try this:

(() => {
let accountCount = {};

async function getAllAccounts() {
    let pages = dv.pages("#Financial/Accounts");
    return pages.map(p => p.file.name).sort();
}

async function processPages() {
    let thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    for (let page of dv.pages().where(page => page.chronicle_date).values) {
        let chronicleDate = new Date(page.chronicle_date);

        if (chronicleDate > thirtyDaysAgo) {
            let content = await dv.io.load(page.file.path);
            let lines = content.split("\n");
            let foundFinancialSection = false;

            for (let line of lines) {
                if (line.trim() === "# Financial Accounting") {
                    foundFinancialSection = true;
                } else if (foundFinancialSection && line.trim().startsWith("💱:")) {
                    let [fromAccount, toAccount, amount] = line.slice(3).split("|").map(s => s.trim());

                    if (accountCount[fromAccount]) {
                        accountCount[fromAccount] += 1;
                    } else {
                        accountCount[fromAccount] = 1;
                    }
                }
            }
        }
    }

    let allAccounts = await getAllAccounts();

    for (let account of allAccounts) {
        if (!accountCount[account]) {
            accountCount[account] = 0;
        }
    }

    let sortedAccounts = Object.entries(accountCount)
        .sort((a, b) => b[1] - a[1])
        .map(([account, count]) => account);

    return dv.list(sortedAccounts);
}

return processPages();
})()
Fertion commented 2 months ago

This code gave an error "cannot read properties of undefined (reading createEl)", so I didn't want to bother you and asked GPT first (it originally wrote the code for me).

It gave me this, and it works:

(() => {
    let accountCount = {};

    async function getAllAccounts() {
        let pages = dv.pages("#Financial/Accounts");
        return pages.map(p => p.file.name).sort();
    }

    async function processPages() {
        let thirtyDaysAgo = new Date();
        thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

        for (let page of dv.pages().where(page => page.chronicle_date).values) {
            let chronicleDate = new Date(page.chronicle_date);

            if (chronicleDate > thirtyDaysAgo) {
                let content = await dv.io.load(page.file.path);
                let lines = content.split("\n");
                let foundFinancialSection = false;

                for (let line of lines) {
                    if (line.trim() === "# Financial Accounting") {
                        foundFinancialSection = true;
                    } else if (foundFinancialSection && line.trim().startsWith("💱:")) {
                        let [fromAccount, toAccount, amount] = line.slice(3).split("|").map(s => s.trim());
                        accountCount[fromAccount] = (accountCount[fromAccount] || 0) + 1;
                    }
                }
            }
        }

        let allAccounts = await getAllAccounts();

        for (let account of allAccounts) {
            if (!accountCount[account]) {
                accountCount[account] = 0;
            }
        }

        let sortedAccounts = Object.entries(accountCount)
            .sort(([, countA], [, countB]) => countB - countA)
            .map(([account]) => account);

        return sortedAccounts;
    }

    return processPages().then(sortedAccounts => {
        dv.list(sortedAccounts);
        return sortedAccounts;
    });
})();

The problem is solved. Thank you for your help, and I want to say that your plugin is a miracle. Apparently, I have finally found a way to manage finances properly in Obsidian.

danielo515 commented 2 months ago

Thank you for your help, and I want to say that your plugin is a miracle. Apparently, I have finally found a way to manage finances properly in Obsidian.

Happy to see that you got your problem solved, and that you have what you want. Thanks for the nice words about the plugin. Your use case is interesting, if you feel like, you can share it in the obsidian forums.

By the way, I'm pretty sure you can remove your dv.list(sortedAccounts) because that is only for displaying in the obsidian note, and has no effect in this case. So the last part can be like:

// ... same as before ...
       let sortedAccounts = Object.entries(accountCount)
            .sort(([, countA], [, countB]) => countB - countA)
            .map(([account]) => account);

        return sortedAccounts;
    }

    return processPages()
})();
Fertion commented 2 months ago

you can share it in the obsidian forums

Yes, I plan to share it in a while, once I finish the entire system.

Fertion commented 2 months ago

you can share it in the obsidian forums

I tried to show how this could work https://forum.obsidian.md/t/financial-tracking-in-obsidian/86830/1