Get/add/update/delete winners from lottery.
This commit is contained in:
		
							
								
								
									
										101
									
								
								api/controllers/lotteryWinnerController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								api/controllers/lotteryWinnerController.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| const path = require("path"); | ||||
| const lotteryRepository = require(path.join(__dirname, "../lottery")); | ||||
|  | ||||
| const allWinners = (req, res) => { | ||||
|   const isAdmin = req.isAuthenticated() || true; | ||||
|  | ||||
|   return lotteryRepository | ||||
|     .allWinners(isAdmin) | ||||
|     .then(winners => | ||||
|       res.send({ | ||||
|         winners: winners, | ||||
|         success: true | ||||
|       }) | ||||
|     ) | ||||
|     .catch(error => { | ||||
|       const { statusCode, message } = error; | ||||
|  | ||||
|       return res.status(statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: message || "Unable to fetch lottery winners." | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const winnerById = (req, res) => { | ||||
|   const { id } = req.params; | ||||
|  | ||||
|   return lotteryRepository | ||||
|     .winnerById(id) | ||||
|     .then(winner => | ||||
|       res.send({ | ||||
|         winner, | ||||
|         success: true | ||||
|       }) | ||||
|     ) | ||||
|     .catch(error => { | ||||
|       const { statusCode, message } = error; | ||||
|  | ||||
|       return res.status(statusCode || 500).send({ | ||||
|         message: message || "Unexpected error occured, unable to fetch winner by id.", | ||||
|         success: false | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const deleteWinnerById = (req, res) => { | ||||
|   const isAdmin = req.isAuthenticated() || true; | ||||
|   const { id } = req.params; | ||||
|  | ||||
|   return lotteryRepository | ||||
|     .deleteWinnerById(id, isAdmin) | ||||
|     .then(removedWinner => { | ||||
|       var io = req.app.get("socketio"); | ||||
|       io.emit("refresh_data", {}); | ||||
|       return removedWinner; | ||||
|     }) | ||||
|     .then(winner => | ||||
|       res.send({ | ||||
|         message: `Removed winner: ${winner.name}`, | ||||
|         success: true | ||||
|       }) | ||||
|     ) | ||||
|     .catch(error => { | ||||
|       const { statusCode, message } = error; | ||||
|  | ||||
|       return res.status(statusCode || 500).send({ | ||||
|         message: message || "Unexpected error occured while deleteing wine by id.", | ||||
|         success: false | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const deleteWinners = (req, res) => { | ||||
|   return lotteryRepository | ||||
|     .deleteWinners() | ||||
|     .then(_ => { | ||||
|       var io = req.app.get("socketio"); | ||||
|       io.emit("refresh_data", {}); | ||||
|     }) | ||||
|     .then(_ => | ||||
|       res.send({ | ||||
|         message: "Removed all winners.", | ||||
|         success: true | ||||
|       }) | ||||
|     ) | ||||
|     .catch(error => { | ||||
|       const { statusCode, message } = error; | ||||
|  | ||||
|       return res.status(statusCode || 500).send({ | ||||
|         message: message || "Unexpected error occured while deleting wines", | ||||
|         success: false | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| module.exports = { | ||||
|   allWinners, | ||||
|   winnerById, | ||||
|   deleteWinnerById, | ||||
|   deleteWinners | ||||
| }; | ||||
							
								
								
									
										236
									
								
								api/lottery.js
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								api/lottery.js
									
									
									
									
									
								
							| @@ -1,6 +1,10 @@ | ||||
| const path = require("path"); | ||||
|  | ||||
| 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 crypto = require("crypto"); | ||||
|  | ||||
| class UserNotFound extends Error { | ||||
|   constructor(message = "User not found.") { | ||||
| @@ -22,6 +26,33 @@ class WineNotFound extends Error { | ||||
|   // TODO log missing user | ||||
| } | ||||
|  | ||||
| class WinnerNotFound extends Error { | ||||
|   constructor(message = "Winner not found.") { | ||||
|     super(message); | ||||
|     this.name = "WinnerNotFound"; | ||||
|     this.statusCode = 404; | ||||
|   } | ||||
|  | ||||
|   // TODO log missing user | ||||
| } | ||||
|  | ||||
| class NoMoreAttendeesToWinError extends Error { | ||||
|   constructor(message = "No more attendees left to drawn from.") { | ||||
|     super(message); | ||||
|     this.name = "NoMoreAttendeesToWinError"; | ||||
|     this.statusCode = 404; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class CouldNotFindNewWinnerAfterNTriesError extends Error { | ||||
|   constructor(tries) { | ||||
|     let message = `Could not a new winner after ${tries} tries.`; | ||||
|     super(message); | ||||
|     this.name = "CouldNotFindNewWinnerAfterNTriesError"; | ||||
|     this.statusCode = 404; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const redactAttendeeInfoMapper = attendee => { | ||||
|   return { | ||||
|     name: attendee.name, | ||||
| @@ -33,6 +64,13 @@ const redactAttendeeInfoMapper = attendee => { | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| const redactWinnerInfoMapper = winner => { | ||||
|   return { | ||||
|     name: winner.name, | ||||
|     color: winner.color | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| const allAttendees = isAdmin => { | ||||
|   if (!isAdmin) { | ||||
|     return Attendee.find().then(attendees => attendees.map(redactAttendeeInfoMapper)); | ||||
| @@ -63,15 +101,20 @@ const updateAttendeeById = (id, updateModel) => { | ||||
|       throw new UserNotFound(); | ||||
|     } | ||||
|  | ||||
|     console.log(updateModel); | ||||
|  | ||||
|     const updatedAttendee = { | ||||
|       name: updateModel.name || attendee.name, | ||||
|       green: updateModel.green || attendee.green, | ||||
|       red: updateModel.red || attendee.red, | ||||
|       blue: updateModel.blue || attendee.blue, | ||||
|       yellow: updateModel.yellow || attendee.yellow, | ||||
|       phoneNumber: updateModel.phoneNumber || attendee.phoneNumber | ||||
|       name: updateModel.name != null ? updateModel.name : attendee.name, | ||||
|       green: updateModel.green != null ? updateModel.green : attendee.green, | ||||
|       red: updateModel.red != null ? updateModel.red : attendee.red, | ||||
|       blue: updateModel.blue != null ? updateModel.blue : attendee.blue, | ||||
|       yellow: updateModel.yellow != null ? updateModel.yellow : attendee.yellow, | ||||
|       phoneNumber: updateModel.phoneNumber != null ? updateModel.phoneNumber : attendee.phoneNumber, | ||||
|       winner: updateModel.winner != null ? updateModel.winner : attendee.winner | ||||
|     }; | ||||
|  | ||||
|     console.log(updatedAttendee); | ||||
|  | ||||
|     return Attendee.updateOne({ _id: id }, updatedAttendee).then(_ => updatedAttendee); | ||||
|   }); | ||||
| }; | ||||
| @@ -119,13 +162,13 @@ const updateWineById = (id, updateModel) => { | ||||
|     } | ||||
|  | ||||
|     const updatedWine = { | ||||
|       name: updateModel.name || wine.name, | ||||
|       vivinoLink: updateModel.vivinoLink || wine.vivinoLink, | ||||
|       rating: updateModel.rating || wine.rating, | ||||
|       image: updateModel.image || wine.image, | ||||
|       price: updateModel.price || wine.price, | ||||
|       country: updateModel.country || wine.country, | ||||
|       id: updateModel.id || wine.id | ||||
|       name: updateModel.name != null ? updateModel.name : wine.name, | ||||
|       vivinoLink: updateModel.vivinoLink != null ? updateModel.vivinoLink : wine.vivinoLink, | ||||
|       rating: updateModel.rating != null ? updateModel.rating : wine.rating, | ||||
|       image: updateModel.image != null ? updateModel.image : wine.image, | ||||
|       price: updateModel.price != null ? updateModel.price : wine.price, | ||||
|       country: updateModel.country != null ? updateModel.country : wine.country, | ||||
|       id: updateModel.id != null ? updateModel.id : wine.id | ||||
|     }; | ||||
|  | ||||
|     return PreLotteryWine.updateOne({ _id: id }, updatedWine).then(_ => updatedWine); | ||||
| @@ -146,6 +189,166 @@ const deleteWines = () => { | ||||
|   return PreLotteryWine.deleteMany(); | ||||
| }; | ||||
|  | ||||
| const allWinners = isAdmin => { | ||||
|   if (!isAdmin) { | ||||
|     return VirtualWinner.find().then(winners => winners.map(redactWinnerInfoMapper)); | ||||
|   } else { | ||||
|     return VirtualWinner.find(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const winnerById = (id, isAdmin) => { | ||||
|   return VirtualWinner.findOne({ _id: id }).then(winner => { | ||||
|     if (winner == null) { | ||||
|       throw new WinnerNotFound(); | ||||
|     } | ||||
|  | ||||
|     if (!isAdmin) { | ||||
|       return redactWinnerInfoMapper(winner); | ||||
|     } else { | ||||
|       return winner; | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const deleteWinnerById = id => { | ||||
|   return VirtualWinner.findOne({ _id: id }).then(winner => { | ||||
|     if (winner == null) { | ||||
|       throw new WinnerNotFound(); | ||||
|     } | ||||
|  | ||||
|     return VirtualWinner.deleteOne({ _id: id }).then(_ => winner); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const deleteWinners = () => { | ||||
|   return VirtualWinner.deleteMany(); | ||||
| }; | ||||
|  | ||||
| const drawWinner = async () => { | ||||
|   let allContestants = await Attendee.find({ winner: false }); | ||||
|  | ||||
|   if (allContestants.length == 0) { | ||||
|     throw new NoMoreAttendeesToWinError(); | ||||
|   } | ||||
|  | ||||
|   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 CouldNotFindNewWinnerAfterNTriesError(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 winners = await VirtualWinner.find({ timestamp_sent: undefined }).sort({ | ||||
|     timestamp_drawn: 1 | ||||
|   }); | ||||
|  | ||||
|   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 } }); | ||||
|  | ||||
|   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 = { | ||||
|   allAttendees, | ||||
|   addAttendee, | ||||
| @@ -156,5 +359,10 @@ module.exports = { | ||||
|   addWines, | ||||
|   updateWineById, | ||||
|   deleteWineById, | ||||
|   deleteWines | ||||
|   deleteWines, | ||||
|   allWinners, | ||||
|   winnerById, | ||||
|   deleteWinnerById, | ||||
|   deleteWinners, | ||||
|   drawWinner | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user