Add pandas dependency, enhance inventory summary visualization, and update table rendering
This commit is contained in:
parent
7673b4e1b6
commit
25e67cce28
6 changed files with 86 additions and 6 deletions
|
@ -1,3 +1,4 @@
|
||||||
flask
|
flask
|
||||||
flask_sqlalchemy
|
flask_sqlalchemy
|
||||||
pyodbc
|
pyodbc
|
||||||
|
pandas
|
||||||
|
|
46
routes.py
46
routes.py
|
@ -6,6 +6,7 @@ from typing import Callable, Any, List
|
||||||
from . import db
|
from . import db
|
||||||
from .utils import eager_load_user_relationships, eager_load_inventory_relationships, eager_load_room_relationships, eager_load_worklog_relationships, chunk_list
|
from .utils import eager_load_user_relationships, eager_load_inventory_relationships, eager_load_room_relationships, eager_load_worklog_relationships, chunk_list
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
main = Blueprint('main', __name__)
|
main = Blueprint('main', __name__)
|
||||||
|
|
||||||
|
@ -141,7 +142,42 @@ def index():
|
||||||
|
|
||||||
stale_pagination = make_paginated_data(worklog_query, stale_worklog_page, 3)
|
stale_pagination = make_paginated_data(worklog_query, stale_worklog_page, 3)
|
||||||
stale_count = len(worklog_query.all())
|
stale_count = len(worklog_query.all())
|
||||||
stale_worklog_headers = {k: v for k, v in worklog_headers.items() if k not in ['End Time', 'Quick Analysis?', 'Complete?', 'Follow Up?']}
|
stale_worklog_headers = {
|
||||||
|
k: v for k, v in worklog_headers.items()
|
||||||
|
if k not in ['End Time', 'Quick Analysis?', 'Complete?', 'Follow Up?']
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory_query = eager_load_inventory_relationships(
|
||||||
|
db.session.query(Inventory)
|
||||||
|
)
|
||||||
|
|
||||||
|
results = inventory_query.all()
|
||||||
|
|
||||||
|
data = [{
|
||||||
|
'id': item.id,
|
||||||
|
'condition': item.condition
|
||||||
|
} for item in results]
|
||||||
|
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
|
||||||
|
# Count items per condition
|
||||||
|
expected_conditions = [
|
||||||
|
'Deployed', 'Disposed', 'Inoperable', 'Partially Inoperable',
|
||||||
|
'Removed', 'Unverified', 'Working'
|
||||||
|
]
|
||||||
|
pivot = df['condition'].value_counts().reindex(expected_conditions, fill_value=0)
|
||||||
|
|
||||||
|
# Convert pandas/numpy int64s to plain old Python ints
|
||||||
|
pivot = pivot.astype(int)
|
||||||
|
labels = list(pivot.index)
|
||||||
|
data = [int(x) for x in pivot.values] # <- This is what fixes the JSON error
|
||||||
|
|
||||||
|
datasets = [{
|
||||||
|
'type': 'bar',
|
||||||
|
'x': labels,
|
||||||
|
'y': data,
|
||||||
|
'name': 'Inventory Conditions'
|
||||||
|
}]
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
|
@ -149,9 +185,15 @@ def index():
|
||||||
stale_pagination=stale_pagination,
|
stale_pagination=stale_pagination,
|
||||||
stale_count=stale_count,
|
stale_count=stale_count,
|
||||||
stale_worklog_headers=stale_worklog_headers,
|
stale_worklog_headers=stale_worklog_headers,
|
||||||
stale_worklog_rows=[{"id": log.id, "cells": [fn(log) for fn in stale_worklog_headers.values()]} for log in stale_pagination['items']],
|
stale_worklog_rows=[{
|
||||||
|
"id": log.id,
|
||||||
|
"cells": [fn(log) for fn in stale_worklog_headers.values()]
|
||||||
|
} for log in stale_pagination['items']],
|
||||||
|
labels=labels,
|
||||||
|
datasets=datasets
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def link(text, endpoint, **values):
|
def link(text, endpoint, **values):
|
||||||
return {"text": text, "url": url_for(endpoint, **values)}
|
return {"text": text, "url": url_for(endpoint, **values)}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
{% if rows %}
|
{% if rows %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table
|
<table
|
||||||
|
id="datatable-{{ title|default('table')|replace(' ', '-')|lower }}"
|
||||||
class="table table-bordered table-sm table-hover table-striped table-light m-0{% if title %} caption-top{% endif %}">
|
class="table table-bordered table-sm table-hover table-striped table-light m-0{% if title %} caption-top{% endif %}">
|
||||||
{% if title %}
|
{% if title %}
|
||||||
<caption>{{ title }}</caption>
|
<caption>{{ title }}</caption>
|
||||||
|
|
|
@ -32,6 +32,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Summary</h5>
|
||||||
|
<div id="summary"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
const data = {{ datasets|tojson }};
|
||||||
|
const layout = { title: 'Summary' };
|
||||||
|
Plotly.newPlot('summary', data, layout)
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -13,8 +13,14 @@
|
||||||
<title>{% block title %}Inventory{% endblock %}</title>
|
<title>{% block title %}Inventory{% endblock %}</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css" rel="stylesheet"
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT" crossorigin="anonymous">
|
integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT" crossorigin="anonymous">
|
||||||
|
<link
|
||||||
|
href="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.css"
|
||||||
|
rel="stylesheet" integrity="sha384-gdnBcErvPbrURVoR9w3NhVMliw+ZmcTCmq+64xj2Ksx21nRJFX3qW0zFvBotL5rm"
|
||||||
|
crossorigin="anonymous">
|
||||||
<style>
|
<style>
|
||||||
{% block style %}
|
{
|
||||||
|
% block style %
|
||||||
|
}
|
||||||
|
|
||||||
.sticky-top {
|
.sticky-top {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
@ -41,7 +47,9 @@
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
{% endblock %}
|
{
|
||||||
|
% endblock %
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -75,6 +83,11 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"
|
||||||
integrity="sha512-5CYOlHXGh6QpOFA/TeTylKLWfB3ftPsde7AnmhuitiTX4K5SqCLBeKro6sPS8ilsz1Q4NRx3v8Ko2IBiszzdww=="
|
integrity="sha512-5CYOlHXGh6QpOFA/TeTylKLWfB3ftPsde7AnmhuitiTX4K5SqCLBeKro6sPS8ilsz1Q4NRx3v8Ko2IBiszzdww=="
|
||||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
||||||
|
<script
|
||||||
|
src="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.js"
|
||||||
|
integrity="sha384-tNYRX2RiDDDRKCJgPF8Pw3rTxC1GUe1pt5qH1SBmwcazrEUj7Ii4C1Tz9wCCRUI4"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
<script src="{{ url_for('static', filename='js/datalist.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/datalist.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
const searchInput = document.querySelector('#search');
|
const searchInput = document.querySelector('#search');
|
||||||
|
@ -83,6 +96,15 @@
|
||||||
searchInput.addEventListener('input', () => {
|
searchInput.addEventListener('input', () => {
|
||||||
searchButton.disabled = searchInput.value.trim() === '';
|
searchButton.disabled = searchInput.value.trim() === '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
$('table[id^="datatable-"]').DataTable({
|
||||||
|
pageLength: 25,
|
||||||
|
lengthChange: false,
|
||||||
|
ordering: true,
|
||||||
|
stateSave: true
|
||||||
|
})
|
||||||
|
})
|
||||||
{% block script %} {% endblock %}
|
{% block script %} {% endblock %}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -11,5 +11,5 @@
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
{{ tables.render_table(header, rows, entry_route) }}
|
{{ tables.render_table(header, rows, entry_route) }}
|
||||||
{{ tables.render_pagination(endpoint, page, has_prev, has_next, total_pages, extra_args=extra_args) }}
|
{# { tables.render_pagination(endpoint, page, has_prev, has_next, total_pages, extra_args=extra_args) } #}
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue