diff --git a/app.py b/app.py index 701f35e..8222165 100644 --- a/app.py +++ b/app.py @@ -97,7 +97,7 @@ def create_app(config_object="config"): poller.start() # ------------------------------------------------------------------ # - # Dashboard + # Home / Battery list # ------------------------------------------------------------------ # @app.route("/sw.js") @@ -106,26 +106,14 @@ def create_app(config_object="config"): mimetype="application/javascript") @app.route("/") - def dashboard(): - batteries = db.query(Battery).order_by(Battery.label).all() - storage_locations = [ - r[0] for r in db.query(Battery.storage_location) - .filter(Battery.storage_location.isnot(None)) - .distinct().order_by(Battery.storage_location).all() - ] - devices = db.query(Device).order_by(Device.name).all() - devices_with_slots = [d for d in devices if d.installed_count() < d.battery_slots and not d.has_children()] + def home(): + batteries = db.query(Battery).all() today = date.today() one_year_ago = (today - timedelta(days=365)).isoformat() total_charges = db.query(func.count(ChargeLog.id)).scalar() or 0 charges_last_year = (db.query(func.count(ChargeLog.id)) .filter(ChargeLog.charged_date >= one_year_ago) .scalar()) or 0 - last_charged_map = { - r[0]: r[1] - for r in db.query(ChargeLog.battery_id, func.max(ChargeLog.charged_date)) - .group_by(ChargeLog.battery_id).all() - } active = [b for b in batteries if b.status in ("available", "installed")] needs_attention = { "low_capacity": [ @@ -138,14 +126,57 @@ def create_app(config_object="config"): if b.battery_percentage is not None and b.battery_percentage < 20 ] if ha_client.enabled else [], } - return render_template("dashboard.html", batteries=batteries, + total_batteries = len(batteries) + avail_count = sum(1 for b in batteries if b.status == "available") + installed_count = sum(1 for b in batteries if b.status == "installed") + retired_count = sum(1 for b in batteries if b.status == "retired") + devices = db.query(Device).filter(Device.parent_id == None).all() + total_devices = len(devices) + full_devices = sum( + 1 for d in devices + if d.effective_slots() > 0 and d.effective_installed_count() >= d.effective_slots() + ) + partial_devices = sum( + 1 for d in devices + if 0 < d.effective_installed_count() < d.effective_slots() + ) + empty_devices = sum(1 for d in devices if d.effective_installed_count() == 0) + return render_template("home.html", + total_batteries=total_batteries, + avail_count=avail_count, + installed_count=installed_count, + retired_count=retired_count, + total_devices=total_devices, + full_devices=full_devices, + partial_devices=partial_devices, + empty_devices=empty_devices, + total_charges=total_charges, + charges_last_year=charges_last_year, + needs_attention=needs_attention, + ha_enabled=ha_client.enabled, + today=today) + + @app.route("/battery/") + def battery_list(): + batteries = db.query(Battery).order_by(Battery.label).all() + storage_locations = [ + r[0] for r in db.query(Battery.storage_location) + .filter(Battery.storage_location.isnot(None)) + .distinct().order_by(Battery.storage_location).all() + ] + devices = db.query(Device).order_by(Device.name).all() + devices_with_slots = [d for d in devices if d.installed_count() < d.battery_slots and not d.has_children()] + today = date.today() + last_charged_map = { + r[0]: r[1] + for r in db.query(ChargeLog.battery_id, func.max(ChargeLog.charged_date)) + .group_by(ChargeLog.battery_id).all() + } + return render_template("battery_list.html", batteries=batteries, storage_locations=storage_locations, devices=devices, devices_with_slots=devices_with_slots, ha_enabled=ha_client.enabled, - total_charges=total_charges, - charges_last_year=charges_last_year, last_charged_map=last_charged_map, - needs_attention=needs_attention, today=today) # ------------------------------------------------------------------ # @@ -201,7 +232,7 @@ def create_app(config_object="config"): purchase_date=purchase_date, storage_location=storage_location)) db.commit() flash(f"Added {count} {brand} batter{'y' if count == 1 else 'ies'}.", "success") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) brands = [r[0] for r in db.query(Battery.brand).distinct().order_by(Battery.brand).all()] storage_locations = [ @@ -454,7 +485,7 @@ def create_app(config_object="config"): battery.device_id = device.id db.commit() flash(f"{battery.label} assigned to {device.name}.", "success") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) return render_template("assign.html", battery=battery, devices=devices_with_slots) @@ -471,7 +502,7 @@ def create_app(config_object="config"): battery.device_id = None db.commit() flash(f"{battery.label} unassigned and marked available.", "success") - return redirect(_safe_next(url_for("dashboard"))) + return redirect(_safe_next(url_for("battery_list"))) # ------------------------------------------------------------------ # # Battery — retire @@ -489,7 +520,7 @@ def create_app(config_object="config"): battery.device_id = None db.commit() flash(f"{battery.label} has been retired.", "success") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) # ------------------------------------------------------------------ # # Battery — unretire @@ -522,7 +553,7 @@ def create_app(config_object="config"): db.delete(battery) db.commit() flash(f"Battery {label} permanently deleted.", "success") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) return render_template("battery_delete.html", battery=battery) # ------------------------------------------------------------------ # @@ -534,7 +565,7 @@ def create_app(config_object="config"): ids = request.form.getlist("battery_ids", type=int) if not ids: flash("No batteries selected.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) batteries = db.query(Battery).filter(Battery.id.in_(ids)).all() action = request.form.get("action") @@ -563,7 +594,7 @@ def create_app(config_object="config"): new_brand = request.form.get("new_brand", "").strip() if not new_brand: flash("Brand name is required.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) for b in batteries: b.brand = new_brand db.commit() @@ -572,15 +603,15 @@ def create_app(config_object="config"): device_id = request.form.get("device_id", type=int) if not device_id: flash("Please select a device.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) device = db.get(Device, device_id) if device is None: flash("Device not found.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) if device.has_children(): flash(f"{device.name} has sub-components; install batteries into those instead.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) already_here = [b for b in batteries if b.device_id == device.id] retired_sel = [b for b in batteries if b.is_retired()] @@ -589,7 +620,7 @@ def create_app(config_object="config"): if not to_process: flash("No eligible batteries to install.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) free_slots = device.battery_slots - device.installed_count() if len(to_process) > free_slots: @@ -598,7 +629,7 @@ def create_app(config_object="config"): f"but {len(to_process)} need installing.", "error", ) - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) existing_brands = device.installed_brands() new_brands = set(b.brand for b in to_process) @@ -627,10 +658,10 @@ def create_app(config_object="config"): allowed = {"brand", "storage_location"} if field_name not in allowed: flash("Invalid field.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) if field_name == "brand" and not field_value: flash("Brand name is required.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) for b in batteries: setattr(b, field_name, field_value) db.commit() @@ -640,7 +671,7 @@ def create_app(config_object="config"): date_val = _parse_date(request.form.get("charged_date", "").strip()) if not date_val: flash("A valid date (YYYY-MM-DD) is required.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) increment = 1 if request.form.get("increment_cycles") else 0 for b in batteries: _record_charge(db, b, date_val, increment, notes=None) @@ -654,7 +685,7 @@ def create_app(config_object="config"): else: flash("Unknown action.", "error") - return redirect(url_for("dashboard")) + return redirect(url_for("battery_list")) # ------------------------------------------------------------------ # # Devices — list diff --git a/templates/base.html b/templates/base.html index d9a68f7..e2f0583 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,9 +5,10 @@ {% block title %}Battery Tracker{% endblock %} - + + - + @@ -16,7 +17,8 @@