Skip to content

Commit faa16b5

Browse files
authored
Training packs (#360)
* Training packs :D * Training pack interface * Remove import * Better display (and creation) * Allow modification of BaseURL * Update requirements.txt * Catch exceptions properly * Order by match date when selecting replays * Show create button when none exist * Fix style * Fix style * Remove local link * Fix rotations * Add some conditions to shots * Preview shots in replay viewer * Fix UI a bit * Add date picker * Move replay viewer to top when half screen * Remove non-standard playlists * Move to from celery tasks and filter by map/playlist * Add header and game id * Add modal to create pack * Adjust ball speed to better match replays * Remove kickoffs * Add beta restriction * Fix JS style * Fix Python test * Fixes * Remove logging * Add Crypto to test * Query fixes * Add comment with license * Move things around a bit * Move more stuff around * Remove extra commented stuff * Misc fixes * Add task id and metric to find task time * Fix for importerror * Add compact * changed coverage ignore This should ignore files that we do not care about coverage for * ignore parsing files * Few fixes * Raise exception when user has no replays for this * Add logger * Catch error with config * Add training packs test * Fix pack test * Fix test (to account for new mag)
1 parent 93a6baa commit faa16b5

38 files changed

+2175
-137
lines changed

.coveragerc

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ omit =
1313
loader.py
1414
imports_test.py
1515
helpers/*
16-
redis/*
16+
redis/*
17+
backend/tasks/training_packs/parsing/*

backend/blueprints/spa_api/service_layers/replay/replay_positions.py

+11
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@ def __init__(self, id_: str,
1919
@staticmethod
2020
def create_from_id(id_: str, query_params=None) -> 'ReplayPositions':
2121
filter_frames = None
22+
filter_frame_start = None
23+
filter_frame_count = None
2224
if query_params is not None and 'frame' in query_params:
2325
filter_frames = query_params['frame']
26+
if query_params is not None and 'frame_start' in query_params and 'frame_count' in query_params:
27+
filter_frame_start = query_params['frame_start']
28+
filter_frame_count = query_params['frame_count']
2429

2530
data_frame = FileManager.get_pandas(id_)
2631
if filter_frames is not None:
2732
data_frame = ReplayPositions.filter_frames(filter_frames, data_frame)
33+
if filter_frame_start is not None and filter_frame_count is not None:
34+
data_frame = ReplayPositions.filter_frames_range(filter_frame_start, filter_frame_count, data_frame)
2835
protobuf_game = FileManager.get_proto(id_)
2936

3037
cs = ['pos_x', 'pos_y', 'pos_z']
@@ -62,3 +69,7 @@ def process_player_df(game) -> List[ReplayPlayer]:
6269
@staticmethod
6370
def filter_frames(frames, data_frame):
6471
return data_frame.loc[data_frame.index.isin(frames)]
72+
73+
@staticmethod
74+
def filter_frames_range(fmin, count, data_frame):
75+
return data_frame.loc[(data_frame.index >= fmin) & (data_frame.index < fmin + count)]

backend/blueprints/spa_api/spa_api.py

+42-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from carball.analysis.utils.proto_manager import ProtobufManager
1313
from flask import jsonify, Blueprint, current_app, request, send_from_directory, Response
14+
from sqlalchemy import desc
1415
from werkzeug.utils import secure_filename, redirect
1516

1617
from backend.blueprints.spa_api.service_layers.homepage.patreon import PatreonProgress
@@ -25,6 +26,7 @@
2526
player_id, heatmap_query_params
2627
from backend.database.startup import lazy_get_redis
2728
from backend.tasks.add_replay import create_replay_task, parsed_replay_processing
29+
from backend.tasks.celery_tasks import create_training_pack
2830
from backend.utils.logging import ErrorLogger
2931
from backend.utils.global_functions import get_current_user_id
3032
from backend.blueprints.spa_api.service_layers.replay.visualizations import Visualizations
@@ -45,16 +47,18 @@
4547
REPLAY_BUCKET = config.REPLAY_BUCKET
4648
PROTO_BUCKET = config.PROTO_BUCKET
4749
PARSED_BUCKET = config.PARSED_BUCKET
50+
TRAINING_PACK_BUCKET = config.TRAINING_PACK_BUCKET
4851
except:
4952
print('Not uploading to buckets')
5053
REPLAY_BUCKET = ''
5154
PROTO_BUCKET = ''
5255
PARSED_BUCKET = ''
56+
TRAINING_PACK_BUCKET = ''
5357

5458
from backend.blueprints.spa_api.service_layers.stat import get_explanations
5559
from backend.blueprints.spa_api.service_layers.utils import with_session
5660
from backend.blueprints.steam import get_vanity_to_steam_id_or_random_response, steam_id_to_profile
57-
from backend.database.objects import Game, GameVisibilitySetting
61+
from backend.database.objects import Game, GameVisibilitySetting, TrainingPack
5862
from backend.database.wrapper.chart.chart_data import convert_to_csv
5963
from backend.tasks import celery_tasks
6064
from backend.blueprints.spa_api.errors.errors import CalculatedError, NotYetImplemented, PlayerNotFound, \
@@ -76,6 +80,12 @@
7680
from backend.blueprints.spa_api.utils.decorators import require_user, with_query_params
7781
from backend.blueprints.spa_api.utils.query_params_handler import QueryParam, get_query_params
7882

83+
try:
84+
from backend.tasks.training_packs.task import TrainingPackCreation
85+
except (ModuleNotFoundError, ImportError):
86+
TrainingPackCreation = None
87+
print("Missing config or AES Key and CRC, not creating training packs")
88+
7989
logger = logging.getLogger(__name__)
8090

8191
bp = Blueprint('api', __name__, url_prefix='/api/')
@@ -270,8 +280,12 @@ def api_get_replay_basic_team_stats_download(id_):
270280

271281

272282
@bp.route('replay/<id_>/positions')
273-
@with_query_params(accepted_query_params=[QueryParam(name='frame', type_=int, optional=True, is_list=True),
274-
QueryParam(name='as_proto', type_=bool, optional=True)])
283+
@with_query_params(accepted_query_params=[
284+
QueryParam(name='frame', type_=int, optional=True, is_list=True),
285+
QueryParam(name='frame_start', type_=int, optional=True),
286+
QueryParam(name='frame_count', type_=int, optional=True),
287+
QueryParam(name='as_proto', type_=bool, optional=True)
288+
])
275289
def api_get_replay_positions(id_, query_params=None):
276290
positions = ReplayPositions.create_from_id(id_, query_params=query_params)
277291
if query_params is None or 'as_proto' not in query_params:
@@ -514,6 +528,31 @@ def api_handle_error(error: CalculatedError):
514528
return response
515529

516530

531+
@bp.route('/training/create')
532+
@require_user
533+
@with_query_params(accepted_query_params=[
534+
QueryParam(name="date_start", type_=str, optional=True),
535+
QueryParam(name="date_end", type_=str, optional=True)
536+
])
537+
def api_create_trainingpack(query_params=None):
538+
date_start = None
539+
date_end = None
540+
if 'date_start' in query_params:
541+
date_start = query_params['date_start']
542+
if 'date_end' in query_params:
543+
date_end = query_params['date_end']
544+
task = create_training_pack.delay(get_current_user_id(), 10, date_start, date_end)
545+
return better_jsonify({'status': 'Success', 'id': task.id})
546+
547+
548+
@bp.route('/training/list')
549+
@require_user
550+
@with_session
551+
def api_find_trainingpack(session=None):
552+
player = get_current_user_id()
553+
return better_jsonify(TrainingPackCreation.list_packs(player, session))
554+
555+
517556
# Homepage
518557

519558
@bp.route('/home/twitch')

backend/database/objects.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import datetime
33
import enum
44

5-
from sqlalchemy import Column, Integer, String, Boolean, Float, ForeignKey, DateTime, Enum, Table, UniqueConstraint, \
6-
and_, ForeignKeyConstraint
5+
from sqlalchemy import Column, Integer, String, Boolean, Float, ForeignKey, DateTime, Enum, UniqueConstraint, \
6+
ForeignKeyConstraint, JSON
77
from sqlalchemy.dialects import postgresql
88
from sqlalchemy.ext.declarative import declarative_base
99
from sqlalchemy.orm import relationship, validates
@@ -381,3 +381,12 @@ class GameVisibility(DBObjectBase):
381381
player = Column(String(40), ForeignKey('players.platformid'))
382382
visibility = Column(Enum(GameVisibilitySetting))
383383
release_date = Column(DateTime, default=datetime.datetime.max)
384+
385+
386+
class TrainingPack(DBObjectBase):
387+
__tablename__ = "training_packs"
388+
id = Column(Integer, primary_key=True, autoincrement=True)
389+
guid = Column(String(40))
390+
player = Column(String(40), ForeignKey('players.platformid'), index=True)
391+
shots = Column(JSON)
392+
creation_date = Column(DateTime, default=datetime.datetime.utcnow)

backend/initial_setup.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
users = []
3636
prod = False
3737

38+
try:
39+
from config import BASE_URL
40+
except ImportError:
41+
BASE_URL = 'https://calculated.gg'
42+
3843

3944
class CalculatedServer:
4045

@@ -111,7 +116,7 @@ def set_up_app_config(app: Flask):
111116
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
112117
app.config['MAX_CONTENT_LENGTH'] = 512 * 1024 * 1024
113118
app.config['TEMPLATES_AUTO_RELOAD'] = True
114-
app.config['BASE_URL'] = 'https://calculated.gg'
119+
app.config['BASE_URL'] = 'https://calculated.gg' if BASE_URL is None else BASE_URL
115120
app.config['REPLAY_DIR'] = os.path.join(BASE_FOLDER, 'data', 'rlreplays')
116121
app.config['PARSED_DIR'] = os.path.join(BASE_FOLDER, 'data', 'parsed')
117122
app.config['VERSION'] = CalculatedServer.get_version()

backend/tasks/celery_tasks.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Celery workers
22
import base64
33
import json
4+
import time
45
from enum import Enum, auto
56
from typing import Dict
67

@@ -18,6 +19,17 @@
1819
from backend.tasks.add_replay import parse_replay
1920
from backend.tasks.middleware import DBTask
2021
from backend.tasks.periodic_stats import calculate_global_distributions
22+
try:
23+
from backend.tasks.training_packs.task import TrainingPackCreation
24+
from backend.utils.metrics import METRICS_TRAINING_PACK_CREATION_TIME
25+
except (ModuleNotFoundError, ImportError):
26+
TrainingPackCreation = None
27+
print("Missing config or AES Key and CRC, not creating training packs")
28+
29+
try:
30+
from backend.tasks.training_packs.training_packs import create_pack_from_replays
31+
except:
32+
pass
2133

2234
celery = Celery(__name__, broker=celeryconfig.broker_url)
2335

@@ -32,7 +44,7 @@ def create_celery_config():
3244

3345
@celery.on_after_configure.connect
3446
def setup_periodic_tasks(sender, **kwargs):
35-
sender.add_periodic_task(60 * 60 * 3, calc_global_stats.s(), name='calculate global stats every 3 hrs')
47+
sender.add_periodic_task(60 * 60 * 24 * 3, calc_global_stats.s(), name='calculate global stats every 3 days')
3648
sender.add_periodic_task(60 * 60 * 24, calc_global_dists.s(), name='calculate global dists every day')
3749
sender.add_periodic_task(60 * 60 * 24, calc_leaderboards.s(), name='calculate leaderboards every day')
3850
sender.add_periodic_task(60 * 60 * 24 * 3, calc_leaderboards.s(), name='calculate item stats every 3 days')
@@ -96,6 +108,21 @@ def calc_item_stats(self, session=None):
96108
lazy_get_redis().set('item_stats', json.dumps(results))
97109

98110

111+
@celery.task(base=DBTask, bind=True, priority=9)
112+
def create_training_pack(self, id_, n=10, date_start=None, date_end=None, session=None):
113+
if session is None:
114+
sess = self.session()
115+
else:
116+
sess = session
117+
start = time.time()
118+
url = TrainingPackCreation.create_from_player(id_, n, date_start, date_end, sess)
119+
end = time.time()
120+
METRICS_TRAINING_PACK_CREATION_TIME.observe(
121+
start - end
122+
)
123+
return url
124+
125+
99126
class ResultState(Enum):
100127
PENDING = auto()
101128
STARTED = auto()

backend/tasks/training_packs/__init__.py

Whitespace-only changes.
Binary file not shown.

backend/tasks/training_packs/parsing/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)