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
 | 
						|
};
 |