forked from winsleague/winsleague
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/185 top upcoming games (winsleague#206)
* renaming * renaming * initial framework and top picks calculator * tests for top picks interest calculator * fix home page test. need to figure out why this passed CI! * fix test * logging * added email template and route * some refactoring plus owners close in standings calculator * don't use close in standings if there aren't enough games played * initial league team recent wins calculator * remove logging * tweaked numbers * renaming and more tests * refactored email template sending * fix template reference * tweak ratings algo * tweaked send time so it's farther from upcoming games mail * test fix * test fix * test fix * fix tests * release spy * better date formatting
- Loading branch information
Showing
21 changed files
with
729 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Meteor } from 'meteor/meteor'; | ||
import { _ } from 'lodash'; | ||
|
||
import { PoolTeams } from '../../pool_teams/pool_teams'; | ||
|
||
export default { | ||
getPlayerEmails(poolId, seasonId) { | ||
// each userId can have multiple emails | ||
// each email has an address property and a verified property | ||
// return in the format 'name <[email protected]>, name <[email protected]>' | ||
|
||
const players = PoolTeams.find({ poolId, seasonId }) | ||
.map(poolTeam => { | ||
return { | ||
_id: poolTeam.userId, | ||
teamName: poolTeam.userTeamName, | ||
}; | ||
}); | ||
|
||
const emailArray = _.flatten(players.map(player => { | ||
return _.flatten(Meteor.users.find(player._id) | ||
.map(user => user.emails)) | ||
.map(email => `${player.teamName} <${email.address}>`); | ||
})); | ||
|
||
return emailArray.join(', '); | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,47 @@ | ||
import log from '../../../utils/log'; | ||
import { _ } from 'lodash'; | ||
import { Mailer } from 'meteor/lookback:emails'; | ||
|
||
import log from '../../../utils/log'; | ||
|
||
import Common from './common'; | ||
import { Seasons } from '../../seasons/seasons'; | ||
import { Pools } from '../../pools/pools'; | ||
import { PoolTeams } from '../../pool_teams/pool_teams'; | ||
|
||
export default { | ||
emailReports() { | ||
log.info('Sending out weekly email report'); | ||
sendAll() { | ||
log.info('Sending out weekly leaderboard email'); | ||
|
||
const seasons = this.findActiveSeasons(); | ||
seasons.forEach(season => { | ||
const poolIds = this.findEligiblePoolIds(season._id); | ||
poolIds.forEach(poolId => { | ||
this.emailReport(poolId, season._id); | ||
this.sendIndividual(poolId, season._id); | ||
}); | ||
}); | ||
}, | ||
|
||
emailReport(poolId, seasonId) { | ||
log.info(`Emailing weekly report to pool ${poolId} for season ${seasonId}`); | ||
sendIndividual(poolId, seasonId) { | ||
log.info(`Emailing weekly leaderboard report to pool ${poolId}`); | ||
|
||
const pool = Pools.findOne(poolId); | ||
const poolName = pool.name; | ||
const poolTeams = PoolTeams.find({ poolId, seasonId }, | ||
{ sort: { totalWins: -1, totalPlusMinus: -1 } }); | ||
const poolTeams = PoolTeams.find({ | ||
poolId, | ||
seasonId, | ||
}, { | ||
sort: { | ||
totalWins: -1, | ||
totalPlusMinus: -1, | ||
}, | ||
}); | ||
|
||
const playerEmails = this.getPlayerEmails(poolId, seasonId); | ||
const playerEmails = Common.getPlayerEmails(poolId, seasonId); | ||
|
||
Mailer.send({ | ||
to: playerEmails, | ||
subject: `Wins Leaderboard for ${poolName}`, | ||
template: 'weeklyEmail', | ||
template: 'weeklyLeaderboardTemplate', | ||
data: { | ||
poolId, | ||
seasonId, | ||
|
@@ -70,26 +79,4 @@ export default { | |
]); | ||
return poolAggregation.map(result => result._id); | ||
}, | ||
|
||
getPlayerEmails(poolId, seasonId) { | ||
// each userId can have multiple emails | ||
// each email has an address property and a verified property | ||
// return in the format 'name <[email protected]>, name <[email protected]>' | ||
|
||
const players = PoolTeams.find({ poolId, seasonId }) | ||
.map(poolTeam => { | ||
return { | ||
_id: poolTeam.userId, | ||
teamName: poolTeam.userTeamName, | ||
}; | ||
}); | ||
|
||
const emailArray = _.flatten(players.map(player => { | ||
return _.flatten(Meteor.users.find(player._id) | ||
.map(user => user.emails)) | ||
.map(email => `${player.teamName} <${email.address}>`); | ||
})); | ||
|
||
return emailArray.join(', '); | ||
}, | ||
}; |
91 changes: 91 additions & 0 deletions
91
app/imports/api/emails/server/weekly-top-upcoming-games.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { Mailer } from 'meteor/lookback:emails'; | ||
import moment from 'moment'; | ||
|
||
import log from '../../../utils/log'; | ||
|
||
import RatingCalculator from '../../pool_game_interest_ratings/server/calculator'; | ||
import Common from './common'; | ||
import LeagueFinder from '../../leagues/finder'; | ||
import { Games } from '../../games/games'; | ||
import { Pools } from '../../pools/pools'; | ||
import { PoolGameInterestRatings } from '../../pool_game_interest_ratings/pool_game_interest_ratings'; | ||
|
||
export default { | ||
sendAll() { | ||
log.info('Sending out weekly top upcoming games email'); | ||
|
||
this.calculateAllInterestRatings(); | ||
|
||
this.nflPools().forEach(pool => { | ||
this.sendPoolEmail(pool); | ||
}); | ||
}, | ||
|
||
calculateAllInterestRatings() { | ||
this.nflPools().forEach(pool => { | ||
this.calculatePoolInterestRatings(pool); | ||
}); | ||
}, | ||
|
||
calculatePoolInterestRatings(pool) { | ||
this.upcomingGames().forEach(game => { | ||
RatingCalculator.calculate(pool, game); | ||
}); | ||
}, | ||
|
||
upcomingGames() { | ||
const leagueId = LeagueFinder.getIdByName('NFL'); | ||
|
||
const startDate = moment().toDate(); | ||
const endDate = moment().add(7, 'days').toDate(); | ||
|
||
log.info(`Upcoming games from ${startDate} to ${endDate}`); | ||
|
||
return Games.find({ | ||
leagueId, | ||
gameDate: { | ||
$gte: startDate, | ||
$lte: endDate, | ||
}, | ||
}); | ||
}, | ||
|
||
nflPools() { | ||
const leagueId = LeagueFinder.getIdByName('NFL'); | ||
|
||
return Pools.find({ leagueId }); | ||
}, | ||
|
||
sendPoolEmail(pool) { | ||
log.info(`Emailing top upcoming games email to pool ${pool._id} for season ${pool.latestSeasonId}`); | ||
|
||
const poolId = pool._id; | ||
const poolName = pool.name; | ||
const poolGameInterestRatings = PoolGameInterestRatings.find( | ||
{ | ||
poolId: pool._id, | ||
rating: { | ||
$gte: 50, | ||
}, | ||
}, | ||
{ | ||
sort: { | ||
rating: -1, | ||
}, | ||
limit: 5, | ||
}); | ||
|
||
const playerEmails = this.getPlayerEmails(poolId, pool.latestSeasonId); | ||
|
||
Mailer.send({ | ||
to: playerEmails, | ||
subject: `Top Upcoming Games for ${poolName}`, | ||
template: 'weeklyTopUpcomingGamesTemplate', | ||
data: { | ||
poolId, | ||
poolName, | ||
poolGameInterestRatings, | ||
}, | ||
}); | ||
}, | ||
}; |
87 changes: 87 additions & 0 deletions
87
app/imports/api/pool_game_interest_ratings/pool_game_interest_ratings.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { Mongo } from 'meteor/mongo'; | ||
import { SimpleSchema } from 'meteor/aldeed:simple-schema'; | ||
import { Factory } from 'meteor/dburles:factory'; | ||
import { Random } from 'meteor/random'; | ||
import faker from 'faker'; | ||
import moment from 'moment-timezone'; | ||
import log from '../../utils/log'; | ||
|
||
import { Games } from '../games/games'; | ||
|
||
export const PoolGameInterestRatings = new Mongo.Collection('pool_game_interest_ratings'); | ||
|
||
PoolGameInterestRatings.schema = new SimpleSchema({ | ||
poolId: { | ||
type: String, | ||
regEx: SimpleSchema.RegEx.Id, | ||
}, | ||
|
||
gameId: { | ||
type: String, | ||
regEx: SimpleSchema.RegEx.Id, | ||
}, | ||
|
||
rating: { | ||
type: Number, | ||
}, | ||
|
||
gameTitle: { | ||
type: String, | ||
}, | ||
|
||
justification: { | ||
type: String, | ||
}, | ||
|
||
createdAt: { | ||
// Force value to be current date (on server) upon insert | ||
// and prevent updates thereafter. | ||
type: Date, | ||
autoValue() { | ||
if (this.isInsert) { | ||
return new Date(); | ||
} else if (this.isUpsert) { | ||
return { $setOnInsert: new Date() }; | ||
} | ||
this.unset(); // Prevent user from supplying their own value | ||
}, | ||
}, | ||
|
||
updatedAt: { | ||
// Force value to be current date (on server) upon update | ||
// and don't allow it to be set upon insert. | ||
type: Date, | ||
autoValue() { | ||
if (this.isUpdate) { | ||
return new Date(); | ||
} | ||
}, | ||
denyInsert: true, | ||
optional: true, | ||
}, | ||
}); | ||
|
||
PoolGameInterestRatings.attachSchema(PoolGameInterestRatings.schema); | ||
|
||
PoolGameInterestRatings.helpers({ | ||
gameTime() { | ||
const game = Games.findOne(this.gameId); | ||
const date = moment(game.gameDate).format('ddd,'); | ||
const est = moment(game.gameDate).tz('US/Eastern').format('ha zz'); | ||
const pst = moment(game.gameDate).tz('US/Pacific').format('ha zz'); | ||
return `${date} ${est}/${pst}`; | ||
}, | ||
}); | ||
|
||
// Deny all client-side updates since we will be using methods to manage this collection | ||
PoolGameInterestRatings.deny({ | ||
insert() { return true; }, | ||
update() { return true; }, | ||
remove() { return true; }, | ||
}); | ||
|
||
Factory.define('pool_game_interest_rating', PoolGameInterestRatings, { | ||
// TODO | ||
}).after(factory => { | ||
log.debug('pool game interest rating factory created:', factory); | ||
}); |
Oops, something went wrong.