commit 61ebe8b7
Add stats for each map
Changed files
| M | src/oastat_static_web_generator.cpp before |
| M | templates/index.tpl before |
| A | templates/map.tpl |
diff --git a/src/oastat_static_web_generator.cpp b/src/oastat_static_web_generator.cpp
index 0ba870e..a5d1e04 100644
--- a/src/oastat_static_web_generator.cpp
+++ b/src/oastat_static_web_generator.cpp
@@ -247,6 +247,91 @@ void write_html_game(cppdb::session& database, const OastatGame& game) {
myfile.close();
}
+std::string getWeaponName(int modtype) {
+ switch(modtype) {
+ case MOD_SHOTGUN: return "Shotgun";
+ case MOD_GAUNTLET: return "Gauntlet";
+ case MOD_MACHINEGUN: return "Machinegun";
+ case MOD_GRENADE:
+ case MOD_GRENADE_SPLASH: return "Grenade";
+ case MOD_ROCKET:
+ case MOD_ROCKET_SPLASH: return "Rocket";
+ case MOD_PLASMA:
+ case MOD_PLASMA_SPLASH: return "Plasma";
+ case MOD_RAILGUN: return "Railgun";
+ case MOD_LIGHTNING: return "Lightning";
+ case MOD_BFG:
+ case MOD_BFG_SPLASH: return "BFG";
+ case MOD_NAIL: return "Nailgun";
+ case MOD_CHAINGUN: return "Chaingun";
+ case MOD_TELEFRAG: return "Telefrag";
+ case MOD_FALLING: return "Falling";
+ default: return "Other";
+ }
+}
+
+void getMapWeaponKills(cppdb::session& database, const std::string& mapname, std::map<int, int>& weapon_kills) {
+ std::string sql = "select k.modtype, count(0) c from oastat.oastat_kills k, oastat.oastat_games g "
+ "where g.gamenumber = k.gamenumber and g.mapname = ? and k.attacker <> k.target "
+ "group by k.modtype order by c desc";
+ cppdb::statement st = database.prepare(sql);
+ st.bind(1, mapname);
+ cppdb::result res = st.query();
+ while(res.next()) {
+ int modtype;
+ int count;
+ res >> modtype >> count;
+ weapon_kills[modtype] = count;
+ }
+}
+
+void getMapRecentGames(cppdb::session& database, const std::string& mapname, std::vector<OastatGame>& games) {
+ std::string sql = "select gamenumber, gametype, mapname, time, basegame, second, servername "
+ "from oastat.oastat_games where mapname = ? order by gamenumber desc limit 10";
+ cppdb::statement st = database.prepare(sql);
+ st.bind(1, mapname);
+ cppdb::result res = st.query();
+ while(res.next()) {
+ OastatGame game;
+ res >> game.gamenumber >> game.gametype >> game.mapname >> game.time >> game.basegame >> game.second >> game.servername;
+ games.push_back(game);
+ write_html_game(database, game);
+ }
+}
+
+void write_html_map(cppdb::session& database, const std::string& mapname) {
+ ctemplate::TemplateDictionary map_tpl("templates/map.tpl");
+ map_tpl.SetValue("GENERATION_DATE", timestamp_now_as_string(database));
+ map_tpl.SetValue("MAP_NAME", mapname);
+
+ // Get weapon kills for this map
+ std::map<int, int> weapon_kills;
+ getMapWeaponKills(database, mapname, weapon_kills);
+ for (const auto& wk : weapon_kills) {
+ ctemplate::TemplateDictionary* sub_dict = map_tpl.AddSectionDictionary("WEAPON_KILLS");
+ sub_dict->SetValue("WEAPON_NAME", getWeaponName(wk.first));
+ sub_dict->SetValue("KILL_COUNT", std::to_string(wk.second));
+ }
+
+ // Get recent matches for this map
+ std::vector<OastatGame> games;
+ getMapRecentGames(database, mapname, games);
+ for (size_t i = 0; i < games.size(); ++i) {
+ const OastatGame& game = games[i];
+ ctemplate::TemplateDictionary* sub_dict = map_tpl.AddSectionDictionary("RECENT_MATCHES");
+ sub_dict->SetValue("GAMENUMBER", std::to_string(game.gamenumber));
+ sub_dict->SetValue("TIME", getTimeStamp(game.time));
+ sub_dict->SetValue("SERVERNAME", game.servername);
+ }
+
+ std::string output;
+ ctemplate::ExpandTemplate("templates/map.tpl", ctemplate::DO_NOT_STRIP, &map_tpl, &output);
+ std::ofstream myfile;
+ myfile.open (cmdargs.output_dir+"/map/"+mapname+".html");
+ myfile << output;
+ myfile.close();
+}
+
void write_html_index(cppdb::session& database) {
ctemplate::TemplateDictionary index_tpl("templates/index.tpl");
index_tpl.SetValue("GENERATION_DATE", timestamp_now_as_string(database));
@@ -320,6 +405,11 @@ void write_html_index(cppdb::session& database) {
void write_files(cppdb::session& database) {
write_html_index(database);
+ // Generate map pages
+ std::vector<std::string> map_list = get_most_played_maps(database);
+ for (const std::string& mapname : map_list) {
+ write_html_map(database, mapname);
+ }
}
@@ -367,6 +457,7 @@ int main(int argc, const char* argv[]) {
write_current_time(database);
create_dir_recursive(cmdargs.output_dir+"/static");
create_dir_recursive(cmdargs.output_dir+"/game");
+ create_dir_recursive(cmdargs.output_dir+"/map");
copy_static_files("static_content", cmdargs.output_dir+"/static");
write_files(database);
return 0;
diff --git a/templates/index.tpl b/templates/index.tpl
index 8724ced..12bf57f 100644
--- a/templates/index.tpl
+++ b/templates/index.tpl
@@ -114,7 +114,7 @@
</tr>
{{#MAP_LIST}}
<tr>
- <td>{{MAP_NAME}}</td>
+ <td><a href="map/{{MAP_NAME}}.html">{{MAP_NAME}}</a></td>
<td>{{TIMES_PLAYED}}</td>
<td>{{LAST_PLAYED}}</td>
</tr>
diff --git a/templates/map.tpl b/templates/map.tpl
new file mode 100644
index 0000000..8e50aa2
--- /dev/null
+++ b/templates/map.tpl
@@ -0,0 +1,77 @@
+{{%AUTOESCAPE context="HTML"}}
+
+{{!
+ Copyright (c) 2019 Poul Sander
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+}}
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="en">
+ <meta name="description" content="OpenArena stats by oastats">
+ <link rel="stylesheet" href="../static/css/oastat.css" type="text/css">
+ <title>OpenArena Stats - Map {{MAP_NAME}}</title>
+</head>
+<body>
+<div class="main-section">
+ <h1>Map: {{MAP_NAME}}</h1>
+
+ <div class="levelshot">
+ <img src="../static/images/oa640x400/{{MAP_NAME}}.jpg" alt="{{MAP_NAME}}">
+ </div>
+
+ <h2>Weapon Kills on this Map</h2>
+ <table>
+ <tr>
+ <th>Weapon</th>
+ <th>Kills</th>
+ </tr>
+ {{#WEAPON_KILLS}}
+ <tr>
+ <td>{{WEAPON_NAME}}</td>
+ <td>{{KILL_COUNT}}</td>
+ </tr>
+ {{/WEAPON_KILLS}}
+ </table>
+
+ <h2>Most Recent Matches</h2>
+ <table>
+ <tr>
+ <th>Game</th>
+ <th>Played at</th>
+ <th>Server name</th>
+ </tr>
+ {{#RECENT_MATCHES}}
+ <tr>
+ <td><a href="../game/{{GAMENUMBER}}.html">{{GAMENUMBER}}</a></td>
+ <td>{{TIME}}</td>
+ <td>{{SERVERNAME}}</td>
+ </tr>
+ {{/RECENT_MATCHES}}
+ </table>
+
+ <p><a href="../index.html">Back to main page</a></p>
+ <p class="update">Last updated on {{GENERATION_DATE}}</p>
+</div>
+</body>
+</html>