8
n8n 한국어amn8n.com

항공편 분석 복사본

고급

이것은Market Research, Multimodal AI분야의자동화 워크플로우로, 24개의 노드를 포함합니다.주로 If, Code, Switch, Telegram, HttpRequest 등의 노드를 사용하며. Chart.js, QuickChart API, Telegram 로봇을 사용하여 항공 데이터를 시각화

사전 요구사항
  • Telegram Bot Token
  • 대상 API의 인증 정보가 필요할 수 있음
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
  "id": "yQaIw7M18cPt7vGT",
  "meta": {
    "instanceId": "5cee0adb1ef2b84ac8a86937fac5115d710898b6c70f9f7c3f3ca3ef70a11bf7",
    "templateCredsSetupCompleted": true
  },
  "name": "Flight_Analytics copy",
  "tags": [],
  "nodes": [
    {
      "id": "7d5cc476-05c7-4c5f-a419-a60d3684a63d",
      "name": "Telegram 트리거",
      "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": "시작 확인",
      "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": "환영 메시지 전송",
      "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": "스위치",
      "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": "파일에서 추출",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -780,
        -280
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "03d88aa3-94f3-4816-9e18-9cb0ca6f7178",
      "name": "CSV 파일 읽기",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        -960,
        -280
      ],
      "parameters": {
        "options": {},
        "fileSelector": "/data/flights.csv"
      },
      "typeVersion": 1
    },
    {
      "id": "7db739f9-0202-4c3a-95a9-9112a89b27ce",
      "name": "데이터 처리 및 막대 그래프 생성",
      "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": "막대 그래프 이미지 가져오기",
      "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": "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": "데이터 처리 및 원형 차트 생성",
      "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": "원형 차트 이미지 가져오기",
      "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": "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": "도넛 차트 이미지 가져오기",
      "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": "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": "데이터 처리 및 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": "데이터 처리 및 도넛 차트 생성",
      "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": "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": "Telegram에 Line 차트 전송",
      "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": "스티커 메모",
      "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": "스티커 메모1",
      "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": "스티커 메모2",
      "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": "스티커 메모3",
      "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": "스티커 메모4",
      "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": "스티커 메모5",
      "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
          }
        ]
      ]
    }
  }
}
자주 묻는 질문

이 워크플로우를 어떻게 사용하나요?

위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.

이 워크플로우는 어떤 시나리오에 적합한가요?

고급 - 시장 조사, 멀티모달 AI

유료인가요?

이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.

워크플로우 정보
난이도
고급
노드 수24
카테고리2
노드 유형9
난이도 설명

고급 사용자를 위한 16+개 노드의 복잡한 워크플로우

저자
DataMinex

DataMinex

@dataminex

Smart Connection Analysis from Open Data, Globally at Scale

외부 링크
n8n.io에서 보기

이 워크플로우 공유

카테고리

카테고리: 34