Remove diet estimates command

This commit is contained in:
Ben Stuart 2025-05-15 12:50:21 -05:00
parent 1f0b6f0c1a
commit 63813648da
6 changed files with 1 additions and 223 deletions

View file

@ -9,14 +9,11 @@ This plugin comes with two commands:
Adds a custom tag at your cursor location which the generation command looks for and replaces if it is available. The tag can be changed in settings. A tag command and setting should be available for each generation command. Adds a custom tag at your cursor location which the generation command looks for and replaces if it is available. The tag can be changed in settings. A tag command and setting should be available for each generation command.
### Generate Through The Years ### Generate Through The Years
Uses AI to find Daily Notes from the same date as your current note in years past. Generates a brief summary of this date for each instance of it through the years and places it in your note. Uses AI to find Daily Notes from the same date as your current note in years past. Generates a brief summary of this date for each instance of it through the years and places it in your note.
### Generate Diet Estimates
Parse your daily journal for food-related lines, and create a `dietEstimates` code-block that estimates your daily calories and macros.
## How to Use ## How to Use
- Enable the plugin in the settings menu and update the plugin settings accordingly. - Enable the plugin in the settings menu and update the plugin settings accordingly.
- Open the command palette and search for On This Day I to see the commands. - Open the command palette and search for On This Day I to see the commands.
- If you want the output to be placed at a certain location in your note, add a placeholder (default is <!OTDI>, can be changed in settings). Run the `On This Day I: Add Placeholder at Cursor` command if you'd like to automatically add a placeholder to your note at cursor location. - If you want the output to be placed at a certain location in your note, add a placeholder (default is <!OTDI>, can be changed in settings). Run the `On This Day I: Add Placeholder at Cursor` command if you'd like to automatically add a placeholder to your note at cursor location.
- Run the command `On This Day I: Generate Through The Years` to generate your yearly insights. - Run the command `On This Day I: Generate Through The Years` to generate your yearly insights.
- Run the command `On This Day I: Generate Diet Estimates` to generate a calorie and macro estimates code-block.
### OpenAI ### OpenAI
- **This plugin makes a network call to the OpenAI API to generate the journal summaries.** - **This plugin makes a network call to the OpenAI API to generate the journal summaries.**
- Must have OpenAI API key. See [platform.openai.com](https://platform.openai.com/). Add this key to plugin settings. - Must have OpenAI API key. See [platform.openai.com](https://platform.openai.com/). Add this key to plugin settings.
@ -25,8 +22,6 @@ Parse your daily journal for food-related lines, and create a `dietEstimates` co
- Must be run from a note with a valid Date format. Update the date format in settings to match your daily note format. - Must be run from a note with a valid Date format. Update the date format in settings to match your daily note format.
- Daily notes must all follow the same date format (e.g, MMMM D, YYYY would be something like February 6, 2025) - Daily notes must all follow the same date format (e.g, MMMM D, YYYY would be something like February 6, 2025)
- There must be at least one other daily note from this day in a past year. - There must be at least one other daily note from this day in a past year.
### Diet Estimates Requirements
- Meant to be run on a daily journal that includes information documenting what you have eaten in a day.
## How to Install ## How to Install
### From within Obsidian ### From within Obsidian
You can activate this plugin within Obsidian by doing the following: You can activate this plugin within Obsidian by doing the following:

146
main.ts
View file

@ -2,12 +2,11 @@ import { MarkdownView, Notice, Plugin, TFolder, Editor } from "obsidian";
import OpenAI from "openai"; import OpenAI from "openai";
import { OnThisDayPluginSettings, DEFAULT_SETTINGS } from "src/settings"; import { OnThisDayPluginSettings, DEFAULT_SETTINGS } from "src/settings";
import OnThisDaySettingTab from "src/OnThisDaySettingTab"; import OnThisDaySettingTab from "src/OnThisDaySettingTab";
import { sysPrompt, constructPrompt, constructDietPrompt } from "src/prompt"; import { sysPrompt, constructPrompt } from "src/prompt";
import { isValidDateString, parseDateFromString } from "src/dateUtils"; import { isValidDateString, parseDateFromString } from "src/dateUtils";
import { import {
getMarkdownFilesInFolder, getMarkdownFilesInFolder,
buildOutputBlock, buildOutputBlock,
buildHealthOutputBlock,
} from "src/markdownUtils"; } from "src/markdownUtils";
export default class OnThisDayPlugin extends Plugin { export default class OnThisDayPlugin extends Plugin {
@ -38,27 +37,6 @@ export default class OnThisDayPlugin extends Plugin {
}, },
}); });
// Conditions: placeholder must be defined in settings
this.addCommand({
id: "diet-estimates-placeholder",
name: "Add diet estimates placeholder at cursor",
editorCheckCallback: (
checking: boolean,
editor: Editor,
view: MarkdownView
) => {
const placeholderIsEmpty =
this.settings.dietEstimatePlaceholder.trim() === "";
if (!placeholderIsEmpty) {
if (!checking) {
this.addDietEstimatesPlaceholder();
}
return true;
}
return false;
},
});
// Conditions: date format, daily notes folder, output header, model version, and apiKey // Conditions: date format, daily notes folder, output header, model version, and apiKey
this.addCommand({ this.addCommand({
id: "generate-date-summaries", id: "generate-date-summaries",
@ -83,29 +61,6 @@ export default class OnThisDayPlugin extends Plugin {
return false; return false;
}, },
}); });
// Conditions: section header, model version, and apiKey
this.addCommand({
id: "diet-estimates",
name: "Generate diet estimates",
editorCheckCallback: (
checking: boolean,
editor: Editor,
view: MarkdownView
) => {
if (
this.settings.foodHeader.trim() != "" &&
this.settings.model.trim() != "" &&
this.settings.openaiApiKey.trim() != ""
) {
if (!checking) {
this.generateDateSummaries();
}
return true;
}
return false;
},
});
} }
async loadSettings() { async loadSettings() {
@ -244,95 +199,6 @@ export default class OnThisDayPlugin extends Plugin {
new Notice("On This Day summaries inserted."); new Notice("On This Day summaries inserted.");
} }
async generateDietEstimates() {
const activeFile = this.app.workspace.getActiveFile();
if (!activeFile) {
new Notice("No active file found.");
return;
}
const content = await this.app.vault.read(activeFile);
// Find the index of the designated header (compare trimmed lines).
const lines = content.split("\n");
const headerIndex = lines.findIndex(
(line) => line.trim() === this.settings.foodHeader.trim()
);
if (headerIndex === -1) {
new Notice(
"Food section not found using header: " +
this.settings.foodHeader
);
return;
}
// Collect all lines after the header until a new header is encountered.
const foodSectionLines: string[] = [];
for (let i = headerIndex + 1; i < lines.length; i++) {
if (lines[i].trim().startsWith("#")) {
break;
}
foodSectionLines.push(lines[i]);
}
const foodInfo = foodSectionLines.join("\n").trim();
if (!foodInfo) {
new Notice(
"No food-related details found under the header: " +
this.settings.foodHeader
);
return;
}
const prompt = constructDietPrompt(foodInfo);
const loadingNotice = new Notice(
"Calculating diet estimates... Please wait.",
0
);
let responseJSON: Record<string, any>;
try {
responseJSON = await this.callOpenAI(prompt);
} catch (error: any) {
loadingNotice.hide();
console.error("Diet estimates calculation failed:", error);
new Notice("Diet estimates calculation failed: " + error.message);
return;
}
loadingNotice.hide();
new Notice("Diet estimates calculated!");
const outputBlock = buildHealthOutputBlock(
responseJSON.calories,
responseJSON.protein,
responseJSON.carbs,
responseJSON.fats,
responseJSON.est_deficit
);
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView) {
loadingNotice.hide();
new Notice("No active editor found.");
return;
}
const editor = (activeView as MarkdownView).editor;
// Insert the output block at a placeholder if available, else at cursor location
const placeholder = this.settings.dietEstimatePlaceholder;
if (content.includes(placeholder)) {
const newContent = content.replace(placeholder, outputBlock);
editor.setValue(newContent);
} else {
editor.replaceSelection(outputBlock);
}
new Notice("Diet estimates inserted into the note.");
}
async callOpenAI(prompt: string): Promise<Record<string, string>> { async callOpenAI(prompt: string): Promise<Record<string, string>> {
const client = new OpenAI({ const client = new OpenAI({
apiKey: this.settings.openaiApiKey, apiKey: this.settings.openaiApiKey,
@ -389,14 +255,4 @@ export default class OnThisDayPlugin extends Plugin {
const editor = (activeView as MarkdownView).editor; const editor = (activeView as MarkdownView).editor;
editor.replaceSelection(this.settings.placeholder); editor.replaceSelection(this.settings.placeholder);
} }
addDietEstimatesPlaceholder() {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView) {
new Notice("No active editor found.");
return;
}
const editor = (activeView as MarkdownView).editor;
editor.replaceSelection(this.settings.dietEstimatePlaceholder);
}
} }

View file

@ -190,39 +190,5 @@ export default class OnThisDaySettingTab extends PluginSettingTab {
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}) })
); );
new Setting(containerEl).setName("Health estimates").setHeading();
new Setting(containerEl)
.setName("Health estimates placeholder tag")
.setDesc(
"The plugin will search for this text in your active file to replace with its return block. Otherwise outputs at cursor. Default <!OTDI diet>"
)
.addText((text) =>
text
.setPlaceholder("<!OTDI diet>")
.setValue(
this.plugin.settings.dietEstimatePlaceholder ||
"<!OTDI diet>"
)
.onChange(async (value) => {
this.plugin.settings.dietEstimatePlaceholder = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Food section header")
.setDesc(
"Enter the header that marks the food section in your daily note. Default is '### Food'."
)
.addText((text) =>
text
.setPlaceholder("### Food")
.setValue(this.plugin.settings.foodHeader || "### Food")
.onChange(async (value) => {
this.plugin.settings.foodHeader = value;
await this.plugin.saveSettings();
})
);
} }
} }

View file

@ -56,20 +56,3 @@ export function buildOutputBlock(
return output; return output;
} }
export function buildHealthOutputBlock(
calories: string,
protein: string,
carbs: string,
fats: string,
deficit: string
): string {
const outputBlock =
`\`\`\`dietEstimates
calories: ${calories}
protein: ${protein}
carbs: ${carbs}
fats: ${fats}
\`\`\``;
return outputBlock;
}

View file

@ -26,20 +26,3 @@ export function constructPrompt(
const numberOfSentencesAppendage = `Each year's summary should be approximately ${numberOfSentences} sentences`; const numberOfSentencesAppendage = `Each year's summary should be approximately ${numberOfSentences} sentences`;
return `${promptInstructions}\n\nData:\n${dataString}\n${numberOfSentencesAppendage}`; return `${promptInstructions}\n\nData:\n${dataString}\n${numberOfSentencesAppendage}`;
} }
export function constructDietPrompt(foodInfo: string): string {
const prompt = `You are a health assistant. Based on the following food details extracted from a daily journal,
provide an estimated nutritional analysis.
Food Information:
${foodInfo}
Provide the output in JSON format with the following keys:
- calories: estimated total calories consumed,
- protein: estimated grams of protein,
- carbs: estimated grams of carbohydrates,
- fats: estimated grams of fats,
Return only a JSON object with these keys, with no additional text. Include units in the JSON values.`;
return prompt;
}

View file

@ -14,9 +14,6 @@ export interface OnThisDayPluginSettings {
throughTheYearsHeader: string; throughTheYearsHeader: string;
shouldOutputLinkToNotes: boolean; shouldOutputLinkToNotes: boolean;
outputLengthSentences: number; outputLengthSentences: number;
// Health estimate settings
dietEstimatePlaceholder: string;
foodHeader: string;
} }
export const DEFAULT_SETTINGS: OnThisDayPluginSettings = { export const DEFAULT_SETTINGS: OnThisDayPluginSettings = {
@ -30,6 +27,4 @@ export const DEFAULT_SETTINGS: OnThisDayPluginSettings = {
throughTheYearsHeader: "On This Day", throughTheYearsHeader: "On This Day",
shouldOutputLinkToNotes: true, shouldOutputLinkToNotes: true,
outputLengthSentences: 3, outputLengthSentences: 3,
dietEstimatePlaceholder: "<!OTDI diet>",
foodHeader: "### Food",
}; };