mirror of
https://github.com/benstuart0/on-this-day-i-obsidian.git
synced 2026-07-22 05:38:19 +00:00
Remove diet estimates command
This commit is contained in:
parent
1f0b6f0c1a
commit
63813648da
6 changed files with 1 additions and 223 deletions
|
|
@ -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
146
main.ts
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue