If archiving a lottery, let archive-functionality circumvent SMS-service if the winners have been set on the archive page. - Also remove unused file
297 lines
7.9 KiB
JavaScript
297 lines
7.9 KiB
JavaScript
const path = require("path");
|
|
const crypto = require("crypto");
|
|
|
|
const Attendee = require(path.join(__dirname, "/schemas/Attendee"));
|
|
const PreLotteryWine = require(path.join(__dirname, "/schemas/PreLotteryWine"));
|
|
const VirtualWinner = require(path.join(__dirname, "/schemas/VirtualWinner"));
|
|
const Lottery = require(path.join(__dirname, "/schemas/Purchase"));
|
|
const { WineNotFound } = require(path.join(__dirname, "/vinlottisErrors"));
|
|
|
|
const Message = require(path.join(__dirname, "/message"));
|
|
const historyRepository = require(path.join(__dirname, "/history"));
|
|
const wineRepository = require(path.join(__dirname, "/wine"));
|
|
const winnerRepository = require(path.join(__dirname, "/winner"));
|
|
const prelotteryWineRepository = require(path.join(__dirname, "/prelotteryWine"));
|
|
|
|
const {
|
|
WinnerNotFound,
|
|
NoMoreAttendeesToWin,
|
|
CouldNotFindNewWinnerAfterNTries,
|
|
LotteryByDateNotFound
|
|
} = require(path.join(__dirname, "/vinlottisErrors"));
|
|
|
|
const moveUnfoundPrelotteryWineToWines = async (error, tempWine) => {
|
|
if(!(error instanceof WineNotFound)) {
|
|
throw error
|
|
}
|
|
|
|
if(!tempWine.winner) {
|
|
throw new WinnerNotFound()
|
|
}
|
|
|
|
const prelotteryWine = await prelotteryWineRepository.wineById(tempWine._id);
|
|
const winner = await winnerRepository.winnerById(tempWine.winner.id, true);
|
|
|
|
return wineRepository
|
|
.addWine(prelotteryWine)
|
|
.then(_ => prelotteryWineRepository.addWinnerToWine(prelotteryWine, winner)) // prelotteryWine.deleteById
|
|
.then(_ => historyRepository.addWinnerWithWine(winner, prelotteryWine))
|
|
.then(_ => winnerRepository.setWinnerChosenById(winner.id))
|
|
}
|
|
|
|
const archive = (date, raffles, stolen, wines) => {
|
|
const { blue, red, yellow, green } = raffles;
|
|
const bought = blue + red + yellow + green;
|
|
|
|
return Promise.all(
|
|
wines.map(wine => wineRepository
|
|
.findWine(wine)
|
|
.catch(error => moveUnfoundPrelotteryWineToWines(error, wine)
|
|
.then(_ => wineRepository.findWine(wine))
|
|
))
|
|
).then(resolvedWines => {
|
|
const lottery = new Lottery({
|
|
date,
|
|
blue,
|
|
red,
|
|
yellow,
|
|
green,
|
|
bought,
|
|
stolen,
|
|
wines: resolvedWines
|
|
});
|
|
|
|
return lottery.save();
|
|
});
|
|
};
|
|
|
|
const lotteryByDate = date => {
|
|
const startOfDay = new Date(date.setHours(0, 0, 0, 0));
|
|
const endOfDay = new Date(date.setHours(24, 59, 59, 99));
|
|
|
|
const query = [
|
|
{
|
|
$match: {
|
|
date: {
|
|
$gte: startOfDay,
|
|
$lte: endOfDay
|
|
}
|
|
}
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "wines",
|
|
localField: "wines",
|
|
foreignField: "_id",
|
|
as: "wines"
|
|
}
|
|
}
|
|
];
|
|
|
|
const aggregateLottery = Lottery.aggregate(query);
|
|
return aggregateLottery.project("-_id -__v").then(lotteries => {
|
|
if (lotteries.length == 0) {
|
|
throw new LotteryByDateNotFound(date);
|
|
}
|
|
return lotteries[0];
|
|
});
|
|
};
|
|
|
|
const allLotteries = (sort = "asc", yearFilter = undefined) => {
|
|
const sortDirection = sort == "asc" ? 1 : -1;
|
|
|
|
let startQueryDate = new Date("1970-01-01");
|
|
let endQueryDate = new Date("2999-01-01");
|
|
if (yearFilter) {
|
|
startQueryDate = new Date(`${yearFilter}-01-01`);
|
|
endQueryDate = new Date(`${Number(yearFilter) + 1}-01-01`);
|
|
}
|
|
|
|
const query = [
|
|
{
|
|
$match: {
|
|
date: {
|
|
$gte: startQueryDate,
|
|
$lte: endQueryDate
|
|
}
|
|
}
|
|
},
|
|
{
|
|
$sort: {
|
|
date: sortDirection
|
|
}
|
|
},
|
|
{
|
|
$unset: ["_id", "__v"]
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "wines",
|
|
localField: "wines",
|
|
foreignField: "_id",
|
|
as: "wines"
|
|
}
|
|
}
|
|
];
|
|
|
|
return Lottery.aggregate(query);
|
|
};
|
|
|
|
const allLotteriesIncludingWinners = async (sort = "asc", yearFilter = undefined) => {
|
|
const lotteries = await allLotteries(sort, yearFilter);
|
|
const allWinners = await historyRepository.groupByDate(false, sort);
|
|
|
|
return lotteries.map(lottery => {
|
|
const { winners } = allWinners.pop();
|
|
|
|
return {
|
|
wines: lottery.wines,
|
|
date: lottery.date,
|
|
blue: lottery.blue,
|
|
green: lottery.green,
|
|
yellow: lottery.yellow,
|
|
red: lottery.red,
|
|
bought: lottery.bought,
|
|
stolen: lottery.stolen,
|
|
winners: winners
|
|
};
|
|
});
|
|
};
|
|
|
|
const latestLottery = async () => {
|
|
return Lottery.findOne().sort({ date: -1 });
|
|
};
|
|
|
|
const drawWinner = async () => {
|
|
let allContestants = await Attendee.find({ winner: false });
|
|
|
|
if (allContestants.length == 0) {
|
|
throw new NoMoreAttendeesToWin();
|
|
}
|
|
|
|
let raffleColors = [];
|
|
for (let i = 0; i < allContestants.length; i++) {
|
|
let currentContestant = allContestants[i];
|
|
for (let blue = 0; blue < currentContestant.blue; blue++) {
|
|
raffleColors.push("blue");
|
|
}
|
|
for (let red = 0; red < currentContestant.red; red++) {
|
|
raffleColors.push("red");
|
|
}
|
|
for (let green = 0; green < currentContestant.green; green++) {
|
|
raffleColors.push("green");
|
|
}
|
|
for (let yellow = 0; yellow < currentContestant.yellow; yellow++) {
|
|
raffleColors.push("yellow");
|
|
}
|
|
}
|
|
|
|
raffleColors = shuffle(raffleColors);
|
|
|
|
let colorToChooseFrom = raffleColors[Math.floor(Math.random() * raffleColors.length)];
|
|
let findObject = { winner: false };
|
|
|
|
findObject[colorToChooseFrom] = { $gt: 0 };
|
|
|
|
let tries = 0;
|
|
const maxTries = 3;
|
|
let contestantsToChooseFrom = undefined;
|
|
while (contestantsToChooseFrom == undefined && tries < maxTries) {
|
|
const hit = await Attendee.find(findObject);
|
|
if (hit && hit.length) {
|
|
contestantsToChooseFrom = hit;
|
|
break;
|
|
}
|
|
tries++;
|
|
}
|
|
if (contestantsToChooseFrom == undefined) {
|
|
throw new CouldNotFindNewWinnerAfterNTries(maxTries);
|
|
}
|
|
|
|
let attendeeListDemocratic = [];
|
|
|
|
let currentContestant;
|
|
for (let i = 0; i < contestantsToChooseFrom.length; i++) {
|
|
currentContestant = contestantsToChooseFrom[i];
|
|
for (let y = 0; y < currentContestant[colorToChooseFrom]; y++) {
|
|
attendeeListDemocratic.push({
|
|
name: currentContestant.name,
|
|
phoneNumber: currentContestant.phoneNumber,
|
|
red: currentContestant.red,
|
|
blue: currentContestant.blue,
|
|
green: currentContestant.green,
|
|
yellow: currentContestant.yellow
|
|
});
|
|
}
|
|
}
|
|
|
|
attendeeListDemocratic = shuffle(attendeeListDemocratic);
|
|
|
|
let winner = attendeeListDemocratic[Math.floor(Math.random() * attendeeListDemocratic.length)];
|
|
|
|
let newWinnerElement = new VirtualWinner({
|
|
name: winner.name,
|
|
phoneNumber: winner.phoneNumber,
|
|
color: colorToChooseFrom,
|
|
red: winner.red,
|
|
blue: winner.blue,
|
|
green: winner.green,
|
|
yellow: winner.yellow,
|
|
id: sha512(winner.phoneNumber, genRandomString(10)),
|
|
timestamp_drawn: new Date().getTime()
|
|
});
|
|
|
|
await newWinnerElement.save();
|
|
await Attendee.updateOne({ name: winner.name, phoneNumber: winner.phoneNumber }, { $set: { winner: true } });
|
|
|
|
let winners = await VirtualWinner.find({ timestamp_sent: undefined }).sort({
|
|
timestamp_drawn: 1
|
|
});
|
|
|
|
return { winner, color: colorToChooseFrom, winners };
|
|
};
|
|
|
|
/** - - UTILS - - **/
|
|
const genRandomString = function(length) {
|
|
return crypto
|
|
.randomBytes(Math.ceil(length / 2))
|
|
.toString("hex") /** convert to hexadecimal format */
|
|
.slice(0, length); /** return required number of characters */
|
|
};
|
|
|
|
const sha512 = function(password, salt) {
|
|
var hash = crypto.createHmac("md5", salt); /** Hashing algorithm sha512 */
|
|
hash.update(password);
|
|
var value = hash.digest("hex");
|
|
return value;
|
|
};
|
|
|
|
function shuffle(array) {
|
|
let currentIndex = array.length,
|
|
temporaryValue,
|
|
randomIndex;
|
|
|
|
// While there remain elements to shuffle...
|
|
while (0 !== currentIndex) {
|
|
// Pick a remaining element...
|
|
randomIndex = Math.floor(Math.random() * currentIndex);
|
|
currentIndex -= 1;
|
|
|
|
// And swap it with the current element.
|
|
temporaryValue = array[currentIndex];
|
|
array[currentIndex] = array[randomIndex];
|
|
array[randomIndex] = temporaryValue;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
module.exports = {
|
|
drawWinner,
|
|
archive,
|
|
lotteryByDate,
|
|
allLotteries,
|
|
allLotteriesIncludingWinners,
|
|
latestLottery
|
|
};
|