Copia de análisis de vuelos

Avanzado

Este es unMarket Research, Multimodal AIflujo de automatización del dominio deautomatización que contiene 24 nodos.Utiliza principalmente nodos como If, Code, Switch, Telegram, HttpRequest. Visualización de datos de vuelos con Chart.js, QuickChart API y un bot de Telegram

Requisitos previos
  • Bot Token de Telegram
  • Pueden requerirse credenciales de autenticación para la API de destino
Vista previa del flujo de trabajo
Visualización de las conexiones entre nodos, con soporte para zoom y panorámica
Exportar flujo de trabajo
Copie la siguiente configuración JSON en n8n para importar y usar este flujo de trabajo
{
  "id": "yQaIw7M18cPt7vGT",
  "meta": {
    "instanceId": "5cee0adb1ef2b84ac8a86937fac5115d710898b6c70f9f7c3f3ca3ef70a11bf7",
    "templateCredsSetupCompleted": true
  },
  "name": "Flight_Analytics copy",
  "tags": [],
  "nodes": [
    {
      "id": "7d5cc476-05c7-4c5f-a419-a60d3684a63d",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -1320,
        -460
      ],
      "webhookId": "04fc191f-7669-4134-a4c5-bd5ea2ab97ad",
      "parameters": {
        "updates": [
          "message",
          "callback_query"
        ],
        "additionalFields": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "0f89bae3-d898-4196-be65-685505c8e66e",
      "name": "Verificar Inicio",
      "type": "n8n-nodes-base.if",
      "position": [
        -1140,
        -460
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "eae97e9f-8b8b-4432-bc12-67221d750682",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.message.text }}",
              "rightValue": "/start"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f0548827-9fad-4331-a361-b1f7c966a1e8",
      "name": "Enviar Mensaje de Bienvenida",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -820,
        -520
      ],
      "webhookId": "5f3e36d5-0318-488f-be7b-9284924e73ec",
      "parameters": {
        "text": "=✈️ Welcome to Flight Data Analytics Bot!\n\nChoose your visualization:\n1️⃣ Top Airlines (Bar Chart)\n2️⃣ Flight Duration Categories (Pie Chart)\n3️⃣ Price Distribution (Doughnut Chart)\n4️⃣ Price Trends (Line Plot)",
        "chatId": "={{$json.message.chat.id}}",
        "replyMarkup": "replyKeyboard",
        "replyKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "1️⃣ Top Airlines (Bar Chart)",
                    "additionalFields": {}
                  },
                  {
                    "text": "2️⃣ Flight Duration Categories (Pie Chart)",
                    "additionalFields": {}
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "3️⃣ Price Distribution (Doughnut Chart)",
                    "additionalFields": {}
                  },
                  {
                    "text": "4️⃣ Price Trends (Line Plot)",
                    "additionalFields": {}
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {},
        "replyKeyboardOptions": {
          "resize_keyboard": true
        }
      },
      "typeVersion": 1
    },
    {
      "id": "83374731-a213-4727-9df6-2a3cab65530d",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -600,
        -300
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "bar",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c09fbabf-08c1-46f1-bdc3-0b25032db26d",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "1️⃣ Top Airlines (Bar Chart)"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "pie",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "581a5fc0-6703-4cfb-b71d-aacec99f92e0",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "2️⃣ Flight Duration Categories (Pie Chart)"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "doughnut",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "48132477-7d85-4816-b896-ab8617207a21",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "3️⃣ Price Distribution (Doughnut Chart)"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "line",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "8aa9bcea-0334-4ee3-9ef5-b8a8eb186815",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "4️⃣ Price Trends (Line Plot)"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "a0031a81-05bb-4609-a6a8-f4ba2dc3cf86",
      "name": "Extraer de Archivo",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -780,
        -280
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "03d88aa3-94f3-4816-9e18-9cb0ca6f7178",
      "name": "Leer Archivo CSV",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        -960,
        -280
      ],
      "parameters": {
        "options": {},
        "fileSelector": "/data/flights.csv"
      },
      "typeVersion": 1
    },
    {
      "id": "7db739f9-0202-4c3a-95a9-9112a89b27ce",
      "name": "Procesar Datos y Crear Gráfico de Barras",
      "type": "n8n-nodes-base.code",
      "position": [
        -300,
        -380
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create bar chart\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Count flights by airline\nconst airlineCounts = {};\nflights.forEach(flight => {\n  const airline = flight.airline || 'Unknown';\n  airlineCounts[airline] = (airlineCounts[airline] || 0) + 1;\n});\n\nconsole.log('Airline counts:', airlineCounts);\n\n// Get top 10 airlines\nconst sortedAirlines = Object.entries(airlineCounts)\n  .sort(([,a], [,b]) => b - a)\n  .slice(0, 10);\n\nconsole.log('Top 10 airlines:', sortedAirlines);\n\n// Create Chart.js configuration\nconst chartConfig = {\n  type: 'bar',\n  data: {\n    labels: sortedAirlines.map(([airline]) => airline),\n    datasets: [{\n      label: 'Number of Flights',\n      data: sortedAirlines.map(([, count]) => count),\n      backgroundColor: [\n        '#3498db', '#2980b9', '#1f77b4', '#ff7f0e', '#2ca02c',\n        '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f'\n      ],\n      borderColor: '#2c3e50',\n      borderWidth: 1\n    }]\n  },\n  options: {\n    responsive: true,\n    plugins: {\n      title: {\n        display: true,\n        text: 'Top 10 Busiest Airlines',\n        font: { size: 18, weight: 'bold' }\n      },\n      legend: { display: false }\n    },\n    scales: {\n      y: {\n        beginAtZero: true,\n        title: {\n          display: true,\n          text: 'Number of Flights'\n        }\n      },\n      x: {\n        title: {\n          display: true,\n          text: 'Airlines'\n        }\n      }\n    }\n  }\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=800&h=600&f=png`;\n\n// Generate insights\nconst topAirline = sortedAirlines[0][0];\nconst topCount = sortedAirlines[0][1];\nconst totalFlights = sortedAirlines.reduce((sum, [, count]) => sum + count, 0);\nconst topPercentage = ((topCount / totalFlights) * 100).toFixed(1);\n\nconst insights = [\n  `✈️ ${topAirline} leads with ${topCount.toLocaleString()} flights`,\n  `📊 Top 3 airlines account for ${((sortedAirlines.slice(0, 3).reduce((sum, [, count]) => sum + count, 0) / totalFlights) * 100).toFixed(1)}% of total flights`,\n  `📈 Total flights analyzed: ${totalFlights.toLocaleString()}`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "b017ea54-0b01-4a82-9265-0db8a6cc8134",
      "name": "Obtener Imagen del Gráfico de Barras",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        -380
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "cf9d57e3-f762-431e-9b4a-ca0a9905d2a7",
      "name": "Enviar Gráfico de Barras a Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        120,
        -380
      ],
      "webhookId": "6d331e30-db24-45aa-872b-6d108599405e",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Bar Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Vistara soars high with 350+ flights, leading the pack! ✈️\n\n/start again?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "f8542715-72cc-43a4-a69c-c66a8fbf3489",
      "name": "Procesar Datos y Crear Gráfico Circular",
      "type": "n8n-nodes-base.code",
      "position": [
        -300,
        -180
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create pie chart for flight duration categories\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Count flights by duration categories\nconst durationCounts = {\n  'Short-haul (< 3h)': 0,\n  'Medium-haul (3-6h)': 0, \n  'Long-haul (6h+)': 0\n};\n\nflights.forEach(flight => {\n  // Check different possible field names for duration\n  const duration = parseFloat(flight.duration || flight.Duration || flight.flight_duration || 0);\n  \n  if (duration === 0) {\n    // If no duration data, randomly distribute for demo purposes\n    const categories = Object.keys(durationCounts);\n    const randomCategory = categories[Math.floor(Math.random() * categories.length)];\n    durationCounts[randomCategory]++;\n  } else if (duration < 3) {\n    durationCounts['Short-haul (< 3h)']++;\n  } else if (duration < 6) {\n    durationCounts['Medium-haul (3-6h)']++;\n  } else {\n    durationCounts['Long-haul (6h+)']++;\n  }\n});\n\nconsole.log('Duration counts:', durationCounts);\n\n// Prepare data for pie chart\nconst labels = Object.keys(durationCounts);\nconst values = Object.values(durationCounts);\nconst total = values.reduce((sum, val) => sum + val, 0);\n\n// Create Chart.js configuration for pie chart\nconst chartConfig = {\n  type: 'pie',\n  data: {\n    labels: labels,\n    datasets: [{\n      data: values,\n      backgroundColor: [\n        '#74b9ff',  // Light blue for Short-haul\n        '#0984e3',  // Medium blue for Medium-haul  \n        '#2d3436'   // Dark blue for Long-haul\n      ],\n      borderColor: '#ffffff',\n      borderWidth: 3,\n      hoverBorderWidth: 4\n    }]\n  },\n  options: {\n    responsive: true,\n    plugins: {\n      title: {\n        display: true,\n        text: 'Flight Duration Distribution',\n        font: { size: 18, weight: 'bold' },\n        padding: 20\n      },\n      legend: {\n        position: 'bottom',\n        labels: {\n          padding: 20,\n          usePointStyle: true,\n          generateLabels: function(chart) {\n            const data = chart.data;\n            return data.labels.map((label, i) => {\n              const value = data.datasets[0].data[i];\n              const percentage = ((value / total) * 100).toFixed(1);\n              return {\n                text: `${label}: ${value.toLocaleString()} (${percentage}%)`,\n                fillStyle: data.datasets[0].backgroundColor[i],\n                strokeStyle: data.datasets[0].borderColor,\n                lineWidth: data.datasets[0].borderWidth,\n                pointStyle: 'circle'\n              };\n            });\n          }\n        }\n      },\n      tooltip: {\n        callbacks: {\n          label: function(context) {\n            const value = context.raw;\n            const percentage = ((value / total) * 100).toFixed(1);\n            return `${context.label}: ${value.toLocaleString()} flights (${percentage}%)`;\n          }\n        }\n      }\n    },\n    layout: {\n      padding: 20\n    }\n  }\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=800&h=600&f=png&devicePixelRatio=2`;\n\n// Generate insights\nconst shortPercent = ((durationCounts['Short-haul (< 3h)'] / total) * 100).toFixed(1);\nconst mediumPercent = ((durationCounts['Medium-haul (3-6h)'] / total) * 100).toFixed(1);\nconst longPercent = ((durationCounts['Long-haul (6h+)'] / total) * 100).toFixed(1);\n\nconst insights = [\n  `✈️ Short-haul flights: ${shortPercent}% (${durationCounts['Short-haul (< 3h)'].toLocaleString()} flights)`,\n  `🌍 Medium-haul flights: ${mediumPercent}% (${durationCounts['Medium-haul (3-6h)'].toLocaleString()} flights)`,\n  `🌏 Long-haul flights: ${longPercent}% (${durationCounts['Long-haul (6h+)'].toLocaleString()} flights)`,\n  `📊 Total flights analyzed: ${total.toLocaleString()}`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights,\n  durationBreakdown: durationCounts\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9f89f22a-4b02-4d9f-b725-5ef8ceaa1b9b",
      "name": "Obtener Imagen del Gráfico Circular",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        -180
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "7291ea67-ee78-475a-be67-e62d49093518",
      "name": "Enviar Gráfico Circular a Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        140,
        -180
      ],
      "webhookId": "36ec709e-cdca-479f-b9c7-18d5bf861aac",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Pie Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Long-haul dominates with 611 flights, while short-haul adds 279! 🌍\n\nDo you need /start?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "cebfd41e-9d89-42a3-9269-69fac651f245",
      "name": "Obtener Imagen del Gráfico de Dona",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -40,
        40
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "1c11494a-da57-4a66-82ed-57700a559798",
      "name": "Enviar Gráfico de Dona a Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        180,
        40
      ],
      "webhookId": "b0f1576e-6b2c-43cf-90eb-681a455a496c",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Doughnut Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Budget rules with 475 bookings under ₹10K! 💸\n\n/start again?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "da036178-aa0d-418d-81e3-7ac126d6e6cf",
      "name": "Procesar Datos y Crear Gráfico Line",
      "type": "n8n-nodes-base.code",
      "position": [
        -240,
        240
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create beautiful line chart for price trends\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Group flights by duration ranges and calculate average prices\nconst durationRanges = {\n  '1-2h': [],\n  '2-4h': [],\n  '4-6h': [],\n  '6-8h': [],\n  '8-10h': [],\n  '10-12h': [],\n  '12h+':[],\n};\n\nflights.forEach(flight => {\n  const duration = parseFloat(flight.duration || 0);\n  const price = parseFloat(flight.price || 0);\n  \n  if (duration > 0 && price > 0) {\n    if (duration <= 2) {\n      durationRanges['1-2h'].push(price);\n    } else if (duration <= 4) {\n      durationRanges['2-4h'].push(price);\n    } else if (duration <= 6) {\n      durationRanges['4-6h'].push(price);\n    } else if (duration <= 8) {\n      durationRanges['6-8h'].push(price);\n    } else if (duration <= 10) {\n      durationRanges['8-10h'].push(price);\n    } else if (duration <= 12) {\n      durationRanges['10-12h'].push(price);\n    } else {\n      durationRanges['12h+'].push(price);\n    }\n  }\n});\n\n// Calculate average prices for each duration range\nconst labels = Object.keys(durationRanges);\nconst avgPrices = labels.map(range => {\n  const prices = durationRanges[range];\n  return prices.length > 0 ? Math.round(prices.reduce((sum, p) => sum + p, 0) / prices.length) : 0;\n});\n\n// Filter out ranges with no data\nconst validData = labels.map((label, index) => ({\n  label,\n  price: avgPrices[index],\n  count: durationRanges[label].length\n})).filter(item => item.count > 0);\n\nconst finalLabels = validData.map(item => item.label);\nconst finalPrices = validData.map(item => item.price);\n\nconsole.log('Duration ranges with data:', finalLabels);\nconsole.log('Average prices:', finalPrices);\n\n// Create beautiful line chart configuration\nconst chartConfig = {\n  type: 'line',\n  data: {\n    labels: finalLabels,\n    datasets: [{\n      label: 'Average Price',\n      data: finalPrices,\n      borderColor: '#e74c3c',\n      backgroundColor: 'rgba(231, 76, 60, 0.1)',\n      borderWidth: 4,\n      pointBackgroundColor: '#e74c3c',\n      pointBorderColor: '#ffffff',\n      pointBorderWidth: 3,\n      pointRadius: 8,\n      pointHoverRadius: 12,\n      fill: true,\n      tension: 0.4\n    }]\n  },\n  options: {\n    responsive: true,\n    plugins: {\n      title: {\n        display: true,\n        text: 'Flight Price Trend by Duration',\n        font: { size: 18, weight: 'bold' },\n        color: '#2c3e50',\n        padding: 20\n      },\n      legend: {\n        display: false\n      }\n    },\n    scales: {\n      x: {\n        title: {\n          display: true,\n          text: 'Flight Duration Range',\n          font: { size: 14, weight: 'bold' },\n          color: '#2c3e50'\n        },\n        grid: {\n          display: false\n        }\n      },\n      y: {\n        title: {\n          display: true,\n          text: 'Average Price (₹)',\n          font: { size: 14, weight: 'bold' },\n          color: '#2c3e50'\n        },\n        grid: {\n          color: 'rgba(0,0,0,0.1)'\n        },\n        ticks: {\n          callback: function(value) {\n            return '₹' + value.toLocaleString();\n          }\n        }\n      }\n    },\n    elements: {\n      line: {\n        capBezierPoints: false\n      }\n    }\n  }\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=800&h=600&f=png`;\n\nconsole.log('Chart URL length:', quickChartUrl.length);\n\n// Calculate insights\nconst minPrice = Math.min(...finalPrices);\nconst maxPrice = Math.max(...finalPrices);\nconst minIndex = finalPrices.indexOf(minPrice);\nconst maxIndex = finalPrices.indexOf(maxPrice);\n\nconst totalFlights = validData.reduce((sum, item) => sum + item.count, 0);\nconst overallAvg = Math.round(finalPrices.reduce((sum, p) => sum + p, 0) / finalPrices.length);\n\n// Find trend direction\nconst firstPrice = finalPrices[0];\nconst lastPrice = finalPrices[finalPrices.length - 1];\nconst trendDirection = lastPrice > firstPrice ? 'increasing' : 'decreasing';\nconst trendEmoji = lastPrice > firstPrice ? '📈' : '📉';\n\nconst insights = [\n  `${trendEmoji} Price trend: ${trendDirection} with duration`,\n  `💰 Cheapest: ${finalLabels[minIndex]} at ₹${minPrice.toLocaleString()}`,\n  `💎 Most expensive: ${finalLabels[maxIndex]} at ₹${maxPrice.toLocaleString()}`,\n  `📊 Overall average: ₹${overallAvg.toLocaleString()} (${totalFlights} flights)`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights,\n  trendData: validData\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "27e79d12-3632-44af-9389-ad60460a1a0b",
      "name": "Procesar Datos y Crear Gráfico de Dona",
      "type": "n8n-nodes-base.code",
      "position": [
        -260,
        40
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create doughnut chart for price distribution\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Count flights by price ranges\nconst priceRanges = {\n  'Budget\\n₹0-10K': 0,\n  'Economy\\n₹10K-25K': 0,\n  'Standard\\n₹25K-50K': 0,\n  'Premium\\n₹50K-100K': 0,\n  'Luxury\\n₹100K+': 0\n};\n\nflights.forEach(flight => {\n  const price = parseFloat(flight.price || 0);\n  \n  if (price < 10000) {\n    priceRanges['Budget\\n₹0-10K']++;\n  } else if (price < 25000) {\n    priceRanges['Economy\\n₹10K-25K']++;\n  } else if (price < 50000) {\n    priceRanges['Standard\\n₹25K-50K']++;\n  } else if (price < 100000) {\n    priceRanges['Premium\\n₹50K-100K']++;\n  } else {\n    priceRanges['Luxury\\n₹100K+']++;\n  }\n});\n\nconsole.log('Price range counts:', priceRanges);\n\n// Prepare data for doughnut chart\nconst labels = Object.keys(priceRanges);\nconst values = Object.values(priceRanges);\nconst total = values.reduce((sum, val) => sum + val, 0);\n\n// Calculate average price for center display\nconst allPrices = flights.map(f => parseFloat(f.price || 0)).filter(p => p > 0);\nconst avgPrice = Math.round(allPrices.reduce((sum, price) => sum + price, 0) / allPrices.length);\n\n// Create Chart.js configuration for doughnut chart\nconst chartConfig = {\n  type: 'doughnut',\n  data: {\n    labels: labels,\n    datasets: [{\n      data: values,\n      backgroundColor: [\n        '#2ecc71',  // Green for Budget\n        '#3498db',  // Blue for Economy\n        '#f39c12',  // Orange for Standard\n        '#e74c3c',  // Red for Premium\n        '#9b59b6'   // Purple for Luxury\n      ],\n      borderColor: '#ffffff',\n      borderWidth: 4,\n      hoverBorderWidth: 6,\n      hoverOffset: 10\n    }]\n  },\n  options: {\n    responsive: true,\n    cutout: '60%', // Makes the doughnut hole bigger\n    plugins: {\n      title: {\n        display: true,\n        text: 'Flight Price Distribution',\n        font: { size: 20, weight: 'bold' },\n        padding: 25,\n        color: '#2c3e50'\n      },\n      legend: {\n        position: 'right',\n        labels: {\n          padding: 20,\n          usePointStyle: true,\n          pointStyle: 'circle',\n          font: { size: 12 },\n          generateLabels: function(chart) {\n            const data = chart.data;\n            return data.labels.map((label, i) => {\n              const value = data.datasets[0].data[i];\n              const percentage = ((value / total) * 100).toFixed(1);\n              return {\n                text: `${label.replace('\\\\n', ' ')}: ${percentage}%`,\n                fillStyle: data.datasets[0].backgroundColor[i],\n                strokeStyle: data.datasets[0].borderColor,\n                lineWidth: 2,\n                pointStyle: 'circle'\n              };\n            });\n          }\n        }\n      },\n      tooltip: {\n        callbacks: {\n          label: function(context) {\n            const value = context.raw;\n            const percentage = ((value / total) * 100).toFixed(1);\n            return `${context.label.replace('\\\\n', ' ')}: ${value.toLocaleString()} flights (${percentage}%)`;\n          }\n        },\n        backgroundColor: 'rgba(0,0,0,0.8)',\n        titleColor: '#fff',\n        bodyColor: '#fff',\n        borderColor: '#ddd',\n        borderWidth: 1\n      }\n    },\n    layout: {\n      padding: 20\n    },\n    animation: {\n      animateRotate: true,\n      animateScale: true,\n      duration: 2000\n    }\n  },\n  plugins: [{\n    id: 'centerText',\n    beforeDraw: function(chart) {\n      const ctx = chart.ctx;\n      const centerX = chart.chartArea.left + (chart.chartArea.right - chart.chartArea.left) / 2;\n      const centerY = chart.chartArea.top + (chart.chartArea.bottom - chart.chartArea.top) / 2;\n      \n      ctx.save();\n      ctx.textAlign = 'center';\n      ctx.textBaseline = 'middle';\n      \n      // Main text\n      ctx.font = 'bold 24px Arial';\n      ctx.fillStyle = '#2c3e50';\n      ctx.fillText('₹' + avgPrice.toLocaleString(), centerX, centerY - 10);\n      \n      // Subtitle\n      ctx.font = '14px Arial';\n      ctx.fillStyle = '#7f8c8d';\n      ctx.fillText('Avg Price', centerX, centerY + 15);\n      \n      ctx.restore();\n    }\n  }]\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=900&h=600&f=png&devicePixelRatio=2`;\n\n// Find most popular price range\nconst maxCount = Math.max(...values);\nconst popularRange = labels[values.indexOf(maxCount)];\n\n// Calculate statistics\nconst minPrice = Math.min(...allPrices);\nconst maxPrice = Math.max(...allPrices);\n\nconst insights = [\n  `🎯 Most popular: ${popularRange.replace('\\\\n', ' ')} (${((maxCount / total) * 100).toFixed(1)}%)`,\n  `💰 Average price: ₹${avgPrice.toLocaleString()}`,\n  `📊 Price spread: ₹${minPrice.toLocaleString()} to ₹${maxPrice.toLocaleString()}`,\n  `✈️ Total flights: ${total.toLocaleString()}`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights,\n  priceBreakdown: priceRanges\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "94303358-470a-45e2-8d6d-2ea39f138a13",
      "name": "Obtener Imagen del Gráfico Line",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -20,
        240
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "215a0189-af79-4622-adba-8a4030a9ec3b",
      "name": "Enviar Gráfico Line a Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        200,
        240
      ],
      "webhookId": "ec4e46b4-a2bd-471e-b33f-09286e31fe23",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Line Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Average prices peak at 14K for 6-8 hour flights! 📈\n\n/start ?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "419e5f4f-add0-479c-9848-cb259fac8427",
      "name": "Nota Adhesiva",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1640,
        -760
      ],
      "parameters": {
        "width": 320,
        "height": 260,
        "content": "## 📱 ENTRY POINT\nListens for Telegram messages & button clicks\n\n✅ Triggers on:\n- /start command\n- Menu button selections\n- Chart type selections\n\n💡 TIP: This is where users first interact with the bot!"
      },
      "typeVersion": 1
    },
    {
      "id": "13d61fe2-f91f-4cbf-807e-20dabeb4d4f5",
      "name": "Nota Adhesiva1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1300,
        -760
      ],
      "parameters": {
        "color": 2,
        "width": 360,
        "height": 220,
        "content": "## 🎯 COMMAND DETECTOR\nSmart filter to detect /start command\n\n✅ Purpose:\n- Shows welcome menu for new users\n- Routes existing interactions to chart generation\n- Prevents unnecessary menu displays\n\n⚡ Simple but essential routing logic!"
      },
      "typeVersion": 1
    },
    {
      "id": "e2720e7c-8b98-4c1b-9e78-f28cf4b1eed5",
      "name": "Nota Adhesiva2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -880,
        -800
      ],
      "parameters": {
        "color": 3,
        "width": 320,
        "height": 260,
        "content": "## 🎨 USER INTERFACE\nBeautiful welcome menu with chart options\n\n🎯 Features:\n- Reply keyboard for easy selection\n- Emoji-enhanced buttons\n- Clear chart type descriptions\n- Resize keyboard for mobile UX\n\n💡 First impression matters - make it count!"
      },
      "typeVersion": 1
    },
    {
      "id": "f8b7943c-3633-45ca-825c-3da80754de93",
      "name": "Nota Adhesiva3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1260,
        -300
      ],
      "parameters": {
        "color": 4,
        "width": 600,
        "height": 640,
        "content": "## 📊 DATA SOURCE\nReads flight dataset from local storage\n\n📍 File Location: /data/flights.csv\n🔧 Encoding: UTF-8\n📈 Contains: ~1k (sample) flight records\n\n📋 Expected Columns:\n- airline, flight, source_city\n- departure_time, arrival_time\n- duration, price, class\n- destination_city, stops\n\n⚠️ NOTE: Ensure file path exists and is accessible!\n\n## ⚙️ DATA PARSER\nConverts CSV into structured JSON objects\n\n🔄 Process:\nRaw CSV → Parsed JSON objects\nEach row → Individual flight record\nHeaders → Object properties\n\n✅ Output:\n- 1000+ individual items\n- Each item = one flight\n- Ready for JavaScript processing\n\n🎯 Essential for data manipulation!"
      },
      "typeVersion": 1
    },
    {
      "id": "83f8b42a-7eb0-48d6-a258-9c1a8dc1158d",
      "name": "Nota Adhesiva4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        -340
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 540,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## 🎛️ TRAFFIC CONTROLLER\nRoutes users to correct chart generation\n\n\n✅ Smart Matching:\n- Text-based button detection\n- Exact string matching\n- Clean output routing\n\n📊 One input → Four possible chart outputs!"
      },
      "typeVersion": 1
    },
    {
      "id": "152d7277-9969-4a1a-b2ab-5234f8af2581",
      "name": "Nota Adhesiva5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -360,
        -780
      ],
      "parameters": {
        "color": 6,
        "width": 860,
        "height": 1180,
        "content": "## 🎨 CHART GENERATOR (4 Types)\n\n📈 BAR: Top 10 Airlines by flight count\n🥧 PIE: Duration categories (Short/Medium/Long)  \n🍩 DOUGHNUT: Price ranges (Budget→Luxury)\n📊 LINE: Price trends by flight duration\n\n🔧 Process Flow:\nData → Count/Group → Chart.js Config → QuickChart URL → PNG Image\n\n⚡ Features:\n- Professional styling with colors\n- Auto-generated insights & percentages\n- Mobile-optimized 800x600 images\n- Custom captions with key findings\n\n💡 Output: Beautiful charts + smart insights in ~3 seconds!"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "68a0e3af-8acf-4f65-b0ff-41553223e18e",
  "connections": {
    "83374731-a213-4727-9df6-2a3cab65530d": {
      "main": [
        [
          {
            "node": "7db739f9-0202-4c3a-95a9-9112a89b27ce",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "f8542715-72cc-43a4-a69c-c66a8fbf3489",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "27e79d12-3632-44af-9389-ad60460a1a0b",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "da036178-aa0d-418d-81e3-7ac126d6e6cf",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0f89bae3-d898-4196-be65-685505c8e66e": {
      "main": [
        [
          {
            "node": "f0548827-9fad-4331-a361-b1f7c966a1e8",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "03d88aa3-94f3-4816-9e18-9cb0ca6f7178",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "03d88aa3-94f3-4816-9e18-9cb0ca6f7178": {
      "main": [
        [
          {
            "node": "a0031a81-05bb-4609-a6a8-f4ba2dc3cf86",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7d5cc476-05c7-4c5f-a419-a60d3684a63d": {
      "main": [
        [
          {
            "node": "0f89bae3-d898-4196-be65-685505c8e66e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a0031a81-05bb-4609-a6a8-f4ba2dc3cf86": {
      "main": [
        [
          {
            "node": "83374731-a213-4727-9df6-2a3cab65530d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b017ea54-0b01-4a82-9265-0db8a6cc8134": {
      "main": [
        [
          {
            "node": "cf9d57e3-f762-431e-9b4a-ca0a9905d2a7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "9f89f22a-4b02-4d9f-b725-5ef8ceaa1b9b": {
      "main": [
        [
          {
            "node": "7291ea67-ee78-475a-be67-e62d49093518",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "94303358-470a-45e2-8d6d-2ea39f138a13": {
      "main": [
        [
          {
            "node": "215a0189-af79-4622-adba-8a4030a9ec3b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "cebfd41e-9d89-42a3-9269-69fac651f245": {
      "main": [
        [
          {
            "node": "1c11494a-da57-4a66-82ed-57700a559798",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7db739f9-0202-4c3a-95a9-9112a89b27ce": {
      "main": [
        [
          {
            "node": "b017ea54-0b01-4a82-9265-0db8a6cc8134",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "f8542715-72cc-43a4-a69c-c66a8fbf3489": {
      "main": [
        [
          {
            "node": "9f89f22a-4b02-4d9f-b725-5ef8ceaa1b9b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "da036178-aa0d-418d-81e3-7ac126d6e6cf": {
      "main": [
        [
          {
            "node": "94303358-470a-45e2-8d6d-2ea39f138a13",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "27e79d12-3632-44af-9389-ad60460a1a0b": {
      "main": [
        [
          {
            "node": "cebfd41e-9d89-42a3-9269-69fac651f245",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Preguntas frecuentes

¿Cómo usar este flujo de trabajo?

Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.

¿En qué escenarios es adecuado este flujo de trabajo?

Avanzado - Investigación de mercado, IA Multimodal

¿Es de pago?

Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.

Información del flujo de trabajo
Nivel de dificultad
Avanzado
Número de nodos24
Categoría2
Tipos de nodos9
Descripción de la dificultad

Adecuado para usuarios avanzados, flujos de trabajo complejos con 16+ nodos

Autor
DataMinex

DataMinex

@dataminex

Smart Connection Analysis from Open Data, Globally at Scale

Enlaces externos
Ver en n8n.io

Compartir este flujo de trabajo

Categorías

Categorías: 34