diff --git a/Sources/App/Controllers/StatsController.swift b/Sources/App/Controllers/StatsController.swift index 2020da5..0dba26f 100644 --- a/Sources/App/Controllers/StatsController.swift +++ b/Sources/App/Controllers/StatsController.swift @@ -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 { - - let startTime = Date() - - var date = getStartDate() - var previousMonths:[CODDate] = [] - - repeat { - //let stats = getStatsByMonth(year: date.year, month: date.month, req: req) //returns eventloopfuture - 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, 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.. 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 { - // - // var date = getStartDate() - // var previousMonths:[CODDate] = [] - // - // repeat { - // //let stats = getStatsByMonth(year: date.year, month: date.month, req: req) //returns eventloopfuture - // 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, 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.. 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 { + 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, 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 - } - - - -} diff --git a/Sources/App/Data/Extensions/Date+components.swift b/Sources/App/Data/Extensions/Date+components.swift new file mode 100644 index 0000000..8a7eed8 --- /dev/null +++ b/Sources/App/Data/Extensions/Date+components.swift @@ -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 + } + + + +} diff --git a/Sources/App/Data/Extensions/Double+Truncate.swift b/Sources/App/Data/Extensions/Double+Truncate.swift new file mode 100644 index 0000000..d2b2d82 --- /dev/null +++ b/Sources/App/Data/Extensions/Double+Truncate.swift @@ -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))) + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 227f6f7..41ab0b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: \ No newline at end of file