add compatibility stats to mod page

This commit is contained in:
Jesse Plamondon-Willard 2018-11-22 16:16:59 -05:00
parent aeca48c419
commit ba7d6a0e51
No known key found for this signature in database
GPG Key ID: 7D7C8097B62033CE
3 changed files with 67 additions and 22 deletions

View File

@ -1,6 +1,7 @@
# Release notes
## Upcoming
* For the web UI:
* Added stats to compatibility list.
* Fixed compatibility list showing beta header when there's no beta in progress.
## 2.8.2

View File

@ -4,11 +4,11 @@
ViewData["Title"] = "SMAPI mod compatibility";
}
@section Head {
<link rel="stylesheet" href="~/Content/css/mods.css?r=20181109" />
<link rel="stylesheet" href="~/Content/css/mods.css?r=20181122" />
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tablesorter@2.31.0/dist/js/jquery.tablesorter.combined.min.js" crossorigin="anonymous"></script>
<script src="~/Content/js/mods.js?r=20181109"></script>
<script src="~/Content/js/mods.js?r=20181122"></script>
<script>
$(function() {
var data = @Json.Serialize(Model.Mods, new JsonSerializerSettings { Formatting = Formatting.None });
@ -36,7 +36,7 @@
</div>
<div id="filter-area">
<input type="checkbox" id="show-advanced" v-model="showAdvanced" />
<label for="show-advanced">show detailed options</label>
<label for="show-advanced">show advanced info and options</label>
<div id="filters" v-show="showAdvanced">
<div v-for="(filterGroup, key) in filters">
{{key}}: <span v-for="filter in filterGroup" v-bind:class="{ active: filter.value }"><input type="checkbox" v-bind:id="filter.id" v-model="filter.value" v-on:change="applyFilters" /> <label v-bind:for="filter.id">{{filter.label}}</label></span>
@ -44,7 +44,10 @@
</div>
</div>
</div>
<div id="mod-count" v-show="showAdvanced">{{visibleCount}} mods shown.</div>
<div id="mod-count" v-show="showAdvanced">
<span v-if="visibleStats.total > 0">{{visibleStats.total}} mods shown ({{Math.round((visibleStats.compatible + visibleStats.workaround) / visibleStats.total * 100)}}% compatible or have a workaround, {{Math.round((visibleStats.soon + visibleStats.broken) / visibleStats.total * 100)}}% broken, {{Math.round(visibleStats.abandoned / visibleStats.total * 100)}}% obsolete).</span>
<span v-else>No matching mods found.</span>
</div>
<table class="wikitable" id="mod-list">
<thead>
<tr>

View File

@ -4,10 +4,19 @@ var smapi = smapi || {};
var app;
smapi.modList = function (mods) {
// init data
var defaultStats = {
total: 0,
compatible: 0,
workaround: 0,
soon: 0,
broken: 0,
abandoned: 0,
invalid: 0
};
var data = {
mods: mods,
visibleCount: mods.length,
showAdvanced: false,
visibleStats: $.extend({}, defaultStats),
filters: {
source: {
open: {
@ -130,27 +139,16 @@ smapi.modList = function (mods) {
var words = data.search.toLowerCase().split(" ");
// apply criteria
data.visibleCount = data.mods.length;
var stats = data.visibleStats = $.extend({}, defaultStats);
for (var i = 0; i < data.mods.length; i++) {
var mod = data.mods[i];
mod.Visible = true;
// check filters
if (!this.matchesFilters(mod)) {
mod.Visible = false;
data.visibleCount--;
continue;
}
// check search terms (all search words should match)
if (words.length) {
for (var w = 0; w < words.length; w++) {
if (mod.SearchableText.indexOf(words[w]) === -1) {
mod.Visible = false;
data.visibleCount--;
break;
}
}
mod.Visible = this.matchesFilters(mod, words);
if (mod.Visible) {
stats.total++;
stats[this.getCompatibilityGroup(mod)]++;
}
}
},
@ -159,9 +157,10 @@ smapi.modList = function (mods) {
/**
* Get whether a mod matches the current filters.
* @param {object} mod The mod to check.
* @param {string[]} searchWords The search words to match.
* @returns {bool} Whether the mod matches the filters.
*/
matchesFilters: function(mod) {
matchesFilters: function(mod, searchWords) {
var filters = data.filters;
// check source
@ -198,8 +197,50 @@ smapi.modList = function (mods) {
return false;
}
// check search terms
for (var w = 0; w < searchWords.length; w++) {
if (mod.SearchableText.indexOf(searchWords[w]) === -1)
return false;
}
return true;
},
/**
* Get a mod's compatibility group for mod metrics.
* @param {object} mod The mod to check.
* @returns {string} The compatibility group (one of 'compatible', 'workaround', 'soon', 'broken', 'abandoned', or 'invalid').
*/
getCompatibilityGroup: function (mod) {
var status = (mod.BetaCompatibility || mod.Compatibility).Status;
switch (status) {
// obsolete
case "abandoned":
case "obsolete":
return "abandoned";
// compatible
case "ok":
case "optional":
return "compatible";
// workaround
case "workaround":
case "unofficial":
return "workaround";
// soon/broken
case "broken":
if (mod.SourceUrl)
return "soon";
else
return "broken";
default:
return "invalid";
}
}
}
});
app.applyFilters();
};