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 path = require("path"); | ||||||
|  |  | ||||||
| const Attendee = require(path.join(__dirname, "/schemas/Attendee")); | const Attendee = require(path.join(__dirname, "/schemas/Attendee")); | ||||||
| const PreLotteryWine = require(path.join(__dirname, "/schemas/PreLotteryWine")); | const PreLotteryWine = require(path.join(__dirname, "/schemas/PreLotteryWine")); | ||||||
|  | const VirtualWinner = require(path.join(__dirname, "/schemas/VirtualWinner")); | ||||||
|  |  | ||||||
|  | const crypto = require("crypto"); | ||||||
|  |  | ||||||
| class UserNotFound extends Error { | class UserNotFound extends Error { | ||||||
|   constructor(message = "User not found.") { |   constructor(message = "User not found.") { | ||||||
| @@ -22,6 +26,33 @@ class WineNotFound extends Error { | |||||||
|   // TODO log missing user |   // 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 => { | const redactAttendeeInfoMapper = attendee => { | ||||||
|   return { |   return { | ||||||
|     name: attendee.name, |     name: attendee.name, | ||||||
| @@ -33,6 +64,13 @@ const redactAttendeeInfoMapper = attendee => { | |||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | const redactWinnerInfoMapper = winner => { | ||||||
|  |   return { | ||||||
|  |     name: winner.name, | ||||||
|  |     color: winner.color | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  |  | ||||||
| const allAttendees = isAdmin => { | const allAttendees = isAdmin => { | ||||||
|   if (!isAdmin) { |   if (!isAdmin) { | ||||||
|     return Attendee.find().then(attendees => attendees.map(redactAttendeeInfoMapper)); |     return Attendee.find().then(attendees => attendees.map(redactAttendeeInfoMapper)); | ||||||
| @@ -63,15 +101,20 @@ const updateAttendeeById = (id, updateModel) => { | |||||||
|       throw new UserNotFound(); |       throw new UserNotFound(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     console.log(updateModel); | ||||||
|  |  | ||||||
|     const updatedAttendee = { |     const updatedAttendee = { | ||||||
|       name: updateModel.name || attendee.name, |       name: updateModel.name != null ? updateModel.name : attendee.name, | ||||||
|       green: updateModel.green || attendee.green, |       green: updateModel.green != null ? updateModel.green : attendee.green, | ||||||
|       red: updateModel.red || attendee.red, |       red: updateModel.red != null ? updateModel.red : attendee.red, | ||||||
|       blue: updateModel.blue || attendee.blue, |       blue: updateModel.blue != null ? updateModel.blue : attendee.blue, | ||||||
|       yellow: updateModel.yellow || attendee.yellow, |       yellow: updateModel.yellow != null ? updateModel.yellow : attendee.yellow, | ||||||
|       phoneNumber: updateModel.phoneNumber || attendee.phoneNumber |       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); |     return Attendee.updateOne({ _id: id }, updatedAttendee).then(_ => updatedAttendee); | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| @@ -119,13 +162,13 @@ const updateWineById = (id, updateModel) => { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const updatedWine = { |     const updatedWine = { | ||||||
|       name: updateModel.name || wine.name, |       name: updateModel.name != null ? updateModel.name : wine.name, | ||||||
|       vivinoLink: updateModel.vivinoLink || wine.vivinoLink, |       vivinoLink: updateModel.vivinoLink != null ? updateModel.vivinoLink : wine.vivinoLink, | ||||||
|       rating: updateModel.rating || wine.rating, |       rating: updateModel.rating != null ? updateModel.rating : wine.rating, | ||||||
|       image: updateModel.image || wine.image, |       image: updateModel.image != null ? updateModel.image : wine.image, | ||||||
|       price: updateModel.price || wine.price, |       price: updateModel.price != null ? updateModel.price : wine.price, | ||||||
|       country: updateModel.country || wine.country, |       country: updateModel.country != null ? updateModel.country : wine.country, | ||||||
|       id: updateModel.id || wine.id |       id: updateModel.id != null ? updateModel.id : wine.id | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     return PreLotteryWine.updateOne({ _id: id }, updatedWine).then(_ => updatedWine); |     return PreLotteryWine.updateOne({ _id: id }, updatedWine).then(_ => updatedWine); | ||||||
| @@ -146,6 +189,166 @@ const deleteWines = () => { | |||||||
|   return PreLotteryWine.deleteMany(); |   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 = { | module.exports = { | ||||||
|   allAttendees, |   allAttendees, | ||||||
|   addAttendee, |   addAttendee, | ||||||
| @@ -156,5 +359,10 @@ module.exports = { | |||||||
|   addWines, |   addWines, | ||||||
|   updateWineById, |   updateWineById, | ||||||
|   deleteWineById, |   deleteWineById, | ||||||
|   deleteWines |   deleteWines, | ||||||
|  |   allWinners, | ||||||
|  |   winnerById, | ||||||
|  |   deleteWinnerById, | ||||||
|  |   deleteWinners, | ||||||
|  |   drawWinner | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user