Merge pull request #23 from Argon2000/master

Use db session instead of engine
This commit is contained in:
oldnapalm
2020-11-18 10:47:25 -03:00
committed by GitHub
3 changed files with 100 additions and 64 deletions
+6 -4
View File
@@ -33,10 +33,12 @@
{% block content %}{% endblock %}
</div>
<script>
var elements = document.getElementsByClassName('text-shadow');
for(var i = 0; i < elements.length; i++) {
var el = elements[i];
el.setAttribute('data-innertext', el.textContent);
if(document.getElementsByClassName) {
var elements = document.getElementsByClassName('text-shadow');
for(var i = 0; i < elements.length; i++) {
var el = elements[i];
el.setAttribute('data-innertext', el.textContent);
}
}
</script>
{% block scripts %}{% endblock %}
+37 -1
View File
@@ -20,7 +20,17 @@
</div>
<div class="row">
<div class="col-md-12 top-buffer">
<input type="submit" value="Login" class="btn btn-sm btn-light">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="rememberlogin" name="rememberlogin">
<label class="form-check-label text-shadow" for="rememberlogin">
Remember login
</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 top-buffer">
<input type="submit" value="Login" class="btn btn-sm btn-light" onclick="rememberLogin()">
</div>
</div>
</form>
@@ -38,3 +48,29 @@
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
var checkbox = document.getElementById('rememberlogin');
var username = document.getElementById('username');
var password = document.getElementById('password');
function rememberLogin() {
if(checkbox.checked && username.value !== '' && password.value !== '') {
localStorage.setItem('username', username.value);
localStorage.setItem('password', password.value);
}
else if(!checkbox.checked) {
localStorage.removeItem('username')
localStorage.removeItem('password');
}
}
var usernameVal = localStorage.getItem('username');
var passwordVal = localStorage.getItem('password');
if(usernameVal !== undefined && usernameVal !== null && usernameVal !== '' &&
passwordVal !== undefined && passwordVal !== null && passwordVal !== '') {
checkbox.checked = true;
username.value = usernameVal;
password.value = passwordVal;
}
</script>
{% endblock %}
+57 -59
View File
@@ -463,7 +463,8 @@ def insert_protobuf_into_db(table_name, msg):
columns = ', '.join(list(msg_dict.keys()))
placeholders = ':'+', :'.join(list(msg_dict.keys()))
query = 'INSERT INTO %s (%s) VALUES (%s)' % (table_name, columns, placeholders)
db.engine.execute(query, msg_dict)
db.session.execute(query, msg_dict)
db.session.commit()
# XXX: can't be used to 'nullify' a column value
@@ -480,7 +481,8 @@ def update_protobuf_in_db(table_name, msg, id):
placeholders = ':'+', :'.join(list(msg_dict.keys()))
setters = ', '.join('{}=:{}'.format(key, key) for key in msg_dict)
query = 'UPDATE %s SET %s WHERE id=%s' % (table_name, setters, id)
db.engine.execute(query, msg_dict)
db.session.execute(query, msg_dict)
db.session.commit()
def row_to_protobuf(row, msg, exclude_fields=[]):
@@ -503,7 +505,7 @@ def get_id(table_name):
# I think activity id is actually only uint32. On the off chance it's
# int32, stick with 31 bits.
ident = int(random.getrandbits(31))
row = db.engine.execute("SELECT id FROM %s WHERE id = %s" % (table_name, ident)).first()
row = db.session.execute(sqlalchemy.text("SELECT id FROM %s WHERE id = %s" % (table_name, ident))).first()
if not row:
break
return ident
@@ -647,9 +649,10 @@ def api_profiles_me():
# However, without it, anyone "upgrading" to multiplayer mode will lose their existing data.
# TODO: need a warning in README that switching to multiplayer mode and back to single player will lose your existing data.
if profile.id != profile_id:
db.engine.execute('UPDATE activity SET player_id = ? WHERE player_id = ?', (str(profile_id), str(profile.id)))
db.engine.execute('UPDATE goal SET player_id = ? WHERE player_id = ?', (str(profile_id), str(profile.id)))
db.engine.execute('UPDATE segment_result SET player_id = ? WHERE player_id = ?', (str(profile_id), str(profile.id)))
db.session.execute(sqlalchemy.text('UPDATE activity SET player_id = %s WHERE player_id = %s' % (profile_id, profile.id)))
db.session.execute(sqlalchemy.text('UPDATE goal SET player_id = %s WHERE player_id = %s' % (profile_id, profile.id)))
db.session.execute(sqlalchemy.text('UPDATE segment_result SET player_id = %s WHERE player_id = %s' % (profile_id, profile.id)))
db.session.commit()
profile.id = profile_id
elif current_user.player_id != profile.id:
# Update AnonUser's player_id to match
@@ -700,19 +703,19 @@ def api_profiles_activities(player_id):
# request.method == 'GET'
activities = activity_pb2.Activities()
# Select every column except 'fit' - despite being a blob python 3 treats it like a utf-8 string and tries to decode it
rows = db.engine.execute("SELECT id, player_id, f3, name, f5, f6, start_date, end_date, distance, avg_heart_rate, max_heart_rate, avg_watts, max_watts, avg_cadence, max_cadence, avg_speed, max_speed, calories, total_elevation, strava_upload_id, strava_activity_id, f23, fit_filename, f29, date FROM activity WHERE player_id = ?", (str(player_id),))
rows = db.session.execute(sqlalchemy.text("SELECT id, player_id, f3, name, f5, f6, start_date, end_date, distance, avg_heart_rate, max_heart_rate, avg_watts, max_watts, avg_cadence, max_cadence, avg_speed, max_speed, calories, total_elevation, strava_upload_id, strava_activity_id, f23, fit_filename, f29, date FROM activity WHERE player_id = %s" % str(player_id)))
should_remove = list()
for row in rows:
activity = activities.activities.add()
row_to_protobuf(row, activity, exclude_fields=['fit'])
a = activity
#If all values for saved activity is 0, remove it from DB and results
#if (a.avg_cadence == 0 and a.avg_heart_rate == 0 and a.avg_speed == 0 and
#a.avg_watts == 0 and a.calories == 0 and a.distance == 0 and a.max_cadence == 0 and
#a.max_heart_rate == 0 and a.max_speed == 0 and a.max_watts == 0):
if a.distance == 0:
db.engine.execute("DELETE FROM activity WHERE id = %s" % a.id)
activities.activities.remove(a)
#Remove activities with less than 100m distance
if a.distance < 100:
should_remove.append(a)
for a in should_remove:
db.session.execute(sqlalchemy.text("DELETE FROM activity WHERE id = %s" % a.id))
db.session.commit()
activities.activities.remove(a)
return activities.SerializeToString(), 200
@@ -909,13 +912,14 @@ def fill_in_goal_progress(goal, player_id):
first_dt, last_dt = get_week_range(now)
else: # monthly
first_dt, last_dt = get_month_range(now)
common_sql = ("""FROM activity
WHERE player_id = %s
AND strftime('%s', start_date) >= strftime('%s', '%s')
AND strftime('%s', start_date) <= strftime('%s', '%s')""" %
(player_id, '%s', '%s', first_dt, '%s', '%s', last_dt))
if goal.type == 0: # distance
distance = db.engine.execute("""SELECT SUM(distance) FROM activity
WHERE player_id = ?
AND strftime('%s', start_date) >= strftime('%s', ?)
AND strftime('%s', start_date) <= strftime('%s', ?)
AND end_date IS NOT NULL""",
(str(player_id), first_dt, last_dt)).first()[0]
distance = db.session.execute(sqlalchemy.text('SELECT SUM(distance) %s' % common_sql)).first()[0]
if distance:
goal.actual_distance = distance
goal.actual_duration = distance
@@ -924,13 +928,7 @@ def fill_in_goal_progress(goal, player_id):
goal.actual_duration = 0.0
else: # duration
duration = db.engine.execute("""SELECT SUM(julianday(end_date) - julianday(start_date))
FROM activity
WHERE player_id = ?
AND strftime('%s', start_date) >= strftime('%s', ?)
AND strftime('%s', start_date) <= strftime('%s', ?)
AND end_date IS NOT NULL""",
(str(player_id), first_dt, last_dt)).first()[0]
duration = db.session.execute(sqlalchemy.text('SELECT SUM(julianday(end_date) - julianday(start_date)) %s' % common_sql)).first()[0]
if duration:
goal.actual_duration = duration*1440 # convert from days to minutes
goal.actual_distance = duration*1440
@@ -968,16 +966,19 @@ def api_profiles_goals(player_id):
# request.method == 'GET'
goals = goal_pb2.Goals()
rows = db.engine.execute("SELECT * FROM goal WHERE player_id = ?", (str(player_id),))
rows = db.session.execute(sqlalchemy.text("SELECT * FROM goal WHERE player_id = %s" % player_id))
need_update = list()
for row in rows:
goal = goals.goals.add()
row_to_protobuf(row, goal)
end_dt = datetime.datetime.fromtimestamp(goal.period_end_date / 1000)
now = get_utc_date_time()
if end_dt < now:
set_goal_end_date(goal, now)
update_protobuf_in_db('goal', goal, goal.id)
need_update.append(goal)
fill_in_goal_progress(goal, player_id)
for goal in need_update:
set_goal_end_date(goal, now)
update_protobuf_in_db('goal', goal, goal.id)
return goals.SerializeToString(), 200
@@ -989,7 +990,8 @@ def api_profiles_goals_id(player_id, goal_id):
if player_id != current_user.player_id:
return '', 401
goal_id = int(goal_id) & 0xffffffffffffffff
db.engine.execute("DELETE FROM goal WHERE id = ?", (str(goal_id),))
db.session.execute(sqlalchemy.text("DELETE FROM goal WHERE id = %s" % goal_id))
db.session.commit()
return '', 200
@@ -1204,30 +1206,25 @@ def relay_periodic_info():
def add_segment_results(segment_id, player_id, only_best, from_date, to_date, results):
where_stmt = "WHERE segment_id = ?"
where_args = [str(segment_id)]
where_stmt = ("WHERE segment_id = %s" % segment_id)
rows = None
if player_id:
where_stmt += " AND player_id = ?"
where_args.append(player_id)
where_stmt += (" AND player_id = %s" % player_id)
if from_date:
where_stmt += " AND strftime('%s', finish_time_str) > strftime('%s', ?)"
where_args.append(from_date)
where_stmt += (" AND strftime('%s', finish_time_str) > strftime('%s', '%s')" % ('%s', '%s', from_date))
if to_date:
where_stmt += " AND strftime('%s', finish_time_str) < strftime('%s', ?)"
where_args.append(to_date)
where_stmt += (" AND strftime('%s', finish_time_str) < strftime('%s', '%s')" % ('%s', '%s', to_date))
if only_best:
where_stmt += " AND world_time > ?"
#Only include results from max 1 hour ago
where_args.append(world_time()-(60*60*1000))
rows = db.engine.execute("""SELECT s1.* FROM segment_result s1
where_stmt += (" AND world_time > '%s'" % (world_time()-(60*60*1000)))
rows = db.session.execute(sqlalchemy.text("""SELECT s1.* FROM segment_result s1
JOIN (SELECT s.player_id, MIN(Cast(s.elapsed_ms AS INTEGER)) AS min_time
FROM segment_result s %s GROUP BY s.player_id) s2 ON s2.player_id = s1.player_id AND s2.min_time = CAST(s1.elapsed_ms AS INTEGER)
GROUP BY s1.player_id, s1.elapsed_ms
ORDER BY CAST(s1.elapsed_ms AS INTEGER)
LIMIT 1000""" % where_stmt, where_args)
LIMIT 1000""" % where_stmt))
else:
rows = db.engine.execute("SELECT * FROM segment_result %s" % where_stmt, where_args)
rows = db.session.execute(sqlalchemy.text("SELECT * FROM segment_result %s" % where_stmt))
for row in rows:
result = results.segment_results.add()
row_to_protobuf(row, result, ['f3', 'f4', 'segment_id', 'event_subgroup_id', 'finish_time_str', 'f14', 'f17', 'f18'])
@@ -1294,9 +1291,7 @@ def relay_worlds_leave(world_id):
@app.teardown_request
def teardown_request(exception):
db.close_all_sessions()
db.session.close()
db.engine.dispose()
if exception != None:
print('Exception: %s' % exception)
@@ -1307,14 +1302,16 @@ def init_database():
with open(DATABASE_INIT_SQL, 'r') as f:
sql_statements = f.read().split('\n\n')
for sql_statement in sql_statements:
db.engine.execute(sql_statement)
db.engine.execute('INSERT INTO version VALUES (?)', (DATABASE_CUR_VER,))
db.session.execute(sql_statement)
db.session.commit()
db.session.execute('INSERT INTO version VALUES (:ver)', {'ver': DATABASE_CUR_VER})
db.session.commit()
return
# Migrate database if necessary
if not os.access(DATABASE_PATH, os.W_OK):
logging.error("zwift-offline.db is not writable. Unable to upgrade database!")
return
version = db.engine.execute('SELECT version FROM version').first()[0]
version = db.session.execute('SELECT version FROM version').first()[0]
if version == DATABASE_CUR_VER:
return
# Database needs to be upgraded, try to back it up first
@@ -1329,17 +1326,19 @@ def init_database():
if version < 1:
# Adjust old world_time values in segment results to new rough estimate of Zwift's
logging.info("Upgrading zwift-offline.db to version 2")
db.engine.execute('UPDATE segment_result SET world_time = world_time-1414016075000')
db.engine.execute('UPDATE version SET version = 2')
db.session.execute('UPDATE segment_result SET world_time = world_time-1414016075000')
db.session.execute('UPDATE version SET version = 2')
db.session.commit()
if version == 1:
logging.info("Upgrading zwift-offline.db to version 2")
db.engine.execute('UPDATE segment_result SET world_time = cast(world_time/64.4131403573055-1414016075 as int)*1000')
db.engine.execute('UPDATE version SET version = 2')
db.session.execute('UPDATE segment_result SET world_time = cast(world_time/64.4131403573055-1414016075 as int)*1000')
db.session.execute('UPDATE version SET version = 2')
db.session.commit()
def check_columns():
rows = db.engine.execute(sqlalchemy.text("PRAGMA table_info(user)"))
rows = db.session.execute(sqlalchemy.text("PRAGMA table_info(user)"))
should_have_columns = User.metadata.tables['user'].columns
current_columns = list()
for row in rows:
@@ -1356,7 +1355,8 @@ def check_columns():
defaulttext = ""
else:
defaulttext = " DEFAULT %s" % column.default.arg
db.engine.execute(sqlalchemy.text("ALTER TABLE user ADD %s %s %s%s;" % (column.name, str(column.type), nulltext, defaulttext)))
db.session.execute(sqlalchemy.text("ALTER TABLE user ADD %s %s %s%s;" % (column.name, str(column.type), nulltext, defaulttext)))
db.session.commit()
def send_server_back_online_message():
@@ -1368,8 +1368,8 @@ def send_server_back_online_message():
def before_first_request():
init_database()
db.create_all(app=app)
db.session.commit()
check_columns()
db.session.close()
send_message_thread = threading.Thread(target=send_server_back_online_message)
send_message_thread.start()
@@ -1512,8 +1512,6 @@ def run_standalone(passed_online, passed_global_pace_partners, passed_global_bot
if not MULTIPLAYER:
login_manager.anonymous_user = AnonUser
login_manager.init_app(app)
db.close_all_sessions()
db.engine.dispose()
@login_manager.user_loader
def load_user(uid):