Update deployment scripts, add leaderboard feature, and bump version to 2.0.0

This commit is contained in:
2026-01-26 20:06:38 +08:00
parent 8d58bf683f
commit f3ce687ab8
13 changed files with 2777 additions and 1672 deletions

121
app.py
View File

@@ -105,6 +105,9 @@ db.users.create_index('username', unique=True)
db.songs.create_index('id', unique=True)
db.songs.create_index('song_type')
db.scores.create_index('username')
# Leaderboard indexes
db.leaderboards.create_index([('song_id', 1), ('difficulty', 1), ('month_key', 1)])
db.leaderboards.create_index([('song_id', 1), ('difficulty', 1), ('month_key', 1), ('score_value', -1)])
class HashException(Exception):
@@ -746,6 +749,124 @@ def route_api_scores_get():
return jsonify({'status': 'ok', 'scores': scores, 'username': user['username'], 'display_name': user['display_name'], 'don': don})
# Leaderboard helper function
def get_month_key():
from datetime import datetime
return datetime.now().strftime('%Y-%m')
@app.route(basedir + 'api/leaderboard')
def route_api_leaderboard():
song_id = request.args.get('song_id')
difficulty = request.args.get('difficulty')
month_key = request.args.get('month_key', get_month_key())
if not song_id or not difficulty:
return abort(400)
entries = list(db.leaderboards.find(
{'song_id': song_id, 'difficulty': difficulty, 'month_key': month_key},
{'_id': False}
).sort('score_value', -1).limit(50))
# Add rank
for i, entry in enumerate(entries):
entry['rank'] = i + 1
return jsonify({'status': 'ok', 'entries': entries, 'month_key': month_key})
@app.route(basedir + 'api/score/check', methods=['POST'])
def route_api_score_check():
data = request.get_json()
if not schema.validate(data, schema.leaderboard_check):
return abort(400)
song_id = str(data.get('song_id'))
difficulty = data.get('difficulty')
score = int(data.get('score'))
month_key = get_month_key()
# Get top 50 for this song/difficulty/month
entries = list(db.leaderboards.find(
{'song_id': song_id, 'difficulty': difficulty, 'month_key': month_key}
).sort('score_value', -1).limit(50))
# Check if qualifies
if len(entries) < 50:
rank = len(entries) + 1
for i, entry in enumerate(entries):
if score > entry['score_value']:
rank = i + 1
break
return jsonify({'status': 'ok', 'qualifies': True, 'rank': rank})
# Check if score beats the 50th entry
if score > entries[-1]['score_value']:
rank = 50
for i, entry in enumerate(entries):
if score > entry['score_value']:
rank = i + 1
break
return jsonify({'status': 'ok', 'qualifies': True, 'rank': rank})
return jsonify({'status': 'ok', 'qualifies': False})
@app.route(basedir + 'api/score/leaderboard/save', methods=['POST'])
def route_api_score_leaderboard_save():
data = request.get_json()
if not schema.validate(data, schema.leaderboard_save):
return abort(400)
song_id = str(data.get('song_id'))
difficulty = data.get('difficulty')
username = data.get('username', '').strip()[:10]
score = int(data.get('score'))
month_key = get_month_key()
if len(username) < 1:
return api_error('invalid_username')
timestamp = int(time.time() * 1000)
# Check existing entry for this user/song/difficulty/month
existing = db.leaderboards.find_one({
'song_id': song_id,
'difficulty': difficulty,
'month_key': month_key,
'username': username
})
if existing:
# Only update if new score is higher
if score > existing['score_value']:
db.leaderboards.update_one(
{'_id': existing['_id']},
{'$set': {'score_value': score, 'timestamp': timestamp}}
)
else:
# Insert new entry
db.leaderboards.insert_one({
'song_id': song_id,
'difficulty': difficulty,
'month_key': month_key,
'username': username,
'score_value': score,
'timestamp': timestamp
})
# Trim to top 50
entries = list(db.leaderboards.find(
{'song_id': song_id, 'difficulty': difficulty, 'month_key': month_key}
).sort('score_value', -1).skip(50))
for entry in entries:
db.leaderboards.delete_one({'_id': entry['_id']})
return jsonify({'status': 'ok'})
@app.route(basedir + 'privacy')
def route_api_privacy():
last_modified = time.strftime('%d %B %Y', time.gmtime(os.path.getmtime('templates/privacy.txt')))