mirror of
https://github.com/titanscouting/tra-superscript.git
synced 2025-09-26 07:10:18 +00:00
Compare commits
32 Commits
competitio
...
snyk-fix-a
Author | SHA1 | Date | |
---|---|---|---|
|
7833cf26ec | ||
|
df37947b21 | ||
|
de4d3d4967 | ||
|
d56411253c | ||
|
c415225afe | ||
|
d684813ee0 | ||
|
26079f3180 | ||
|
99e722c400 | ||
|
f5a0e0fe8c | ||
|
28e423942f | ||
|
8977f8c277 | ||
|
2b0f718aa5 | ||
|
30469a3211 | ||
|
391d4e1996 | ||
|
224f64e8b7 | ||
|
aa7d7ca927 | ||
|
d10c16d483 | ||
|
f211d00f2d | ||
|
69c707689b | ||
|
d2f9c802b3 | ||
|
99e28f5e83 | ||
|
18dbc174bd | ||
|
79689d69c8 | ||
|
80c3f1224b | ||
|
960a1b3165 | ||
|
89fcd366d3 | ||
|
79cde44108 | ||
|
2b896db9a9 | ||
|
483897c011 | ||
|
9287d98fe2 | ||
|
991751a340 | ||
|
9d2476b5eb |
@@ -1,6 +1,7 @@
|
|||||||
FROM python:slim
|
FROM ubuntu:20.04
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
RUN apt-get -y update; apt-get -y upgrade
|
RUN apt-get -y update
|
||||||
RUN apt-get -y install git binutils
|
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||||
COPY requirements.txt .
|
RUN apt-get install -y python3 python3-dev git python3-pip python3-kivy python-is-python3 libgl1-mesa-dev build-essential
|
||||||
RUN pip install -r requirements.txt
|
RUN ln -s $(which pip3) /usr/bin/pip
|
||||||
|
RUN pip install pymongo pandas numpy scipy scikit-learn matplotlib pylint kivy
|
||||||
|
2
.devcontainer/dev-dockerfile
Normal file
2
.devcontainer/dev-dockerfile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FROM titanscout2022/tra-analysis-base:latest
|
||||||
|
WORKDIR /
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "TRA Analysis Development Environment",
|
"name": "TRA Analysis Development Environment",
|
||||||
"build": {
|
"build": {
|
||||||
"dockerfile": "Dockerfile"
|
"dockerfile": "dev-dockerfile",
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"terminal.integrated.shell.linux": "/bin/bash",
|
"terminal.integrated.shell.linux": "/bin/bash",
|
||||||
@@ -18,5 +18,5 @@
|
|||||||
"ms-python.python",
|
"ms-python.python",
|
||||||
"waderyan.gitblame"
|
"waderyan.gitblame"
|
||||||
],
|
],
|
||||||
"postCreateCommand": ""
|
"postCreateCommand": "/usr/bin/pip3 install -r ${containerWorkspaceFolder}/src/requirements.txt && /usr/bin/pip3 install --no-cache-dir pylint && /usr/bin/pip3 install pytest"
|
||||||
}
|
}
|
||||||
|
17
.github/workflows/build-cli.yml
vendored
Normal file
17
.github/workflows/build-cli.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: Superscript Unit Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published, edited]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
generate:
|
||||||
|
name: Build Linux
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout master
|
||||||
|
uses: actions/checkout@master
|
34
.github/workflows/superscript-unit.yml
vendored
Normal file
34
.github/workflows/superscript-unit.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: Superscript Unit Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.7, 3.8]
|
||||||
|
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pytest
|
||||||
|
if [ -f src/requirements.txt ]; then pip install -r src/requirements.txt; fi
|
||||||
|
- name: Test with pytest
|
||||||
|
run: |
|
||||||
|
pytest test/
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -9,6 +9,9 @@
|
|||||||
**/tra_analysis/
|
**/tra_analysis/
|
||||||
**/temp/*
|
**/temp/*
|
||||||
|
|
||||||
|
**/errorlog.txt
|
||||||
|
/dist/superscript.*
|
||||||
|
/dist/superscript
|
||||||
**/*.pid
|
**/*.pid
|
||||||
|
|
||||||
**/profile.*
|
**/profile.*
|
||||||
@@ -18,4 +21,4 @@
|
|||||||
/dist/*
|
/dist/*
|
||||||
|
|
||||||
slurm-tra-superscript.out
|
slurm-tra-superscript.out
|
||||||
config*.json
|
config*.json
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Red Alliance Analysis · 
|
# Red Alliance Analysis · 
|
||||||
|
|
||||||
Titan Robotics 2022 Strategy Team Repository for Data Analysis Tools. Included with these tools are the backend data analysis engine formatted as a python package, associated binaries for the analysis package, and premade scripts that can be pulled directly from this repository and will integrate with other Red Alliance applications to quickly deploy FRC scouting tools.
|
Titan Robotics 2022 Strategy Team Repository for Data Analysis Tools. Included with these tools are the backend data analysis engine formatted as a python package, associated binaries for the analysis package, and premade scripts that can be pulled directly from this repository and will integrate with other Red Alliance applications to quickly deploy FRC scouting tools.
|
||||||
|
|
||||||
@@ -43,4 +43,4 @@ don't worry, you may have just not configured the application correctly, but wou
|
|||||||
|
|
||||||
# Build Statuses
|
# Build Statuses
|
||||||
|
|
||||||
Coming soon!
|
Coming soon!
|
||||||
|
5
build/build-CLI.bat
Normal file
5
build/build-CLI.bat
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
set pathtospec="../src/superscript.spec"
|
||||||
|
set pathtodist="../dist/"
|
||||||
|
set pathtowork="temp/"
|
||||||
|
|
||||||
|
pyinstaller --onefile --clean --distpath %pathtodist% --workpath %pathtowork% %pathtospec%
|
5
build/build-CLI.sh
Normal file
5
build/build-CLI.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pathtospec="../src/superscript.spec"
|
||||||
|
pathtodist="../dist/"
|
||||||
|
pathtowork="temp/"
|
||||||
|
|
||||||
|
pyinstaller --onefile --clean --distpath ${pathtodist} --workpath ${pathtowork} ${pathtospec}
|
19
src/requirements.txt
Normal file
19
src/requirements.txt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
requests
|
||||||
|
pymongo
|
||||||
|
pandas
|
||||||
|
tra-analysis
|
||||||
|
|
||||||
|
dnspython
|
||||||
|
pyinstaller
|
||||||
|
requests
|
||||||
|
pymongo
|
||||||
|
|
||||||
|
numpy
|
||||||
|
scipy
|
||||||
|
scikit-learn
|
||||||
|
six
|
||||||
|
pyparsing
|
||||||
|
pandas
|
||||||
|
|
||||||
|
kivy==2.0.0rc2
|
||||||
|
setuptools>=78.1.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
627
src/superscript.py
Normal file
627
src/superscript.py
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
# Titan Robotics Team 2022: Superscript Script
|
||||||
|
# Written by Arthur Lu, Jacob Levine, and Dev Singh
|
||||||
|
# Notes:
|
||||||
|
# setup:
|
||||||
|
|
||||||
|
__version__ = "0.8.6"
|
||||||
|
|
||||||
|
# changelog should be viewed using print(analysis.__changelog__)
|
||||||
|
__changelog__ = """changelog:
|
||||||
|
0.8.6:
|
||||||
|
- added proper main function
|
||||||
|
0.8.5:
|
||||||
|
- added more gradeful KeyboardInterrupt exiting
|
||||||
|
- redirected stderr to errorlog.txt
|
||||||
|
0.8.4:
|
||||||
|
- added better error message for missing config.json
|
||||||
|
- added automatic config.json creation
|
||||||
|
- added splash text with version and system info
|
||||||
|
0.8.3:
|
||||||
|
- updated matchloop with new regression format (requires tra_analysis 3.x)
|
||||||
|
0.8.2:
|
||||||
|
- readded while true to main function
|
||||||
|
- added more thread config options
|
||||||
|
0.8.1:
|
||||||
|
- optimized matchloop further by bypassing GIL
|
||||||
|
0.8.0:
|
||||||
|
- added multithreading to matchloop
|
||||||
|
- tweaked user log
|
||||||
|
0.7.0:
|
||||||
|
- finished implementing main function
|
||||||
|
0.6.2:
|
||||||
|
- integrated get_team_rankings.py as get_team_metrics() function
|
||||||
|
- integrated visualize_pit.py as graph_pit_histogram() function
|
||||||
|
0.6.1:
|
||||||
|
- bug fixes with analysis.Metric() calls
|
||||||
|
- modified metric functions to use config.json defined default values
|
||||||
|
0.6.0:
|
||||||
|
- removed main function
|
||||||
|
- changed load_config function
|
||||||
|
- added save_config function
|
||||||
|
- added load_match function
|
||||||
|
- renamed simpleloop to matchloop
|
||||||
|
- moved simplestats function inside matchloop
|
||||||
|
- renamed load_metrics to load_metric
|
||||||
|
- renamed metricsloop to metricloop
|
||||||
|
- split push to database functions amon push_match, push_metric, push_pit
|
||||||
|
- moved
|
||||||
|
0.5.2:
|
||||||
|
- made changes due to refactoring of analysis
|
||||||
|
0.5.1:
|
||||||
|
- text fixes
|
||||||
|
- removed matplotlib requirement
|
||||||
|
0.5.0:
|
||||||
|
- improved user interface
|
||||||
|
0.4.2:
|
||||||
|
- removed unessasary code
|
||||||
|
0.4.1:
|
||||||
|
- fixed bug where X range for regression was determined before sanitization
|
||||||
|
- better sanitized data
|
||||||
|
0.4.0:
|
||||||
|
- fixed spelling issue in __changelog__
|
||||||
|
- addressed nan bug in regression
|
||||||
|
- fixed errors on line 335 with metrics calling incorrect key "glicko2"
|
||||||
|
- fixed errors in metrics computing
|
||||||
|
0.3.0:
|
||||||
|
- added analysis to pit data
|
||||||
|
0.2.1:
|
||||||
|
- minor stability patches
|
||||||
|
- implemented db syncing for timestamps
|
||||||
|
- fixed bugs
|
||||||
|
0.2.0:
|
||||||
|
- finalized testing and small fixes
|
||||||
|
0.1.4:
|
||||||
|
- finished metrics implement, trueskill is bugged
|
||||||
|
0.1.3:
|
||||||
|
- working
|
||||||
|
0.1.2:
|
||||||
|
- started implement of metrics
|
||||||
|
0.1.1:
|
||||||
|
- cleaned up imports
|
||||||
|
0.1.0:
|
||||||
|
- tested working, can push to database
|
||||||
|
0.0.9:
|
||||||
|
- tested working
|
||||||
|
- prints out stats for the time being, will push to database later
|
||||||
|
0.0.8:
|
||||||
|
- added data import
|
||||||
|
- removed tba import
|
||||||
|
- finished main method
|
||||||
|
0.0.7:
|
||||||
|
- added load_config
|
||||||
|
- optimized simpleloop for readibility
|
||||||
|
- added __all__ entries
|
||||||
|
- added simplestats engine
|
||||||
|
- pending testing
|
||||||
|
0.0.6:
|
||||||
|
- fixes
|
||||||
|
0.0.5:
|
||||||
|
- imported pickle
|
||||||
|
- created custom database object
|
||||||
|
0.0.4:
|
||||||
|
- fixed simpleloop to actually return a vector
|
||||||
|
0.0.3:
|
||||||
|
- added metricsloop which is unfinished
|
||||||
|
0.0.2:
|
||||||
|
- added simpleloop which is untested until data is provided
|
||||||
|
0.0.1:
|
||||||
|
- created script
|
||||||
|
- added analysis, numba, numpy imports
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = (
|
||||||
|
"Arthur Lu <learthurgo@gmail.com>",
|
||||||
|
"Jacob Levine <jlevine@imsa.edu>",
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"load_config",
|
||||||
|
"save_config",
|
||||||
|
"get_previous_time",
|
||||||
|
"load_match",
|
||||||
|
"matchloop",
|
||||||
|
"load_metric",
|
||||||
|
"metricloop",
|
||||||
|
"load_pit",
|
||||||
|
"pitloop",
|
||||||
|
"push_match",
|
||||||
|
"push_metric",
|
||||||
|
"push_pit",
|
||||||
|
]
|
||||||
|
|
||||||
|
# imports:
|
||||||
|
|
||||||
|
from tra_analysis import analysis as an
|
||||||
|
import data as d
|
||||||
|
from collections import defaultdict
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
from os import system, name
|
||||||
|
from pathlib import Path
|
||||||
|
from multiprocessing import Pool
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
global exec_threads
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
global exec_threads
|
||||||
|
|
||||||
|
sys.stderr = open("errorlog.txt", "w")
|
||||||
|
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
|
splash()
|
||||||
|
|
||||||
|
while (True):
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
current_time = time.time()
|
||||||
|
print("[OK] time: " + str(current_time))
|
||||||
|
|
||||||
|
config = load_config("config.json")
|
||||||
|
competition = config["competition"]
|
||||||
|
match_tests = config["statistics"]["match"]
|
||||||
|
pit_tests = config["statistics"]["pit"]
|
||||||
|
metrics_tests = config["statistics"]["metric"]
|
||||||
|
print("[OK] configs loaded")
|
||||||
|
|
||||||
|
print("[OK] starting threads")
|
||||||
|
cfg_max_threads = config["max-threads"]
|
||||||
|
sys_max_threads = os.cpu_count()
|
||||||
|
if cfg_max_threads > -sys_max_threads and cfg_max_threads < 0 :
|
||||||
|
alloc_processes = sys_max_threads + cfg_max_threads
|
||||||
|
elif cfg_max_threads > 0 and cfg_max_threads < 1:
|
||||||
|
alloc_processes = math.floor(cfg_max_threads * sys_max_threads)
|
||||||
|
elif cfg_max_threads > 1 and cfg_max_threads <= sys_max_threads:
|
||||||
|
alloc_processes = cfg_max_threads
|
||||||
|
elif cfg_max_threads == 0:
|
||||||
|
alloc_processes = sys_max_threads
|
||||||
|
else:
|
||||||
|
print("[ERROR] Invalid number of processes, must be between -" + str(sys_max_threads) + " and " + str(sys_max_threads))
|
||||||
|
exit()
|
||||||
|
exec_threads = Pool(processes = alloc_processes)
|
||||||
|
print("[OK] " + str(alloc_processes) + " threads started")
|
||||||
|
|
||||||
|
apikey = config["key"]["database"]
|
||||||
|
tbakey = config["key"]["tba"]
|
||||||
|
print("[OK] loaded keys")
|
||||||
|
|
||||||
|
previous_time = get_previous_time(apikey)
|
||||||
|
print("[OK] analysis backtimed to: " + str(previous_time))
|
||||||
|
|
||||||
|
print("[OK] loading data")
|
||||||
|
start = time.time()
|
||||||
|
match_data = load_match(apikey, competition)
|
||||||
|
pit_data = load_pit(apikey, competition)
|
||||||
|
print("[OK] loaded data in " + str(time.time() - start) + " seconds")
|
||||||
|
|
||||||
|
print("[OK] running match stats")
|
||||||
|
start = time.time()
|
||||||
|
matchloop(apikey, competition, match_data, match_tests)
|
||||||
|
print("[OK] finished match stats in " + str(time.time() - start) + " seconds")
|
||||||
|
|
||||||
|
print("[OK] running team metrics")
|
||||||
|
start = time.time()
|
||||||
|
metricloop(tbakey, apikey, competition, previous_time, metrics_tests)
|
||||||
|
print("[OK] finished team metrics in " + str(time.time() - start) + " seconds")
|
||||||
|
|
||||||
|
print("[OK] running pit analysis")
|
||||||
|
start = time.time()
|
||||||
|
pitloop(apikey, competition, pit_data, pit_tests)
|
||||||
|
print("[OK] finished pit analysis in " + str(time.time() - start) + " seconds")
|
||||||
|
|
||||||
|
set_current_time(apikey, current_time)
|
||||||
|
print("[OK] finished all tests, looping")
|
||||||
|
|
||||||
|
print_hrule()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n[OK] caught KeyboardInterrupt, killing processes")
|
||||||
|
exec_threads.terminate()
|
||||||
|
print("[OK] processes killed, exiting")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
#clear()
|
||||||
|
|
||||||
|
def clear():
|
||||||
|
|
||||||
|
# for windows
|
||||||
|
if name == 'nt':
|
||||||
|
_ = system('cls')
|
||||||
|
|
||||||
|
# for mac and linux(here, os.name is 'posix')
|
||||||
|
else:
|
||||||
|
_ = system('clear')
|
||||||
|
|
||||||
|
def print_hrule():
|
||||||
|
|
||||||
|
print("#"+38*"-"+"#")
|
||||||
|
|
||||||
|
def print_box(s):
|
||||||
|
|
||||||
|
temp = "|"
|
||||||
|
temp += s
|
||||||
|
temp += (40-len(s)-2)*" "
|
||||||
|
temp += "|"
|
||||||
|
print(temp)
|
||||||
|
|
||||||
|
def splash():
|
||||||
|
|
||||||
|
print_hrule()
|
||||||
|
print_box(" superscript version: " + __version__)
|
||||||
|
print_box(" os: " + platform.system())
|
||||||
|
print_box(" python: " + platform.python_version())
|
||||||
|
print_hrule()
|
||||||
|
|
||||||
|
def load_config(file):
|
||||||
|
|
||||||
|
config_vector = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
f = open(file)
|
||||||
|
except:
|
||||||
|
print("[ERROR] could not locate config.json, generating blank config.json and exiting")
|
||||||
|
f = open(file, "w")
|
||||||
|
f.write(sample_json)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
config_vector = json.load(f)
|
||||||
|
|
||||||
|
return config_vector
|
||||||
|
|
||||||
|
def save_config(file, config_vector):
|
||||||
|
|
||||||
|
with open(file) as f:
|
||||||
|
json.dump(config_vector, f)
|
||||||
|
|
||||||
|
def get_previous_time(apikey):
|
||||||
|
|
||||||
|
previous_time = d.get_analysis_flags(apikey, "latest_update")
|
||||||
|
|
||||||
|
if previous_time == None:
|
||||||
|
|
||||||
|
d.set_analysis_flags(apikey, "latest_update", 0)
|
||||||
|
previous_time = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
previous_time = previous_time["latest_update"]
|
||||||
|
|
||||||
|
return previous_time
|
||||||
|
|
||||||
|
def set_current_time(apikey, current_time):
|
||||||
|
|
||||||
|
d.set_analysis_flags(apikey, "latest_update", {"latest_update":current_time})
|
||||||
|
|
||||||
|
def load_match(apikey, competition):
|
||||||
|
|
||||||
|
return d.get_match_data_formatted(apikey, competition)
|
||||||
|
|
||||||
|
def simplestats(data_test):
|
||||||
|
|
||||||
|
data = np.array(data_test[0])
|
||||||
|
data = data[np.isfinite(data)]
|
||||||
|
ranges = list(range(len(data)))
|
||||||
|
|
||||||
|
test = data_test[1]
|
||||||
|
|
||||||
|
if test == "basic_stats":
|
||||||
|
return an.basic_stats(data)
|
||||||
|
|
||||||
|
if test == "historical_analysis":
|
||||||
|
return an.histo_analysis([ranges, data])
|
||||||
|
|
||||||
|
if test == "regression_linear":
|
||||||
|
return an.regression(ranges, data, ['lin'])
|
||||||
|
|
||||||
|
if test == "regression_logarithmic":
|
||||||
|
return an.regression(ranges, data, ['log'])
|
||||||
|
|
||||||
|
if test == "regression_exponential":
|
||||||
|
return an.regression(ranges, data, ['exp'])
|
||||||
|
|
||||||
|
if test == "regression_polynomial":
|
||||||
|
return an.regression(ranges, data, ['ply'])
|
||||||
|
|
||||||
|
if test == "regression_sigmoidal":
|
||||||
|
return an.regression(ranges, data, ['sig'])
|
||||||
|
|
||||||
|
def matchloop(apikey, competition, data, tests): # expects 3D array with [Team][Variable][Match]
|
||||||
|
|
||||||
|
global exec_threads
|
||||||
|
|
||||||
|
short_mapping = {"regression_linear": "lin", "regression_logarithmic": "log", "regression_exponential": "exp", "regression_polynomial": "ply", "regression_sigmoidal": "sig"}
|
||||||
|
|
||||||
|
class AutoVivification(dict):
|
||||||
|
def __getitem__(self, item):
|
||||||
|
try:
|
||||||
|
return dict.__getitem__(self, item)
|
||||||
|
except KeyError:
|
||||||
|
value = self[item] = type(self)()
|
||||||
|
return value
|
||||||
|
|
||||||
|
return_vector = {}
|
||||||
|
|
||||||
|
team_filtered = []
|
||||||
|
variable_filtered = []
|
||||||
|
variable_data = []
|
||||||
|
test_filtered = []
|
||||||
|
result_filtered = []
|
||||||
|
return_vector = AutoVivification()
|
||||||
|
|
||||||
|
for team in data:
|
||||||
|
|
||||||
|
for variable in data[team]:
|
||||||
|
|
||||||
|
if variable in tests:
|
||||||
|
|
||||||
|
for test in tests[variable]:
|
||||||
|
|
||||||
|
team_filtered.append(team)
|
||||||
|
variable_filtered.append(variable)
|
||||||
|
variable_data.append((data[team][variable], test))
|
||||||
|
test_filtered.append(test)
|
||||||
|
|
||||||
|
result_filtered = exec_threads.map(simplestats, variable_data)
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
result_filtered = list(result_filtered)
|
||||||
|
|
||||||
|
for result in result_filtered:
|
||||||
|
|
||||||
|
filtered = test_filtered[i]
|
||||||
|
|
||||||
|
try:
|
||||||
|
short = short_mapping[filtered]
|
||||||
|
return_vector[team_filtered[i]][variable_filtered[i]][test_filtered[i]] = result[short]
|
||||||
|
except KeyError: # not in mapping
|
||||||
|
return_vector[team_filtered[i]][variable_filtered[i]][test_filtered[i]] = result
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
push_match(apikey, competition, return_vector)
|
||||||
|
|
||||||
|
def load_metric(apikey, competition, match, group_name, metrics):
|
||||||
|
|
||||||
|
group = {}
|
||||||
|
|
||||||
|
for team in match[group_name]:
|
||||||
|
|
||||||
|
db_data = d.get_team_metrics_data(apikey, competition, team)
|
||||||
|
|
||||||
|
if d.get_team_metrics_data(apikey, competition, team) == None:
|
||||||
|
|
||||||
|
elo = {"score": metrics["elo"]["score"]}
|
||||||
|
gl2 = {"score": metrics["gl2"]["score"], "rd": metrics["gl2"]["rd"], "vol": metrics["gl2"]["vol"]}
|
||||||
|
ts = {"mu": metrics["ts"]["mu"], "sigma": metrics["ts"]["sigma"]}
|
||||||
|
|
||||||
|
group[team] = {"elo": elo, "gl2": gl2, "ts": ts}
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
metrics = db_data["metrics"]
|
||||||
|
|
||||||
|
elo = metrics["elo"]
|
||||||
|
gl2 = metrics["gl2"]
|
||||||
|
ts = metrics["ts"]
|
||||||
|
|
||||||
|
group[team] = {"elo": elo, "gl2": gl2, "ts": ts}
|
||||||
|
|
||||||
|
return group
|
||||||
|
|
||||||
|
def metricloop(tbakey, apikey, competition, timestamp, metrics): # listener based metrics update
|
||||||
|
|
||||||
|
elo_N = metrics["elo"]["N"]
|
||||||
|
elo_K = metrics["elo"]["K"]
|
||||||
|
|
||||||
|
matches = d.pull_new_tba_matches(tbakey, competition, timestamp)
|
||||||
|
|
||||||
|
red = {}
|
||||||
|
blu = {}
|
||||||
|
|
||||||
|
for match in matches:
|
||||||
|
|
||||||
|
red = load_metric(apikey, competition, match, "red", metrics)
|
||||||
|
blu = load_metric(apikey, competition, match, "blue", metrics)
|
||||||
|
|
||||||
|
elo_red_total = 0
|
||||||
|
elo_blu_total = 0
|
||||||
|
|
||||||
|
gl2_red_score_total = 0
|
||||||
|
gl2_blu_score_total = 0
|
||||||
|
|
||||||
|
gl2_red_rd_total = 0
|
||||||
|
gl2_blu_rd_total = 0
|
||||||
|
|
||||||
|
gl2_red_vol_total = 0
|
||||||
|
gl2_blu_vol_total = 0
|
||||||
|
|
||||||
|
for team in red:
|
||||||
|
|
||||||
|
elo_red_total += red[team]["elo"]["score"]
|
||||||
|
|
||||||
|
gl2_red_score_total += red[team]["gl2"]["score"]
|
||||||
|
gl2_red_rd_total += red[team]["gl2"]["rd"]
|
||||||
|
gl2_red_vol_total += red[team]["gl2"]["vol"]
|
||||||
|
|
||||||
|
for team in blu:
|
||||||
|
|
||||||
|
elo_blu_total += blu[team]["elo"]["score"]
|
||||||
|
|
||||||
|
gl2_blu_score_total += blu[team]["gl2"]["score"]
|
||||||
|
gl2_blu_rd_total += blu[team]["gl2"]["rd"]
|
||||||
|
gl2_blu_vol_total += blu[team]["gl2"]["vol"]
|
||||||
|
|
||||||
|
red_elo = {"score": elo_red_total / len(red)}
|
||||||
|
blu_elo = {"score": elo_blu_total / len(blu)}
|
||||||
|
|
||||||
|
red_gl2 = {"score": gl2_red_score_total / len(red), "rd": gl2_red_rd_total / len(red), "vol": gl2_red_vol_total / len(red)}
|
||||||
|
blu_gl2 = {"score": gl2_blu_score_total / len(blu), "rd": gl2_blu_rd_total / len(blu), "vol": gl2_blu_vol_total / len(blu)}
|
||||||
|
|
||||||
|
|
||||||
|
if match["winner"] == "red":
|
||||||
|
|
||||||
|
observations = {"red": 1, "blu": 0}
|
||||||
|
|
||||||
|
elif match["winner"] == "blue":
|
||||||
|
|
||||||
|
observations = {"red": 0, "blu": 1}
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
observations = {"red": 0.5, "blu": 0.5}
|
||||||
|
|
||||||
|
red_elo_delta = an.Metric().elo(red_elo["score"], blu_elo["score"], observations["red"], elo_N, elo_K) - red_elo["score"]
|
||||||
|
blu_elo_delta = an.Metric().elo(blu_elo["score"], red_elo["score"], observations["blu"], elo_N, elo_K) - blu_elo["score"]
|
||||||
|
|
||||||
|
new_red_gl2_score, new_red_gl2_rd, new_red_gl2_vol = an.Metric().glicko2(red_gl2["score"], red_gl2["rd"], red_gl2["vol"], [blu_gl2["score"]], [blu_gl2["rd"]], [observations["red"], observations["blu"]])
|
||||||
|
new_blu_gl2_score, new_blu_gl2_rd, new_blu_gl2_vol = an.Metric().glicko2(blu_gl2["score"], blu_gl2["rd"], blu_gl2["vol"], [red_gl2["score"]], [red_gl2["rd"]], [observations["blu"], observations["red"]])
|
||||||
|
|
||||||
|
red_gl2_delta = {"score": new_red_gl2_score - red_gl2["score"], "rd": new_red_gl2_rd - red_gl2["rd"], "vol": new_red_gl2_vol - red_gl2["vol"]}
|
||||||
|
blu_gl2_delta = {"score": new_blu_gl2_score - blu_gl2["score"], "rd": new_blu_gl2_rd - blu_gl2["rd"], "vol": new_blu_gl2_vol - blu_gl2["vol"]}
|
||||||
|
|
||||||
|
for team in red:
|
||||||
|
|
||||||
|
red[team]["elo"]["score"] = red[team]["elo"]["score"] + red_elo_delta
|
||||||
|
|
||||||
|
red[team]["gl2"]["score"] = red[team]["gl2"]["score"] + red_gl2_delta["score"]
|
||||||
|
red[team]["gl2"]["rd"] = red[team]["gl2"]["rd"] + red_gl2_delta["rd"]
|
||||||
|
red[team]["gl2"]["vol"] = red[team]["gl2"]["vol"] + red_gl2_delta["vol"]
|
||||||
|
|
||||||
|
for team in blu:
|
||||||
|
|
||||||
|
blu[team]["elo"]["score"] = blu[team]["elo"]["score"] + blu_elo_delta
|
||||||
|
|
||||||
|
blu[team]["gl2"]["score"] = blu[team]["gl2"]["score"] + blu_gl2_delta["score"]
|
||||||
|
blu[team]["gl2"]["rd"] = blu[team]["gl2"]["rd"] + blu_gl2_delta["rd"]
|
||||||
|
blu[team]["gl2"]["vol"] = blu[team]["gl2"]["vol"] + blu_gl2_delta["vol"]
|
||||||
|
|
||||||
|
temp_vector = {}
|
||||||
|
temp_vector.update(red)
|
||||||
|
temp_vector.update(blu)
|
||||||
|
|
||||||
|
push_metric(apikey, competition, temp_vector)
|
||||||
|
|
||||||
|
def load_pit(apikey, competition):
|
||||||
|
|
||||||
|
return d.get_pit_data_formatted(apikey, competition)
|
||||||
|
|
||||||
|
def pitloop(apikey, competition, pit, tests):
|
||||||
|
|
||||||
|
return_vector = {}
|
||||||
|
for team in pit:
|
||||||
|
for variable in pit[team]:
|
||||||
|
if variable in tests:
|
||||||
|
if not variable in return_vector:
|
||||||
|
return_vector[variable] = []
|
||||||
|
return_vector[variable].append(pit[team][variable])
|
||||||
|
|
||||||
|
push_pit(apikey, competition, return_vector)
|
||||||
|
|
||||||
|
def push_match(apikey, competition, results):
|
||||||
|
|
||||||
|
for team in results:
|
||||||
|
|
||||||
|
d.push_team_tests_data(apikey, competition, team, results[team])
|
||||||
|
|
||||||
|
def push_metric(apikey, competition, metric):
|
||||||
|
|
||||||
|
for team in metric:
|
||||||
|
|
||||||
|
d.push_team_metrics_data(apikey, competition, team, metric[team])
|
||||||
|
|
||||||
|
def push_pit(apikey, competition, pit):
|
||||||
|
|
||||||
|
for variable in pit:
|
||||||
|
|
||||||
|
d.push_team_pit_data(apikey, competition, variable, pit[variable])
|
||||||
|
|
||||||
|
def get_team_metrics(apikey, tbakey, competition):
|
||||||
|
|
||||||
|
metrics = d.get_metrics_data_formatted(apikey, competition)
|
||||||
|
|
||||||
|
elo = {}
|
||||||
|
gl2 = {}
|
||||||
|
|
||||||
|
for team in metrics:
|
||||||
|
|
||||||
|
elo[team] = metrics[team]["metrics"]["elo"]["score"]
|
||||||
|
gl2[team] = metrics[team]["metrics"]["gl2"]["score"]
|
||||||
|
|
||||||
|
elo = {k: v for k, v in sorted(elo.items(), key=lambda item: item[1])}
|
||||||
|
gl2 = {k: v for k, v in sorted(gl2.items(), key=lambda item: item[1])}
|
||||||
|
|
||||||
|
elo_ranked = []
|
||||||
|
|
||||||
|
for team in elo:
|
||||||
|
|
||||||
|
elo_ranked.append({"team": str(team), "elo": str(elo[team])})
|
||||||
|
|
||||||
|
gl2_ranked = []
|
||||||
|
|
||||||
|
for team in gl2:
|
||||||
|
|
||||||
|
gl2_ranked.append({"team": str(team), "gl2": str(gl2[team])})
|
||||||
|
|
||||||
|
return {"elo-ranks": elo_ranked, "glicko2-ranks": gl2_ranked}
|
||||||
|
|
||||||
|
sample_json = """{
|
||||||
|
"max-threads": 0.5,
|
||||||
|
"team": "",
|
||||||
|
"competition": "2020ilch",
|
||||||
|
"key":{
|
||||||
|
"database":"",
|
||||||
|
"tba":""
|
||||||
|
},
|
||||||
|
"statistics":{
|
||||||
|
"match":{
|
||||||
|
"balls-blocked":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"],
|
||||||
|
"balls-collected":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"],
|
||||||
|
"balls-lower-teleop":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"],
|
||||||
|
"balls-lower-auto":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"],
|
||||||
|
"balls-started":["basic_stats","historical_analyss","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"],
|
||||||
|
"balls-upper-teleop":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"],
|
||||||
|
"balls-upper-auto":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"]
|
||||||
|
|
||||||
|
},
|
||||||
|
"metric":{
|
||||||
|
"elo":{
|
||||||
|
"score":1500,
|
||||||
|
"N":400,
|
||||||
|
"K":24
|
||||||
|
},
|
||||||
|
"gl2":{
|
||||||
|
"score":1500,
|
||||||
|
"rd":250,
|
||||||
|
"vol":0.06
|
||||||
|
},
|
||||||
|
"ts":{
|
||||||
|
"mu":25,
|
||||||
|
"sigma":8.33
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pit":{
|
||||||
|
"wheel-mechanism":true,
|
||||||
|
"low-balls":true,
|
||||||
|
"high-balls":true,
|
||||||
|
"wheel-success":true,
|
||||||
|
"strategic-focus":true,
|
||||||
|
"climb-mechanism":true,
|
||||||
|
"attitude":true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
multiprocessing.freeze_support()
|
||||||
|
main()
|
37
src/superscript.spec
Normal file
37
src/superscript.spec
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(['superscript.py'],
|
||||||
|
pathex=['/workspaces/tra-data-analysis/src'],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[
|
||||||
|
"dnspython",
|
||||||
|
"sklearn.utils._weight_vector",
|
||||||
|
"requests",
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[('W ignore', None, 'OPTION')],
|
||||||
|
name='superscript',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True )
|
2
test/test_superscript.py
Normal file
2
test/test_superscript.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def test_():
|
||||||
|
assert 1 == 1
|
Reference in New Issue
Block a user