redef fun render_body do
add "<strong class=\"text-success\">{game.load_players.length}</strong>"
- add " <a href=\"{game.url}/players\">players</a><br><br>"
+ add " <a href=\"{game.url}/players\">players</a><br>"
+ add "<strong class=\"text-success\">{game.stats["achievements"]}</strong>"
+ add " <a href=\"{game.url}/achievements\">achievements</a><br><br>"
add "<strong class=\"text-success\">{game.stats["pulls"]}</strong> pull requests"
add " (<strong>{game.stats["pulls_open"]}</strong> open)<br>"
add "<strong class=\"text-success\">{game.stats["issues"]}</strong> issues"
add "<p class=\"lead\">ranked "
add " <span class=\"text-success\"># {ranking[player.name]}</span></p>"
add "<strong class=\"text-success\">{player.nitcoins}</strong> nitcoins<br><br>"
+ add "<strong class=\"text-success\">{player.stats["achievements"]}</strong> achievements<br><br>"
add "<strong>{player.stats["pulls"]}</strong> pull requests<br>"
add "<strong>{player.stats["issues"]}</strong> issues<br>"
add "<strong>{player.stats["commits"]}</strong> commits"
add "</div>"
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 "<span class=\"glyphicon glyphicon-list\"></span> "
+ add "Achievements unlocked"
+ end
+
+ redef fun render_body do
+ var achs = entity.load_achievements.values.to_a
+ if achs.is_empty then
+ add "<em>No achievement yet...</em>"
+ 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 "<span class=\"glyphicon glyphicon-check\"></span> "
+ add "Achievement details"
+ end
+
+ redef fun render_body do
+ add """<p class=\"lead\">
+ <span class="badge progress-bar-success"
+ style="vertical-align: middle">+{{{achievement.reward}}}</span>
+ {{{achievement.name}}}
+ </p>
+ <p><strong>{{{achievement.desc}}}</strong></p>"""
+
+ var events = achievement.load_events
+
+ if events.is_empty then
+ add "<em>Never unlocked...</em>"
+ return
+ end
+
+ var event = events.last
+ var tpl = event.tpl_event
+ var player = tpl.player
+ add "<hr>"
+ add """<div class="media">
+ <a class="media-left" href="{{{player.url}}}">
+ <span class="badge progress-bar-warning" style="position: absolute">#1</span>
+ <img class=\"img-circle\" style="width:50px"
+ src="{{{player.user.avatar_url}}}" alt="{{{player.name}}}">
+ </a>
+ <div class="media-body">
+ <h4 class="media-heading">Unlocked first by {{{player.link}}}</h4>
+ <span class="text-muted">at {{{event.time}}} </span>
+ </div>
+ </div>"""
+
+ if events.length > 1 then
+ add """<p><br>Also unlocked by <strong class="text-success">
+ {{{events.length}}} players</strong>.</p>"""
+ end
+ end
+end
# Base HTML rendering templates for `nitpg`.
module templates_base
-import statistics
+import achievements
redef class GameEntity
# Return a HTML link to this Issue.
fun link: String do return "<a href=\"{html_url}\">#{number}</a>"
end
+
+redef class Achievement
+ # Return a HTML link to this Issue.
+ fun link: String do return "<a href=\"{url}\">{name}</a>"
+
+ fun list_item: String do
+ return """<div class="media">
+ <div class="media-left" style="width: 50px">
+ <span class="glyphicon glyphicon-check"></span>
+ <span class="badge progress-bar-success"
+ style="position: absolute; margin-top: 10px;
+ margin-left: -5px;">+{{{reward}}}</span>
+ </div>
+ <div class="media-body">
+ <h4 class="media-heading">{{{link}}}</h4>
+ <span class="text-muted">{{{desc}}}</span>
+ </div>
+ </div>"""
+
+ end
+end
# Templates to display `GameEvent` kinds.
module templates_events
-import events
+import achievements
import templates_base
redef class GameEvent
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
return new IssueCommentEvent.from_json(event.game.api, obj)
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 """<div class="media">
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
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.write_to_string
return rsp
end
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 AchievementsListPanel(player)
page.flow_panels.add new EventListPanel(player, list_limit, list_from)
rsp.body = page.write_to_string
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)
+ page.breadcrumbs.add_link(game.url / "achievements", "achievements")
+ page.flow_panels.add new AchievementsListPanel(game)
+ rsp.body = page.write_to_string
+ return rsp
+ end
+end
+
+# Player details page.
+class AchievementHome
+ super GameAction
+
+ redef fun answer(request, url) do
+ var rsp = prepare_response(request, url)
+ 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.write_to_string
+ return rsp
+ end
+end
+
if args.length != 3 then
print "Error: missing argument"
print ""
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))
var fac = new HttpFactory.and_libevent