From 5dbab8fac6905d57abf78076c58916b0b34b35ce Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Thu, 20 Jun 2019 19:31:41 -0400 Subject: [PATCH] nitrpg: Move `nitrpg` to its own repository See https://github.com/Morriar/nitrpg. Signed-off-by: Alexandre Terrasa --- contrib/nitrpg/.gitignore | 4 - contrib/nitrpg/Makefile | 42 -- contrib/nitrpg/README.md | 92 ---- contrib/nitrpg/nitrpg.user.js | 27 - contrib/nitrpg/package.ini | 12 - contrib/nitrpg/src/achievements.nit | 532 ------------------- contrib/nitrpg/src/events.nit | 127 ----- contrib/nitrpg/src/events_generator.nit | 93 ---- contrib/nitrpg/src/game.nit | 337 ------------ contrib/nitrpg/src/listener.nit | 70 --- contrib/nitrpg/src/reactors.nit | 115 ----- contrib/nitrpg/src/statistics.nit | 282 ---------- contrib/nitrpg/src/templates/panels.nit | 569 --------------------- contrib/nitrpg/src/templates/templates.nit | 110 ---- contrib/nitrpg/src/templates/templates_base.nit | 74 --- contrib/nitrpg/src/templates/templates_events.nit | 125 ----- contrib/nitrpg/src/test_achievements.nit | 140 ----- contrib/nitrpg/src/test_events.nit | 139 ----- contrib/nitrpg/src/test_game.nit | 140 ----- contrib/nitrpg/src/test_helper.nit | 73 --- contrib/nitrpg/src/test_listener.nit | 359 ------------- contrib/nitrpg/src/test_statistics.nit | 83 --- contrib/nitrpg/src/web.nit | 313 ------------ contrib/nitrpg/www/styles/main.css | 74 --- 24 files changed, 3932 deletions(-) delete mode 100644 contrib/nitrpg/.gitignore delete mode 100644 contrib/nitrpg/Makefile delete mode 100644 contrib/nitrpg/README.md delete mode 100644 contrib/nitrpg/nitrpg.user.js delete mode 100644 contrib/nitrpg/package.ini delete mode 100644 contrib/nitrpg/src/achievements.nit delete mode 100644 contrib/nitrpg/src/events.nit delete mode 100644 contrib/nitrpg/src/events_generator.nit delete mode 100644 contrib/nitrpg/src/game.nit delete mode 100644 contrib/nitrpg/src/listener.nit delete mode 100644 contrib/nitrpg/src/reactors.nit delete mode 100644 contrib/nitrpg/src/statistics.nit delete mode 100644 contrib/nitrpg/src/templates/panels.nit delete mode 100644 contrib/nitrpg/src/templates/templates.nit delete mode 100644 contrib/nitrpg/src/templates/templates_base.nit delete mode 100644 contrib/nitrpg/src/templates/templates_events.nit delete mode 100644 contrib/nitrpg/src/test_achievements.nit delete mode 100644 contrib/nitrpg/src/test_events.nit delete mode 100644 contrib/nitrpg/src/test_game.nit delete mode 100644 contrib/nitrpg/src/test_helper.nit delete mode 100644 contrib/nitrpg/src/test_listener.nit delete mode 100644 contrib/nitrpg/src/test_statistics.nit delete mode 100644 contrib/nitrpg/src/web.nit delete mode 100644 contrib/nitrpg/www/styles/main.css diff --git a/contrib/nitrpg/.gitignore b/contrib/nitrpg/.gitignore deleted file mode 100644 index 24c4036..0000000 --- a/contrib/nitrpg/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.github_data -nitrpg_data -listener -web diff --git a/contrib/nitrpg/Makefile b/contrib/nitrpg/Makefile deleted file mode 100644 index 0270ede..0000000 --- a/contrib/nitrpg/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -NITC ?= nitc -NITLS ?= nitls -NITUNIT ?= nitunit -NITDOC ?= nitdoc - -.PHONY: all -all: bin/listener bin/web - -bin/listener: $(shell $(NITLS) -M src/listener.nit) - mkdir -p bin/ - $(NITC) src/listener.nit -o bin/listener - -bin/web: $(shell $(NITLS) -M src/web.nit) - mkdir -p bin/ - $(NITC) src/web.nit -o bin/web - -.PHONY: check -check: - $(NITUNIT) . - -.PHONY: doc -doc: - $(NITDOC) . -o doc/ - -.PHONY: clean -clean: - rm -rf bin/ - rm -rf doc/ diff --git a/contrib/nitrpg/README.md b/contrib/nitrpg/README.md deleted file mode 100644 index 5f6a02d..0000000 --- a/contrib/nitrpg/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Welcome to NitRPG! - -NitRPG is a Role Playing Game that takes place on [GitHub](https://github.com/). - -In NitRPG, GitHub users are represented by players that battle on repo for -nitcoins and glory. - -## Features - -* Auto-update with GitHub hooks -* Display repo statistics -* Display players statsitics -* Repo actions are rewarded by nitcoins -* Players can unlock achievements - -## How to install - -From the `nit` root: - -~~~bash -> cd contrib/nitrpg -> make -~~~ - -### Configuring the GitHub hook - -NitRPG needs you to add a new GitHub hook on your repo to keep the game -`listener` up-to-date automatically. - -Hook configuration: - -* **Payload URL**: URL and port to the listener (ex: `http://yourdomain.com:8080`) -* **Content type**: `application/json` -* **Wich events**: `Send me everything` - -Be sure to set the hook as `Active` in the GitHub admin panel. - -### Starting the listener - -The `listener` program is used to listen to GitHub hooks and update game data. -It should alwaysd be up if you want your game to be kept up-to-date. - -To run the listener: - -~~~raw - ./listener -~~~ - -The arguments `host` and `port` must correspond to what you entered in your -GitHub hook settings. - -### Starting the web server - -The `web` program act as a [nitcorn](http://nitlanguage.org/doc/stdlib/module_nitcorn__nitcorn.html) webserver that display the game results live. - -To run the webserver: - -~~~raw - ./web -~~~ - -The arguments `host` and `port` must correspond to what you entered in your -GitHub hook settings. -The `root` argument is used to specify the path from the domain url to the -NitRPG root. - -For example, if NitRPG is installed in `yourdomain.com/nitrpg`: - -~~~raw - ./web localhost 3000 "/nitrpg" -~~~ - -Leave it empty if NitRPG is installed at the root of the domain: - -~~~raw - ./web localhost 3000 "" -~~~ - -The webserver can then be accessed at `http://yourdomain.com:3000/nitrpg/`. - -## RoadMap - -NitRPG stills under heavy development. -Incomming features contain (but are not limited to): - -* Periodized stats (weekly, monthly, yearly, overall) -* Display graphs with stats -* More achievements -* Shop: exchange Nitcoins against glorifying items - -You can suggest new achievements or ideas in the -[NitRPG RoadMap Issue](https://github.com/nitlang/nit/issues/1161). diff --git a/contrib/nitrpg/nitrpg.user.js b/contrib/nitrpg/nitrpg.user.js deleted file mode 100644 index 7e27fa1..0000000 --- a/contrib/nitrpg/nitrpg.user.js +++ /dev/null @@ -1,27 +0,0 @@ -// ==UserScript== -// @name Github.com - Add RPG tab -// @namespace nitlanguage/github/rpg -// @description Adds a "Github RPG" Tab at the end of the tabs. -// @include https://github.com/* -// @downloadURL https://github.com/nitlang/nit/raw/master/contrib/nitrpg/nitrpg.user.js -// @version 2 -// @grant none -// ==/UserScript== - -// The nav bar with tabs -var nav = document.getElementsByClassName('reponav'); -if (!nav || !nav[0]) return; - -// The current repo to link -var repo = $("meta[name='octolytics-dimension-repository_nwo']").attr("content"); -//repo = "nitlang/nit"; -if (!repo) return; - -// The content of the new tab -var html = ' Github RPG'; -//html = '

x

'; - -// Inject the new tab -var div = document.createElement('div'); -div.innerHTML = html; -nav[0].append(div.firstChild); diff --git a/contrib/nitrpg/package.ini b/contrib/nitrpg/package.ini deleted file mode 100644 index 7db8f35..0000000 --- a/contrib/nitrpg/package.ini +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name=nitrpg -tags=devel,web,cli -maintainer=Alexandre Terrasa -license=Apache-2.0 -desc=NitRPG, a Role Playing Game that takes place on GitHub -[upstream] -browse=https://github.com/nitlang/nit/tree/master/contrib/nitrpg/ -git=https://github.com/nitlang/nit.git -git.directory=contrib/nitrpg/ -homepage=http://nitlanguage.org -issues=https://github.com/nitlang/nit/issues diff --git a/contrib/nitrpg/src/achievements.nit b/contrib/nitrpg/src/achievements.nit deleted file mode 100644 index 2097ec3..0000000 --- a/contrib/nitrpg/src/achievements.nit +++ /dev/null @@ -1,532 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# `nitrpg` achievements. -# -# Players can unlock achievements by performing remarkable actions on the repo. -# Achievements are rewarded by nitcoins. -module achievements - -import events -import statistics - -redef class GameEntity - - # Register a new achievement for this game entity. - # - # Saves the achievement in game data. - # Do nothing is the achievement is already registered. - # - # TODO should update the achievement? - fun add_achievement(achievement: Achievement) do - stats.inc("achievements") - achievement.owner = self - achievement.save - end - - # Is `a` unlocked for this `Player`? - fun has_achievement(a: Achievement): Bool do return load_achievement(a.id) != null - - # Load the event from its `id`. - # - # Looks for the event save file in game data. - # Returns `null` if the event cannot be found. - fun load_achievement(id: String): nullable Achievement do - var req = new JsonObject - req["id"] = id - req["game"] = game.key - req["owner"] = key - var obj = game.db.collection("achievements").find(req) - if obj isa JsonObject then - return new Achievement.from_json(game, obj) - end - return null - end - - # List all events registered in this entity. - # - # This list is reloaded from game data each time its called. - # - # To add events see `add_event`. - fun load_achievements: MapRead[String, Achievement] do - var req = new JsonObject - req["game"] = game.key - req["owner"] = key - var res = new HashMap[String, Achievement] - for obj in game.db.collection("achievements").find_all(req) do - var achievement = new Achievement.from_json(game, obj) - res[achievement.id] = achievement - end - return res - end -end - -# Achievements are rewarded by `nitcoins`. -# -# An achievement represents a notable action performed by a `Player`. -# Player that `unlock` achievements are rewarded by nitcoins. -class Achievement - super GameEntity - - redef var collection_name = "achievements" - - redef var game - - redef fun key do - var owner = self.owner - if owner == null then return id - return "{owner.key}-{id}" - end - - # Uniq ID for this achievement. - var id: String - - # Name of this achievement. - var name: String - - # Description of the achievement. - var desc: String - - # Reward that this achievement give in nitcoins. - var reward: Int - - # Is this achievement unlocked by somebody? - var is_unlocked: Bool is lazy do return not load_events.is_empty - - # Game entity this achievement is about. - var owner: nullable GameEntity = null - - # Init `self` from a `json` object. - # - # Used to load achievements from storage. - init from_json(game: Game, json: JsonObject) do - init(game, - json["id"].as(String), - json["name"].as(String), - json["desc"].as(String), - json["reward"].as(Int)) - end - - redef fun to_json_object do - var json = super - json["id"] = id - json["name"] = name - json["desc"] = desc - json["reward"] = reward - json["game"] = game.key - var owner = self.owner - if owner != null then json["owner"] = owner.key - return json - end -end - -redef class Player - # Unlocks an achievement for this Player based on a GithubEvent. - # - # Register the achievement and adds the achievement reward to the player - # nitcoins. - # - # Do nothing is this player has already unlocked the achievement. - # - # TODO: add abstraction so achievements do not depend on GithubEvent. - fun unlock_achievement(a: Achievement, event: GithubEvent) do - if has_achievement(a) then return - nitcoins += a.reward - add_achievement(a) - trigger_unlock_event(a, event) - save - end - - # Create a new event that marks the achievement unlocking. - fun trigger_unlock_event(achievement: Achievement, event: GithubEvent) do - var obj = new JsonObject - obj["player"] = name - obj["reward"] = achievement.reward - obj["achievement"] = achievement.id - obj["github_event"] = event - var ge = new GameEvent(game, "achievement_unlocked", obj) - add_event(ge) - game.add_event(ge) - achievement.add_event(ge) - end -end - -# `GameReactor` dedicated to achievements unlocking. -interface AchievementReactor - super GameReactor - - # Unic ID of the achievement this reactor unlocks. - fun id: String is abstract - - # Name of the achievement this reactor unlocks. - fun name: String is abstract - - # Description of the achievement this reactor unlocks. - fun desc: String is abstract - - # Amount of nitcoins rewarded for unlocking the achievement. - fun reward: Int is abstract - - # Return a new instance of the achievement to unlock. - fun new_achievement(game: Game): Achievement do - var achievement = new Achievement(game, id, name, desc, reward) - game.add_achievement(achievement) - return achievement - end -end - -##################### -### Issues -##################### - -# Unlock achievement after X issues. -# -# Used to factorize behavior. -abstract class PlayerXIssues - super AchievementReactor - - # Number of PR required to unlock the achievement. - var threshold: Int is noinit - - redef fun react_event(game, event) do - if not event isa IssuesEvent then return - if not event.action == "opened" then return - var player = event.issue.user.player(game) - if player.stats["issues"] == threshold then - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -# Player open his first issue. -class Player1Issue - super PlayerXIssues - - redef var id = "player_1_issue" - redef var name = "First complaint" - redef var desc = "Open your first issue." - redef var reward = 10 - redef var threshold = 1 -end - -# Player open 100 issues. -class Player100Issues - super PlayerXIssues - - redef var id = "player_100_issues" - redef var name = "Mature whiner" - redef var desc = "Open 100 issues in the game." - redef var reward = 100 - redef var threshold = 100 -end - -# Player open 1 000 issues. -class Player1KIssues - super PlayerXIssues - - redef var id = "player_1000_issues" - redef var name = "You, sir, complain a lot" - redef var desc = "Open 1000 issues in the game." - redef var reward = 1000 - redef var threshold = 1000 -end - -# Player open an issue about nitdoc. -class IssueAboutNitdoc - super AchievementReactor - - redef var id = "issue_about_nitdoc" - redef var name = "Say nitdoc again, I double dare you!" - redef var desc = "Open an issue with \"nitdoc\" in the title." - redef var reward = 10 - - redef fun react_event(game, event) do - if not event isa IssuesEvent then return - if not event.action == "opened" then return - var player = event.issue.user.player(game) - var re = "nitdoc".to_re - re.ignore_case = true - if event.issue.title.has(re) then - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -# Player open an issue about FFI. -class IssueAboutFFI - super PlayerXIssues - - redef var id = "issue_about_ffi" - redef var name = "Polyglot what?" - redef var desc = "Open an issue with `ffi` in the title." - redef var reward = 10 - - redef fun react_event(game, event) do - if not event isa IssuesEvent then return - if not event.action == "opened" then return - var player = event.issue.user.player(game) - var re = "\\bffi\\b".to_re - re.ignore_case = true - if event.issue.title.has(re) then - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -##################### -### Pull requests -##################### - -# Unlock achievement after X pull requests. -# -# Used to factorize behavior. -abstract class PlayerXPulls - super AchievementReactor - - # Number of PR required to unlock the achievement. - var threshold: Int is noinit - - redef fun react_event(game, event) do - if not event isa PullRequestEvent then return - if not event.action == "opened" then return - var player = event.pull.user.player(game) - if player.stats["pulls"] == threshold then - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -# Open your first pull request. -class Player1Pull - super PlayerXPulls - - redef var id = "player_1_pull" - redef var name = "First PR" - redef var desc = "Open your first pull request." - redef var reward = 10 - redef var threshold = 1 -end - -# Author 100 pull requests. -class Player100Pulls - super PlayerXPulls - - redef var id = "player_100_pulls" - redef var name = "100 pull requests!!!" - redef var desc = "Open 100 pull requests in the game." - redef var reward = 100 - redef var threshold = 100 -end - -# Author 1000 pull requests. -class Player1KPulls - super PlayerXPulls - - redef var id = "player_1000_pulls" - redef var name = "1000 PULL REQUESTS!!!" - redef var desc = "Open 1000 pull requests in the game." - redef var reward = 1000 - redef var threshold = 1000 -end - -##################### -### Commits -##################### - -# Unlock achievement after X merged commits. -# -# Used to factorize behavior. -abstract class PlayerXCommits - super AchievementReactor - - # Number of PR required to unlock the achievement. - var threshold: Int is noinit - - redef fun react_event(game, event) do - if not event isa PullRequestEvent then return - if not event.action == "closed" then return - if not event.pull.merged then return - var player = event.pull.user.player(game) - if player.stats["commits"] >= threshold then - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -# Author your first commit in the game. -class Player1Commit - super PlayerXCommits - - redef var id = "player_1_commit" - redef var name = "First blood" - redef var desc = "Author your first commit in the game." - redef var reward = 10 - redef var threshold = 1 -end - -# Author 100 commits. -class Player100Commits - super PlayerXCommits - - redef var id = "player_100_commits" - redef var name = "100 commits" - redef var desc = "Author 100 commits in the game." - redef var reward = 100 - redef var threshold = 100 -end - -# Author 1 000 commits. -class Player1KCommits - super PlayerXCommits - - redef var id = "player_1000_commits" - redef var name = "1000 commits!!!" - redef var desc = "Author 1000 commits in the game." - redef var reward = 1000 - redef var threshold = 1000 -end - -# Author 10 000 commits. -class Player10KCommits - super PlayerXCommits - - redef var id = "player_10000_commits" - redef var name = "10000 COMMITS!!!" - redef var desc = "Author 10000 commits in the game." - redef var reward = 10000 - redef var threshold = 10000 -end - -##################### -### Issue Comments -##################### - -# Unlock achievement after X issue comments. -# -# Used to factorize behavior. -abstract class PlayerXComments - super AchievementReactor - - # Number of comments required to unlock the achievement. - var threshold: Int is noinit - - redef fun react_event(game, event) do - if not event isa IssueCommentEvent then return - if not event.action == "created" then return - var player = event.comment.user.player(game) - if player.stats["comments"] == threshold then - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -# Player author his first comment in issues. -class Player1Comment - super PlayerXComments - - redef var id = "player_1_comment" - redef var name = "From lurker to member" - redef var desc = "Comment on an issue." - redef var reward = 10 - redef var threshold = 1 -end - -# Player author 100 issue comments. -class Player100Comments - super PlayerXComments - - redef var id = "player_100_comments" - redef var name = "Chatter" - redef var desc = "Comment 100 times on issues." - redef var reward = 100 - redef var threshold = 100 -end - -# Player author 1000 issue comments. -class Player1KComments - super PlayerXComments - - redef var id = "player_1000_comments" - redef var name = "You sir, talk a lot!" - redef var desc = "Comment 1000 times on issues." - redef var reward = 1000 - redef var threshold = 1000 -end - -# Ping @privat in a comment. -class PlayerPingGod - super AchievementReactor - - redef var id = "player_ping_god" - redef var name = "Ping god" - redef var desc = "Ping the owner of the repo for the first time." - redef var reward = 50 - - redef fun react_event(game, event) do - if not event isa IssueCommentEvent then return - var owner = game.repo.owner.login - if event.comment.body.has("@{owner}".to_re) then - var player = event.comment.user.player(game) - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -# Give your first +1 -class PlayerFirstReview - super AchievementReactor - - redef var id = "player_first_review" - redef var name = "First +1" - redef var desc = "Give a +1 for the first time." - redef var reward = 10 - - redef fun react_event(game, event) do - if not event isa IssueCommentEvent then return - # FIXME use a more precise way to locate reviews - if event.comment.is_ack then - var player = event.comment.user.player(game) - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end - -# Talk about nitcoin in issue comments. -class PlayerSaysNitcoin - super AchievementReactor - - redef var id = "player_says_nitcoin" - redef var name = "Talking about money" - redef var desc = "Say something about nitcoins in a comment." - redef var reward = 10 - - redef fun react_event(game, event) do - if not event isa IssueCommentEvent then return - if event.comment.body.has("(n|N)itcoin".to_re) then - var player = event.comment.user.player(game) - var a = new_achievement(game) - player.unlock_achievement(a, event) - end - end -end diff --git a/contrib/nitrpg/src/events.nit b/contrib/nitrpg/src/events.nit deleted file mode 100644 index 08d387d..0000000 --- a/contrib/nitrpg/src/events.nit +++ /dev/null @@ -1,127 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# `nitrpg` game events. -# -# In this module we introduce the concept of `GameEvent`. -# They can be attached to every GameEntities. -module events - -import game - -redef class GameEntity - - # Register a new game event for this entity. - fun add_event(event: GameEvent) do - event.owner = self - event.save - end - - # List all events registered in this entity. - # - # This list is reloaded from game data each time its called. - # - # To add events see `add_event`. - fun load_events: Array[GameEvent] do - var req = new JsonObject - req["game"] = game.key - req["owner"] = key - var res = new Array[GameEvent] - for obj in game.db.collection("events").find_all(req) do - res.add new GameEvent.from_json(game, obj) - end - (new EventTimeComparator).sort(res) - return res - end - - # Load the event from its `id`. - # - # Looks for the event save file in game data. - # Returns `null` if the event cannot be found. - fun load_event(id: String): nullable GameEvent do - var req = new JsonObject - req["game"] = game.key - req["owner"] = key - req["internal_id"] = id - var res = game.db.collection("events").find(req) - if res != null then return new GameEvent.from_json(game, res) - return null - end -end - -# An event that occurs in the `Game`. -class GameEvent - super GameEntity - - redef var collection_name = "events" - - redef var game - - # Entity this event belongs to. - var owner: nullable GameEntity = null - - # String used to dissociate events in the display. - var kind: String - - # GameEvents have raw data associated to them. - # - # These data are stored in a JsonObject. - var data: JsonObject is writable - - # GameEvent uniq id used for storage. - var internal_id: String is noinit - - redef var key = internal_id is lazy - - # Date and time of the event. - var time: ISODate is noinit, writable - - # An event initialized at now `time`. - init do - internal_id = "{get_time}{object_id}{100.rand}" - time = new ISODate - end - - # Init `self` from a `json` object. - # - # Used to load events from json storage. - init from_json(game: Game, json: JsonObject) do - init(game, json["kind"].as(String), json["data"].as(JsonObject)) - internal_id = json["internal_id"].as(String) - time = new ISODate.from_string(json["time"].as(String)) - end - - redef fun to_json_object do - var json = new JsonObject - json["internal_id"] = internal_id.to_s - json["kind"] = kind - json["time"] = time.to_s - json["data"] = data - json["game"] = game.key - var owner = self.owner - if owner != null then json["owner"] = owner.key - return json - end -end - -# Compare `GameEvent` to sort them from the most recent to the older. -class EventTimeComparator - super Comparator - - redef type COMPARED: GameEvent - - redef fun compare(a, b) do return b.time <=> a.time -end diff --git a/contrib/nitrpg/src/events_generator.nit b/contrib/nitrpg/src/events_generator.nit deleted file mode 100644 index 9e5bf30..0000000 --- a/contrib/nitrpg/src/events_generator.nit +++ /dev/null @@ -1,93 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Generate Github events from repo data. -# -# Mainly used for testing and history importation. -module events_generator - -import github::events - -# Github events generator -# -# Generates events from repo data. -class EventsGenerator - - # API client used to get github data. - var api: GithubAPI - - # Gen a fake id for events - fun gen_event_id: String do return get_time.to_s - - # Issues - - # Generate a new IssuesEvent from an issue. - fun issues_event(repo: Repo, action: String, issue: Issue): IssuesEvent do - return new IssuesEvent(gen_event_id, action, repo, issue) - end - - # Generate a new IssuesEvent with an `opened` action. - fun issue_open(repo: Repo, issue: Issue): IssuesEvent do - return issues_event(repo, "opened", issue) - end - - # Generate a new IssuesEvent with an `closed` action. - fun issue_close(repo: Repo, issue: Issue): IssuesEvent do - return issues_event(repo, "closed", issue) - end - - # Generate a new IssuesEvent with an `reopened` action. - fun issue_reopen(repo: Repo, issue: Issue): IssuesEvent do - return issues_event(repo, "reopened", issue) - end - - # Generate a new IssuesEvent from a IssueEvent. - fun issue_raw_event(repo: Repo, issue: Issue, event: IssueEvent): IssuesEvent do - return new IssuesEvent(event.id.to_s, event.event, repo, issue, event.labl, event.assignee) - end - - # Generate a new IssueCommentEvent from a IssueComment. - fun issue_comment_event(repo: Repo, issue: Issue, comment: IssueComment): IssueCommentEvent do - return new IssueCommentEvent(gen_event_id, "created", repo, issue, comment) - end - - # Pull requests - - # Generate a new PullRequestEvent from a `pull` request. - fun pull_event(repo: Repo, action: String, pull: PullRequest): PullRequestEvent do - return new PullRequestEvent(gen_event_id, action, repo, pull.number, pull) - end - - # Generate a new PullRequestEvent with an `opened` action. - fun pull_open(repo: Repo, pull: PullRequest): PullRequestEvent do - return pull_event(repo, "opened", pull) - end - - # Generate a new PullRequestEvent with an `closed` action. - fun pull_close(repo: Repo, pull: PullRequest): PullRequestEvent do - return pull_event(repo, "closed", pull) - end - - # Generate a new PullRequestEvent with an `reopened` action. - fun pull_reopen(repo: Repo, pull: PullRequest): PullRequestEvent do - return pull_event(repo, "reopened", pull) - end - - # Generate a new PullRequestEvent from a IssueEvent. - fun pull_raw_event(repo: Repo, pull: PullRequest, event: IssueEvent): PullRequestEvent do - return new PullRequestEvent(event.id.to_s, event.event, repo, pull.number, pull) - end -end diff --git a/contrib/nitrpg/src/game.nit b/contrib/nitrpg/src/game.nit deleted file mode 100644 index 5413f43..0000000 --- a/contrib/nitrpg/src/game.nit +++ /dev/null @@ -1,337 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# `nitrpg` game structures. -# -# Here we define the main game entities: -# -# * `Game` holds all the entities for a game and provides high level services. -# * `Player` represents a `Github::User` which plays the `Game`. -# -# Developpers who wants to extend the game capabilities should look at -# the `GameReactor` abstraction. -module game - -import mongodb -import github::events - -# An entity within a `Game`. -# -# All game entities can be saved in a json format. -interface GameEntity - # The game instance containing `self`. - fun game: Game is abstract - - # Collection `self` should be saved in. - fun collection_name: String is abstract - - # Uniq key of this entity within the collection. - fun key: String is abstract - - # Saves `self` in db. - fun save do game.db.collection(collection_name).save(to_json_object) - - # Json representation of `self`. - fun to_json_object: JsonObject do - var json = new JsonObject - json["_id"] = key - return json - end - - # Pretty print `self` to be displayed in a terminal. - fun pretty: String is abstract -end - -# Holder for game data and main services. -# -# Game is a `GameEntity` so it can be saved. -class Game - super GameEntity - - redef fun game do return self - - # We need a `GithubAPI` client to load Github data. - var api: GithubAPI - - # A game takes place in a `github::Repo`. - var repo: Repo - - # Game name - var name: String = repo.full_name is lazy - - redef var key = name is lazy - - # Mongo server url where this game data are stored. - var mongo_url = "mongodb://mongo:27017" is writable - - # Mongo db client. - var client = new MongoClient(mongo_url) is lazy - - # Mongo db name where this game data are stored. - var db_name = "nitrpg" is writable - - # Mongo db instance for this game. - var db: MongoDb is lazy do return client.database(db_name) - - redef var collection_name = "games" - - # Init the Game and try to load saved data. - init from_mongo(api: GithubAPI, repo: Repo) do - init(api, repo) - var req = new JsonObject - req["name"] = repo.full_name - var res = db.collection("games").find(req) - if res != null then from_json(res) - end - - # Init `self` from a JsonObject. - # - # Used to load entities from saved data. - fun from_json(json: JsonObject) do end - - redef fun to_json_object do - var json = super - json["name"] = name - return json - end - - # Create a player from a Github `User`. - # - # Or return the existing one from game data. - fun add_player(user: User): Player do - # check if player already exists - var player = load_player(user.login) - if player != null then return player - # create and store new player - player = new Player(self, user.login) - player.save - return player - end - - # Get a Player from his `name` or null if no player was found. - # - # Looks for the player save file in game data. - # - # Returns `null` if the player cannot be found. - # In this case, the player can be created with `add_player`. - fun load_player(name: String): nullable Player do - var req = new JsonObject - req["name"] = name - req["game"] = game.key - var res = db.collection("players").find(req) - if res != null then return new Player.from_json(self, res) - return null - end - - # List known players. - # - # This list is reloaded from game data each time its called. - # - # To add players see `add_player`. - fun load_players: MapRead[String, Player] do - var req = new JsonObject - req["game"] = game.key - var res = new HashMap[String, Player] - for obj in db.collection("players").find_all(req) do - var player = new Player.from_json(self, obj) - res[player.name] = player - end - return res - end - - # Return a list of player name associated to their rank in the game. - fun player_ranking: MapRead[String, Int] do - var arr = load_players.values.to_a - var res = new HashMap[String, Int] - (new PlayerCoinComparator).sort(arr) - var rank = 1 - for player in arr do - res[player.name] = rank - rank += 1 - end - return res - end - - # Erase all saved data for this game. - fun clear do db.collection(collection_name).remove(to_json_object) - - # Verbosity level used fo stdout. - # - # * `-1` quiet - # * `0` error and warnings - # * `1` info - # * `2` debug - var verbose_lvl = 0 is writable - - # Display `msg` if `lvl` >= `verbose_lvl` - fun message(lvl: Int, msg: String) do - if lvl > verbose_lvl then return - print msg - end - - redef fun pretty do - var res = new FlatBuffer - res.append "-------------------------\n" - res.append "{repo.full_name}\n" - res.append "-------------------------\n" - res.append "# {load_players.length} players \n" - return res.write_to_string - end -end - -# Players can battle on nitrpg for nitcoins and glory. -# -# A `Player` is linked to a `Github::User`. -class Player - super GameEntity - - # Stored in collection `players`. - redef var collection_name = "players" - - redef var game - - # FIXME contructor should be private - - # Player name. - # - # This is the unic key for this player. - # Should be equal to the associated `Github::User::login`. - # - # The name is also used to load the user data lazilly from Github API. - var name: String - - redef var key = name is lazy - - # Player amount of nitcoins. - # - # Nitcoins is the currency used in nitrpg. - # They can be obtained by performing actions on the `Game::Repo`. - var nitcoins: Int = 0 is public writable - - # `Github::User` linked to this player. - var user: User is lazy do - var user = game.api.load_user(name) - assert user isa User - return user - end - - # Init `self` from a `json` object. - # - # Used to load players from saved data. - init from_json(game: Game, json: JsonObject) do - init(game, json["name"].as(String)) - nitcoins = json["nitcoins"].as(Int) - end - - redef fun to_json_object do - var json = super - json["game"] = game.key - json["name"] = name - json["nitcoins"] = nitcoins - return json - end - - redef fun pretty do - var res = new FlatBuffer - res.append "-- {name} ({nitcoins} $)\n" - return res.write_to_string - end - - redef fun to_s do return name -end - -redef class User - # The player linked to `self`. - fun player(game: Game): Player do - var player = player_cache.get_or_null(game) - if player != null then return player - player = game.load_player(login) - if player == null then player = game.add_player(self) - player_cache[game] = player - return player - end - - private var player_cache = new HashMap[Game, Player] -end - -# A GameReactor reacts to event sent by a `Github::HookListener`. -# -# Subclasses of `GameReactor` are implemented to handle all kind of -# `GithubEvent`. -# Depending on the received event, the reactor is used to update game data. -# -# Reactors are mostly used with a `Github::HookListener` that dispatchs received -# events from the Github API. -# -# Example: -# -# ~~~ -# import github::hooks -# -# # Reactor that prints received events in console. -# class PrintReactor -# super GameReactor -# -# redef fun react_event(game, e) do print e -# end -# -# # Hook listener that redirect events to reactors. -# class RpgHookListener -# super HookListener -# -# redef fun apply_event(event) do -# var game = new Game(api, event.repo) -# var reactor = new PrintReactor -# reactor.react_event(game, event) -# end -# end -# ~~~ -# -# See module `reactors` and `listener` for more examples. -interface GameReactor - - # Reacts to this `event` and update `game` accordingly. - # - # Concrete `GameReactor` implement this method to update game data - # for each specific GithubEvent. - fun react_event(game: Game, event: GithubEvent) is abstract -end - -# utils - -# Sort games by descending number of players. -# -# The first in the list is the game with the more players. -class GamePlayersComparator - super Comparator - - redef type COMPARED: Game - - redef fun compare(a, b) do - return b.load_players.length <=> a.load_players.length - end -end - -# Sort players by descending number of nitcoins. -# -# The first in the list is the player with the more of nitcoins. -class PlayerCoinComparator - super Comparator - - redef type COMPARED: Player - - redef fun compare(a, b) do return b.nitcoins <=> a.nitcoins -end diff --git a/contrib/nitrpg/src/listener.nit b/contrib/nitrpg/src/listener.nit deleted file mode 100644 index 8bee99b..0000000 --- a/contrib/nitrpg/src/listener.nit +++ /dev/null @@ -1,70 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This tool is runned to listen to `Github::Event` and update the game. -module listener - -import reactors -import achievements -import github::hooks - -# `HookListener` that redirects events to a `Game` instance. -class RpgHookListener - super HookListener - - # Registered reactors list. - var reactors = new Array[GameReactor] - - # Dispatch event to registered `reactors`. - redef fun apply_event(event) do - var game = new Game(api, event.repo) - # TODO handle verbosity with opts - game.verbose_lvl = 1 - game.message(1, "Received event {event} for {game.repo.full_name}") - for reactor in reactors do - game.message(2, "Apply reactor {reactor} on {event}") - reactor.react_event(game, event) - end - end - - # Register a reactor for this listener. - fun add_reactor(reactors: GameReactor...) do self.reactors.add_all reactors -end - -if args.length != 2 then - print "Error: missing argument" - print "" - print "Usage:" - print "listener " - exit 1 -end - -var host = args[0] -var port = args[1].to_i - -var api = new GithubAPI(get_github_oauth) - -var l = new RpgHookListener(api, host, port) -l.add_reactor(new StatisticsReactor, new PlayerReactor) -l.add_reactor(new Player1Issue, new Player100Issues, new Player1KIssues) -l.add_reactor(new Player1Pull, new Player100Pulls, new Player1KPulls) -l.add_reactor(new Player1Commit, new Player100Commits, new Player1KCommits) -l.add_reactor(new IssueAboutNitdoc, new IssueAboutFFI) -l.add_reactor(new Player1Comment, new Player100Comments, new Player1KComments) -l.add_reactor(new PlayerPingGod, new PlayerFirstReview, new PlayerSaysNitcoin) - -print "Listening events on {host}:{port}" -l.listen diff --git a/contrib/nitrpg/src/reactors.nit b/contrib/nitrpg/src/reactors.nit deleted file mode 100644 index 616bd8a..0000000 --- a/contrib/nitrpg/src/reactors.nit +++ /dev/null @@ -1,115 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Various implementations of `GameReactor` can be found here. -module reactors - -import events - -# Reacts to event that can affect players (like giving nitcoins). -class PlayerReactor - super GameReactor - - # Nitcoins rewarded when the player opens a new pull request. - var nc_pull_open = 10 - - # Nitcoins rewarded when the player reviews a pull request. - var nc_pull_review = 2 - - # Nitcoins rewarded when the player has a commit merged. - var nc_commit_merged = 1 - - redef fun react_event(game, e) do e.react_player_event(self, game) -end - -redef class GithubEvent - # Reacts to a player related event. - # - # Called by `PlayerReactor::react_event`. - # No-op by default. - private fun react_player_event(reactor: PlayerReactor, game: Game) do end - - # Generates a GameEvent preinitialized for a reward event. - private fun player_reward_event(kind: String, player: Player, reward: Int): GameEvent do - var obj = new JsonObject - obj["player"] = player.name - obj["reward"] = reward - obj["github_event"] = self - var event = new GameEvent(player.game, kind, obj) - player.game.add_event(event) - return event - end -end - -redef class PullRequestEvent - - # Rewards player for opened pull requests. - redef fun react_player_event(r, game) do - if action == "opened" or action == "reopened" then - react_pull_open(r, game) - else if action == "closed" then - react_pull_close(r, game) - end - end - - private fun react_pull_open(r: PlayerReactor, game: Game) do - var player = pull.user.player(game) - player.nitcoins += r.nc_pull_open - player.save - var event = player_reward_event("pull_open", player, r.nc_pull_open) - player.add_event(event) - end - - private fun react_pull_close(r: PlayerReactor, game: Game) do - var player = pull.user.player(game) - var reward - var event - if pull.merged then - reward = pull.commits * r.nc_commit_merged - event = player_reward_event("pull_merged", player, reward) - else - reward = -r.nc_pull_open - event = player_reward_event("pull_closed", player, reward) - end - player.nitcoins += reward - player.save - player.add_event(event) - end -end - -redef class IssueCommentEvent - - # Rewards player for review comments. - # - # TODO only give nitcoins if reviewers < 2 - # TODO give more points to first reviewer - redef fun react_player_event(r, game) do - if comment.is_ack then - react_player_review(r, game) - end - end - - # TODO same player should not be authorized to review multiple times? How to handle rerols? - private fun react_player_review(r: PlayerReactor, game: Game) do - if issue.state == "closed" then return - var player = comment.user.player(game) - if issue.user == player.user then return - player.nitcoins += r.nc_pull_review - player.save - var event = player_reward_event("pull_review", player, r.nc_pull_review) - player.add_event(event) - end -end diff --git a/contrib/nitrpg/src/statistics.nit b/contrib/nitrpg/src/statistics.nit deleted file mode 100644 index 764616f..0000000 --- a/contrib/nitrpg/src/statistics.nit +++ /dev/null @@ -1,282 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Statistics about the Game. -# -# This module uses `GameReactor` to extract statistics about the game from -# triggered `Github::Event`. -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 - - redef var stats is lazy do return new GameStatsManager(game, self) - - 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 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 - res.append super - res.append "# stats:\n" - res.append stats.pretty - return res.write_to_string - end - - redef fun save do - super - stats.save - end -end - -# Store game stats for defined period. -class GameStatsManager - super GameEntity - super Counter[String] - - redef var game - - # The GameEntity monitored by these statistics. - var owner: GameEntity - - # Current date to extract stats - private var date = new Tm.gmtime - - # Returns the `GameStats` instance for the overall statistics. - var overall: GameStats = load_stats_for("all") is lazy - - # 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 [](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 - - 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 - - redef var collection_name = "statistics" - - # The period these stats are about. - var period: String - - # 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 self do - res.append "# {v} {k}\n" - end - return res.write_to_string - end -end - -# `GameReactor` that computes statistics about the game. -class StatisticsReactor - super GameReactor - - redef fun react_event(game, e) do e.react_stats_event(game) -end - -redef class GithubEvent - # Reacts to a statistics related event. - # - # Called by `StatisticsReactor::react_event`. - # No-op by default. - private fun react_stats_event(game: Game) do end -end - -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.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.inc("issues_open") - game.save - player.stats.inc("issues_open") - player.save - else if action == "closed" then - game.stats.dec("issues_open") - game.save - player.stats.dec("issues_open") - player.save - end - end -end - -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.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.inc("pulls_open") - game.save - player.stats.inc("pulls_open") - player.save - else if action == "closed" then - 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 diff --git a/contrib/nitrpg/src/templates/panels.nit b/contrib/nitrpg/src/templates/panels.nit deleted file mode 100644 index 8f27a5f..0000000 --- a/contrib/nitrpg/src/templates/panels.nit +++ /dev/null @@ -1,569 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Panels templates for `nitpg`. -module panels - -import templates_events -import markdown - -# A panel can be displayed in a html page. -# -# This display a Bootstrap panel. -class Panel - super Template - - redef fun rendering do - add """
-
-

""" - render_title - add """

-
-
""" - render_body - add """
-
""" - end - - # Render the panel title. - # Betweem `

` tags. - fun render_title do end - - # Render the panel body. - fun render_body do end -end - -# A panel that contain only a table as body. -class TablePanel - super Panel - - redef fun rendering do - add """
-
-

""" - render_title - add """ -

-
""" - render_body - add """
""" - end -end - -# Display an error message within a panel. -class ErrorPanel - super Panel - - redef fun rendering do - add """ -
-
-

""" - render_title - add """ -

-
-
""" - render_body - add """ -
-
-""" - end - - # The error message to display as panel body. - var msg: String - - redef fun render_title do - add "  " - add "Error" - end - - redef fun render_body do - add msg.html_escape - end - -end - -# A panel that display a markdown content rendered as HTML. -class MDPanel - super Panel - - # Markdown text to display. - var text: String - - redef fun rendering do - add """
-
{{{text.md_to_html}}}
-
""" - end -end - -# Display a list of active game. -# -# Used for NitRPG homepage. -class GamesShortListPanel - super Panel - - # Root url used for links. - var root_url: String - - # List of NitRPG games to display. - var games: Array[Game] - - redef fun render_title do - add "  " - add "Active games" - end - - redef fun render_body do - if games.is_empty then - add "No game yet..." - return - end - var sorted = games.to_a - (new GamePlayersComparator).sort(sorted) - for game in sorted do - add "{game.link} ({game.load_players.length} players)
" - end - end -end - -# A panel that display a list of player in a repo. -class GamesListPanel - super GamesShortListPanel - super TablePanel - - redef fun render_title do - add "  " - add "Active games" - end - - redef fun render_body do - if games.is_empty then - add "
" - add "No player yet..." - add "
" - return - end - var sorted = games.to_a - (new GamePlayersComparator).sort(sorted) - add """ - - - - - """ - for game in sorted do - add "" - add " " - add " " - add " " - add "" - end - add "
GamePlayersAchievements
{game.link}{game.load_players.length}{game.load_achievements.length}
" - end -end - -# A panel that display repo statistics. -class GameStatusPanel - super Panel - - # Repo to display. - var game: Game - - redef fun render_title do - add "  " - add "{game.link}" - end - - redef fun render_body do - add "{game.load_players.length}" - add " players
" - add "{game.stats["achievements"]}" - add " achievements

" - add "{game.stats["pulls"]} pull requests" - add " ({game.stats["pulls_open"]} open)
" - add "{game.stats["issues"]} issues" - add " ({game.stats["issues_open"]} open)
" - add "{game.stats["commits"]} commits" - end -end - -# Player status panel. -class PlayerStatusPanel - super Panel - - # Game instance. - var game: Game - - # Target player. - var player: Player - - redef fun render_title do - add "" - add " \"{player.name}\"" - add "  {player.link}" - end - - redef fun render_body do - var ranking = game.player_ranking - # TODO player.rank - add "

ranked " - add " # {ranking[player.name]}

" - add "{player.nitcoins} nitcoins

" - add "{player.stats["achievements"]} achievements

" - add "{player.stats["pulls"]} pull requests
" - add "{player.stats["issues"]} issues
" - add "{player.stats["commits"]} commits" - end -end - -# A panel that display a list of player in a repo. -class ShortListPlayersPanel - super Panel - - # Game instance. - var game: Game - - redef fun render_title do - add "  " - add "Players" - end - - redef fun render_body do - var players = game.load_players.values.to_a - if players.is_empty then - add "No player yet..." - return - end - (new PlayerCoinComparator).sort(players) - for player in players do - add "{player.nitcoins} - {player.link}
" - end - end -end - -# A panel that display a list of player in a repo. -class ListPlayersPanel - super TablePanel - - # Game instance. - var game: Game - - redef fun render_title do - add "  " - add "Players" - end - - redef fun render_body do - var players = game.load_players.values.to_a - (new PlayerCoinComparator).sort(players) - if players.is_empty then - add "
" - add "No player yet..." - add "
" - return - end - add """ - - - - - """ - var rank = 1 - for player in players do - add "" - add " " - add " " - add " " - add "" - rank += 1 - end - add "
#PlayerNitcoins
{rank}{player.link}{player.nitcoins}
" - end -end - -# A panel that display the podium. -class PodiumPanel - super Panel - - # Game instance. - var game: Game - - redef fun render_title do - add "  Hall of fame" - end - - redef fun render_body do - var players = game.load_players.values.to_a - (new PlayerCoinComparator).sort(players) - if players.is_empty then - add "No players yet..." - return - end - add """ -
-
""" - var max = players.first.nitcoins - var orders = [3, 1, 0, 2, 4] - for order in orders do - if order >= players.length then continue - var player = players[order] - var size = 0 - if max > 0 then size = player.nitcoins * 300 / max - add """ -
-

- - {{{player.name}}} - -

-

{{{player.link}}}

-

{{{player.nitcoins}}}

-
-
""" - end - add """ -
-
""" - end -end - -# A `Panel` that displays the list of PR to review for a `Player`. -class PlayerReviewsPanel - super Panel - - # Repo to display. - var game: Game - - # Player to display customized list for. - var player: Player - - redef fun render_title do - add "  " - add "Review pull requests and comment issues to gain nitcoins!" - end - - redef fun render_body do - var q = "is:open label:need_review sort:updated-asc " + - "-involves:{player.name}" - - var q2 = "is:open label:request_for_comments sort:updated-asc " + - "-involves:{player.name}" - - var issues = new ArraySet[Issue] - issues.add_all game.api.search_repo_issues(game.repo, q) - issues.add_all game.api.search_repo_issues(game.repo, q2) - if issues.is_empty then - add "No pull request or issue to review yet..." - return - end - for issue in issues do - var user = issue.user - var uplay = user.player(game) - add """
- - {{{uplay.name}}} - -
-

- {{{issue.link}}} {{{issue.title}}} -

- opened by - {{{uplay.link}}} -
-
""" - end - end -end - -# A `Panel` that displays the work assigned or tagged. -class PlayerWorkPanel - super Panel - - # Repo to display. - var game: Game - - # Player to display customized list for. - var player: Player - - redef fun render_title do - add "  " - add "Do your tasks to gain nitcoins!" - end - - redef fun render_body do - var q = "is:open label:need_work sort:updated-asc author:{player.name}" - var q2 = "is:open sort:updated-asc assignee:{player.name}" - - var issues = new ArraySet[Issue] - issues.add_all game.api.search_repo_issues(game.repo, q) - issues.add_all game.api.search_repo_issues(game.repo, q2) - if issues.is_empty then - add "No work to do yet..." - return - end - for issue in issues do - var user = issue.user - var uplay = user.player(game) - add """
- - {{{uplay.name}}} - -
-

- {{{issue.link}}} {{{issue.title}}} -

- opened by - {{{uplay.link}}} -
-
""" - end - end -end - -# A `Panel` that displays a pagined list of events stored in the `entity`. -# -# This way the panel can be used to view events stored under `Game`, `Player`... -class EventListPanel - super Panel - - # Entity to load the events from. - var entity: GameEntity - - # Number of events to display. - var limit: Int - - # From where to start? - var from: Int - - redef fun render_title do - add "  " - add "Last events" - end - - redef fun render_body do - var events = entity.load_events - if events.is_empty then - add "No event yet..." - return - end - # check input - if limit < 0 then limit = 10 - if from < 0 then from = 0 - # display events - for i in [from .. from + limit] do - if i >= events.length then break - add events[i].tpl_event.media_item - end - # pagination - if limit > events.length then return - add "
" - add """
""" - if from > 0 then - add """ - """ - end - if from + limit < events.length then - add """ - - """ - end - add "
" - end -end - -# Achievement unlocked list panel. -class AchievementsListPanel - super Panel - - # Entity to load the events from. - var entity: GameEntity - - redef fun render_title do - add "  " - add "Achievements unlocked" - end - - redef fun render_body do - var achs = entity.load_achievements.values.to_a - if achs.is_empty then - add "No achievement yet..." - return - end - for ach in achs do add ach.list_item - end -end - -# Achievement detail panel. -class AchievementPanel - super Panel - - # Achievement to display. - var achievement: Achievement - - redef fun render_title do - add "  " - add "Achievement details" - end - - redef fun render_body do - add """

- +{{{achievement.reward}}} - {{{achievement.name}}} -

-

{{{achievement.desc}}}

""" - - var events = achievement.load_events - - if events.is_empty then - add "Never unlocked..." - return - end - - var event = events.last - var tpl = event.tpl_event - var player = tpl.player - add "
" - add """
- - #1 - {{{player.name}}} - -
-

Unlocked first by {{{player.link}}}

- at {{{event.time}}} -
-
""" - - if events.length > 1 then - add """


Also unlocked by - {{{events.length}}} players.

""" - end - end -end diff --git a/contrib/nitrpg/src/templates/templates.nit b/contrib/nitrpg/src/templates/templates.nit deleted file mode 100644 index 92a4915..0000000 --- a/contrib/nitrpg/src/templates/templates.nit +++ /dev/null @@ -1,110 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Templates that compose the `nitrpg` site. -module templates - -import panels - -# A page in the nitrp site. -class NitRpgPage - super Template - - # URL used as prefix for all the links generated in this page. - var root_url: String - - # Breadcrumbs to this page if any. - var breadcrumbs: nullable Breadcrumbs = null is public writable - - # Panels to display in the sidebar. - var side_panels = new Array[Panel] - - # Panels to display in the page main container. - var flow_panels = new Array[Panel] - - redef fun rendering do - render_header - render_footer - end - - # Render the header shared by all pages. - fun render_header do - add """ - - - - - Github RPG - - - - - -
-
""" - if not side_panels.is_empty then - add """
""" - for panel in side_panels do add panel - add """
-
""" - else - add """
""" - end - for panel in flow_panels do add panel - add """
-
-
-""" - end - - # Render the footer shared by all pages. - fun render_footer do - add """ - - - - -""" - end -end - -# A Bootstrap breadcrumbs component. -class Breadcrumbs - super Template - - # Items to display in this breadcrumb. - var entries = new Array[String] - - redef fun rendering do - add "
    " - for entry in entries do - add "
  1. {entry}
  2. " - end - add "
" - end - - # Add a link to the breadcrumbs. - fun add_link(href, name: String) do - entries.add "{name}" - end -end diff --git a/contrib/nitrpg/src/templates/templates_base.nit b/contrib/nitrpg/src/templates/templates_base.nit deleted file mode 100644 index fe9efd8..0000000 --- a/contrib/nitrpg/src/templates/templates_base.nit +++ /dev/null @@ -1,74 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Base HTML rendering templates for `nitpg`. -module templates_base - -import achievements - -redef class GameEntity - - # Path to this entity from root. - fun path: String do return collection_name / key - - # URL to this game entity page. - fun url: String do return game.url / path -end - -redef class Game - - # Root URL ise used as a prefix for `url`. - # - # This must be set before any access to `url`. - var root_url: String is noinit, writable - - redef fun url do return "{root_url}/{path}" - - # Return a HTML link to this Game. - fun link: String do return "{name}" -end - -redef class Player - # Return a HTML link to this Player. - fun link: String do return "{name}" -end - -redef class Issue - # Return a HTML link to this Issue. - fun link: String do return "#{number}" -end - -redef class Achievement - # Return a HTML link to this Issue. - fun link: String do return "{name}" - - # Render self as a media item. - fun list_item: String do - return """
-
- - +{{{reward}}} -
-
-

{{{link}}}

- {{{desc}}} -
-
""" - - end -end diff --git a/contrib/nitrpg/src/templates/templates_events.nit b/contrib/nitrpg/src/templates/templates_events.nit deleted file mode 100644 index 3e141b9..0000000 --- a/contrib/nitrpg/src/templates/templates_events.nit +++ /dev/null @@ -1,125 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Templates to display `GameEvent` kinds. -module templates_events - -import achievements -import templates_base - -redef class GameEvent - # See `TplEvent` - fun tpl_event: TplEvent do - if kind == "pull_open" then - return new TplPullOpened(self) - else if kind == "pull_merged" then - return new TplPullMerged(self) - else if kind == "pull_review" then - return new TplPullReview(self) - else if kind == "achievement_unlocked" then - return new TplAchievementUnlocked(self) - end - abort - end -end - -# A TplEvent factorizes HTML rendering methods for `GameEvent`. -class TplEvent - - # Event to display. - var event: GameEvent - - # Title to display. - var title: String is lazy do return "raw event" - - # Load Player from event data. - var player: nullable Player is lazy do - return event.game.load_player(event.data["player"].to_s) - end - - # Load reward from event data. - var reward: Int is lazy do return event.data["reward"].as(Int) - - # Load `github_event` data key as a PullRequestEvent. - var pull_event: PullRequestEvent is lazy do - return event.game.api.deserialize(event.data["github_event"].as(JsonObject).to_json).as(PullRequestEvent) - end - - # Load `github_event` data key as a IssueCommentEvent. - var issue_comment_event: IssueCommentEvent is lazy do - return event.game.api.deserialize(event.data["github_event"].as(JsonObject).to_json).as(IssueCommentEvent) - end - - # Load `achievement` data key as an Achievement. - var achievement: Achievement is lazy do - return player.load_achievement(event.data["achievement"].to_s).as(not null) - end - - # Display a media item for a reward event. - fun media_item: String do - return """
- - +{{{reward}}} - {{{player.name}}} - -
-

{{{title}}}

- at {{{event.time}}} -
-
""" - end -end - -# Event: pull_open -class TplPullOpened - super TplEvent - - redef var title is lazy do - var pull = pull_event.pull - return "{player.link} pushed {pull.link}" - end -end - -# Event: pull_merged -class TplPullMerged - super TplEvent - - redef var title is lazy do - var pull = pull_event.pull - return "{player.link} merged {pull.commits} commits with {pull.link}" - end -end - -# Event: pull_review -class TplPullReview - super TplEvent - - redef var title is lazy do - var issue = issue_comment_event.issue - return "{player.link} reviewed {issue.link}" - end -end - -# Event: achievement_unlocked -class TplAchievementUnlocked - super TplEvent - - redef var title is lazy do - return "{player.link} unlocked {achievement.link}" - end -end diff --git a/contrib/nitrpg/src/test_achievements.nit b/contrib/nitrpg/src/test_achievements.nit deleted file mode 100644 index 41d9dd1..0000000 --- a/contrib/nitrpg/src/test_achievements.nit +++ /dev/null @@ -1,140 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Test module for `achievements.nit` -module test_achievements is test - -import test_helper -import achievements - -class TestGame - super NitrpgTestHelper - test - - fun test_add_achievement is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15) - var a2 = new Achievement(game, "test_id2", "test_name", "test_desc", 15) - game.add_achievement(a1) - game.add_achievement(a2) - assert game.load_achievements.length == 2 - end - - fun test_load_achievement is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15) - var a2 = new Achievement(game, "test_id2", "test_name", "test_desc", 15) - game.add_achievement(a1) - assert game.load_achievement(a1.id).id == "test_id1" - assert game.load_achievement(a2.id) == null - end - - fun test_load_achievements is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15) - var a2 = new Achievement(game, "test_id2", "test_name", "test_desc", 15) - var a3 = new Achievement(game, "test_id3", "test_name", "test_desc", 15) - game.add_achievement(a1) - game.add_achievement(a2) - game.db.collection("achievements").insert(a3.to_json_object) - var ok = [a1.id, a2.id] - var res = game.load_achievements - assert res.length == 2 - for a in res.values do assert ok.has(a.id) - end -end - -class TestPlayer - super NitrpgTestHelper - test - - fun test_add_achievement is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var player1 = new Player(game, "Morriar") - var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15) - var a2 = new Achievement(game, "test_id2", "test_name", "test_desc", 15) - player1.add_achievement(a1) - player1.add_achievement(a2) - assert player1.load_achievements.length == 2 - end - - fun test_load_achievement is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var player1 = new Player(game, "Morriar") - var player2 = new Player(game, "xymus") - var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15) - var a2 = new Achievement(game, "test_id2", "test_name", "test_desc", 15) - player1.add_achievement(a1) - player2.add_achievement(a2) - assert player1.load_achievement(a1.id).id == "test_id1" - assert player1.load_achievement(a2.id) == null - assert player2.load_achievement(a2.id).id == "test_id2" - assert player2.load_achievement(a1.id) == null - end - - fun test_load_achievements is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var player1 = new Player(game, "Morriar") - var player2 = new Player(game, "xymus") - var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15) - var a2 = new Achievement(game, "test_id2", "test_name", "test_desc", 15) - var a3 = new Achievement(game, "test_id3", "test_name", "test_desc", 15) - player1.add_achievement(a1) - player1.add_achievement(a2) - player2.add_achievement(a3) - var ok = [a1.id, a2.id] - var res = player1.load_achievements - assert res.length == 2 - for a in res.values do assert ok.has(a.id) - end -end - -class TestAchievement - super NitrpgTestHelper - test - - fun test_init is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var a = new Achievement(game, "test_id", "test_name", "test_desc", 15) - assert a.id == "test_id" - assert a.name == "test_name" - assert a.desc == "test_desc" - assert a.reward == 15 - end - - fun test_init_from_json is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var json = """{ - "id": "test_id", - "name": "test_name", - "desc": "test_desc", - "reward": 15 - }""".parse_json.as(JsonObject) - var a = new Achievement.from_json(game, json) - assert a.id == "test_id" - assert a.name == "test_name" - assert a.desc == "test_desc" - assert a.reward == 15 - end -end diff --git a/contrib/nitrpg/src/test_events.nit b/contrib/nitrpg/src/test_events.nit deleted file mode 100644 index efe24cf..0000000 --- a/contrib/nitrpg/src/test_events.nit +++ /dev/null @@ -1,139 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Test module for `events.nit` -module test_events is test - -import test_helper -import events - -class TestGame - super NitrpgTestHelper - test - - fun test_add_event is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var event1 = new GameEvent(game, "test_kind", new JsonObject) - var event2 = new GameEvent(game, "test_kind", new JsonObject) - game.add_event(event1) - game.add_event(event2) - assert game.load_events.length == 2 - end - - fun test_load_event is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var event1 = new GameEvent(game, "test_kind", new JsonObject) - var event2 = new GameEvent(game, "test_kind", new JsonObject) - game.add_event(event1) - assert game.load_event(event1.internal_id).kind == "test_kind" - assert game.load_event(event2.internal_id) == null - end - - fun test_load_events is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var event1 = new GameEvent(game, "test_kind", new JsonObject) - var event2 = new GameEvent(game, "test_kind", new JsonObject) - var event3 = new GameEvent(game, "test_kind", new JsonObject) - game.add_event(event1) - game.add_event(event2) - game.db.collection("events").insert(event3.to_json_object) - var ok = [event1.internal_id, event2.internal_id] - var res = game.load_events - assert res.length == 2 - for event in res do assert ok.has(event.internal_id) - end -end - -class TestPlayer - super NitrpgTestHelper - test - - fun test_add_event is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var player1 = new Player(game, "Morriar") - var player2 = new Player(game, "xymus") - var event1 = new GameEvent(game, "test_kind", new JsonObject) - var event2 = new GameEvent(game, "test_kind", new JsonObject) - player1.add_event(event1) - player1.add_event(event2) - assert player1.load_events.length == 2 - assert player2.load_events.length == 0 - end - - fun test_load_event is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var player1 = new Player(game, "Morriar") - var player2 = new Player(game, "xymus") - var event1 = new GameEvent(game, "test_kind", new JsonObject) - var event2 = new GameEvent(game, "test_kind", new JsonObject) - player1.add_event(event1) - player2.add_event(event2) - assert player1.load_event(event1.internal_id).kind == "test_kind" - assert player1.load_event(event2.internal_id) == null - assert player2.load_event(event2.internal_id).kind == "test_kind" - assert player2.load_event(event1.internal_id) == null - end - - fun test_load_events is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var player1 = new Player(game, "Morriar") - var player2 = new Player(game, "xymus") - var event1 = new GameEvent(game, "test_kind", new JsonObject) - var event2 = new GameEvent(game, "test_kind", new JsonObject) - var event3 = new GameEvent(game, "test_kind", new JsonObject) - player1.add_event(event1) - player1.add_event(event2) - player2.add_event(event3) - assert player1.load_events.length == 2 - assert player2.load_events.length == 1 - var ok = [event1.internal_id, event2.internal_id] - for event in player1.load_events do assert ok.has(event.internal_id) - end -end - -class TestGameEvent - super NitrpgTestHelper - test - - fun test_init is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var event = new GameEvent(game, "test_kind", new JsonObject) - assert event.to_json_object["kind"] == "test_kind" - end - - fun test_init_from_json is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var json = """{ - "internal_id": "test_id", - "kind": "test_kind", - "time": "2015-02-05T00:00:00Z", - "data": {"test_field": "test_value"} - }""".parse_json.as(JsonObject) - var event = new GameEvent.from_json(game, json) - assert event.internal_id == "test_id" - assert event.kind == "test_kind" - assert event.data.to_json == """{"test_field":"test_value"}""" - assert event.time.to_s == "2015-02-05T00:00:00Z" - end -end diff --git a/contrib/nitrpg/src/test_game.nit b/contrib/nitrpg/src/test_game.nit deleted file mode 100644 index c2aa677..0000000 --- a/contrib/nitrpg/src/test_game.nit +++ /dev/null @@ -1,140 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Test module for `game.nit`. -module test_game is test - -import test_helper - -class TestGame - super NitrpgTestHelper - test - - fun test_add_player is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var users = ["Morriar", "xymus"] - for name in users do - game.add_player(game.api.load_user(name).as(not null)) - end - var res = game.load_players.values - assert res.length == 2 - for player in res do - assert users.has(player.name) - end - end - - fun test_load_player is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var ogame = load_game("Morriar/nit", db) - - var player1 = new Player(game, "Morriar") - var player2 = new Player(ogame, "privat") - game.db.collection("players").insert(player1.to_json_object) - ogame.db.collection("players").insert(player2.to_json_object) - - assert game.load_player("privat") == null - assert game.load_player("Morriar").name == "Morriar" - assert ogame.load_player("privat").name == "privat" - assert ogame.load_player("Morriar") == null - end - - fun test_load_players is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var ogame = load_game("Morriar/nit", db) - - var player1 = new Player(game, "Morriar") - var player2 = new Player(ogame, "privat") - var player3 = new Player(game, "xymus") - game.db.collection("players").insert(player1.to_json_object) - ogame.db.collection("players").insert(player2.to_json_object) - game.db.collection("players").insert(player3.to_json_object) - - var players = game.load_players - var ok = ["Morriar", "xymus"] - for player in players.values do assert ok.has(player.name) - end -end - -class TestPlayer - super NitrpgTestHelper - test - - fun test_init is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var player = new Player(game, "Morriar") - assert player.name == "Morriar" - assert player.user.login == "Morriar" - assert player.nitcoins == 0 - end - - fun test_init_from_json is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var json = """{"name": "Morriar", "nitcoins": 10}""".parse_json - var player = new Player.from_json(game, json.as(JsonObject)) - assert player.name == "Morriar" - assert player.user.login == "Morriar" - assert player.nitcoins == 10 - end - - fun test_save is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var json = """{"name": "Morriar", "nitcoins": 10}""".parse_json.as(JsonObject) - var player = new Player.from_json(game, json) - player.save - assert game.db.collection("players").find(json) != null - end - - fun test_game_add_player is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - game.add_player(game.api.load_user("Morriar").as(not null)) - var json = """{"name": "Morriar"}""".parse_json.as(JsonObject) - assert game.db.collection("players").find(json) != null - end - - fun test_game_load_player is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var json = """{"name": "Morriar", "nitcoins": 10}""".parse_json.as(JsonObject) - var player = new Player.from_json(game, json) - player.save - var oplayer = game.load_player("Morriar") - assert oplayer != null - assert player.nitcoins == oplayer.nitcoins - end -end - -class TestUser - super NitrpgTestHelper - test - - fun test_player is test do - var db = gen_test_db - var api = new GithubAPI(get_github_oauth) - var game = load_game("privat/nit", db) - var user = api.load_user("Morriar") - assert user != null - var player = user.player(game) - assert player.name == "Morriar" - game.clear - end -end diff --git a/contrib/nitrpg/src/test_helper.nit b/contrib/nitrpg/src/test_helper.nit deleted file mode 100644 index 9d71ca3..0000000 --- a/contrib/nitrpg/src/test_helper.nit +++ /dev/null @@ -1,73 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Test tools for NitRPG. -module test_helper - -import game -import github::cache - -# Used to factorize test treatments. -abstract class NitrpgTestHelper - - # Github API client - var api: GithubAPI do - var api = new GithubAPI(get_github_oauth) - api.enable_cache = true - return api - end - - # Mongo API client - var mongo = new MongoClient("mongodb://mongo:27017/") - - # Load a new test database by with a name - private fun load_db(name: String): MongoDb do return mongo.database(name) - - # Load a repo by its name. - fun load_repo(name: String): Repo do - var repo = api.load_repo(name) - assert repo != null - return repo - end - - # Load a game by its name. - fun load_game(name: String, db: MongoDb): Game do - var game = new Game(api, load_repo(name)) - game.db_name = db.name - return game - end - - # Stack of db used for testing. - var test_dbs = new Array[MongoDb] - - # Gen a test db with a random name (to avoid race conditions). - fun gen_test_db: MongoDb do - var testid = "NIT_TESTING_ID".environ.to_i - var db_name = "test_nitrpg_{testid}" - var db = load_db(db_name) - test_dbs.add db - return db - end - - # Should be called after your test. - fun drop_test_db do - var db = test_dbs.pop - db.drop - end - - # Drop the databse after each test - fun after_test is after do drop_test_db -end diff --git a/contrib/nitrpg/src/test_listener.nit b/contrib/nitrpg/src/test_listener.nit deleted file mode 100644 index d70f3aa..0000000 --- a/contrib/nitrpg/src/test_listener.nit +++ /dev/null @@ -1,359 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Test module for `listener.nit` -module test_listener is test - -import test_helper -import reactors -import achievements -import events_generator - -private class DummyListener - super NitrpgTestHelper - test - - var reactors = new Array[GameReactor] - - fun apply_event(event: GithubEvent, db: MongoDb) do - var game = load_game(event.repo.full_name, db) - for reactor in reactors do - reactor.react_event(game, event) - end - end - - fun add_reactor(reactors: GameReactor...) do self.reactors.add_all reactors -end - -class TestListener - super NitrpgTestHelper - test - - var generator = new EventsGenerator(api) - - var repo: Repo is lazy do return load_repo("Morriar/nit") - - fun test_game_issue_stats is test do - var db = gen_test_db - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - - var issue = api.load_issue(repo, 322) - assert issue != null - - l.apply_event(generator.issue_open(repo, issue), db) - var game = load_game("Morriar/nit", db) - assert game.stats.overall["issues"] == 1 - assert game.stats.overall["issues_open"] == 1 - l.apply_event(generator.issue_close(repo, issue), db) - game = load_game("Morriar/nit", db) - assert game.stats.overall["issues"] == 1 - assert game.stats.overall["issues_open"] == 0 - l.apply_event(generator.issue_reopen(repo, issue), db) - game = load_game("Morriar/nit", db) - assert game.stats.overall["issues"] == 1 - assert game.stats.overall["issues_open"] == 1 - end - - fun test_player_issue_stats is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - - var issue = api.load_issue(repo, 322) - assert issue != null - - l.apply_event(generator.issue_open(repo, issue), db) - var player = new Player(game, "Morriar") - assert player.stats.overall["issues"] == 1 - assert player.stats.overall["issues_open"] == 1 - l.apply_event(generator.issue_close(repo, issue), db) - player = new Player(game, "Morriar") - assert player.stats.overall["issues"] == 1 - assert player.stats.overall["issues_open"] == 0 - l.apply_event(generator.issue_reopen(repo, issue), db) - player = new Player(game, "Morriar") - assert player.stats.overall["issues"] == 1 - assert player.stats.overall["issues_open"] == 1 - end - - fun test_game_pr_stats is test do - var db = gen_test_db - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - - var pr = api.load_pull(repo, 275) - assert pr != null - - l.apply_event(generator.pull_open(repo, pr), db) - var game = load_game("Morriar/nit", db) - assert game.stats.overall["pulls"] == 1 - assert game.stats.overall["pulls_open"] == 1 - assert game.stats.overall["commits"] == 0 - pr.merged = false - l.apply_event(generator.pull_close(repo, pr), db) - game = load_game("Morriar/nit", db) - assert game.stats.overall["pulls"] == 1 - assert game.stats.overall["pulls_open"] == 0 - assert game.stats.overall["commits"] == 0 - l.apply_event(generator.pull_reopen(repo, pr), db) - game = load_game("Morriar/nit", db) - assert game.stats.overall["pulls"] == 1 - assert game.stats.overall["pulls_open"] == 1 - assert game.stats.overall["commits"] == 0 - pr.merged = true - l.apply_event(generator.pull_close(repo, pr), db) - game = load_game("Morriar/nit", db) - assert game.stats.overall["pulls"] == 1 - assert game.stats.overall["pulls_open"] == 0 - assert game.stats.overall["commits"] == 2 - end - - fun test_game_issue_comment_stats is test do - var db = gen_test_db - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - - var issue = api.load_issue(repo, 322) - assert issue != null - var comment = api.load_issue_comment(repo, 76119442) - assert comment != null - - comment.body = "foo bar" - l.apply_event(generator.issue_comment_event(repo, issue, comment), db) - var game = load_game("Morriar/nit", db) - assert game.stats.overall["comments"] == 1 - assert game.stats.overall["reviews"] == 0 - comment.body = "foo +1 bar" - l.apply_event(generator.issue_comment_event(repo, issue, comment), db) - game = load_game("Morriar/nit", db) - assert game.stats.overall["comments"] == 2 - assert game.stats.overall["reviews"] == 1 - end - - fun test_player_pull_reactor is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new PlayerReactor) - - var pull = api.load_pull(repo, 275) - assert pull != null - - l.apply_event(generator.pull_open(repo, pull), db) - var player = new Player(game, "itch76") - assert player.stats.overall["nitcoins"] == 10 - pull.merged = false - l.apply_event(generator.pull_close(repo, pull), db) - player = new Player(game, "itch76") - assert player.stats.overall["nitcoins"] == 0 - l.apply_event(generator.pull_reopen(repo, pull), db) - player = new Player(game, "itch76") - assert player.stats.overall["nitcoins"] == 10 - pull.merged = true - l.apply_event(generator.pull_close(repo, pull), db) - player = new Player(game, "itch76") - assert player.stats.overall["nitcoins"] == 12 - end - - fun test_player_review_reactor is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new PlayerReactor) - - var pull = api.load_pull(repo, 275) - assert pull != null - var comment = api.load_issue_comment(repo, 36961230) - assert comment != null - - # TODO handle multiple review by the same user - - # no review in opened issue - pull.state = "open" - comment.body = "foo bar" - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - var player = new Player(game, "Morriar") - assert player.stats.overall["nitcoins"] == 0 - - # review in opened issue - pull.state = "open" - comment.body = "foo +1 bar" - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - player = new Player(game, "Morriar") - print player.stats.overall["nitcoins"] - assert player.stats.overall["nitcoins"] == 2 - - # review in closed issue - pull.state = "closed" - comment.body = "foo +1 bar" - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - player = new Player(game, "Morriar") - assert player.stats.overall["nitcoins"] == 2 - - # review in reopened issue - pull.state = "open" - comment.body = "foo +1 bar" - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - player = new Player(game, "Morriar") - assert player.stats.overall["nitcoins"] == 4 - end - - fun test_X_issues_achievements is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - l.add_reactor(new Player1Issue, new Player100Issues, new Player1KIssues) - - var issue = api.load_issue(repo, 322) - assert issue != null - - for i in [0, 99, 999] do - var id = "player_{i + 1}_issue" - if i > 0 then id = "{id}s" - var player = new Player(game, "Morriar") - player.stats["issues"] = i - player.save - l.apply_event(generator.issue_open(repo, issue), db) - assert player.load_achievements.has_key(id) - end - var player = new Player(game, "Morriar") - assert player.stats.overall["nitcoins"] == 1110 - end - - fun test_X_pulls_achievements is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - l.add_reactor(new Player1Pull, new Player100Pulls, new Player1KPulls) - - var pull = api.load_pull(repo, 275) - assert pull != null - - for i in [0, 99, 999] do - var id = "player_{i + 1}_pull" - if i > 0 then id = "{id}s" - var player = new Player(game, "itch76") - player.stats["pulls"] = i - player.save - l.apply_event(generator.pull_open(repo, pull), db) - assert player.load_achievements.has_key(id) - end - var player = new Player(game, "itch76") - assert player.stats.overall["nitcoins"] == 1110 - end - - fun test_X_commits_achievements is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - l.add_reactor(new Player1Commit, new Player100Commits) - l.add_reactor(new Player1KCommits, new Player10KCommits) - - var pull = api.load_pull(repo, 275) - assert pull != null - pull.state = "closed" - pull.merged = true - - for i in [0, 99, 999, 9999] do - var id = "player_{i + 1}_commit" - if i > 0 then id = "{id}s" - var player = new Player(game, "itch76") - player.stats["commits"] = i - player.save - l.apply_event(generator.pull_close(repo, pull), db) - assert player.load_achievements.has_key(id) - end - var player = new Player(game, "itch76") - assert player.stats.overall["nitcoins"] == 11110 - end - - fun test_X_comments_achievements is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new StatisticsReactor) - l.add_reactor(new Player1Comment, new Player100Comments, new Player1KComments) - - var pull = api.load_pull(repo, 275) - assert pull != null - var comment = api.load_issue_comment(repo, 36961230) - assert comment != null - - for i in [0, 99, 999] do - var id = "player_{i + 1}_comment" - if i > 0 then id = "{id}s" - var player = new Player(game, "Morriar") - player.stats["comments"] = i - player.save - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - assert player.load_achievements.has_key(id) - end - var player = new Player(game, "Morriar") - assert player.stats.overall["nitcoins"] == 1110 - end - - fun test_issues_achievements is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new IssueAboutNitdoc, new IssueAboutFFI) - - var issue = api.load_issue(repo, 322) - assert issue != null - - issue.title = "nitdoc ffi" - l.apply_event(generator.issue_open(repo, issue), db) - var player = new Player(game, "Morriar") - assert player.load_achievements.has_key("issue_about_nitdoc") - assert player.load_achievements.has_key("issue_about_ffi") - assert player.stats.overall["nitcoins"] == 20 - end - - fun test_comments_reactor is test do - var db = gen_test_db - var game = load_game("Morriar/nit", db) - var l = new DummyListener - l.add_reactor(new PlayerPingGod, new PlayerFirstReview, new PlayerSaysNitcoin) - - var pull = api.load_pull(repo, 275) - assert pull != null - var comment = api.load_issue_comment(repo, 36961230) - assert comment != null - - comment.body = "@{game.repo.owner.login}" - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - var player = new Player(game, "Morriar") - assert player.load_achievements.has_key("player_ping_god") - assert player.stats.overall["nitcoins"] == 50 - - comment.body = "+1" - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - player = new Player(game, "Morriar") - assert player.load_achievements.has_key("player_first_review") - assert player.stats.overall["nitcoins"] == 60 - - comment.body = "Nitcoins" - l.apply_event(generator.issue_comment_event(repo, pull, comment), db) - player = new Player(game, "Morriar") - assert player.load_achievements.has_key("player_says_nitcoin") - assert player.stats.overall["nitcoins"] == 70 - end -end diff --git a/contrib/nitrpg/src/test_statistics.nit b/contrib/nitrpg/src/test_statistics.nit deleted file mode 100644 index 9bdf8fa..0000000 --- a/contrib/nitrpg/src/test_statistics.nit +++ /dev/null @@ -1,83 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Test module for `stats.nit` -module test_statistics is test - -import test_helper -import statistics - -class TestGame - super NitrpgTestHelper - test - - fun test_game_stats is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var stats = game.stats - assert stats.overall["test"] == 0 - stats.overall.inc("test") - assert stats.overall["test"] == 1 - stats.save - var ogame = load_game("privat/nit", db) - var ostats = ogame.stats - ostats.overall.inc("test") - assert ostats.overall["test"] == 2 - end -end - -class TestPlayer - super NitrpgTestHelper - test - - fun test_player_stats is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var player = new Player(game, "Morriar") - var stats = player.stats - assert stats.overall["test"] == 0 - stats.overall.inc("test") - assert stats.overall["test"] == 1 - stats.save - var oplayer = new Player(game, "Morriar") - var ostats = oplayer.stats - ostats.overall.inc("test") - assert ostats.overall["test"] == 2 - end -end - -class TestGameStats - super NitrpgTestHelper - test - - fun test_init_from_json is test do - var db = gen_test_db - var game = load_game("privat/nit", db) - var owner = new Player(game, "Morriar") - var json = """{ - "period": "2015", - "owner": "Morriar", - "values": { - "test1": 10, - "test2": 20 - } - }""".parse_json.as(JsonObject) - var stats = new GameStats.from_json(game, "2015", owner, json) - assert stats["test0"] == 0 - assert stats["test1"] == 10 - assert stats["test2"] == 20 - end -end diff --git a/contrib/nitrpg/src/web.nit b/contrib/nitrpg/src/web.nit deleted file mode 100644 index 3d54672..0000000 --- a/contrib/nitrpg/src/web.nit +++ /dev/null @@ -1,313 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014-2015 Alexandre Terrasa -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Display `nitrpg` data as a website. -module web - -import nitcorn -import templates - -# A custom action forn `nitrpg`. -class RpgAction - super Action - - # Root URL is used as a prefix for all URL generated by the actions. - var root_url: String - - # Github oauth token used for GithubAPI. - var auth: String is lazy do return get_github_oauth - - # API client used to import data from Github. - var api: GithubAPI is lazy do - var api = new GithubAPI(auth) - return api - end - - init do - super - if auth.is_empty then - print "Error: Invalid Github oauth token!" - exit 1 - end - end - - # Return an Error reponse page. - fun bad_request(msg: String): HttpResponse do - var rsp = new HttpResponse(400) - var page = new NitRpgPage(root_url) - var error = new ErrorPanel(msg) - page.flow_panels.add error - rsp.body = page - return rsp - end - - # Returns the game with `name` or null if no game exists with this name. - fun load_game(name: String): nullable Game do - var repo = api.load_repo(name) - if repo == null then return null - var game = new Game.from_mongo(api, repo) - game.root_url = root_url - return game - end - - # Returns the list of saved games from NitRPG data. - fun load_games: Array[Game] do - var res = new Array[Game] - # TODO should be option - var mongo = new MongoClient("mongodb://mongo:27017") - var db = mongo.database("nitrpg") - for obj in db.collection("games").find_all(new JsonObject) do - var repo = api.load_repo(obj["name"].to_s) - assert repo != null - var game = new Game(api, repo) - game.from_json(obj) - game.root_url = root_url - res.add game - end - return res - end -end - -# Repo overview page. -class RpgHome - super RpgAction - - # Response page stub. - var page: NitRpgPage is noinit - - redef fun answer(request, url) do - var readme = load_readme - var games = load_games - var response = new HttpResponse(200) - page = new NitRpgPage(root_url) - page.side_panels.add new GamesShortListPanel(root_url, games) - page.flow_panels.add new MDPanel(readme) - response.body = page - return response - end - - # Load the string content of the nitrpg readme file. - private fun load_readme: String do - var readme = "README.md" - if not readme.file_exists then - return "Unable to locate README file." - end - var file = new FileReader.open(readme) - var text = file.read_all - file.close - return text - end -end - -# Display the list of active game. -class ListGames - super RpgAction - - # Response page stub. - var page: NitRpgPage is noinit - - redef fun answer(request, url) do - var games = load_games - var response = new HttpResponse(200) - page = new NitRpgPage(root_url) - page.breadcrumbs = new Breadcrumbs - page.breadcrumbs.add_link(root_url / "games", "games") - page.flow_panels.add new GamesListPanel(root_url, games) - response.body = page - return response - end -end - -# An action that require a game. -class GameAction - super RpgAction - - # Response page stub. - var page: NitRpgPage is noinit - - # Target game. - var game: Game is noinit - - redef fun answer(request, url) is abstract - - # Check errors and prepare response. - private fun prepare_response(request: HttpRequest, url: String): HttpResponse do - var owner = request.param("owner") - var repo_name = request.param("repo") - if owner == null or repo_name == null then - var msg = "Bad request: should look like /games/:owner/:repo." - return bad_request(msg) - end - var game = load_game("{owner}/{repo_name}") - if game == null then - var msg = api.last_error.message - return bad_request("Repo Error: {msg}") - end - self.game = game - var response = new HttpResponse(200) - page = new NitRpgPage(root_url) - page.side_panels.add new GameStatusPanel(game) - page.breadcrumbs = new Breadcrumbs - page.breadcrumbs.add_link(game.url, game.name) - prepare_pagination(request) - return response - end - - # Parse pagination related parameters. - private fun prepare_pagination(request: HttpRequest) do - var args = request.get_args - list_from = args.get_or_default("pfrom", "0").to_i - list_limit = args.get_or_default("plimit", "10").to_i - end - - # Limit of events to display in lists. - var list_limit = 10 - - # From where to start the display of events related lists. - var list_from = 0 - - # TODO should also check 201, 203 ... - private fun is_response_error(response: HttpResponse): Bool do - return response.status_code != 200 - end -end - -# Repo overview page. -class RepoHome - super GameAction - - redef fun answer(request, url) do - var rsp = prepare_response(request, url) - if is_response_error(rsp) then return rsp - page.side_panels.add new ShortListPlayersPanel(game) - page.flow_panels.add new PodiumPanel(game) - page.flow_panels.add new EventListPanel(game, list_limit, list_from) - page.flow_panels.add new AchievementsListPanel(game) - rsp.body = page - return rsp - end -end - -# Repo players list. -class ListPlayers - super GameAction - - redef fun answer(request, url) do - var rsp = prepare_response(request, url) - if is_response_error(rsp) then return rsp - page.breadcrumbs.add_link(game.url / "players", "players") - page.flow_panels.add new ListPlayersPanel(game) - rsp.body = page - return rsp - end -end - -# Player details page. -class PlayerHome - super GameAction - - redef fun answer(request, url) do - var rsp = prepare_response(request, url) - if is_response_error(rsp) then return rsp - var name = request.param("player") - if name == null then - var msg = "Bad request: should look like /:owner/:repo/:players/:name." - return bad_request(msg) - end - var player = game.load_player(name) - if player == null then - return bad_request("Request Error: unknown player {name}.") - end - page.breadcrumbs.add_link(game.url / "players", "players") - page.breadcrumbs.add_link(player.url, name) - page.side_panels.clear - page.side_panels.add new PlayerStatusPanel(game, player) - page.flow_panels.add new PlayerReviewsPanel(game, player) - page.flow_panels.add new PlayerWorkPanel(game, player) - page.flow_panels.add new AchievementsListPanel(player) - page.flow_panels.add new EventListPanel(player, list_limit, list_from) - rsp.body = page - return rsp - end -end - -# Display the list of achievements unlocked for this game. -class ListAchievements - super GameAction - - redef fun answer(request, url) do - var rsp = prepare_response(request, url) - if is_response_error(rsp) then return rsp - page.breadcrumbs.add_link(game.url / "achievements", "achievements") - page.flow_panels.add new AchievementsListPanel(game) - rsp.body = page - return rsp - end -end - -# Player details page. -class AchievementHome - super GameAction - - redef fun answer(request, url) do - var rsp = prepare_response(request, url) - if is_response_error(rsp) then return rsp - var name = request.param("achievement") - if name == null then - var msg = "Bad request: should look like /:owner/:repo/achievements/:achievement." - return bad_request(msg) - end - var achievement = game.load_achievement(name) - if achievement == null then - return bad_request("Request Error: unknown achievement {name}.") - end - page.breadcrumbs.add_link(game.url / "achievements", "achievements") - page.breadcrumbs.add_link(achievement.url, achievement.name) - page.flow_panels.add new AchievementPanel(achievement) - page.flow_panels.add new EventListPanel(achievement, list_limit, list_from) - rsp.body = page - return rsp - end -end - -if args.length != 3 then - print "Error: missing argument" - print "" - print "Usage:" - print "web " - exit 1 -end - -var host = args[0] -var port = args[1] -var root = args[2] - -var iface = "{host}:{port}" -var vh = new VirtualHost(iface) -vh.routes.add new Route("/styles/", new FileServer("www/styles")) -vh.routes.add new Route("/games/:owner/:repo/players/:player", new PlayerHome(root)) -vh.routes.add new Route("/games/:owner/:repo/players", new ListPlayers(root)) -vh.routes.add new Route("/games/:owner/:repo/achievements/:achievement", new AchievementHome(root)) -vh.routes.add new Route("/games/:owner/:repo/achievements", new ListAchievements(root)) -vh.routes.add new Route("/games/:owner/:repo", new RepoHome(root)) -vh.routes.add new Route("/games", new ListGames(root)) -vh.routes.add new Route("/", new RpgHome(root)) - -var fac = new HttpFactory.and_libevent -fac.config.virtual_hosts.add vh - -print "Launching server on http://{iface}/" -fac.run diff --git a/contrib/nitrpg/www/styles/main.css b/contrib/nitrpg/www/styles/main.css deleted file mode 100644 index 865123e..0000000 --- a/contrib/nitrpg/www/styles/main.css +++ /dev/null @@ -1,74 +0,0 @@ -body { padding-top: 70px; } - -.navbar .breadcrumb { - background-color: transparent; - margin-bottom: 0; - margin-top: 0.5em; -} - -/* columns of same height styles */ -.container-xs-height { - display:table; - padding-left:0px; - padding-right:0px; -} -.row-xs-height { - display:table-row; -} -.col-xs-height { - display:table-cell; - float:none; -} -@media (min-width: 768px) { - .container-sm-height { - display:table; - padding-left:0px; - padding-right:0px; - } - .row-sm-height { - display:table-row; - } - .col-sm-height { - display:table-cell; - float:none; - } -} -@media (min-width: 992px) { - .container-md-height { - display:table; - padding-left:0px; - padding-right:0px; - } - .row-md-height { - display:table-row; - } - .col-md-height { - display:table-cell; - float:none; - } -} -@media (min-width: 1200px) { - .container-lg-height { - display:table; - padding-left:0px; - padding-right:0px; - } - .row-lg-height { - display:table-row; - } - .col-lg-height { - display:table-cell; - float:none; - } -} - -/* vertical alignment styles */ -.col-top { - vertical-align:top; -} -.col-middle { - vertical-align:middle; -} -.col-bottom { - vertical-align:bottom; -} -- 1.7.9.5