Enhance inventory management by updating timestamp type to DateTime, adding stale worklog display on the index page, and improving template styles and scripts for better user experience.
This commit is contained in:
parent
0835248f34
commit
58754c71bd
7 changed files with 73 additions and 16 deletions
Binary file not shown.
|
@ -6,7 +6,7 @@ if TYPE_CHECKING:
|
||||||
from .work_log import WorkLog
|
from .work_log import WorkLog
|
||||||
from .rooms import Room
|
from .rooms import Room
|
||||||
|
|
||||||
from sqlalchemy import Boolean, ForeignKeyConstraint, ForeignKey, Identity, Index, Integer, PrimaryKeyConstraint, String, Unicode, text
|
from sqlalchemy import Boolean, ForeignKeyConstraint, ForeignKey, Identity, Index, Integer, PrimaryKeyConstraint, String, Unicode, DateTime, text
|
||||||
from sqlalchemy.dialects.mssql import DATETIME2, MONEY
|
from sqlalchemy.dialects.mssql import DATETIME2, MONEY
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -20,7 +20,7 @@ class Inventory(db.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column("ID", Integer, Identity(start=1, increment=1), primary_key=True)
|
id: Mapped[int] = mapped_column("ID", Integer, Identity(start=1, increment=1), primary_key=True)
|
||||||
timestamp: Mapped[datetime.datetime] = mapped_column('Date Entered', DATETIME2)
|
timestamp: Mapped[datetime.datetime] = mapped_column('Date Entered', DateTime)
|
||||||
condition: Mapped[str] = mapped_column('Working Condition', Unicode(255))
|
condition: Mapped[str] = mapped_column('Working Condition', Unicode(255))
|
||||||
needed: Mapped[str] = mapped_column("Needed", Unicode(255))
|
needed: Mapped[str] = mapped_column("Needed", Unicode(255))
|
||||||
type_id: Mapped[int] = mapped_column('Item Type', Integer, ForeignKey("Items.ID"))
|
type_id: Mapped[int] = mapped_column('Item Type', Integer, ForeignKey("Items.ID"))
|
||||||
|
|
32
routes.py
32
routes.py
|
@ -1,11 +1,11 @@
|
||||||
from flask import Blueprint, render_template, url_for, request, redirect
|
from flask import Blueprint, render_template, url_for, request, redirect
|
||||||
from .models import Area, Brand, Item, Inventory, RoomFunction, User, WorkLog, Room
|
from .models import Brand, Item, Inventory, RoomFunction, User, WorkLog, Room
|
||||||
import html
|
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from sqlalchemy.orm import aliased
|
from sqlalchemy.orm import aliased
|
||||||
from typing import Callable, Any, List
|
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
|
||||||
|
|
||||||
main = Blueprint('main', __name__)
|
main = Blueprint('main', __name__)
|
||||||
|
|
||||||
|
@ -130,7 +130,27 @@ def render_paginated_table(
|
||||||
|
|
||||||
@main.route("/")
|
@main.route("/")
|
||||||
def index():
|
def index():
|
||||||
return render_template("index.html", title="Inventory Manager")
|
stale_worklog_page = request.args.get('stale_worklog_page', 1, int)
|
||||||
|
cutoff = datetime.utcnow() - timedelta(days=14)
|
||||||
|
|
||||||
|
worklog_query = eager_load_worklog_relationships(
|
||||||
|
db.session.query(WorkLog)
|
||||||
|
).filter(
|
||||||
|
(WorkLog.start_time < cutoff) & (WorkLog.complete == False)
|
||||||
|
)
|
||||||
|
|
||||||
|
stale_pagination = make_paginated_data(worklog_query, stale_worklog_page, 3)
|
||||||
|
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?']}
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"index.html",
|
||||||
|
title="Inventory Manager",
|
||||||
|
stale_pagination=stale_pagination,
|
||||||
|
stale_count=stale_count,
|
||||||
|
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']],
|
||||||
|
)
|
||||||
|
|
||||||
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)}
|
||||||
|
@ -338,10 +358,12 @@ def search():
|
||||||
UserAlias.last_name.ilike(f"%{query}%")
|
UserAlias.last_name.ilike(f"%{query}%")
|
||||||
))
|
))
|
||||||
inventory_pagination = make_paginated_data(inventory_query, inventory_page)
|
inventory_pagination = make_paginated_data(inventory_query, inventory_page)
|
||||||
user_query = eager_load_user_relationships(db.session.query(User)).filter(
|
user_query = eager_load_user_relationships(db.session.query(User).join(UserAlias, User.supervisor)).filter(
|
||||||
or_(
|
or_(
|
||||||
User.first_name.ilike(f"%{query}%"),
|
User.first_name.ilike(f"%{query}%"),
|
||||||
User.last_name.ilike(f"%{query}%")
|
User.last_name.ilike(f"%{query}%"),
|
||||||
|
UserAlias.first_name.ilike(f"%{query}%"),
|
||||||
|
UserAlias.last_name.ilike(f"%{query}%")
|
||||||
))
|
))
|
||||||
user_pagination = make_paginated_data(user_query, user_page)
|
user_pagination = make_paginated_data(user_query, user_page)
|
||||||
worklog_query = eager_load_worklog_relationships(db.session.query(WorkLog).join(UserAlias, WorkLog.contact).join(InventoryAlias, WorkLog.work_item)).filter(
|
worklog_query = eager_load_worklog_relationships(db.session.query(WorkLog).join(UserAlias, WorkLog.contact).join(InventoryAlias, WorkLog.work_item)).filter(
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
{% if cell.type == 'bool' %}
|
{% if cell.type == 'bool' %}
|
||||||
{{ cell.html | safe }}
|
{{ cell.html | safe }}
|
||||||
{% elif cell.url %}
|
{% elif cell.url %}
|
||||||
<a href="{{ cell.url }}">{{ cell.text }}</a>
|
<a class="link-success link-underline-opacity-0" href="{{ cell.url }}">{{ cell.text }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ cell.text or '-' }}
|
{{ cell.text or '-' }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -7,5 +7,31 @@
|
||||||
<div class="container text-center">
|
<div class="container text-center">
|
||||||
<h1 class="display-4">Welcome to Inventory Manager</h1>
|
<h1 class="display-4">Welcome to Inventory Manager</h1>
|
||||||
<p class="lead">Find out about all of your assets.</p>
|
<p class="lead">Find out about all of your assets.</p>
|
||||||
|
<div class="row">
|
||||||
|
{% if stale_pagination['items'] %}
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Stale Worklogs</h5>
|
||||||
|
<h6 class="card-subtitle mb-2 text-body-secondary">You have {{ stale_count }} worklogs
|
||||||
|
that need attention!</h6>
|
||||||
|
{{ tables.render_table(
|
||||||
|
stale_worklog_headers,
|
||||||
|
stale_worklog_rows,
|
||||||
|
'index'
|
||||||
|
)}}
|
||||||
|
{{ tables.render_pagination(
|
||||||
|
'index',
|
||||||
|
stale_pagination['page'],
|
||||||
|
stale_pagination['has_prev'],
|
||||||
|
stale_pagination['has_next'],
|
||||||
|
stale_pagination['total_pages'],
|
||||||
|
page_variable='stale_worklog_page'
|
||||||
|
)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -14,9 +14,7 @@
|
||||||
<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">
|
||||||
<style>
|
<style>
|
||||||
{
|
{% block style %}
|
||||||
% block style %
|
|
||||||
}
|
|
||||||
|
|
||||||
.sticky-top {
|
.sticky-top {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
@ -43,18 +41,16 @@
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{% endblock %}
|
||||||
% endblock %
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-tertiary text-primary-emphasis">
|
<body class="bg-tertiary text-primary-emphasis">
|
||||||
<nav class="navbar navbar-expand bg-body-tertiary border-bottom">
|
<nav class="navbar navbar-expand bg-body-tertiary border-bottom">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<span class="navbar-brand">
|
<a class="navbar-brand" href="{{ url_for('index') }}">
|
||||||
Inventory Manager
|
Inventory Manager
|
||||||
</span>
|
</a>
|
||||||
<button class="navbar-toggler">
|
<button class="navbar-toggler">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -76,6 +72,9 @@
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js"
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js"
|
||||||
integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO"
|
integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"
|
||||||
|
integrity="sha512-5CYOlHXGh6QpOFA/TeTylKLWfB3ftPsde7AnmhuitiTX4K5SqCLBeKro6sPS8ilsz1Q4NRx3v8Ko2IBiszzdww=="
|
||||||
|
crossorigin="anonymous" referrerpolicy="no-referrer"></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');
|
||||||
|
|
|
@ -76,4 +76,14 @@ title=title,
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
{% if query %}
|
||||||
|
const query = "{{ query|e }}";
|
||||||
|
if (query) {
|
||||||
|
const instance = new Mark(document.querySelector("main"));
|
||||||
|
instance.mark(query);
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue