optimzations for dashbaord

This commit is contained in:
Michael Simard
2021-01-15 13:48:06 -06:00
parent b391da4dfb
commit ce46570099
4 changed files with 194 additions and 265 deletions

View File

@@ -18,9 +18,7 @@ struct StatsController: RouteCollection {
statsRoute.get("totalWins", use: totalWins)
statsRoute.get("totalLosses", use: totalLosses)
statsRoute.get("overall", use: overall)
statsRoute.get("all", use: test)
statsRoute.get("allDaily", use: allDaily)
statsRoute.get("test", use: test)
statsRoute.post("logMatch", use: logMatch)
statsRoute.get("history","page",":page", use: history)
statsRoute.get("history", use: history)
@@ -85,11 +83,18 @@ struct StatsController: RouteCollection {
return Stats( totalWins: Int(winCount), totalLosses: Int(lossCount))
}
func getStatsWithMostRecentDailyRecord(matches:[Match], game:String? = nil) -> StatsWithMostRecentDailyRecord {
func getStatsWithMostRecentDailyRecord(sortedMatches:[Match], game:String? = nil) -> StatsWithMostRecentDailyRecord {
let stats = getStats(matches: matches)
let mostRecentDailyStats = self.mostRecentDailyStats(matches: matches, game: game)
let startTime = Date()
//print ("MRR START \(Date().timeIntervalSince(startTime))")
let stats = getStats(matches: sortedMatches)
//print ("MRR STATS \(Date().timeIntervalSince(startTime))")
let mostRecentDailyStats = self.mostRecentDailyStats(matches: sortedMatches, game: game)
//print ("MRR DAILY \(Date().timeIntervalSince(startTime))")
let ret = StatsWithMostRecentDailyRecord(winLoss: stats.winLossRatio, totalWins: stats.totalWins, totalLosses: stats.totalLosses, mostRecentRecord:"\(mostRecentDailyStats.totalWins)-\(mostRecentDailyStats.totalLosses)")
return ret
@@ -97,9 +102,15 @@ struct StatsController: RouteCollection {
func mostRecentDailyStats (matches:[Match], game:String? = nil) -> Stats{
let daysPlayed = getDaysPlayed(matches: matches)
let startTime = Date()
let daysPlayed = getDaysPlayed(sortedMatches: matches)
let lastDayPlayed = daysPlayed.last
//print ("MDD days played \(Date().timeIntervalSince(startTime))")
return getStats(matches: matches.filter({ (match) -> Bool in
var shouldInclude =
match.date.day == lastDayPlayed?.day &&
@@ -108,20 +119,8 @@ struct StatsController: RouteCollection {
self.shouldCountMatch(match: match)
if let game = game {
// if game == "mw" {
// shouldInclude = shouldInclude && match.codGame == "mw"
// }
// else if game == "bocw" {
// shouldInclude = shouldInclude && match.codGame == "bocw"
//
// }else {
//
// }
shouldInclude = shouldInclude && match.codGame == game
}
return shouldInclude
}))
}
@@ -141,16 +140,20 @@ struct StatsController: RouteCollection {
return match.players?.components(separatedBy: ",").count ?? 0
}
private func getDaysPlayed(matches:[Match]) -> [CODDate] {
private func getDaysPlayed(sortedMatches:[Match]) -> [CODDate] {
var sortedMatches = matches
sortedMatches.sort { (match1, match2) -> Bool in
return match1.date < match2.date
let startTime = Date()
//print ("MDP Sort \(Date().timeIntervalSince(startTime))")
let dates = sortedMatches.suffix(30).map { (match) -> CODDate in
return CODDate(month: match.date.month, year: match.date.year, day: match.date.day, hour: match.date.hour, minute: match.date.minute)
}
return sortedMatches.map { (match) -> CODDate in
return CODDate(month: match.date.month, year: match.date.year, day: match.date.day, hour: match.date.hour, minute: match.date.minute)
}.reduce([CODDate]()) { (datesPlayed, codDate) -> [CODDate] in
//print ("MDP to dates \(Date().timeIntervalSince(startTime))")
return dates.reduce([CODDate]()) { (datesPlayed, codDate) -> [CODDate] in
if datesPlayed.contains(where: { (existingDate) -> Bool in
if codDate.month == existingDate.month && codDate.year == existingDate.year && existingDate.day == codDate.day{
@@ -170,7 +173,7 @@ struct StatsController: RouteCollection {
func getCumulativeWinLossRatios(matches:[Match]) -> [DataPoint] {
let daysPlayed = getDaysPlayed(matches: matches)
let daysPlayed = getDaysPlayed(sortedMatches: matches)
var cumulativeRatios : [DataPoint] = []
var cumulativeWins:Int = 0
@@ -215,106 +218,6 @@ struct StatsController: RouteCollection {
return cumulativeRatios
}
func test(req: Request) throws -> EventLoopFuture<AllStats> {
let startTime = Date()
var date = getStartDate()
var previousMonths:[CODDate] = []
repeat {
//let stats = getStatsByMonth(year: date.year, month: date.month, req: req) //returns eventloopfuture<Stats>
previousMonths.append(CODDate(month: date.month, year: date.year, day: 15, hour:6, minute: 0))
date = Calendar.current.date(byAdding: .month, value: 1, to: date)!
} while ( date < Calendar.current.date(byAdding: .month, value: 1, to: Date())!)
// print (date - Date().timeIntervalSince(startTime))
return Match.query(on: req.db).all().map { (matches) -> AllStats in
let overallStats = self.getStatsWithMostRecentDailyRecord(matches: matches)
// print ( Date().timeIntervalSince(startTime))
let mwStats = self.getStatsWithMostRecentDailyRecord(matches: matches.filter({ (match) -> Bool in
return match.codGame == "mw"
}))
// print ( Date().timeIntervalSince(startTime))
let bocwStats = self.getStatsWithMostRecentDailyRecord(matches: matches.filter({ (match) -> Bool in
return match.codGame == "bocw"
}))
// print ( Date().timeIntervalSince(startTime))
let mostRecentDailyStats = self.mostRecentDailyStats(matches: matches)
let monthlyStats = previousMonths.reversed().map { (codDate) -> MonthStats in
let relevantMatches = matches.filter { (match) -> Bool in
return match.date.month == codDate.month
}
return MonthStats(month: codDate.month, year: codDate.year, stats: self.getStats(matches: relevantMatches ))
}
// print ( Date().timeIntervalSince(startTime))
// let cumulativeWinLossRatios = self.getCumulativeWinLossRatios(matches: matches)
// print ( Date().timeIntervalSince(startTime))
// let highestWinLossRatio = cumulativeWinLossRatios.reduce("0") { (highestRatio, dataPoint) -> String in
// if dataPoint.y > Double(highestRatio)!{
// return String(dataPoint.y)
// }
// return highestRatio
// }
// print ( Date().timeIntervalSince(startTime))
return AllStats.init(overall: overallStats,mwStats: mwStats, bocwStats: bocwStats, byMonth: monthlyStats, mostRecentRecord: "\(mostRecentDailyStats.totalWins) - \(mostRecentDailyStats.totalLosses)")
}
// func getMonthStats (_ remaining: ArraySlice<CODDate>, allMonthlyStats: inout [MonthStats], eventLoop: EventLoop) -> EventLoopFuture<[MonthStats]> {
// var remaining = remaining
// if let first = remaining.popLast() {
//
// return getstatsForMonth(year: first.year, month: first.month, req: req).flatMap { [remaining, allMonthlyStats] (stats) -> EventLoopFuture<[MonthStats]> in
// var allMonthlyStats = allMonthlyStats
// allMonthlyStats.append(MonthStats(month: first.month, year: first.year, stats:stats ))
// return getMonthStats(remaining, allMonthlyStats:&allMonthlyStats, eventLoop: eventLoop)
// }
//
// } else {
// return req.eventLoop.makeSucceededFuture(allMonthlyStats)
// }
// }
//
// var stats:[MonthStats] = []
// let monthstats = getMonthStats(previousMonths[0..<previousMonths.count], allMonthlyStats:&stats, eventLoop: req.eventLoop)
// let cumulativeRatios = getCumulativeWinLossRatios(req: req)
// let mostRecentDayStats = mostRecentDailyStats(req: req)
//
// return try overall(req: req).and(monthstats).and(cumulativeRatios).and(mostRecentDayStats).map { arg -> AllStats in
//
// let (((overall, monthlyStats), cumulativeRatios), mostRecentDayStats) = arg
// let highestWinLossRatio = cumulativeRatios.reduce("0") { (highestRatio, dataPoint) -> String in
// if dataPoint.y > Double(highestRatio)!{
// return String(dataPoint.y)
// }
// return highestRatio
// }
//
// return AllStats.init(overall: overall,mwStats: overall, bocwStats: overall, byMonth: monthlyStats, highestWinLossRatio: highestWinLossRatio, dataPoints: cumulativeRatios, mostRecentRecord: "\(mostRecentDayStats.totalWins) - \(mostRecentDayStats.totalLosses)")
// }
}
func index(req: Request) throws -> EventLoopFuture<[Match]> {
return Match.query(on: req.db).sort(\.$date).all()
}
@@ -490,62 +393,12 @@ struct StatsController: RouteCollection {
}
}
// func all(req: Request) throws -> EventLoopFuture<AllStats> {
//
// var date = getStartDate()
// var previousMonths:[CODDate] = []
//
// repeat {
// //let stats = getStatsByMonth(year: date.year, month: date.month, req: req) //returns eventloopfuture<Stats>
// previousMonths.append(CODDate(month: date.month, year: date.year, day: 15, hour:6, minute: 0))
// date = Calendar.current.date(byAdding: .month, value: 1, to: date)!
// }while ( date < Calendar.current.date(byAdding: .month, value: 1, to: Date())!)
//
//
// func getMonthStats (_ remaining: ArraySlice<CODDate>, allMonthlyStats: inout [MonthStats], eventLoop: EventLoop) -> EventLoopFuture<[MonthStats]> {
// var remaining = remaining
// if let first = remaining.popLast() {
//
// return getstatsForMonth(year: first.year, month: first.month, req: req).flatMap { [remaining, allMonthlyStats] (stats) -> EventLoopFuture<[MonthStats]> in
// var allMonthlyStats = allMonthlyStats
// allMonthlyStats.append(MonthStats(month: first.month, year: first.year, stats:stats ))
// return getMonthStats(remaining, allMonthlyStats:&allMonthlyStats, eventLoop: eventLoop)
// }
//
// } else {
// return req.eventLoop.makeSucceededFuture(allMonthlyStats)
// }
// }
//
// var stats:[MonthStats] = []
// let monthstats = getMonthStats(previousMonths[0..<previousMonths.count], allMonthlyStats:&stats, eventLoop: req.eventLoop)
// let cumulativeRatios = getCumulativeWinLossRatios(req: req)
// let mostRecentDayStats = mostRecentDailyStats(req: req)
//
// return try overall(req: req).and(monthstats).and(cumulativeRatios).and(mostRecentDayStats).map { arg -> AllStats in
//
// let (((overall, monthlyStats), cumulativeRatios), mostRecentDayStats) = arg
// let highestWinLossRatio = cumulativeRatios.reduce("0") { (highestRatio, dataPoint) -> String in
// if dataPoint.y > Double(highestRatio)!{
// return String(dataPoint.y)
// }
// return highestRatio
// }
//
//
// return AllStats.init(overall: overall,mwStats: overall, bocwStats: overall, byMonth: monthlyStats, highestWinLossRatio: highestWinLossRatio, dataPoints: cumulativeRatios, mostRecentRecord: "\(mostRecentDayStats.totalWins) - \(mostRecentDayStats.totalLosses)")
// }
// }
//
func overall(req: Request) throws -> EventLoopFuture<OverallStats> {
let startTime = Date()
let statsWithHyder = statsWithPlayer(req: req, playerId: 5)
let statsWithoutHyder = statsWithoutPlayer(req: req, playerId: 5)
@@ -554,13 +407,17 @@ struct StatsController: RouteCollection {
let hyderStats = hyderFuture.map { (withHyder, withoutHyder) -> [Stats] in
return [withHyder, withoutHyder]
//print ("Hyder done \(Date().timeIntervalSince(startTime))")
}
let matches = Match.query(on: req.db).all()
let matches = Match.query(on: req.db).sort( \.$date).all()
return matches.and(hyderStats).map { (matches, hyderStats) -> (OverallStats) in
//print ("got matches \(Date().timeIntervalSince(startTime))")
let queue = DispatchQueue(label: "com.sledsoft.cod-tracker.queue", attributes: .concurrent)
let group = DispatchGroup()
@@ -568,18 +425,25 @@ struct StatsController: RouteCollection {
var mwStats:StatsWithMostRecentDailyRecord?
var bocwStats:StatsWithMostRecentDailyRecord?
var mostRecentStats:Stats?
var mapStats:[Int:Stats]?
var worstMap:Int?
var bestMap:Int?
group.enter()
queue.async {
overallStats = self.getStatsWithMostRecentDailyRecord(matches: matches)
overallStats = self.getStatsWithMostRecentDailyRecord(sortedMatches: matches)
group.leave()
//print ("all stats done \(Date().timeIntervalSince(startTime))")
}
group.enter()
queue.async {
mwStats = self.getStatsWithMostRecentDailyRecord(matches: matches.filter({ (match) -> Bool in
mwStats = self.getStatsWithMostRecentDailyRecord(sortedMatches: matches.filter({ (match) -> Bool in
return match.codGame == "mw" && self.shouldCountMatch(match: match )
}))
//print ("mw stats done \(Date().timeIntervalSince(startTime))")
group.leave()
}
@@ -587,24 +451,46 @@ struct StatsController: RouteCollection {
group.enter()
queue.async {
bocwStats = self.getStatsWithMostRecentDailyRecord(matches: matches.filter({ (match) -> Bool in
bocwStats = self.getStatsWithMostRecentDailyRecord(sortedMatches: matches.filter({ (match) -> Bool in
return match.codGame == "bocw" && self.shouldCountMatch(match: match )
}))
//print ("cw done \(Date().timeIntervalSince(startTime))")
group.leave()
}
// group.enter()
// queue.async {
// mostRecentStats = self.mostRecentDailyStats(matches: matches)
// group.leave()
// }
group.wait()
group.enter()
queue.async {
mapStats = self.getMapStats(matches: matches)
//print ("maps done \(Date().timeIntervalSince(startTime))")
group.leave()
}
//
group.enter()
queue.async {
let mapStats = self.getMapStats(matches: matches)
let bestMap = self.getBestMap(records: mapStats)
let worstMap = self.getWorstMap(records: mapStats)
bestMap = self.getBestMap(records: mapStats)
//print ("best done \(Date().timeIntervalSince(startTime))")
group.leave()
}
group.enter()
queue.async {
let mapStats = self.getMapStats(matches: matches)
worstMap = self.getWorstMap(records: mapStats)
//print ("worst done \(Date().timeIntervalSince(startTime))")
group.leave()
}
group.wait()
let dashboardItems = [
DashboardItem(title: "Overall", content: overallStats!.record, title2: "Ratio", content2: overallStats!.winLossRatio),
@@ -613,21 +499,12 @@ struct StatsController: RouteCollection {
DashboardItem(title: "Cold War Overall", content: bocwStats!.record, title2: "Ratio", content2: bocwStats!.winLossRatio),
DashboardItem(title: "With Hyder", content: hyderStats[0].record, title2: "Ratio", content2: hyderStats[0].winLossRatio),
DashboardItem(title: "No Hyder", content: hyderStats[1].record, title2: "Ratio", content2: hyderStats[1].winLossRatio),
DashboardItem(title: "Best Map", content: MapData.allMaps[bestMap]?.name ?? "error", title2: "Ratio", content2: "\(mapStats[bestMap]!.winLossRatio) \(mapStats[bestMap]!.record)"),
DashboardItem(title: "Worst Map", content: MapData.allMaps[worstMap]?.name ?? "error", title2: "Ratio", content2: "\(mapStats[worstMap]!.winLossRatio) \(mapStats[worstMap]!.record)"),
DashboardItem(title: "Best Map", content: MapData.allMaps[bestMap!]?.name ?? "error", title2: "Ratio", content2: "\(mapStats![bestMap!]!.winLossRatio) \(mapStats![bestMap!]!.record)"),
DashboardItem(title: "Worst Map", content: MapData.allMaps[worstMap!]?.name ?? "error", title2: "Ratio", content2: "\(mapStats![worstMap!]!.winLossRatio) \(mapStats![worstMap!]!.record)"),
DashboardItem(title: "Final Kills Ruined by Adam", content: "\(matches.filter{$0.finalKillRuinedPlayerId == 6}.count + 7)", title2: "", content2: ""),
]
return OverallStats(overall: overallStats!, mwStats: mwStats!, bocwStats: bocwStats!, mostRecentRecord: "Temporarily Unavailable", statsWithHyder:hyderStats[0], statsWithoutHyder: hyderStats[1], dashboardItems:dashboardItems)
}
// return Match.query(on: req.db).all().map { (matches) -> OverallStats in
//
//
// }
}
@@ -647,7 +524,7 @@ struct StatsController: RouteCollection {
var loss:Double = 0
for record in records {
print("\(record.map.name) \(record.stats.record) \(record.ratio)")
//print("\(record.map.name) \(record.stats.record) \(record.ratio)")
wins = wins + Double(record.stats.totalWins)
loss = loss + Double(record.stats.totalLosses)
@@ -817,14 +694,9 @@ struct StatsController: RouteCollection {
func getCumulativeWinLossRatios(req:Request) -> EventLoopFuture<[DataPoint]> {
// let previousDays = getDaysPlayed().reversed()
return getDaysPlayed(req: req).flatMap { (previousDays) -> (EventLoopFuture<[DataPoint]>) in
func getRatios (_ remaining: ArraySlice<CODDate>, allDailyStats: inout [DailyStats], cumulativeWinLossRatios: inout [DataPoint], eventLoop: EventLoop) -> EventLoopFuture<[DataPoint]> {
var remaining = remaining
if let first = remaining.popLast() {
@@ -843,7 +715,7 @@ struct StatsController: RouteCollection {
if !(stats.totalWins == 0 && stats.totalLosses == 0) {
let date = self.createDate(day: first.day, month: first.month, year: first.year, hour: first.hour + 6, minute:first.minute) // 6 hours to make sure we pick a time that isnt on borders of us time zones
// print ("p \(date.timeIntervalSince1970)")
// //print ("p \(date.timeIntervalSince1970)")
let x = Double(cumulativeWinLossRatios.count)
let d = Date(timeIntervalSince1970: date.timeIntervalSince1970)
@@ -868,56 +740,3 @@ struct StatsController: RouteCollection {
}
extension Double
{
func truncate(places : Int)-> Double
{
return Double(floor(pow(10.0, Double(places)) * self)/pow(10.0, Double(places)))
}
}
extension Date {
var month:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.month ?? 1
}
var year:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.year ?? 1
}
var day:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.day ?? 1
}
var hour:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.hour ?? 1
}
var minute:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.minute ?? 1
}
}

View File

@@ -0,0 +1,55 @@
//
// Date+components.swift
// App
//
// Created by Michael Simard on 1/15/21.
//
import Foundation
extension Date {
var month:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.month ?? 1
}
var year:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.year ?? 1
}
var day:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.day ?? 1
}
var hour:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.hour ?? 1
}
var minute:Int {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "GMT")!
let calanderDate = calendar.dateComponents([.minute, .month, .year, .hour, .day], from: self)
return calanderDate.minute ?? 1
}
}

View File

@@ -0,0 +1,16 @@
//
// Double+Truncate.swift
// App
//
// Created by Michael Simard on 1/15/21.
//
import Foundation
extension Double
{
func truncate(places : Int)-> Double
{
return Double(floor(pow(10.0, Double(places)) * self)/pow(10.0, Double(places)))
}
}

View File

@@ -25,8 +25,39 @@ x-shared_environment: &shared_environment
DATABASE_NAME: cod_db
DATABASE_USERNAME: cod
DATABASE_PASSWORD: pw4cod
VIRTUAL_HOST: app.local
services:
nginx-proxy:
image: jwilder/nginx-proxy:alpine
restart: "always" # Always restart container
ports:
- "80:80" # Port mappings in format host:container
- "443:443" # Port mappings in format host:container
networks:
- nginx-proxy # Name of the etwork these two containers will share
labels:
- "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy" # Label needed for Let's Encrypt companion container
volumes: # Volumes needed for container to configure proixes and access certificates genereated by Let's Encrypt companion container
- /var/run/docker.sock:/tmp/docker.sock:ro
- "nginx-conf:/etc/nginx/conf.d"
- "nginx-vhost:/etc/nginx/vhost.d"
- "html:/usr/share/nginx/html"
- "certs:/etc/nginx/certs:ro"
letsencrypt-nginx-proxy-companion:
image: jrcs/letsencrypt-nginx-proxy-companion
restart: always
container_name: letsencrypt-nginx-proxy-companion
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "nginx-conf:/etc/nginx/conf.d"
- "nginx-vhost:/etc/nginx/vhost.d"
- "html:/usr/share/nginx/html"
- "certs:/etc/nginx/certs:rw"
depends_on: # Make sure we start nginx proxy container first
- app
- nginx-proxy
app:
image: cod-backend:latest
build:
@@ -72,3 +103,11 @@ services:
POSTGRES_DB: cod_db
ports:
- '5432:5432'
networks:
nginx-proxy: # Name of our shared network that containers will use
volumes: # Names of volumes that out containers will share. Those will persist on docker's host machine.
nginx-conf:
nginx-vhost:
html:
certs:
db_data: