AutomationFlowsAI & RAG › Generate Personalized Weather Reports with Openweathermap, Python and…

Generate Personalized Weather Reports with Openweathermap, Python and…

Original n8n title: Generate Personalized Weather Reports with Openweathermap, Python and Gpt-4.1-mini

ByMoe Ahad @moe-ahad on n8n.io

User enters name of a city for which most current weather information will be gathered Custom Python code processes the weather data and generates a custom email about the weather AI agent further customizes the email and add a related joke about the weather Recipient gets the…

Event trigger★★★★☆ complexityAI-powered11 nodesHTTP RequestGmailAgentOpenAI ChatForm Trigger
AI & RAG Trigger: Event Nodes: 11 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #6400 — we link there as the canonical source.

This workflow follows the Agent → Form Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "BrYg4iQRGkBoypeu",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Custom Weather Email using Python and AI",
  "tags": [],
  "nodes": [
    {
      "id": "822a0d12-4591-40d0-85f9-0c940372ed5f",
      "name": "Fetch Weather Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        304
      ],
      "parameters": {
        "url": "https://api.openweathermap.org/data/2.5/weather",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "=q",
              "value": "={{ $json.City }}"
            },
            {
              "name": "appid",
              "value": "<your_API_key>"
            },
            {
              "name": "units",
              "value": "metric"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "99ddfbb3-c834-4017-9ec4-f32f870e1482",
      "name": "Process Weather Data",
      "type": "n8n-nodes-base.code",
      "position": [
        32,
        496
      ],
      "parameters": {
        "language": "python",
        "pythonCode": "import json\nfrom datetime import datetime\nimport math\n\n# Get the weather data from the previous node\nweather_data = items[0]['json']\n\n# Extract relevant information\ncity = weather_data['name']\ncountry = weather_data['sys']['country']\ntemperature = weather_data['main']['temp']\nfeels_like = weather_data['main']['feels_like']\nhumidity = weather_data['main']['humidity']\npressure = weather_data['main']['pressure']\ndescription = weather_data['weather'][0]['description']\nwind_speed = weather_data['wind']['speed']\nvisibility = weather_data.get('visibility', 0) / 1000  # Convert to km\n\n# Calculate temperature in Fahrenheit\ntemp_fahrenheit = (temperature * 9/5) + 32\nfeels_like_fahrenheit = (feels_like * 9/5) + 32\n\n# Calculate wind speed in different units\nwind_speed_kmh = wind_speed * 3.6\nwind_speed_mph = wind_speed * 2.237\n\n# Determine comfort level based on temperature and humidity\ndef get_comfort_level(temp, humidity):\n    if temp < 0:\n        return \"Freezing\"\n    elif temp < 10:\n        return \"Cold\"\n    elif temp > 35:\n        return \"Very Hot\"\n    elif temp > 28:\n        return \"Hot\"\n    elif humidity > 80:\n        return \"Humid\"\n    elif 18 <= temp <= 24 and 40 <= humidity <= 60:\n        return \"Ideal\"\n    elif 15 <= temp <= 27 and humidity <= 70:\n        return \"Comfortable\"\n    else:\n        return \"Moderate\"\n\n# Get clothing suggestion\ndef get_clothing_suggestion(temp, wind, desc):\n    if \"rain\" in desc.lower() or \"drizzle\" in desc.lower():\n        clothing = \"Waterproof jacket and umbrella\"\n    elif \"snow\" in desc.lower():\n        clothing = \"Heavy winter coat, boots, gloves, and hat\"\n    elif temp < 0:\n        clothing = \"Heavy winter coat, thermal layers, gloves, and hat\"\n    elif temp < 10:\n        clothing = \"Warm jacket or heavy sweater\"\n    elif temp < 18:\n        clothing = \"Light jacket or cardigan\"\n    elif temp < 25:\n        clothing = \"Long sleeves or light sweater\"\n    elif temp < 30:\n        clothing = \"T-shirt and light pants/shorts\"\n    else:\n        clothing = \"Light, breathable clothing and sun protection\"\n    \n    if wind > 5:\n        clothing += \" (consider wind protection)\"\n    \n    return clothing\n\n# Get activity recommendation\ndef get_activity_recommendation(temp, desc, wind):\n    desc_lower = desc.lower()\n    \n    if \"thunderstorm\" in desc_lower or \"heavy rain\" in desc_lower:\n        return \"Stay indoors, perfect for indoor activities\"\n    elif \"rain\" in desc_lower or \"drizzle\" in desc_lower:\n        return \"Indoor activities recommended, or covered outdoor areas\"\n    elif \"snow\" in desc_lower:\n        return \"Winter sports, snowman building, or cozy indoor activities\"\n    elif temp < -5:\n        return \"Limit outdoor exposure, indoor activities recommended\"\n    elif temp < 5:\n        return \"Short outdoor activities, winter sports, or indoor activities\"\n    elif 15 <= temp <= 25 and wind < 7:\n        return \"Perfect for any outdoor activities - hiking, sports, picnics\"\n    elif temp > 30:\n        return \"Early morning or evening outdoor activities, stay hydrated\"\n    elif wind > 10:\n        return \"Be cautious with outdoor activities, secure loose items\"\n    else:\n        return \"Good for moderate outdoor activities\"\n\n# Determine weather emoji\ndef get_weather_emoji(desc):\n    desc_lower = desc.lower()\n    if \"clear\" in desc_lower:\n        return \"\u2600\ufe0f\"\n    elif \"cloud\" in desc_lower:\n        return \"\u2601\ufe0f\" if \"few\" not in desc_lower else \"\u26c5\"\n    elif \"rain\" in desc_lower:\n        return \"\ud83c\udf27\ufe0f\"\n    elif \"thunderstorm\" in desc_lower:\n        return \"\u26c8\ufe0f\"\n    elif \"snow\" in desc_lower:\n        return \"\u2744\ufe0f\"\n    elif \"mist\" in desc_lower or \"fog\" in desc_lower:\n        return \"\ud83c\udf2b\ufe0f\"\n    else:\n        return \"\ud83c\udf24\ufe0f\"\n\ncomfort_level = get_comfort_level(temperature, humidity)\nweather_emoji = get_weather_emoji(description)\n\n# Create a comprehensive weather report\nweather_report = {\n    \"location\": f\"{city}, {country}\",\n    \"timestamp\": datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"),\n    \"weather_emoji\": weather_emoji,\n    \"current_conditions\": {\n        \"temperature_celsius\": round(temperature, 1),\n        \"temperature_fahrenheit\": round(temp_fahrenheit, 1),\n        \"feels_like_celsius\": round(feels_like, 1),\n        \"feels_like_fahrenheit\": round(feels_like_fahrenheit, 1),\n        \"description\": description.title(),\n        \"humidity\": humidity,\n        \"pressure\": pressure,\n        \"visibility_km\": round(visibility, 1),\n        \"wind_speed_ms\": round(wind_speed, 1),\n        \"wind_speed_kmh\": round(wind_speed_kmh, 1),\n        \"wind_speed_mph\": round(wind_speed_mph, 1)\n    },\n    \"analysis\": {\n        \"comfort_level\": comfort_level,\n        \"is_good_weather\": temperature >= 15 and temperature <= 25 and humidity <= 70 and \"rain\" not in description.lower(),\n        \"clothing_suggestion\": get_clothing_suggestion(temperature, wind_speed, description),\n        \"activity_recommendation\": get_activity_recommendation(temperature, description, wind_speed),\n        \"weather_quality_score\": min(100, max(0, \n            100 - abs(20 - temperature) * 3 - max(0, humidity - 60) - max(0, wind_speed - 5) * 5\n        ))\n    },\n    \"raw_data\": weather_data\n}\n\n# Return the processed data\nreturn [{\"json\": weather_report}]"
      },
      "typeVersion": 2
    },
    {
      "id": "0d850dd1-2930-407f-9f87-f418bc588f4a",
      "name": "Generate Email Content",
      "type": "n8n-nodes-base.code",
      "position": [
        160,
        320
      ],
      "parameters": {
        "language": "python",
        "pythonCode": "# Get the processed weather data\nweather_data = items[0]['json']\n\n# Create HTML email content\nhtml_content = f\"\"\"\n<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Weather Report</title>\n    <style>\n        body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f5f7fa; }}\n        .container {{ max-width: 600px; margin: 0 auto; background-color: white; border-radius: 15px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1); }}\n        .header {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; }}\n        .header h1 {{ margin: 0; font-size: 2em; }}\n        .location {{ font-size: 1.2em; margin-top: 10px; opacity: 0.9; }}\n        .section {{ padding: 25px; border-bottom: 1px solid #eee; }}\n        .section:last-child {{ border-bottom: none; }}\n        .section h2 {{ color: #2c3e50; margin-top: 0; display: flex; align-items: center; gap: 10px; }}\n        .temp-main {{ font-size: 3em; font-weight: bold; color: #e74c3c; text-align: center; margin: 20px 0; }}\n        .temp-feels {{ font-size: 1.2em; color: #7f8c8d; text-align: center; margin-bottom: 20px; }}\n        .conditions-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0; }}\n        .condition-item {{ background-color: #f8f9fa; padding: 15px; border-radius: 8px; text-align: center; }}\n        .condition-value {{ font-size: 1.4em; font-weight: bold; color: #2c3e50; }}\n        .condition-label {{ font-size: 0.9em; color: #7f8c8d; margin-top: 5px; }}\n        .comfort-badge {{ display: inline-block; padding: 8px 16px; border-radius: 20px; font-weight: bold; text-transform: uppercase; font-size: 0.9em; }}\n        .comfort-ideal {{ background-color: #d4edda; color: #155724; }}\n        .comfort-comfortable {{ background-color: #cce5ff; color: #004085; }}\n        .comfort-moderate {{ background-color: #fff3cd; color: #856404; }}\n        .comfort-hot {{ background-color: #f8d7da; color: #721c24; }}\n        .comfort-cold {{ background-color: #e2e3e5; color: #383d41; }}\n        .recommendation {{ background-color: #e8f5e8; padding: 20px; border-radius: 10px; margin: 15px 0; border-left: 4px solid #28a745; }}\n        .score {{ text-align: center; margin: 20px 0; }}\n        .score-circle {{ display: inline-block; width: 80px; height: 80px; border-radius: 50%; background: conic-gradient(#28a745 0deg {weather_data['analysis']['weather_quality_score'] * 3.6}deg, #e9ecef {weather_data['analysis']['weather_quality_score'] * 3.6}deg 360deg); position: relative; }}\n        .score-inner {{ position: absolute; top: 10px; left: 10px; width: 60px; height: 60px; border-radius: 50%; background-color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #2c3e50; }}\n        .footer {{ background-color: #f8f9fa; padding: 20px; text-align: center; font-size: 0.9em; color: #6c757d; }}\n    </style>\n</head>\n<body>\n    <div class=\"container\">\n        <div class=\"header\">\n            <h1>{weather_data['weather_emoji']} Weather Report</h1>\n            <div class=\"location\">{weather_data['location']}</div>\n            <div style=\"font-size: 0.9em; margin-top: 5px;\">{weather_data['timestamp']}</div>\n        </div>\n        \n        <div class=\"section\">\n            <div class=\"temp-main\">{weather_data['current_conditions']['temperature_celsius']}\u00b0C</div>\n            <div class=\"temp-feels\">Feels like {weather_data['current_conditions']['feels_like_celsius']}\u00b0C</div>\n            <div style=\"text-align: center; font-size: 1.3em; color: #495057; margin-bottom: 20px;\">\n                {weather_data['current_conditions']['description']}\n            </div>\n            \n            <div style=\"text-align: center; margin: 20px 0;\">\n                <span class=\"comfort-badge comfort-{weather_data['analysis']['comfort_level'].lower().replace(' ', '-')}\">\n                    {weather_data['analysis']['comfort_level']}\n                </span>\n            </div>\n        </div>\n        \n        <div class=\"section\">\n            <h2>\ud83d\udcca Current Conditions</h2>\n            <div class=\"conditions-grid\">\n                <div class=\"condition-item\">\n                    <div class=\"condition-value\">{weather_data['current_conditions']['humidity']}%</div>\n                    <div class=\"condition-label\">Humidity</div>\n                </div>\n                <div class=\"condition-item\">\n                    <div class=\"condition-value\">{weather_data['current_conditions']['pressure']}</div>\n                    <div class=\"condition-label\">Pressure (hPa)</div>\n                </div>\n                <div class=\"condition-item\">\n                    <div class=\"condition-value\">{weather_data['current_conditions']['wind_speed_kmh']}</div>\n                    <div class=\"condition-label\">Wind (km/h)</div>\n                </div>\n                <div class=\"condition-item\">\n                    <div class=\"condition-value\">{weather_data['current_conditions']['visibility_km']}</div>\n                    <div class=\"condition-label\">Visibility (km)</div>\n                </div>\n            </div>\n        </div>\n        \n        <div class=\"section\">\n            <h2>\ud83c\udfaf Recommendations</h2>\n            <div class=\"recommendation\">\n                <strong>\ud83d\udc55 What to Wear:</strong><br>\n                {weather_data['analysis']['clothing_suggestion']}\n            </div>\n            <div class=\"recommendation\">\n                <strong>\ud83c\udfc3 Activities:</strong><br>\n                {weather_data['analysis']['activity_recommendation']}\n            </div>\n        </div>\n        \n        <div class=\"section\">\n            <h2>\u2b50 Weather Quality Score</h2>\n            <div class=\"score\">\n                <div class=\"score-circle\">\n                    <div class=\"score-inner\">{int(weather_data['analysis']['weather_quality_score'])}</div>\n                </div>\n                <div style=\"margin-top: 10px; color: #6c757d;\">Out of 100</div>\n            </div>\n        </div>\n        \n        <div class=\"footer\">\n            <strong>\ud83e\udd16 Generated by n8n Weather Pipeline</strong><br>\n            Powered by Python \u2022 OpenWeatherMap API<br>\n            <em>Stay informed, stay prepared!</em>\n        </div>\n    </div>\n</body>\n</html>\n\"\"\"\n\n# Create plain text version\ntext_content = f\"\"\"\n\ud83c\udf24\ufe0f DAILY WEATHER REPORT\n\n\ud83d\udccd Location: {weather_data['location']}\n\ud83d\udd52 Time: {weather_data['timestamp']}\n\n\ud83c\udf21\ufe0f CURRENT CONDITIONS\n- Temperature: {weather_data['current_conditions']['temperature_celsius']}\u00b0C ({weather_data['current_conditions']['temperature_fahrenheit']}\u00b0F)\n- Feels Like: {weather_data['current_conditions']['feels_like_celsius']}\u00b0C ({weather_data['current_conditions']['feels_like_fahrenheit']}\u00b0F)\n- Condition: {weather_data['current_conditions']['description']}\n- Humidity: {weather_data['current_conditions']['humidity']}%\n- Wind Speed: {weather_data['current_conditions']['wind_speed_kmh']} km/h\n- Pressure: {weather_data['current_conditions']['pressure']} hPa\n- Visibility: {weather_data['current_conditions']['visibility_km']} km\n\n\ud83d\udca1 ANALYSIS\n- Comfort Level: {weather_data['analysis']['comfort_level']}\n- Weather Quality Score: {int(weather_data['analysis']['weather_quality_score'])}/100\n\n\ud83d\udc55 CLOTHING RECOMMENDATION\n{weather_data['analysis']['clothing_suggestion']}\n\n\ud83c\udfc3 ACTIVITY RECOMMENDATION\n{weather_data['analysis']['activity_recommendation']}\n\n---\nGenerated by n8n Weather Pipeline\nPowered by Python & OpenWeatherMap API\n\"\"\"\n\n# Return the email content\nreturn [{\n    \"json\": {\n        \"subject\": f\"{weather_data['weather_emoji']} Weather Report - {weather_data['location']} ({weather_data['current_conditions']['temperature_celsius']}\u00b0C)\",\n        \"html_content\": html_content,\n        \"text_content\": text_content,\n        \"weather_data\": weather_data\n    }\n}]"
      },
      "typeVersion": 2
    },
    {
      "id": "22cbe6d0-2360-4039-a7b5-fc242c3061ff",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        800,
        304
      ],
      "parameters": {
        "sendTo": "emailAddress",
        "message": "={{ $json.output }}",
        "options": {},
        "subject": "={{ $('Generate Email Content').item.json.subject }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        384,
        304
      ],
      "parameters": {
        "text": "=You are an email drafting expert. Take the weather information from {{ $json.text_content }} and make it pretty and easy to read. Use proper spacing, use of paragraphs and bullet points.\n\nPrepare your email in html format.\n\nAdd a joke relevant to the weather if possible. ",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "1557d3ed-d6fa-4542-ab01-961873998ca4",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        432,
        512
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "eccbbc05-da0c-40b9-8954-3f0fadb0640f",
      "name": "On form submission",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -320,
        304
      ],
      "parameters": {
        "options": {},
        "formTitle": "Enter a City",
        "formFields": {
          "values": [
            {
              "fieldLabel": "City",
              "placeholder": "New York"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "52859605-1f9d-4e87-a722-920ac296095b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -400,
        192
      ],
      "parameters": {
        "height": 304,
        "content": "Enter the name of a city in the form"
      },
      "typeVersion": 1
    },
    {
      "id": "bcd1c3e0-ee2d-479d-9fc4-1d391d20c0e9",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        176
      ],
      "parameters": {
        "width": 480,
        "height": 464,
        "content": "Add OpenWeather API key to get current weather information about the cit, replace <your_API_key> with your actual API key. \nPython script will process and generate custom email relating to the weather. "
      },
      "typeVersion": 1
    },
    {
      "id": "49c4cdfb-13ea-41fa-90bc-7ced8147fc19",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        176
      ],
      "parameters": {
        "width": 352,
        "height": 480,
        "content": "Add OpenAI API Key. AI Agent will add a custom joke about the weather for the city you entered as part of the email message"
      },
      "typeVersion": 1
    },
    {
      "id": "46ed9532-4693-4740-819f-81a1ddf51ca1",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        720,
        176
      ],
      "parameters": {
        "height": 304,
        "content": "Add your Gmail credentials and update header and body of message. Node will send the custom email to the recipient"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "83a69a4e-f4cd-472f-812e-1492a06ddbc5",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Weather Data": {
      "main": [
        [
          {
            "node": "Process Weather Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission": {
      "main": [
        [
          {
            "node": "Fetch Weather Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Weather Data": {
      "main": [
        [
          {
            "node": "Generate Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Email Content": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

User enters name of a city for which most current weather information will be gathered Custom Python code processes the weather data and generates a custom email about the weather AI agent further customizes the email and add a related joke about the weather Recipient gets the…

Source: https://n8n.io/workflows/6400/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This workflow automates end-to-end contract and invoice management using AI intelligence. It processes proposals through intelligent contract generation, approval workflows, and automated invoicing. O

Form Trigger, Data Table, Agent +4
AI & RAG

Automates SaaS operations by consolidating user management, AI-driven support triage, analytics, and billing into one unified system. User signups flow through registration, support requests route via

Form Trigger, Data Table, Agent +7
AI & RAG

The workflow runs every hour with a randomized delay of 5–20 minutes to help distribute load. It records the exact date and time a lead is emailed so you can track outreach. Follow-ups are automatical

Google Sheets, Agent, OpenAI Chat +5
AI & RAG

This n8n workflow automates turning short user ideas into production-ready real-estate marketing assets (photorealistic images and optional 360° videos). A form submission seeds a prompt board → an LL

Form Trigger, Google Sheets, Agent +6
AI & RAG

This workflow automatically analyzes a website for UX and SEO quality. It uses Airtop for realistic web scraping, OpenAI for structured evaluation of metadata (title, description, and overall SEO sign

Airtop Tool, Form Trigger, OpenAI Chat +6