Skip to content

Instantly share code, notes, and snippets.

@aviskase
Created August 2, 2021 23:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aviskase/02a3d1834bf49b8d16f1083d705a85a4 to your computer and use it in GitHub Desktop.
Save aviskase/02a3d1834bf49b8d16f1083d705a85a4 to your computer and use it in GitHub Desktop.
QuickAdd macro for generating monthly roundups: 1 file per each level 2 heading
tags aliases cssclass
actions/srs

<% tp.file.title%>

{{VALUE}}

module.exports = async function generateMonthly(params) {
const SOURCE_DIR_ROOT = '1x Dated/11 Daily';
const SOURCE_MONTH_PATTERN = /\d{4}-\d{2}$/;
const TARGET_DIR_ROOT = '1x Dated/12 Monthly';
const NUM_OF_LAST_MONTHS = 6;
const MONTHLY_TEMPLATE = '0x Meta/08 Templates/dated_monthly.md';
const {app, quickAddApi: {suggester}} = params;
if (!(await app.vault.adapter.exists(MONTHLY_TEMPLATE))) {
new Notice(`Cannot load template: ${MONTHLY_TEMPLATE}`);
return;
}
const templateContent = await app.vault.adapter.read(MONTHLY_TEMPLATE);
const months = (await app.vault.adapter.list(SOURCE_DIR_ROOT)).folders.slice(-NUM_OF_LAST_MONTHS).reverse();
const dailyDir = await suggester(months, months);
if (!dailyDir) return;
const targetFileNamePrefix = dailyDir.match(SOURCE_MONTH_PATTERN)[0];
const targetDirName = `${TARGET_DIR_ROOT}/${targetFileNamePrefix}`;
if (await app.vault.adapter.exists(targetDirName)) {
new Notice(`Monthly folder already exists: ${targetDirName}`);
return;
}
const markdowns = this.app.vault.getMarkdownFiles();
const dailies = markdowns.filter(f => f.path.startsWith(dailyDir)).sort((a, b) => a.name.localeCompare(b.name));
const dataPerDay = await Promise.all(dailies.map(async (dailyFile) => {
const data = {};
const currentFileCache = app.metadataCache.getFileCache(dailyFile);
const headings = currentFileCache.headings?.filter(h => h.level === 2);
if (!headings) return data;
const fileContent = await app.vault.read(dailyFile);
headings.forEach((heading, index) => {
const nextPos = headings?.[index+1]?.position.start.offset;
const text = fileContent.slice(heading.position.end.offset, nextPos).trim();
if (text) {
const sanitizedHeading = heading.heading.trim().replace(/[\\,#%&\{\}\/*<>$\'\":@]*/g, '');
data[sanitizedHeading] = `![[${dailyFile.basename}#${sanitizedHeading}]]`;
}
});
return data;
}));
const dataPerHeading = {};
dataPerDay.forEach(d => {
for([heading, content] of Object.entries(d)) {
let prefix = '';
if (heading in dataPerHeading) {
prefix = dataPerHeading[heading] + '\n\n';
}
dataPerHeading[heading] = prefix + content;
}
});
for([header, content] of Object.entries(dataPerHeading)) {
const title = `${targetFileNamePrefix} ${header}`;
const filePath = `${targetDirName}/${title}.md`;
const fullContent = templateContent.replace(NAME_VALUE_REGEX, content);
await app.vault.createFolder(targetDirName);
const createdFile = await app.vault.create(filePath, fullContent);
await replaceTemplaterTemplatesInCreatedFile(app, createdFile);
console.log(`Created monthly file: ${filePath}`)
}
};
const NAME_VALUE_REGEX = new RegExp(/{{NAME}}|{{VALUE}}/);
async function replaceTemplaterTemplatesInCreatedFile(app, file, force = false) {
const templater = app.plugins.plugins["templater-obsidian"];
if (templater && (force || !templater?.settings["trigger_on_file_creation"])) {
await templater.templater.overwrite_file_templates(file);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment