Initial commit
This commit is contained in:
114
xuexiao/routes/dashboard.py
Normal file
114
xuexiao/routes/dashboard.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""仪表盘路由"""
|
||||
from datetime import date, datetime
|
||||
|
||||
from flask import render_template
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from models import (
|
||||
db, Student, Course, Teacher, Class_, StudentAccount,
|
||||
MonthlySnapshot, ConsumptionRecord,
|
||||
)
|
||||
from utils import login_required
|
||||
from keshi_display import get_latest_period, snapshot_lessons_display
|
||||
from keshi_stats import period_overview, monthly_trend_from_snapshots
|
||||
import business_rules as rules
|
||||
|
||||
|
||||
def register_routes(app):
|
||||
|
||||
@app.route('/', endpoint='dashboard')
|
||||
@app.route('/dashboard', endpoint='dashboard')
|
||||
@login_required
|
||||
def dashboard():
|
||||
today = date.today()
|
||||
py, pm = get_latest_period()
|
||||
period_label = f'{py}-{pm:02d}' if py and pm else ''
|
||||
|
||||
total_students = Student.query.filter_by(status=1).count()
|
||||
total_courses = Course.query.filter_by(status=1).count()
|
||||
total_teachers = Teacher.query.filter_by(status=1).count()
|
||||
total_classes = Class_.query.filter_by(status=1).count()
|
||||
|
||||
month_recharge = 0.0
|
||||
month_consumption = 0.0
|
||||
if py and pm:
|
||||
ov = period_overview(py, pm)
|
||||
month_recharge = ov['recharge']
|
||||
month_consumption = ov['consumed_hours']
|
||||
|
||||
today_consumption = ConsumptionRecord.query.filter(
|
||||
ConsumptionRecord.consume_date == today,
|
||||
).count()
|
||||
|
||||
recent_recharges = []
|
||||
|
||||
threshold = rules.LOW_HOURS_WARNING_THRESHOLD
|
||||
low_hours_accounts = []
|
||||
if py and pm:
|
||||
snaps = (
|
||||
MonthlySnapshot.query.options(
|
||||
joinedload(MonthlySnapshot.account).joinedload(StudentAccount.student),
|
||||
joinedload(MonthlySnapshot.account).joinedload(StudentAccount.course),
|
||||
)
|
||||
.filter(MonthlySnapshot.year == py, MonthlySnapshot.month == pm)
|
||||
.all()
|
||||
)
|
||||
candidates = []
|
||||
for s in snaps:
|
||||
acc = s.account
|
||||
if not acc or not acc.student:
|
||||
continue
|
||||
disp = snapshot_lessons_display(s, acc)
|
||||
total_h = disp.get('end_total_lessons')
|
||||
if total_h is None:
|
||||
continue
|
||||
th = float(total_h)
|
||||
if 0 < th <= threshold:
|
||||
candidates.append((acc, th, disp))
|
||||
candidates.sort(key=lambda x: x[1])
|
||||
low_hours_accounts = candidates[:8]
|
||||
|
||||
recent_consumptions = ConsumptionRecord.query.order_by(
|
||||
ConsumptionRecord.consume_date.desc(),
|
||||
).limit(8).all()
|
||||
|
||||
trend = monthly_trend_from_snapshots()
|
||||
monthly_chart = []
|
||||
max_amount = 1.0
|
||||
for item in trend[-6:]:
|
||||
amt = float(item['recharge'])
|
||||
max_amount = max(max_amount, amt)
|
||||
monthly_chart.append({
|
||||
'label': item['label'],
|
||||
'amount': amt,
|
||||
'is_current': item['month'] == period_label,
|
||||
})
|
||||
if not monthly_chart:
|
||||
max_amount = 1.0
|
||||
|
||||
hour = datetime.now().hour
|
||||
if hour < 12:
|
||||
greeting = '早上好'
|
||||
elif hour < 18:
|
||||
greeting = '下午好'
|
||||
else:
|
||||
greeting = '晚上好'
|
||||
|
||||
return render_template(
|
||||
'dashboard.html',
|
||||
greeting=greeting,
|
||||
monthly_chart=monthly_chart,
|
||||
chart_max=max_amount,
|
||||
recent_consumptions=recent_consumptions,
|
||||
total_students=total_students,
|
||||
total_courses=total_courses,
|
||||
total_teachers=total_teachers,
|
||||
total_classes=total_classes,
|
||||
month_recharge=float(month_recharge),
|
||||
month_consumption=float(month_consumption),
|
||||
today_consumption=today_consumption,
|
||||
recent_recharges=recent_recharges,
|
||||
low_hours_accounts=low_hours_accounts,
|
||||
period_label=period_label,
|
||||
)
|
||||
Reference in New Issue
Block a user