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.
### 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.
### 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
- 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.
- 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 Diet Estimates` to generate a calorie and macro estimates code-block.
### OpenAI
- **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.
@ -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.
- 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.
### 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
### From within Obsidian
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 { OnThisDayPluginSettings, DEFAULT_SETTINGS } from "src/settings";
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 {
getMarkdownFilesInFolder,
buildOutputBlock,
buildHealthOutputBlock,
} from "src/markdownUtils";
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
this.addCommand({
id: "generate-date-summaries",
@ -83,29 +61,6 @@ export default class OnThisDayPlugin extends Plugin {
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() {
@ -244,95 +199,6 @@ export default class OnThisDayPlugin extends Plugin {
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>> {
const client = new OpenAI({
apiKey: this.settings.openaiApiKey,
@ -389,14 +255,4 @@ export default class OnThisDayPlugin extends Plugin {
const editor = (activeView as MarkdownView).editor;
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();
})
);
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;
}
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`;
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;
shouldOutputLinkToNotes: boolean;
outputLengthSentences: number;
// Health estimate settings
dietEstimatePlaceholder: string;
foodHeader: string;
}
export const DEFAULT_SETTINGS: OnThisDayPluginSettings = {
@ -30,6 +27,4 @@ export const DEFAULT_SETTINGS: OnThisDayPluginSettings = {
throughTheYearsHeader: "On This Day",
shouldOutputLinkToNotes: true,
outputLengthSentences: 3,
dietEstimatePlaceholder: "<!OTDI diet>",
foodHeader: "### Food",
};