From 3bb728b7cc12814ceda512ee5652b3ff742f35fe Mon Sep 17 00:00:00 2001 From: oldnapalm <38410858+oldnapalm@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:42:45 -0300 Subject: [PATCH] Add segment ride stats --- protobuf/segment-result.proto | 10 ++++++++++ protobuf/segment_result_pb2.py | 4 +++- variants.txt | 2 +- zwift_offline.py | 16 ++++++++++++++-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/protobuf/segment-result.proto b/protobuf/segment-result.proto index 59e00b2..e9bfb7c 100644 --- a/protobuf/segment-result.proto +++ b/protobuf/segment-result.proto @@ -32,3 +32,13 @@ message SegmentResults { optional uint64 event_subgroup_id = 3; repeated SegmentResult segment_results = 4; } + +message SegmentRideStats { + required int64 segment_id = 1; + optional uint32 f2 = 2; + optional uint32 number_of_results = 3; + optional uint64 latest_time = 4; + optional float latest_percentile = 5; + optional uint64 best_time = 6; + optional float best_percentile = 7; +} diff --git a/protobuf/segment_result_pb2.py b/protobuf/segment_result_pb2.py index d784959..49c698e 100644 --- a/protobuf/segment_result_pb2.py +++ b/protobuf/segment_result_pb2.py @@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default() import profile_pb2 as profile__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14segment-result.proto\x1a\rprofile.proto\"\xe6\x03\n\rSegmentResult\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x11\n\tplayer_id\x18\x02 \x02(\x04\x12\x14\n\x0cserver_realm\x18\x03 \x01(\x04\x12\x11\n\tcourse_id\x18\x04 \x01(\x04\x12\x12\n\nsegment_id\x18\x05 \x01(\x03\x12\x19\n\x11\x65vent_subgroup_id\x18\x06 \x01(\x04\x12\x12\n\nfirst_name\x18\x07 \x02(\t\x12\x11\n\tlast_name\x18\x08 \x02(\t\x12\x12\n\nworld_time\x18\t \x01(\x04\x12\x17\n\x0f\x66inish_time_str\x18\n \x01(\t\x12\x12\n\nelapsed_ms\x18\x0b \x02(\x04\x12&\n\x12power_source_model\x18\x0c \x01(\x0e\x32\n.PowerType\x12\x17\n\x0fweight_in_grams\x18\r \x01(\r\x12\x0b\n\x03\x66\x31\x34\x18\x0e \x01(\r\x12\x11\n\tavg_power\x18\x0f \x01(\r\x12\x0f\n\x07is_male\x18\x10 \x01(\x08\x12\x0c\n\x04time\x18\x11 \x01(\t\x12 \n\x0bplayer_type\x18\x12 \x01(\x0e\x32\x0b.PlayerType\x12\x0e\n\x06\x61vg_hr\x18\x13 \x01(\r\x12\x15\n\x05sport\x18\x14 \x01(\x0e\x32\x06.Sport\x12\x13\n\x0b\x61\x63tivity_id\x18\x15 \x01(\x03\x12\x0b\n\x03\x66\x32\x32\x18\x16 \x01(\x08\x12\x0b\n\x03\x66\x32\x33\x18\x17 \x01(\t\"~\n\x0eSegmentResults\x12\x14\n\x0cserver_realm\x18\x01 \x02(\x04\x12\x12\n\nsegment_id\x18\x02 \x02(\x03\x12\x19\n\x11\x65vent_subgroup_id\x18\x03 \x01(\x04\x12\'\n\x0fsegment_results\x18\x04 \x03(\x0b\x32\x0e.SegmentResult') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14segment-result.proto\x1a\rprofile.proto\"\xe6\x03\n\rSegmentResult\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x11\n\tplayer_id\x18\x02 \x02(\x04\x12\x14\n\x0cserver_realm\x18\x03 \x01(\x04\x12\x11\n\tcourse_id\x18\x04 \x01(\x04\x12\x12\n\nsegment_id\x18\x05 \x01(\x03\x12\x19\n\x11\x65vent_subgroup_id\x18\x06 \x01(\x04\x12\x12\n\nfirst_name\x18\x07 \x02(\t\x12\x11\n\tlast_name\x18\x08 \x02(\t\x12\x12\n\nworld_time\x18\t \x01(\x04\x12\x17\n\x0f\x66inish_time_str\x18\n \x01(\t\x12\x12\n\nelapsed_ms\x18\x0b \x02(\x04\x12&\n\x12power_source_model\x18\x0c \x01(\x0e\x32\n.PowerType\x12\x17\n\x0fweight_in_grams\x18\r \x01(\r\x12\x0b\n\x03\x66\x31\x34\x18\x0e \x01(\r\x12\x11\n\tavg_power\x18\x0f \x01(\r\x12\x0f\n\x07is_male\x18\x10 \x01(\x08\x12\x0c\n\x04time\x18\x11 \x01(\t\x12 \n\x0bplayer_type\x18\x12 \x01(\x0e\x32\x0b.PlayerType\x12\x0e\n\x06\x61vg_hr\x18\x13 \x01(\r\x12\x15\n\x05sport\x18\x14 \x01(\x0e\x32\x06.Sport\x12\x13\n\x0b\x61\x63tivity_id\x18\x15 \x01(\x03\x12\x0b\n\x03\x66\x32\x32\x18\x16 \x01(\x08\x12\x0b\n\x03\x66\x32\x33\x18\x17 \x01(\t\"~\n\x0eSegmentResults\x12\x14\n\x0cserver_realm\x18\x01 \x02(\x04\x12\x12\n\nsegment_id\x18\x02 \x02(\x03\x12\x19\n\x11\x65vent_subgroup_id\x18\x03 \x01(\x04\x12\'\n\x0fsegment_results\x18\x04 \x03(\x0b\x32\x0e.SegmentResult\"\xa9\x01\n\x10SegmentRideStats\x12\x12\n\nsegment_id\x18\x01 \x02(\x03\x12\n\n\x02\x66\x32\x18\x02 \x01(\r\x12\x19\n\x11number_of_results\x18\x03 \x01(\r\x12\x13\n\x0blatest_time\x18\x04 \x01(\x04\x12\x19\n\x11latest_percentile\x18\x05 \x01(\x02\x12\x11\n\tbest_time\x18\x06 \x01(\x04\x12\x17\n\x0f\x62\x65st_percentile\x18\x07 \x01(\x02') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'segment_result_pb2', globals()) @@ -25,4 +25,6 @@ if _descriptor._USE_C_DESCRIPTORS == False: _SEGMENTRESULT._serialized_end=526 _SEGMENTRESULTS._serialized_start=528 _SEGMENTRESULTS._serialized_end=654 + _SEGMENTRIDESTATS._serialized_start=657 + _SEGMENTRIDESTATS._serialized_end=826 # @@protoc_insertion_point(module_scope) diff --git a/variants.txt b/variants.txt index a2d165d..f22a35b 100644 --- a/variants.txt +++ b/variants.txt @@ -555,7 +555,7 @@ "boolValue": false }, "EnableHeroicDifficulty": { - "boolValue": false + "boolValue": true }, "MustHaveDefaultRoad": { "boolValue": false diff --git a/zwift_offline.py b/zwift_offline.py index 7b3a40f..f443c72 100644 --- a/zwift_offline.py +++ b/zwift_offline.py @@ -3221,8 +3221,20 @@ def api_personal_records_my_records(): @jwt_to_session_cookie @login_required def api_personal_records_my_segment_ride_stats(sport): - # TODO - return '', 200 + if not request.args.get('segmentId'): + return '', 422 + stats = segment_result_pb2.SegmentRideStats() + stats.segment_id = int(request.args.get('segmentId')) + where_stmt = "WHERE segment_id = :s AND player_id = :p AND sport = :sp" + args = {"s": stats.segment_id, "p": current_user.player_id, "sp": profile_pb2.Sport.Value(sport)} + row = db.session.execute(sqlalchemy.text("SELECT * FROM segment_result %s ORDER BY elapsed_ms LIMIT 1" % where_stmt), args).first() + if row: + stats.number_of_results = db.session.execute(sqlalchemy.text("SELECT COUNT(*) FROM segment_result %s" % where_stmt), args).scalar() + stats.latest_time = row.elapsed_ms # Zwift sends only best + stats.latest_percentile = 100 + stats.best_time = row.elapsed_ms + stats.best_percentile = 100 + return stats.SerializeToString(), 200 @app.route('/api/personal-records/results/summary/profiles/me/', methods=['GET'])