Skip to content

Commit

Permalink
WebHost: Add a summary row to the Multiworld Tracker (#1965)
Browse files Browse the repository at this point in the history
* WebHost: Add a summary row to the Multiworld Tracker

Implements suggestions from the generation-suggestions channel:
- https://discord.com/channels/731205301247803413/1124186131911688262
- https://discord.com/channels/731205301247803413/1109513647274856518

* Improve secondsToHours function, and remove jQuery from footerCallback function.

* Don't show the summary row on game-specific multi trackers

---------

Co-authored-by: Chris Wilson <[email protected]>
  • Loading branch information
remyjette and LegendaryLinux authored Aug 29, 2023
1 parent 30e747b commit 9323f7d
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 12 deletions.
35 changes: 29 additions & 6 deletions WebHostLib/static/assets/trackerCommon.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ const adjustTableHeight = () => {
}
};

/**
* Convert an integer number of seconds into a human readable HH:MM format
* @param {Number} seconds
* @returns {string}
*/
const secondsToHours = (seconds) => {
let hours = Math.floor(seconds / 3600);
let minutes = Math.floor((seconds - (hours * 3600)) / 60).toString().padStart(2, '0');
return `${hours}:${minutes}`;
};

window.addEventListener('load', () => {
const tables = $(".table").DataTable({
paging: false,
Expand All @@ -27,7 +38,18 @@ window.addEventListener('load', () => {
stateLoadCallback: function(settings) {
return JSON.parse(localStorage.getItem(`DataTables_${settings.sInstance}_/tracker`));
},
footerCallback: function(tfoot, data, start, end, display) {
if (tfoot) {
const activityData = this.api().column('lastActivity:name').data().toArray().filter(x => !isNaN(x));
Array.from(tfoot?.children).find(td => td.classList.contains('last-activity')).innerText =
(activityData.length) ? secondsToHours(Math.min(...activityData)) : 'None';
}
},
columnDefs: [
{
targets: 'last-activity',
name: 'lastActivity'
},
{
targets: 'hours',
render: function (data, type, row) {
Expand All @@ -40,11 +62,7 @@ window.addEventListener('load', () => {
if (data === "None")
return data;

let hours = Math.floor(data / 3600);
let minutes = Math.floor((data - (hours * 3600)) / 60);

if (minutes < 10) {minutes = "0"+minutes;}
return hours+':'+minutes;
return secondsToHours(data);
}
},
{
Expand Down Expand Up @@ -114,11 +132,16 @@ window.addEventListener('load', () => {
if (status === "success") {
target.find(".table").each(function (i, new_table) {
const new_trs = $(new_table).find("tbody>tr");
const footer_tr = $(new_table).find("tfoot>tr");
const old_table = tables.eq(i);
const topscroll = $(old_table.settings()[0].nScrollBody).scrollTop();
const leftscroll = $(old_table.settings()[0].nScrollBody).scrollLeft();
old_table.clear();
old_table.rows.add(new_trs).draw();
if (footer_tr.length) {
$(old_table.table).find("tfoot").html(footer_tr);
}
old_table.rows.add(new_trs);
old_table.draw();
$(old_table.settings()[0].nScrollBody).scrollTop(topscroll);
$(old_table.settings()[0].nScrollBody).scrollLeft(leftscroll);
});
Expand Down
12 changes: 8 additions & 4 deletions WebHostLib/static/styles/tracker.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@ table.dataTable thead{
font-family: LexendDeca-Regular, sans-serif;
}

table.dataTable tbody{
table.dataTable tbody, table.dataTable tfoot{
background-color: #dce2bd;
font-family: LexendDeca-Light, sans-serif;
}

table.dataTable tbody tr:hover{
table.dataTable tbody tr:hover, table.dataTable tfoot tr:hover{
background-color: #e2eabb;
}

table.dataTable tbody td{
table.dataTable tbody td, table.dataTable tfoot td{
padding: 4px 6px;
}

Expand Down Expand Up @@ -97,10 +97,14 @@ table.dataTable thead th.lower-row{
top: 46px;
}

table.dataTable tbody td{
table.dataTable tbody td, table.dataTable tfoot td{
border: 1px solid #bba967;
}

table.dataTable tfoot td{
font-weight: bold;
}

div.dataTables_scrollBody{
background-color: inherit !important;
}
Expand Down
15 changes: 14 additions & 1 deletion WebHostLib/templates/multiTracker.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
{% endblock %}
<th class="center-column">Checks</th>
<th class="center-column">&percnt;</th>
<th class="center-column hours">Last<br>Activity</th>
<th class="center-column hours last-activity">Last<br>Activity</th>
</tr>
</thead>
<tbody>
Expand All @@ -64,6 +64,19 @@
</tr>
{%- endfor -%}
</tbody>
{% if not self.custom_table_headers() | trim %}
<tfoot>
<tr>
<td></td>
<td>Total</td>
<td>All Games</td>
<td>{{ completed_worlds }}/{{ players|length }} Complete</td>
<td class="center-column">{{ players.values()|sum(attribute='Total') }}/{{ total_locations[team] }}</td>
<td class="center-column">{{ (players.values()|sum(attribute='Total') / total_locations[team] * 100) | int }}</td>
<td class="center-column last-activity"></td>
</tr>
</tfoot>
{% endif %}
</table>
</div>
{% endfor %}
Expand Down
10 changes: 9 additions & 1 deletion WebHostLib/tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,10 @@ def _get_multiworld_tracker_data(tracker: UUID) -> typing.Optional[typing.Dict[s
for playernumber in range(1, len(team) + 1) if playernumber not in groups}
for teamnumber, team in enumerate(names)}

total_locations = {teamnumber: sum(len(locations[playernumber])
for playernumber in range(1, len(team) + 1) if playernumber not in groups)
for teamnumber, team in enumerate(names)}

hints = {team: set() for team in range(len(names))}
if room.multisave:
multisave = restricted_loads(room.multisave)
Expand All @@ -1390,11 +1394,14 @@ def _get_multiworld_tracker_data(tracker: UUID) -> typing.Optional[typing.Dict[s
activity_timers[team, player] = now - datetime.datetime.utcfromtimestamp(timestamp)

player_names = {}
completed_worlds = 0
states: typing.Dict[typing.Tuple[int, int], int] = {}
for team, names in enumerate(names):
for player, name in enumerate(names, 1):
player_names[team, player] = name
states[team, player] = multisave.get("client_game_state", {}).get((team, player), 0)
if states[team, player] == 30: # Goal Completed
completed_worlds += 1
long_player_names = player_names.copy()
for (team, player), alias in multisave.get("name_aliases", {}).items():
player_names[team, player] = alias
Expand All @@ -1410,7 +1417,8 @@ def _get_multiworld_tracker_data(tracker: UUID) -> typing.Optional[typing.Dict[s
activity_timers=activity_timers, video=video, hints=hints,
long_player_names=long_player_names,
multisave=multisave, precollected_items=precollected_items, groups=groups,
locations=locations, games=games, states=states,
locations=locations, total_locations=total_locations, games=games, states=states,
completed_worlds=completed_worlds,
custom_locations=custom_locations, custom_items=custom_items,
)

Expand Down

0 comments on commit 9323f7d

Please sign in to comment.