440 lines
13 KiB
JavaScript
440 lines
13 KiB
JavaScript
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
|
|
}
|
|
}); |