const asyncHandler = require("express-async-handler"); const moment = require('moment-timezone'); const { ObjectId } = require('mongodb'); const { SaleDb } = require("../services/saleDb"); const saleDb = new SaleDb(); const { LotDb } = require("../services/lotDb"); const lotDb = new LotDb(); const agenda = require('../services/agenda'); const {Agent} = require('../services/agent'); const agent = new Agent(); const ExcelJS = require('exceljs'); exports.getSaleInfos = asyncHandler(async (req, res, next) => { let url = req.params.url agent.getSaleInfos(url) .then(data => { return res.status(200).json(data); }) .catch(error => { console.error(error); return res.status(500).send(error); }); // url = encodeURIComponent(url); // fetch(ApiAgentURL+'/sale/getSaleInfos/'+url) // .then(response => response.json()) // .then(data => { // res.json(data); // }) // .catch(error => { // console.error(error); // }); }); exports.prepareSale = asyncHandler(async (req, res, next) => { try{ const id = req.params.id; agent.prepareSale(id) .then(data => { return res.status(200).json({"message": "Lots created"}); }) .catch(error => { console.error(error); return res.status(500).send(error); }); // url = encodeURIComponent(url); // fetch(ApiAgentURL+'/sale/getLotList/'+url) // .then(response => response.json()) // .then(async data => { // console.log(data); // for (let lot of data) { // lot.sale_id = Sale._id // await lotDb.post(lot); // } // res.status(200).send({"message": "Lots created"}) // }) // .catch(error => { // console.error(error); // return res.status(500).send(error); // }); }catch(err){ console.error(err); return res.status(500).send(err); } }); exports.followSale = asyncHandler(async (req, res, next) => { try{ const id = req.params.id; agent.followSale(id) .then(data => { res.status(200).send(data); }) .catch(error => { console.error(error); return res.status(500).send(error); }); }catch(err){ console.error(err); return res.status(500).send(err); } }); // DB exports.get = asyncHandler(async (req, res, next) => { try{ const id = req.params.id; let result = await saleDb.get(id); res.status(200).send(result); }catch(err){ console.log(err); return res.status(500).send(err); } }); exports.post = asyncHandler(async (req, res, next) => { try{ // check if double let Sale = await saleDb.getByIDPlatform(req.body.idPlatform, req.body.platform); if(Sale){ return res.status(500).send("Sale already exists"); } let createData = await saleDb.post(req.body); console.log(createData.insertedId); const NowParis = moment.tz(new Date(),"Europe/Paris") // Scheduling the Prepare job const dateSaleMinus24Hours = moment.tz(req.body.date, "Europe/Paris").subtract(24, 'hours'); if(dateSaleMinus24Hours.isAfter(NowParis)){ const jobPrepare = agenda.create('prepareSale', { saleId: createData.insertedId }); jobPrepare.schedule(dateSaleMinus24Hours.toDate()); await jobPrepare.save(); }else{ console.log("Sale is less than 24 hours away, no Prepare Job");} // Scheduling the Follow job const dateSale = moment.tz(req.body.date, "Europe/Paris"); if(dateSale.isAfter(NowParis)){ const jobFollow = agenda.create('followSale', { saleId: createData.insertedId }); jobFollow.schedule(dateSale.toDate()); await jobFollow.save(); }else{ console.log("Sale is in the past, no Follow Job");} res.status(204).send(); }catch(err){ console.log(err); return res.status(500).send(err); } }); exports.put = asyncHandler(async (req, res, next) => { try{ const id = req.params.id; let updatedDocument = { ...req.body }; delete updatedDocument._id; console.log(updatedDocument); let result = await saleDb.put(id, updatedDocument); console.log(result); res.status(200).send(result); }catch(err){ console.log(err); return res.status(500).send(err); } }); exports.delete = asyncHandler(async (req, res, next) => { try{ const id = req.params.id; // Remove all lots linked to the sale console.log("Deleting lots sale_id: "+id); await lotDb.deleteAllLotBySaleId(id); // Remove the sale await saleDb.remove(id); //remove the Jobs const JobSale = await agenda.jobs({ 'data.saleId': new ObjectId(id) }); for (const job of JobSale) { await job.remove(); } res.status(200).send({"message": "Sale and Lots deleted"}); }catch(err){ console.log(err); return res.status(500).send(err); } }); // Fucntions exports.getAll = asyncHandler(async (req, res, next) => { try{ let result = await saleDb.getAll(); res.status(200).send(result); }catch(err){ console.log(err); return res.status(500).send(err); } }); exports.getByUrl = asyncHandler(async (req, res, next) => { try{ let url = req.params.url url = decodeURIComponent(url); let result = await saleDb.getByUrl(url); //console.log(result); res.status(200).send(result); }catch(err){ console.log(err); return res.status(500).send(err); } }); exports.postProcessing = asyncHandler(async (req, res, next) => { try{ const id = req.params.id; Sale = await saleDb.get(id); if(!Sale){ console.error("Sale not found"); return res.status(404).send("Sale not found"); } Lots = await lotDb.getBySaleId(Sale._id.toString(),Sale.platform); TimestampInSecond = (timestamp) => { const stringTimestamp = String(timestamp); if (stringTimestamp.length === 13) { return timestamp / 1000; } else if (stringTimestamp.length === 10) { return timestamp; } else { return 0; } } // Create an array to hold the updated lots let updatedLots = []; let bidsDuration = 0; // process each lot for (let lot of Lots) { let highestBid, duration, percentageAboveUnderLow, percentageAboveUnderHigh = 0; // if bid let nbrBids = 0; if (Array.isArray(lot.Bids)) { nbrBids = lot.Bids.length; highestBid = lot.Bids.reduce((prev, current) => (prev.amount > current.amount) ? prev : current).amount; let startTime = TimestampInSecond(lot.Bids[0].timestamp); let endTime = TimestampInSecond(lot.Bids[lot.Bids.length-1].timestamp); duration = endTime - startTime; // total time of bids bidsDuration += duration; duration = duration.toFixed(0); } // if auctioned percentageAboveUnderLow = 0; percentageAboveUnderHigh = 0; if (lot.auctioned) { if(lot.EstimateLow){ percentageAboveUnderLow = ((lot.auctioned.amount - lot.EstimateLow) / lot.EstimateLow) * 100; } if(lot.EstimateHigh){ percentageAboveUnderHigh = ((lot.auctioned.amount - lot.EstimateHigh) / lot.EstimateHigh) * 100; } } let lotPostProcessing = { nbrBids: nbrBids, highestBid: highestBid, duration: duration, percentageAboveUnderLow: percentageAboveUnderLow.toFixed(0), percentageAboveUnderHigh: percentageAboveUnderHigh.toFixed(0) } lot.postProcessing = lotPostProcessing; await lotDb.put(lot._id, lot); // Add the updated lot to the array updatedLots.push(lot); } // refresh with postprocess datas Lots = updatedLots; let startTime = 0; if (Array.isArray(Lots[0].Bids)) { startTime = TimestampInSecond(Lots[0].Bids[0].timestamp); }else{ startTime = TimestampInSecond(Lots[0].timestamp); } let LastBid = [...Lots].reverse().find(lot => lot.auctioned !== undefined); let endTime = 0; if (Array.isArray(LastBid.Bids)) { endTime = TimestampInSecond(LastBid.Bids[LastBid.Bids.length-1].timestamp); }else{ endTime = TimestampInSecond(LastBid.timestamp); } console.log("Start Time: "+startTime); console.log("End Time: "+endTime); let duration = (endTime-startTime).toFixed(0); let totalAmount = 0; let unsoldLots = 0; for (let lot of Lots) { if (lot.auctioned) { totalAmount += lot.auctioned.amount; } else { unsoldLots++; } } function calculateMedian(array) { array.sort((a, b) => a - b); let middleIndex = Math.floor(array.length / 2); if (array.length % 2 === 0) { // array has an even length return (array[middleIndex - 1] + array[middleIndex]) / 2; } else { // array has an odd length return array[middleIndex]; } } const amounts = Lots.map(lot => lot.auctioned?.amount).filter(Boolean); //console.error(Lots); let postProcessing = { nbrLots: Lots.length, duration: duration, bidsDuration: bidsDuration.toFixed(0), durationPerLots: (duration/Lots.length).toFixed(0), totalAmount: totalAmount, averageAmount: (totalAmount/Lots.length).toFixed(2), medianAmount: calculateMedian(amounts).toFixed(2), minAmount: Math.min(...amounts).toFixed(2), maxAmount: Math.max(...amounts).toFixed(2), unsoldLots: unsoldLots, unsoldPercentage: ((unsoldLots/Lots.length)*100).toFixed(2) } console.log(postProcessing); Sale.postProcessing = postProcessing; await saleDb.put(Sale._id, Sale); res.status(200).send({"message": "Post Processing done"}); }catch(err){ console.log(err); return res.status(500).send(err); } }); exports.SaleStatXsl = asyncHandler(async (req, res, next) => { try{ const id = req.params.id; Sale = await saleDb.get(id); if(!Sale){ console.error("Sale not found"); return res.status(404).send("Sale not found"); } Lots = await lotDb.getBySaleId(Sale._id.toString(),Sale.platform); const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet('Sale Stats'); worksheet.columns = [ { header: 'Lot #', key: 'lotNumber', width: 10 }, { header: 'Description', key: 'description', width: 100 }, { header: 'Auctioned Amount', key: 'auctionedAmount', width: 20 }, { header: 'Estimate Low', key: 'estimateLow', width: 20 }, { header: 'Estimate High', key: 'estimateHigh', width: 20 }, { header: 'Bids', key: 'nbrBids', width: 10 }, { header: 'Highest Bid', key: 'highestBid', width: 20 }, { header: 'Duration (in s)', key: 'duration', width: 10 }, { header: '% Above Low', key: 'percentageAboveUnderLow', width: 20 }, { header: '% Above High', key: 'percentageAboveUnderHigh', width: 20 } ]; let row = 2; for (let lot of Lots) { let Row = worksheet.addRow({ lotNumber: lot.lotNumber, description: lot.description, auctionedAmount: lot.auctioned?.amount, estimateLow: lot.EstimateLow, estimateHigh: lot.EstimateHigh, nbrBids: lot.postProcessing?.nbrBids, highestBid: lot.postProcessing?.highestBid, duration: lot.postProcessing?.duration, percentageAboveUnderLow: lot.postProcessing?.percentageAboveUnderLow/100, percentageAboveUnderHigh: lot.postProcessing?.percentageAboveUnderHigh/100 }); Row.getCell('C').numFmt = '€0.00'; Row.getCell('D').numFmt = '€0.00'; Row.getCell('E').numFmt = '€0.00'; Row.getCell('B').numFmt = '€0.00'; Row.getCell('G').numFmt = '€0.00'; Row.getCell('I').numFmt = '0%'; Row.getCell('J').numFmt = '0%'; row++; } worksheet.addRow({ lotNumber: 'Total', auctionedAmount: Sale.postProcessing?.totalAmount, estimateLow: '', estimateHigh: '', nbrBids: '', highestBid: '', duration: Sale.postProcessing?.duration, percentageAboveUnderLow: '', percentageAboveUnderHigh: '' }); // send the Xls File res.setHeader( "Content-Disposition", "attachment; filename=" + "SaleStats.xlsx" ); await workbook.xlsx.write(res); return res.status(200).end(); }catch(err){ console.log(err); return res.status(500).send } });