X-Git-Url: http://nitlanguage.org diff --git a/contrib/nitrpg/src/statistics.nit b/contrib/nitrpg/src/statistics.nit index 65d5a69..764616f 100644 --- a/contrib/nitrpg/src/statistics.nit +++ b/contrib/nitrpg/src/statistics.nit @@ -22,24 +22,38 @@ module statistics import game import github::hooks +import counter + +redef class GameEntity + + # Statistics manager for this entity. + fun stats: GameStatsManager is abstract +end redef class Game - # Statistics for this game instance. - var stats = new GameStats + redef var stats is lazy do return new GameStatsManager(game, self) - redef fun from_json(json) do - super - if json.has_key("statistics") then - stats.from_json(json["statistics"].as(JsonObject)) - end + redef fun pretty do + var res = new FlatBuffer + res.append super + res.append "# stats:\n" + res.append stats.pretty + return res.write_to_string end - redef fun to_json do - var obj = super - obj["statistics"] = stats.to_json - return obj + redef fun save do + super + stats.save end +end + +redef class Player + + redef var stats is lazy do return new GameStatsManager(game, self) + + redef fun nitcoins do return stats["nitcoins"] + redef fun nitcoins=(nc) do stats["nitcoins"] = nc redef fun pretty do var res = new FlatBuffer @@ -49,54 +63,128 @@ redef class Game return res.write_to_string end - redef fun clear do + redef fun save do super - stats.clear + stats.save end end -# Game statistics structure that can be saved as a `GameEntity`. -class GameStats +# Store game stats for defined period. +class GameStatsManager super GameEntity + super Counter[String] + + redef var game - redef var key = "statistics" + # The GameEntity monitored by these statistics. + var owner: GameEntity - # Used internally to stats values. - private var stats = new HashMap[String, Int] + # Current date to extract stats + private var date = new Tm.gmtime - init do clear + # Returns the `GameStats` instance for the overall statistics. + var overall: GameStats = load_stats_for("all") is lazy - # Load `self` from saved data - private fun from_json(json: JsonObject) do - for k, v in json do stats[k] = v.as(Int) + # Returns the `GameStats` instance for the current year statistics. + var yearly: GameStats = load_stats_for(date.strftime("%Y")) is lazy + + # Returns the `GameStats` instance for the current month statistics. + var monthly: GameStats = load_stats_for(date.strftime("%Y-%m")) is lazy + + # Returns the `GameStats` instance for the current day statistics. + var daily: GameStats = load_stats_for(date.strftime("%Y-%m-%d")) is lazy + + # Returns the `GameStats` instance for the current week statistics. + var weekly: GameStats = load_stats_for(date.strftime("%Y-W%U")) is lazy + + # Load statistics for a `period` key. + fun load_stats_for(period: String): GameStats do + var req = new JsonObject + req["period"] = period + req["owner"] = owner.key + var obj = game.db.collection("statistics").find(req) + if obj isa JsonObject then + return new GameStats.from_json(game, period, owner, obj) + else + return new GameStats(game, period, owner) + end end - redef fun to_json do - var obj = new JsonObject - for k, v in stats do obj[k] = v - return obj + redef fun [](key) do return overall[key] + + redef fun []=(key, value) do + overall[key] = value + yearly[key] = value + monthly[key] = value + daily[key] = value + weekly[key] = value + end + + redef fun inc(e) do + overall.inc(e) + yearly.inc(e) + monthly.inc(e) + daily.inc(e) + weekly.inc(e) + end + + redef fun dec(e) do + overall.dec(e) + yearly.dec(e) + monthly.dec(e) + daily.dec(e) + weekly.dec(e) + end + + redef fun save do + overall.save + yearly.save + monthly.save + daily.save + weekly.save end - # Retrieves the current value of `key` statistic entry. - fun [](key: String): Int do return stats[key] + redef fun pretty do return overall.pretty +end + +# Game statistics structure that can be saved as a `GameEntity`. +class GameStats + super GameEntity + super Counter[String] + + redef var game - # Increments the value of `key` statistic entry by 1. - fun incr(key: String) do stats[key] += 1 + redef var collection_name = "statistics" - # Decrements the value of `key` statistic entry by 1. - fun decr(key: String) do stats[key] -= 1 + # The period these stats are about. + var period: String - # Reset game stats. - fun clear do - stats["issues"] = 0 - stats["issues_open"] = 0 - stats["pulls"] = 0 - stats["pulls_open"] = 0 + # The game entity these stats are about. + var owner: GameEntity + + redef var key = "{owner.key}-{period}" is lazy + + # Load `self` from saved data. + init from_json(game: Game, period: String, owner: GameEntity, json: JsonObject) do + init(game, period, owner) + var values = json.get_or_null("values") + if not values isa JsonObject then return + for k, v in values do self[k] = v.as(Int) + end + + redef fun to_json_object do + var obj = super + obj["period"] = period + obj["owner"] = owner.key + var values = new JsonObject + values.add_all(self) + obj["values"] = values + return obj end redef fun pretty do var res = new FlatBuffer - for k, v in stats do + for k, v in self do res.append "# {v} {k}\n" end return res.write_to_string @@ -107,11 +195,7 @@ end class StatisticsReactor super GameReactor - redef fun react_event(game, e) do - super # log events - e.react_stats_event(game) - game.save - end + redef fun react_event(game, e) do e.react_stats_event(game) end redef class GithubEvent @@ -126,13 +210,24 @@ redef class IssuesEvent # Count opened and closed issues. redef fun react_stats_event(game) do + var player = issue.user.player(game) if action == "opened" then - game.stats.incr("issues") - game.stats.incr("issues_open") + game.stats.inc("issues") + game.stats.inc("issues_open") + game.save + player.stats.inc("issues") + player.stats.inc("issues_open") + player.save else if action == "reopened" then - game.stats.incr("issues_open") + game.stats.inc("issues_open") + game.save + player.stats.inc("issues_open") + player.save else if action == "closed" then - game.stats.decr("issues_open") + game.stats.dec("issues_open") + game.save + player.stats.dec("issues_open") + player.save end end end @@ -141,13 +236,47 @@ redef class PullRequestEvent # Count opened and closed pull requests. redef fun react_stats_event(game) do + var player = pull.user.player(game) if action == "opened" then - game.stats.incr("pulls") - game.stats.incr("pulls_open") + game.stats.inc("pulls") + game.stats.inc("pulls_open") + game.save + player.stats.inc("pulls") + player.stats.inc("pulls_open") + player.save else if action == "reopened" then - game.stats.incr("pulls_open") + game.stats.inc("pulls_open") + game.save + player.stats.inc("pulls_open") + player.save else if action == "closed" then - game.stats.decr("pulls_open") + game.stats.dec("pulls_open") + player.stats.dec("pulls_open") + if pull.merged then + game.stats["commits"] += pull.commits + player.stats["commits"] += pull.commits + end + game.save + player.save + end + end +end + +redef class IssueCommentEvent + + # Count posted comments + redef fun react_stats_event(game) do + if action == "created" then + var player = comment.user.player(game) + game.stats.inc("comments") + player.stats.inc("comments") + # FIXME use a more precise way to locate reviews + if comment.is_ack then + game.stats.inc("reviews") + player.stats.inc("reviews") + end + game.save + player.save end end end