Update deployment scripts, add leaderboard feature, and bump version to 2.0.0
This commit is contained in:
121
app.py
121
app.py
@@ -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')))
|
||||
|
||||
Reference in New Issue
Block a user