{
  "name": "Track and Visualize Daily Moods with OpenAI, Wolfram Alpha, and Google Sheets",
  "tags": [],
  "nodes": [
    {
      "id": "8eccdbf8-3950-4660-bad1-d97a744341f2",
      "name": "Webhook: Mood Input",
      "type": "n8n-nodes-base.webhook",
      "position": [
        544,
        832
      ],
      "parameters": {
        "path": "mood",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "9383f009-71d7-4146-ad93-5b1d1220ebf7",
      "name": "Parse AI Response",
      "type": "n8n-nodes-base.set",
      "position": [
        1120,
        736
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "valence",
              "type": "number",
              "value": "={{ JSON.parse($json.output).valence }}"
            },
            {
              "id": "id-2",
              "name": "energy",
              "type": "number",
              "value": "={{ JSON.parse($json.output).energy }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "05a99125-61a9-444f-a18d-21bf4b742113",
      "name": "Prepare Data Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        1344,
        736
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "moodText",
              "type": "string",
              "value": "={{ $json.mood || $('Webhook: Mood Input').first().json.body.mood }}"
            },
            {
              "id": "id-2",
              "name": "userId",
              "type": "string",
              "value": "={{ $json.userId || $('Webhook: Mood Input').first().json.body.userId }}"
            },
            {
              "id": "id-3",
              "name": "valence",
              "type": "number",
              "value": "={{ $json.valence }}"
            },
            {
              "id": "id-4",
              "name": "energy",
              "type": "number",
              "value": "={{ $json.energy }}"
            },
            {
              "id": "id-5",
              "name": "createdAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9d4ed4b4-3fdf-4a02-9501-0bbf1766c8cb",
      "name": "Create Wolfram Query",
      "type": "n8n-nodes-base.set",
      "position": [
        1568,
        736
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "wolframQuery",
              "type": "string",
              "value": "=plot y = {{ $json.valence }} * x from x = 0 to 10"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "af63908c-0915-4286-997d-50610c8ec6d1",
      "name": "Generate Mood Graph",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2144,
        736
      ],
      "parameters": {
        "url": "https://api.wolframalpha.com/v1/simple",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "appid",
              "value": "<__PLACEHOLDER_VALUE__YOUR_WOLFRAMALPHA_APP_ID__>"
            },
            {
              "name": "i",
              "value": "={{ $('Create Wolfram Query').first().json.wolframQuery }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "ce61267c-863e-4ce2-acc2-44fbbec5c123",
      "name": "Log Mood to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2368,
        736
      ],
      "parameters": {
        "columns": {
          "values": {
            "energy": "={{ $('Prepare Data Fields').first().json.energy }}",
            "userId": "={{ $('Prepare Data Fields').first().json.userId }}",
            "valence": "={{ $('Prepare Data Fields').first().json.valence }}",
            "feedback": "={{ $('AI: Generate Feedback').first().json.response }}",
            "moodText": "={{ $('Prepare Data Fields').first().json.moodText }}",
            "createdAt": "={{ $('Prepare Data Fields').first().json.createdAt }}",
            "wolframQuery": "={{ $('Create Wolfram Query').first().json.wolframQuery }}"
          }
        },
        "options": {},
        "operation": "append",
        "sheetName": "Sheet1",
        "documentId": "<__PLACEHOLDER_VALUE__YOUR_GOOGLE_SHEET_ID__>"
      },
      "typeVersion": 4.2
    },
    {
      "id": "e404fca8-8ce1-452c-a7dd-8614755f8c6b",
      "name": "Return API Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2592,
        736
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={\n  \"mood\": \"{{ $('Prepare Data Fields').first().json.moodText }}\",\n  \"valence\": {{ $('Prepare Data Fields').first().json.valence }},\n  \"energy\": {{ $('Prepare Data Fields').first().json.energy }},\n  \"wolframQuery\": \"{{ $('Create Wolfram Query').first().json.wolframQuery }}\",\n  \"feedback\": \"{{ $('AI: Generate Feedback').first().json.response }}\",\n  \"graph\": \"{{ $('Generate Mood Graph').first().binary.data.dataUrl }}\"\n}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "45bb5ad6-42e5-41d0-ab50-785bbb313900",
      "name": "Webhook: History Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        320,
        1072
      ],
      "parameters": {
        "path": "history",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "16c4e99c-fc86-4cdf-9e8c-8589e68125e0",
      "name": "Get History from Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        544,
        1072
      ],
      "parameters": {
        "options": {},
        "sheetName": "Sheet1",
        "documentId": "<__PLACEHOLDER_VALUE__YOUR_GOOGLE_SHEET_ID__>"
      },
      "typeVersion": 4.2
    },
    {
      "id": "d03f826e-bb96-4dc8-9ee9-593c76513102",
      "name": "Create Time Series Query",
      "type": "n8n-nodes-base.code",
      "position": [
        768,
        1072
      ],
      "parameters": {
        "jsCode": "// Get all items from the previous node ('Get History from Sheet').\nconst allItems = $input.all();\n\n// Get the target userId and limit from the initial webhook trigger.\nconst webhookData = $('Webhook: History Request').first().json.body;\nconst targetUserId = webhookData.userId;\nconst limit = webhookData.limit || 30;\n\n// 1. Filter items for the target user.\n// NOTE: We check for 'userId' (correct) and 'userid' (from your sheet) to be safe.\nconst userItems = allItems.filter(item => {\n  const currentUserId = item.json.userId || item.json.userid;\n  return currentUserId === targetUserId;\n});\n\n// 2. Sort items by createdAt descending to get the most recent ones.\nconst sortedDesc = userItems.sort((a, b) => new Date(b.json.createdAt) - new Date(a.json.createdAt));\n\n// 3. Limit to the specified number of records.\nconst limitedItems = sortedDesc.slice(0, limit);\n\n// 4. Sort again by createdAt ascending for the time series plot.\nconst sortedAsc = limitedItems.sort((a, b) => new Date(a.json.createdAt) - new Date(b.json.createdAt));\n\n// Format as time series data points for Wolfram Alpha.\nconst dataPoints = sortedAsc\n  .filter(item => {\n    const v = item.json.valence;\n    return v !== null && v !== undefined && v !== '';\n  })\n  .map(item => {\n    const date = new Date(item.json.createdAt);\n    // IMPORTANT: Ensure the date is correctly formatted\n    const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;\n    const valenceNum = parseFloat(item.json.valence);\n    return `{\"${dateStr}\", ${valenceNum}}`;\n  }).join(', ');\n\n// Create Wolfram Alpha time series plot query.\nconst wolframQuery = `time series plot {${dataPoints}}`;\n\n// Return the query string and the history data.\nreturn [{\n  json: {\n    wolframTimeSeriesQuery: wolframQuery,\n    history: sortedAsc.map(i => i.json)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "87887a97-3202-4b5a-952e-22e102aea84d",
      "name": "Generate History Graph",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        992,
        1072
      ],
      "parameters": {
        "url": "https://api.wolframalpha.com/v1/simple",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "appid",
              "value": "<__PLACEHOLDER_VALUE__YOUR_WOLFRAMALPHA_APP_ID__>"
            },
            {
              "name": "i",
              "value": "={{ $json.wolframTimeSeriesQuery }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "97e9edfc-b6f0-4e20-98de-925f9f1e7e63",
      "name": "AI: Analyze Mood to Numbers",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        768,
        640
      ],
      "parameters": {
        "text": "=# Instruction\nAnalyze the user's text and evaluate the valence (mood) and energy (vitality) of the emotion.\n\n# Constraints\n- You must act as an API and respond only in JSON format.\n- Valence is a number ranging from -1.0 to 1.0, where -1.0 is the most negative and 1.0 is the most positive.\n- Energy is a number ranging from 0.0 to 1.0, where 0.0 is the lowest energy and 1.0 is the highest.\n- Absolutely do not include explanations, preambles, or markdown like ```json ... ```. Output only a pure JSON object.\n\n# Example Output Format\n{ \"valence\": 0.8, \"energy\": 0.7 }\n\n# Text to Analyze\n{{ $json.mood || $('Webhook: Mood Input').first().json.body.mood }}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "e2f0fb66-caca-499a-a5dd-1f9bea436793",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        768,
        800
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "ef72599f-e8fa-4961-8818-bffcd9917f39",
      "name": "AI: Generate Feedback",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1792,
        736
      ],
      "parameters": {
        "text": "=You are an expert at analyzing a user's mood and providing brief advice. Based on the user's mood text and its quantified valence (brightness) and energy (vitality), generate a positive and short piece of advice in 1-2 sentences in English on how to spend their day.\n\n---\nUser Information:\nOriginal Mood Text: \"{{ $('Prepare Data Fields').first().json.moodText }}\"\nNumerical Analysis Results:\n- valence: {{ $('Prepare Data Fields').first().json.valence }}\n- energy: {{ $('Prepare Data Fields').first().json.energy }}\n---\n\nBased on this information, generate the advice.",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "d243908b-cd0c-4a8f-bb1e-a2ddbdc5e8f3",
      "name": "OpenAI Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1792,
        896
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "182df94f-42ac-4394-9d1d-1b75d78677dc",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        320,
        640
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "7a14d6b7-9439-4d85-b845-1948763f93e9",
      "name": "Set Test Data",
      "type": "n8n-nodes-base.set",
      "position": [
        544,
        640
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "5ed22629-60ca-4f8b-a325-93dc95d7445b",
              "name": "mood",
              "type": "string",
              "value": "What a great day to build a workflow!"
            },
            {
              "id": "b887a15f-3429-4faa-b33e-f3799b58afdb",
              "name": "userId",
              "type": "string",
              "value": "n8n-user-123"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "efdcd499-5e33-4bed-8622-6228664c0c8b",
      "name": "Mood Graph Studio",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": "#FBEB93",
        "width": 1040,
        "height": 592,
        "content": "## Track and Visualize Daily Moods\nThis workflow analyzes your daily mood using OpenAI, visualizes it with Wolfram Alpha, and logs everything to Google Sheets. It also includes a feature to retrieve and graph your mood history.\n\n## How it works\n1.  **Analyze**: An AI Agent quantifies your mood text into `valence` (positivity) and `energy`.\n2.  **Graph**: Wolfram Alpha generates a linear graph based on these scores.\n3.  **Feedback**: A second AI provides personalized advice.\n4.  **Log**: All data is saved to Google Sheets.\n5.  **History**: A separate flow fetches past data and posts a time-series graph to Slack.\n\n## Setup steps\n1.  **Credentials**: Configure credentials for OpenAI, Google Sheets, and Slack in the respective nodes.\n2.  **Wolfram Alpha**: Get a free App ID from the Developer Portal and paste it into the `appid` field in both `Generate...Graph` nodes.\n3.  **Google Sheet**: Create a new Sheet and add its ID to both Google Sheets nodes.\n    **Crucial**: The first row must have these exact headers:\n    `userId`, `moodText`, `valence`, `energy`, `createdAt`, `wolframQuery`, `feedback`\n\n## How to Use\n-   **Test**: Edit the \"Set Test Data\" node and click \"Execute Workflow\".\n-   **Production**: Send a POST request to the `/mood` or `/history` webhook URLs."
      },
      "typeVersion": 1
    },
    {
      "id": "ea201318-01da-4775-939f-bff6c9a797b8",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        832
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 176,
        "content": "## 1. How to Start\nFor testing, edit `Set Test Data` and hit 'Execute Workflow'. For real use, send a POST request to the `/mood` webhook. The workflow is smart enough to handle both!"
      },
      "typeVersion": 1
    },
    {
      "id": "9344e177-639f-4fcf-badc-105ccf826bdc",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1088,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 176,
        "content": "## 2. Analyze & Prep\nHere's where the magic starts. An AI quantifies your mood into scores. We then parse the response and gather all the data needed, like your `userId` and a timestamp."
      },
      "typeVersion": 1
    },
    {
      "id": "34122569-d531-461d-a7a0-e73e884d6bf0",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1728,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "content": "## 3. Generate Output\nA second AI writes you an encouraging message. At the same time, we build a query and send it to Wolfram Alpha, which returns your cool new mood graph."
      },
      "typeVersion": 1
    },
    {
      "id": "41ddc8ed-166e-412c-95a0-242349fb1dab",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2352,
        480
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 176,
        "content": "## 4. Log & Respond\nTime to save our work. We log the complete record to your Google Sheet. If you used the webhook, we also send a JSON response back with all the analysis and a link to your graph."
      },
      "typeVersion": 1
    },
    {
      "id": "0bcdfdfc-9df3-4fe0-87a0-2630a42452ac",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        1264
      ],
      "parameters": {
        "color": 6,
        "width": 352,
        "height": 208,
        "content": "## 5. History Graph Flow\nThis separate flow looks back at your mood history. Triggered by a POST request to `/history`, it fetches your data, builds a query, and posts a time-series graph to Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "5a2bdb8b-6a75-4b44-acd8-8152c0efd00e",
      "name": "Upload a file",
      "type": "n8n-nodes-base.slack",
      "position": [
        1200,
        1072
      ],
      "parameters": {
        "options": {},
        "resource": "file"
      },
      "typeVersion": 2.3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "",
  "connections": {
    "Set Test Data": {
      "main": [
        [
          {
            "node": "AI: Analyze Mood to Numbers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Set Test Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI: Analyze Mood to Numbers",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "Prepare Data Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI: Generate Feedback",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Generate Mood Graph": {
      "main": [
        [
          {
            "node": "Log Mood to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Data Fields": {
      "main": [
        [
          {
            "node": "Create Wolfram Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook: Mood Input": {
      "main": [
        [
          {
            "node": "AI: Analyze Mood to Numbers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Wolfram Query": {
      "main": [
        [
          {
            "node": "AI: Generate Feedback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: Generate Feedback": {
      "main": [
        [
          {
            "node": "Generate Mood Graph",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate History Graph": {
      "main": [
        [
          {
            "node": "Upload a file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get History from Sheet": {
      "main": [
        [
          {
            "node": "Create Time Series Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Time Series Query": {
      "main": [
        [
          {
            "node": "Generate History Graph",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Mood to Google Sheet": {
      "main": [
        [
          {
            "node": "Return API Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook: History Request": {
      "main": [
        [
          {
            "node": "Get History from Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: Analyze Mood to Numbers": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}