CoolFace
Apppublic

im-amrith/crisp

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
dataProcessor.ts352 linesDownload Raw Back to utils
1import { ProcessedMarketData, PriceForecast, SeasonalPattern, MarketInfo } from '../types/market';2 3export class DataProcessor {4  private data: ProcessedMarketData[] = [];5 6  async loadData(): Promise<void> {7    try {8      // List all CSV filenames in the data directory9      const csvFiles = [10        "Almond(Badam).csv",11        "Ambada Seed.csv",12        "Antawala.csv",13        "Bamboo.csv",14        "Guava.csv",15        "Methi(Leaves).csv",16        "Papaya (Raw).csv",17        "Paddy(Dhan)(Basmati).csv",18        "Peas(Dry).csv"19      ];20 21      // Fetch and parse all CSV files in parallel22      const allDataArrays = await Promise.all(23        csvFiles.map(async (filename) => {24          const response = await fetch(`/data/${filename}`);25          const text = await response.text();26          return this.parseCSV(text);27        })28      );29 30      // Flatten the array of arrays into a single array31      this.data = allDataArrays.flat();32    } catch (error) {33      console.error('Error loading market data:', error);34      throw new Error('Failed to load market data');35    }36  }37 38  private parseCSV(csvText: string): ProcessedMarketData[] {39    const lines = csvText.split('\n');40    const headers = lines[0].split(',');41    const data: ProcessedMarketData[] = [];42 43    for (let i = 1; i < lines.length; i++) {44      const line = lines[i].trim();45      if (!line) continue;46 47      const values = this.parseCSVLine(line);48      if (values.length !== headers.length) continue;49 50      try {51        const row: ProcessedMarketData = {52          state: values[0] || '',53          district: values[1] || '',54          market: values[2] || '',55          variety: values[3] || '',56          group: values[4] || '',57          arrivals: parseFloat(values[5]) || 0,58          minPrice: parseFloat(values[6]) || 0,59          maxPrice: parseFloat(values[7]) || 0,60          modalPrice: parseFloat(values[8]) || 0,61          date: new Date(values[9] || '2023-01-01')62        };63 64        if (row.modalPrice > 0 && row.state && row.district && row.market) {65          data.push(row);66        }67      } catch (error) {68        console.warn('Error parsing row:', line, error);69      }70    }71 72    return data;73  }74 75  private parseCSVLine(line: string): string[] {76    const result: string[] = [];77    let current = '';78    let inQuotes = false;79 80    for (let i = 0; i < line.length; i++) {81      const char = line[i];82      83      if (char === '"') {84        inQuotes = !inQuotes;85      } else if (char === ',' && !inQuotes) {86        result.push(current.trim());87        current = '';88      } else {89        current += char;90      }91    }92    93    result.push(current.trim());94    return result;95  }96 97  getStates(): string[] {98    const states = new Set(this.data.map(d => d.state));99    return Array.from(states).sort();100  }101 102  getDistricts(state: string): string[] {103    const districts = new Set(104      this.data105        .filter(d => d.state === state)106        .map(d => d.district)107    );108    return Array.from(districts).sort();109  }110 111  getMarkets(state: string, district: string): string[] {112    const markets = new Set(113      this.data114        .filter(d => d.state === state && d.district === district)115        .map(d => d.market)116    );117    return Array.from(markets).sort();118  }119 120  getVarieties(): string[] {121    const varieties = new Set(this.data.map(d => d.variety));122    return Array.from(varieties).filter(v => v && v !== 'Other').sort();123  }124 125  getMarketData(state?: string, district?: string, variety?: string): ProcessedMarketData[] {126    return this.data.filter(d => {127      if (state && d.state !== state) return false;128      if (district && d.district !== district) return false;129      if (variety && d.variety !== variety) return false;130      return true;131    });132  }133 134  generatePriceForecast(variety: string, months: number = 12): PriceForecast[] {135    const varietyData = this.data.filter(d => d.variety === variety);136    if (varietyData.length === 0) return [];137 138    // Sort by date139    varietyData.sort((a, b) => a.date.getTime() - b.date.getTime());140 141    // Calculate moving average and trend142    const forecasts: PriceForecast[] = [];143    const currentDate = new Date();144    145    // Get recent price trend146    const recentData = varietyData.slice(-30); // Last 30 records147    const avgPrice = recentData.reduce((sum, d) => sum + d.modalPrice, 0) / recentData.length;148    149    // Calculate seasonal patterns150    const monthlyAvg = this.calculateMonthlyAverages(varietyData);151    152    for (let i = 0; i < months; i++) {153      const forecastDate = new Date(currentDate);154      forecastDate.setMonth(forecastDate.getMonth() + i);155      156      const month = forecastDate.getMonth();157      const seasonalMultiplier = monthlyAvg[month] / avgPrice;158      159      // Simple trend calculation with seasonal adjustment160      const trendFactor = 1 + (Math.random() - 0.5) * 0.1; // ±5% random variation161      const predictedPrice = avgPrice * seasonalMultiplier * trendFactor;162      163      forecasts.push({164        date: forecastDate.toISOString().split('T')[0],165        predictedPrice: Math.round(predictedPrice),166        confidence: Math.max(0.6, 1 - (i * 0.05)), // Decreasing confidence over time167        trend: predictedPrice > avgPrice ? 'up' : predictedPrice < avgPrice ? 'down' : 'stable'168      });169    }170 171    return forecasts;172  }173 174  private calculateMonthlyAverages(data: ProcessedMarketData[]): number[] {175    const monthlyData: { [key: number]: number[] } = {};176    177    data.forEach(d => {178      const month = d.date.getMonth();179      if (!monthlyData[month]) monthlyData[month] = [];180      monthlyData[month].push(d.modalPrice);181    });182 183    const monthlyAvg: number[] = [];184    for (let i = 0; i < 12; i++) {185      if (monthlyData[i] && monthlyData[i].length > 0) {186        monthlyAvg[i] = monthlyData[i].reduce((sum, price) => sum + price, 0) / monthlyData[i].length;187      } else {188        // Use overall average if no data for this month189        const overallAvg = data.reduce((sum, d) => sum + d.modalPrice, 0) / data.length;190        monthlyAvg[i] = overallAvg;191      }192    }193 194    return monthlyAvg;195  }196 197  private calculateMonthlyPriceAverages(data: ProcessedMarketData[], priceType: 'minPrice' | 'maxPrice' | 'modalPrice'): { month: string, avg: number }[] {198    const monthlyData: { [key: number]: number[] } = {};199    200    data.forEach(d => {201      const month = d.date.getMonth();202      if (!monthlyData[month]) monthlyData[month] = [];203      const price = priceType === 'minPrice' ? d.minPrice : priceType === 'maxPrice' ? d.maxPrice : d.modalPrice;204      if (price > 0) { // Only include valid prices205        monthlyData[month].push(price);206      }207    });208 209    const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];210    211    const overallAvg = data.length > 0 212      ? data.reduce((sum, d) => {213          const price = priceType === 'minPrice' ? d.minPrice : priceType === 'maxPrice' ? d.maxPrice : d.modalPrice;214          return sum + price;215        }, 0) / data.filter(d => (priceType === 'minPrice' ? d.minPrice : priceType === 'maxPrice' ? d.maxPrice : d.modalPrice) > 0).length216      : 0;217 218    const monthlyAvgs: { month: string, avg: number }[] = [];219    for (let i = 0; i < 12; i++) {220        let avgForMonth: number;221        if (monthlyData[i] && monthlyData[i].length > 0) {222            avgForMonth = monthlyData[i].reduce((sum, price) => sum + price, 0) / monthlyData[i].length;223        } else {224            avgForMonth = overallAvg; // Fallback to overall average225        }226        monthlyAvgs.push({ month: months[i], avg: avgForMonth });227    }228 229    return monthlyAvgs;230  }231 232  getSeasonalPatterns(variety: string): SeasonalPattern[] {233    const varietyData = this.data.filter(d => d.variety === variety);234    if (varietyData.length === 0) return [];235 236    const monthlyAvg = this.calculateMonthlyAverages(varietyData);237    const overallAvg = monthlyAvg.reduce((sum, price) => sum + price, 0) / 12;238 239    const months = [240      'January', 'February', 'March', 'April', 'May', 'June',241      'July', 'August', 'September', 'October', 'November', 'December'242    ];243 244    return months.map((month, index) => {245      const priceIndex = monthlyAvg[index] / overallAvg;246      let recommendation: 'excellent' | 'good' | 'average' | 'poor';247      248      if (priceIndex >= 1.15) recommendation = 'excellent';249      else if (priceIndex >= 1.05) recommendation = 'good';250      else if (priceIndex >= 0.95) recommendation = 'average';251      else recommendation = 'poor';252 253      return {254        month,255        averagePrice: Math.round(monthlyAvg[index]),256        priceIndex,257        recommendation258      };259    });260  }261 262  getBestMarkets(variety: string, userState?: string, userMarket?: string, limit: number = 5): MarketInfo[] {263    let varietyData = this.data.filter(d => d.variety === variety);264 265    // Filter by market if one is provided266    if (userMarket) {267      varietyData = varietyData.filter(d => d.market === userMarket);268    }269    270    // Group by market271    const marketGroups: { [key: string]: ProcessedMarketData[] } = {};272    273    varietyData.forEach(d => {274      const key = `${d.state}-${d.district}-${d.market}`;275      if (!marketGroups[key]) marketGroups[key] = [];276      marketGroups[key].push(d);277    });278 279    const marketAverages = Object.entries(marketGroups).map(([key, data]) => {280      if (data.length === 0) return null;281 282      // Find highest and lowest prices from monthly averages283      const monthlyMaxPrices = this.calculateMonthlyPriceAverages(data, 'maxPrice');284      const monthlyMinPrices = this.calculateMonthlyPriceAverages(data, 'minPrice');285 286      const highPriceEntry = monthlyMaxPrices.reduce((max, p) => p.avg > max.avg ? p : max, { avg: 0, month: 'N/A' });287      const lowPriceEntry = monthlyMinPrices.reduce((min, p) => (p.avg < min.avg && p.avg > 0) ? p : min, { avg: Infinity, month: 'N/A' });288 289      // The main price for sorting and display will be the historical high price average290      const displayPrice = highPriceEntry.avg;291 292      return {293        ...data.sort((a, b) => b.date.getTime() - a.date.getTime())[0], // Use latest for base info294        modalPrice: displayPrice,295        highPrice: Math.round(highPriceEntry.avg),296        highPriceMonth: highPriceEntry.month,297        lowPrice: lowPriceEntry.avg === Infinity ? 0 : Math.round(lowPriceEntry.avg),298        lowPriceMonth: lowPriceEntry.month,299        arrivals: data.reduce((sum, d) => sum + d.arrivals, 0) / data.length300      };301    }).filter((m): m is MarketInfo => m !== null && m.highPrice > 0);302 303    // Sort by high price (descending) and prioritize user's state304    marketAverages.sort((a, b) => {305      if (userState) {306        if (a.state === userState && b.state !== userState) return -1;307        if (b.state === userState && a.state !== userState) return 1;308      }309      return (b.highPrice || 0) - (a.highPrice || 0);310    });311 312    return marketAverages.slice(0, limit);313  }314 315  getBestStateMarkets(variety: string, state: string, limit: number = 3): MarketInfo[] {316    const stateVarietyData = this.data.filter(d => d.variety === variety && d.state === state);317 318    const marketGroups: { [key: string]: ProcessedMarketData[] } = {};319    320    stateVarietyData.forEach(d => {321      const key = `${d.state}-${d.district}-${d.market}`;322      if (!marketGroups[key]) marketGroups[key] = [];323      marketGroups[key].push(d);324    });325 326    const marketAverages = Object.entries(marketGroups).map(([key, data]) => {327      if (data.length === 0) return null;328 329      const monthlyMaxPrices = this.calculateMonthlyPriceAverages(data, 'maxPrice');330      const monthlyMinPrices = this.calculateMonthlyPriceAverages(data, 'minPrice');331 332      const highPriceEntry = monthlyMaxPrices.reduce((max, p) => p.avg > max.avg ? p : max, { avg: 0, month: 'N/A' });333      const lowPriceEntry = monthlyMinPrices.reduce((min, p) => (p.avg < min.avg && p.avg > 0) ? p : min, { avg: Infinity, month: 'N/A' });334      335      const displayPrice = highPriceEntry.avg;336 337      return {338        ...data.sort((a, b) => b.date.getTime() - a.date.getTime())[0],339        modalPrice: displayPrice,340        highPrice: Math.round(highPriceEntry.avg),341        highPriceMonth: highPriceEntry.month,342        lowPrice: lowPriceEntry.avg === Infinity ? 0 : Math.round(lowPriceEntry.avg),343        lowPriceMonth: lowPriceEntry.month,344        arrivals: data.reduce((sum, d) => sum + d.arrivals, 0) / data.length345      };346    }).filter((m): m is MarketInfo => m !== null && m.highPrice > 0);347 348    return marketAverages349      .sort((a, b) => (b.highPrice || 0) - (a.highPrice || 0))350      .slice(0, limit);351  }352}