Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assets UI pagination #1202

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ New features
* Speed up loading the users page, by making the pagination backend-based and adding support for that in the API [see `PR #1160 <https://github.com/FlexMeasures/flexmeasures/pull/1160>`]
* X-axis labels in CLI plots show datetime values in a readable and informative format [see `PR #1172 <https://github.com/FlexMeasures/flexmeasures/pull/1172>`_]
* Speed up loading the accounts page,by making the pagination backend-based and adding support for that in the API [see `PR #1196 <https://github.com/FlexMeasures/flexmeasures/pull/1196>`_]
* Speed up loading the account detail page by by switching to server-side pagination for assets, replacing client-side pagination [see `PR #1202 <https://github.com/FlexMeasures/flexmeasures/pull/1202>`_]

Infrastructure / Support
----------------------
Expand Down
149 changes: 90 additions & 59 deletions flexmeasures/ui/templates/crud/account.html
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ <h3 id="usersTableTitle">All users</h3>
<div class="table-responsive">
<table
class="table table-striped paginate nav-on-click"
title="View this asset"
title="View this user"
id="usersTable"
></table>
</div>
Expand All @@ -276,69 +276,100 @@ <h3>Assets</h3>
<table
class="table table-striped paginate nav-on-click"
title="View this asset"
>
<thead>
<tr>
<th><i class="left-icon">Name</i></th>
<th>Location</th>
<th>Asset ID</th>
<th>Account</th>

<th>Sensors</th>
<th class="no-sort">Status</th>
<th class="text-right no-sort">
{% if user_can_create_assets %}
<form action="/assets/new" method="get">
<button
class="btn btn-sm btn-responsive btn-success create-button"
type="submit"
>
Create new asset
</button>
</form>
{% endif %}
</th>
<th class="d-none">URL</th>
</tr>
</thead>
<tbody>
{% for asset in assets %}
<tr>
<td>
<i
class="{{ asset.generic_asset_type.name | asset_icon }} left-icon"
>{{ asset.name }}</i
>
</td>
<td>
{% if asset.latitude and asset.longitude %} LAT: {{
"{:,.4f}".format( asset.latitude ) }} LONG: {{
"{:,.4f}".format( asset.longitude ) }} {% endif %}
</td>
<td>{{ asset.id }}</td>
<td>
{% if asset.owner %} {{ asset.owner.name }} {% else %} PUBLIC
{% endif %}
</td>
<td>{{ asset.sensors | length }}</td>
<td>
<a href="/assets/{{ asset.id }}/status">
<button type="button" class="btn">Status</button>
</a>
</td>
<td class="text-right"></td>
<td class="d-none">/assets/{{ asset.id }}</td>
</tr>
{% endfor %}
</tbody>
</table>
id="assetTable"
></table>
</div>
</div>
</div>
<div class="col-md-2"></div>
</div>
</div>
{% block paginate_tables_script %} {{ super() }} {% endblock %}

<script>
const asset_icon_map = JSON.parse("{{ asset_icon_map | tojson | safe }}");

function Asset(id, name, account, latitude, longitude, sensors, asset_type) {
let icon = asset_icon_map[asset_type.toLowerCase()];
if (icon === undefined) icon = `icon-${asset_type}`;

this.name = `
<i class="${icon} left-icon">${name}</i>
`;

this.id = id;
this.location = "";
this.url = `/assets/${id}`;
this.status = `
<a href="/assets/${id}/status">
<button type="button" class="btn btn-primary">Status</button>
</a>
`;

if (account == null) this.owner = "PUBLIC";
else
this.owner = `
<a href="/accounts/${account["id"]}" title="View this account">${account["name"]}</a>
`;

this.num_sensors = sensors.length;

if (latitude != null && longitude != null)
this.location = `LAT: ${latitude}, LONG: ${longitude}`;
}

$(document).ready(function () {
$("#assetTable").dataTable({
serverSide: true,
columns: [
{ data: "id", title: "Asset ID" },
{ data: "name", title: "Name" },
{ data: "owner", title: "Account" },
{ data: "location", title: "Location" },
{ data: "num_sensors", title: "Sensors" },
{ data: "status", title: "Status" },
{ data: "url", title: "URL", className: "d-none" },
],
ajax: function (data, callback, settings) {
let filter = data["search"]["value"];
let url = `{{url_for("AssetAPI:index")}}?page=${
Math.floor(data["start"] / data["length"]) + 1
}&per_page=${data["length"]}&all_accessible=true`;
joshuaunity marked this conversation as resolved.
Show resolved Hide resolved
if (filter.length > 0) {
url = `${url}&filter=${filter}`;
}
$.ajax({
type: "get",
url: url,
success: function (response, text) {
let clean_response = [];
response["data"].forEach((element) =>
clean_response.push(
new Asset(
element["id"],
element["name"],
element["owner"],
element["latitude"],
element["longitude"],
element["sensors"],
element["generic_asset_type"]["name"]
)
)
);
callback({
data: clean_response,
recordsTotal: response["num-records"],
recordsFiltered: response["filtered-records"],
});
},
error: function (request, status, error) {
console.log("Error: ", error);
},
});
},
});
});
</script>

<script defer>
let currentPage = 1;
const basePath = window.location.origin;
Expand Down Expand Up @@ -407,4 +438,4 @@ <h3>Assets</h3>
});
});
</script>
{% endblock %}
{% block paginate_tables_script %} {{ super() }} {% endblock %} {% endblock %}
136 changes: 65 additions & 71 deletions flexmeasures/ui/templates/crud/assets.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,78 +8,72 @@

<script>

const asset_icon_map = JSON.parse('{{ asset_icon_map | tojson | safe }}');

function Asset ( id, name, asset_type, sensors, account, latitude, longitude) {
let icon = asset_icon_map[asset_type.toLowerCase()];
if (icon === undefined)
icon = `icon-${asset_type}`

this.name = `
<i class="${ icon } left-icon">${ name }</i>
`;

this.id = id;
this.location = "";
this.url = `/assets/${id}`
this.status = `
<a href="/assets/${id}/status">
<button type="button" class="btn btn-primary">Status</button>
</a>
`;

if (account == null) this.owner = "PUBLIC";
else
this.owner = `
<a href="/accounts/${ account['id'] }" title="View this account">${ account["name"] }</a>
`

this.num_sensors = sensors.length;

if (latitude != null && longitude != null) this.location = `LAT: ${latitude}, LONG: ${longitude}`;
};

$(document).ready(function() {

/*
Initialize a DataTable on an HTML element with the ID #assetTable, enabling server-side processing.
The table is configured to display columns for the asset's name, ID, account owner, location, number of sensors, status, and a hidden URL field.
The data for the table is fetched from a server using AJAX.
*/
$("#assetTable").dataTable({
serverSide: true,
columns: [
{data: "name", title: "Name"},
{data: "id", title: "Asset ID"},
{data: "owner", title: "Account"},
{data: "location", title: "Location"},
{data: "num_sensors", title: "Sensors"},
{data: "status", title: "Status"},
{data: "url", title: "URL", className: "d-none"},
],
ajax: function (data, callback, settings) {
let filter = data["search"]["value"];
let url = `{{url_for("AssetAPI:index")}}?page=${Math.floor((data["start"])/data["length"] ) + 1 }&per_page=${data["length"]}&all_accessible=true`;
if (filter.length > 0) {
url = `${url}&filter=${filter}`;
}
$.ajax({
type: "get",
url: url,
success: function(response, text) {
let clean_response = [];
response["data"].forEach( (element) => clean_response.push(
new Asset(element["id"], element["name"], element["generic_asset_type"]["name"], element["sensors"], element["owner"], element["latitude"], element["longitude"])
))
callback({"data": clean_response, "recordsTotal": response["num-records"], "recordsFiltered": response["filtered-records"]});
},
error: function (request, status, error) {
console.log("Error: ", error)
const asset_icon_map = JSON.parse('{{ asset_icon_map | tojson | safe }}');
joshuaunity marked this conversation as resolved.
Show resolved Hide resolved

function Asset ( id, name, asset_type, sensors, account, latitude, longitude) {
let icon = asset_icon_map[asset_type.toLowerCase()];
if (icon === undefined)
icon = `icon-${asset_type}`

this.name = `
<i class="${ icon } left-icon">${ name }</i>
`;

this.id = id;
this.location = "";
this.url = `/assets/${id}`
this.status = `
<a href="/assets/${id}/status">
<button type="button" class="btn btn-primary">Status</button>
</a>
`;

if (account == null) this.owner = "PUBLIC";
else
this.owner = `
<a href="/accounts/${ account['id'] }" title="View this account">${ account["name"] }</a>
`

this.num_sensors = sensors.length;

if (latitude != null && longitude != null) this.location = `LAT: ${latitude}, LONG: ${longitude}`;
};

$(document).ready(function() {
$("#assetTable").dataTable({
serverSide: true,
columns: [
{data: "name", title: "Name"},
{data: "id", title: "Asset ID"},
{data: "owner", title: "Account"},
{data: "location", title: "Location"},
{data: "num_sensors", title: "Sensors"},
{data: "status", title: "Status"},
{data: "url", title: "URL", className: "d-none"},
],
ajax: function (data, callback, settings) {
let filter = data["search"]["value"];
let url = `{{url_for("AssetAPI:index")}}?page=${Math.floor((data["start"])/data["length"] ) + 1 }&per_page=${data["length"]}&all_accessible=true`;
if (filter.length > 0) {
url = `${url}&filter=${filter}`;
}
});
}
});
})
$.ajax({
type: "get",
url: url,
success: function(response, text) {
let clean_response = [];
response["data"].forEach( (element) => clean_response.push(
new Asset(element["id"], element["name"], element["generic_asset_type"]["name"], element["sensors"], element["owner"], element["latitude"], element["longitude"])
))
callback({"data": clean_response, "recordsTotal": response["num-records"], "recordsFiltered": response["filtered-records"]});
},
error: function (request, status, error) {
console.log("Error: ", error)
}
});
}
});
})

</script>

Expand Down
Loading