Files
ziwei-power/app.py

186 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""ziwei-power Flask 应用 — 磁场管理打卡系统"""
import os
from datetime import timedelta
from functools import wraps
from flask import Flask, render_template, request, jsonify, session, redirect, url_for
from werkzeug.security import generate_password_hash, check_password_hash
from database import init_db, get_checkin, save_checkin, delete_checkin, get_all_checkins
app = Flask(__name__)
app.secret_key = os.urandom(24)
# 会话持久化30 天
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=30)
# ── 用户库 ────────────────────────────────────────────
USERS = {
'qiukai': {
'password_hash': generate_password_hash('AiAlex2018$'),
'name': '凯哥'
}
}
# ── 登录检查装饰器 ────────────────────────────────────
def login_required(f):
@wraps(f)
def decorated(*args, **kwargs):
if not session.get('logged_in'):
return redirect(url_for('login_page', next=request.path))
return f(*args, **kwargs)
return decorated
# ── 登录路由 ──────────────────────────────────────────
@app.route('/login', methods=['GET', 'POST'])
def login_page():
if request.method == 'POST':
username = request.form.get('username', '').strip()
password = request.form.get('password', '')
remember = request.form.get('remember') == 'on'
user = USERS.get(username)
if user and check_password_hash(user['password_hash'], password):
session.permanent = remember
session['logged_in'] = True
session['username'] = username
session['display_name'] = user['name']
next_url = request.args.get('next', '/')
return jsonify({'ok': True, 'next': next_url})
else:
return jsonify({'ok': False, 'error': '账号或密码错误'}), 401
# GET如果已登录直接跳转
if session.get('logged_in'):
return redirect(url_for('index'))
return render_template('login.html')
@app.route('/logout')
def logout():
session.clear()
return redirect(url_for('login_page'))
# ── 主页 ──────────────────────────────────────────────
def compute_stats():
"""计算统计数据,供 API 和模板共用"""
rows = get_all_checkins()
total_days = len(rows)
total_morning = 0
total_evening = 0
total_study = 0
calendar = {}
for row in rows:
d = row['date']
data = row['data']
morning = data.get('morning', [])
evening = data.get('evening', [])
study = data.get('study', [])
total_morning += sum(1 for x in morning if isinstance(x, str) and x.strip())
total_evening += sum(1 for x in evening if (
isinstance(x, str) and x.strip() or
isinstance(x, dict) and (x.get('mistake', '') or '').strip()
))
total_study += sum(1 for x in study if x.get('done'))
day_score = 0
has_morning = any(isinstance(x, str) and x.strip() for x in morning)
has_evening = any(
(isinstance(x, str) and x.strip()) or
(isinstance(x, dict) and (x.get('mistake', '') or '').strip())
for x in evening
)
has_study = any(x.get('done') for x in study)
if has_morning: day_score += 1
if has_evening: day_score += 1
if has_study: day_score += 1
calendar[d] = 'pass' if day_score >= 2 else 'fail'
return dict(
total_days=total_days, total_morning=total_morning,
total_evening=total_evening, total_study=total_study,
calendar=calendar
)
@app.route('/')
@login_required
def index():
import json
stats = compute_stats()
return render_template('index.html',
username=session.get('display_name', session.get('username', '')),
initial_stats=json.dumps(stats, ensure_ascii=False))
# ── API ──────────────────────────────────────────────
@app.route('/api/checkin', methods=['GET'])
@login_required
def api_get_checkin():
date = request.args.get('date', '')
if not date:
return jsonify({'ok': False, 'error': '缺少 date 参数'}), 400
row = get_checkin(date)
return jsonify({'ok': True, 'data': row})
@app.route('/api/checkin', methods=['POST'])
@login_required
def api_save_checkin():
body = request.get_json(force=True)
date = body.get('date', '')
if not date:
return jsonify({'ok': False, 'error': '缺少 date 字段'}), 400
data = body.get('data', {})
save_checkin(date, data)
return jsonify({'ok': True})
@app.route('/api/checkin/<date>', methods=['DELETE'])
@login_required
def api_delete_checkin(date):
delete_checkin(date)
return jsonify({'ok': True})
@app.route('/api/history', methods=['GET'])
@login_required
def api_history():
rows = get_all_checkins()
return jsonify({'ok': True, 'data': rows})
@app.route('/api/stats', methods=['GET'])
@login_required
def api_stats():
stats = compute_stats()
return jsonify({'ok': True, **stats})
@app.route('/api/user', methods=['GET'])
@login_required
def api_user():
return jsonify({
'ok': True,
'username': session.get('username'),
'display_name': session.get('display_name')
})
# ── 启动 ──────────────────────────────────────────────
if __name__ == '__main__':
init_db()
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.run(host='0.0.0.0', port=5058, debug=False)