159 Commits

Author SHA1 Message Date
Arthur Lu
93091b6bd2 appeased pylint in config.py attr lookup 2022-03-31 02:18:30 +00:00
Arthur Lu
0024a94f4e added file logging with default,
added basic progress bars for each module
2022-03-30 04:53:40 +00:00
Arthur Lu
5885224231 removed match printing,
CLI args use argparse

Signed-off-by: Arthur Lu <learthurgo@gmail.com>
2022-03-29 23:09:37 +00:00
Arthur Lu
64ea7c227c removed commented code
Signed-off-by: Arthur Lu <learthurgo@gmail.com>
2022-03-29 22:23:24 +00:00
Arthur Lu
ddf6faeecf fixed metrics processing ordering,
added metrics logging
2022-03-29 21:15:24 +00:00
Arthur Lu
b4766d1b3e fixed Module template __init__ definition
Signed-off-by: Arthur Lu <learthurgo@gmail.com>
2022-03-29 16:49:38 +00:00
Arthur Lu
e04245952a merged data and pull functions into Client class,
removed pull.py dep.py,
modified existing code to work with new Client class
2022-03-29 05:48:39 +00:00
Arthur Lu
2ebaddb92c updated usage 2022-03-29 04:44:59 +00:00
Arthur Lu
8b09e155dc updated changelog 2022-03-29 04:42:26 +00:00
Arthur Lu
5ca474d158 finished removing socket functionality 2022-03-29 04:39:52 +00:00
Arthur Lu
e7a8a259fc finished removing daemon functionality 2022-03-29 04:35:01 +00:00
Arthur Lu
5553e3dddf fixed CLI options,
implemented better config attr search member,
fixed imports
2022-03-29 04:28:09 +00:00
Arthur Lu
0212e6b2ca pylint now uses tab indent 2022-03-29 04:15:47 +00:00
Arthur Lu
14f8901803 removed unnecessary imports 2022-03-28 23:22:42 +00:00
Arthur Lu
a5f9e55cf4 fixed build scripts 2022-03-28 23:15:13 +00:00
Arthur Lu
34f0b3f10c removed: daemonization,
socket messaging

added: CLI option to specify config file

Not working, requires data.py changes in competition branch
2022-03-28 22:42:04 +00:00
Arthur Lu
6b070c7b08 fixed merge changes 2022-03-15 05:31:51 +00:00
Arthur Lu
9279311664 Merge branch 'master' into superscript-v1 2022-03-15 05:27:11 +00:00
Arthur Lu
4836f48a34 pyinstaller onedir workaround for requests issue,
moved spec file to build folder


Former-commit-id: 4427eba5c993222cd2ebfdfc76a4685e25e18b0c
2022-02-24 02:56:08 +00:00
Arthur Lu
9a1a45f1c9 removed cert verification for requests library
Former-commit-id: 4914b98a21478a10be7d2f737dd6d5669b4c5681
2022-02-20 23:38:18 +00:00
Arthur Lu
d7ed695ad1 fixed build script,
added binutils to docker container for build requirements,
added required scipy modules to superscript.spec


Former-commit-id: 60a6fc1106dc90514bd1270138ab9166efa29290
2022-02-20 23:16:02 +00:00
Arthur Lu
21d92e65b2 add binary paths to devcontainer.json
Former-commit-id: a323f3161d6ea326b0215d5ed5ebbadae0f38597
2022-02-20 22:53:11 +00:00
Arthur Lu
0cace3cec3 improved exit code reporting
Former-commit-id: f36f45bc6a2773b8a821d0e046ce1b47fc4ced0f
2022-02-19 23:29:58 +00:00
Arthur Lu
80b63269df readded zmq messaging
Former-commit-id: e38653c4dc4ffe6aada19cac2251c3ec28bfbc2b
2022-02-19 22:58:58 +00:00
Arthur Lu
56447603e1 implemented logger,
dropped zmq because of constant inconsistency,
daemon process now outputs to file,
deleted client.py


Former-commit-id: d3251e5f6404d0dce42da49e5d577651e718e168
2022-02-19 08:52:49 +00:00
Arthur Lu
2130182212 fixed pull APIError calls (removed endpoint arg)
Former-commit-id: 5090eefdbd5b13bfda285d5480e79896dd3610a5
2022-02-19 06:50:34 +00:00
Arthur Lu
b1eff19ea4 removec __all__ property from superscript
Former-commit-id: 67c4684b5943467cc07b8cc638dc438ad0ee3e54
2022-02-19 06:46:39 +00:00
Arthur Lu
b43836899d moved config functions into Configuration class,
simplified exception class by removing error codes,
removed exec_threads from module parameters


Former-commit-id: 545ef765653970e1cdebac692eccd227effb2508
2022-02-19 06:19:13 +00:00
Arthur Lu
524a0a211d removed gui (last commit tagged "gui"),
removed print statement in pit module


Former-commit-id: 4978aee142eaf9431913b44eabfc0dfb79c7b600
2022-02-09 05:36:19 +00:00
Arthur Lu
9c152fb109 Merge pull request #23 from titanscouting/improve-devdocker
switch docker image to python:slim

Former-commit-id: 28b440725d338dd830ac9d3e0905eba54b05811f
2022-02-08 20:17:38 -08:00
Arthur Lu
491508a400 switch docker image to python:slim,
move requirements.txt to .devcontainer/


Former-commit-id: 427a37e514
2022-02-04 08:38:03 +00:00
Arthur Lu
b0ffdbab9a fixed pit processing issue due to config change
Former-commit-id: 15f95b778b
2022-02-04 03:28:08 +00:00
Arthur Lu
c4bb633e08 fixed pit data fetching
Former-commit-id: d017ec52a7
2022-02-04 03:24:47 +00:00
Dev Singh
c46e74036b get team nums at comp
Former-commit-id: 028f6f775d
2022-02-04 03:16:45 +00:00
Arthur Lu
c08d3d3ae7 Merge branch 'superscript-v1' of https://github.com/titanscouting/tra-superscript into superscript-v1
Former-commit-id: 2b72743b27
2022-02-04 03:06:14 +00:00
Arthur Lu
6cfe4e3f33 fixed bugs in data.py to work with database,
fixed object field in module.py: Metric,
fixed timestamp input in superscripy.py


Former-commit-id: 1e18cddc47
2022-02-04 03:06:09 +00:00
Dev Singh
40673169b2 update validation schema
Former-commit-id: 982cb0aa6b
2022-02-04 02:44:48 +00:00
Arthur Lu
7d08194636 patched issue in multiprocessing,
moved ConfigurationError to exceptions.py,
made pull use load_config in config.py


Former-commit-id: aed03369c9
2022-01-10 20:23:14 +00:00
Dev Singh
a940f3ffeb use tab size 4
Former-commit-id: 2f83604e14
2022-01-10 20:02:58 +00:00
Dev Singh
db4b020851 allow overriding competition in config
Former-commit-id: 40252dd74c
2022-01-10 15:40:58 +00:00
Dev Singh
18d4438f7f fix typo
Former-commit-id: e16295da2b
2022-01-10 15:35:06 +00:00
Dev Singh
c77dd1ea5f set tab config
Former-commit-id: 1c3fc9a872
2022-01-10 09:23:43 -06:00
Dev Singh
74542c1d8f add validation schema for persistent keys
Former-commit-id: ceaf3f2891
2022-01-10 09:21:46 -06:00
Dev Singh
b4c9ef22b6 pull some data from API, fix some datatype compats
Former-commit-id: 03ec0dd8fd
2022-01-09 22:19:50 -06:00
Arthur Lu
af74a69579 removed commented out code
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 3669712bec
2021-11-24 02:27:03 +00:00
Arthur Lu
ac66545226 changed Module interface to 3 functions
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 356b71be62
2021-11-23 22:23:59 +00:00
Arthur Lu
4b5db1aba8 moved config parsing functions to config.py
Former-commit-id: a251fef024
2021-11-06 01:50:55 +00:00
Arthur Lu
b9610b7b46 improved and streamlined config error handling
Former-commit-id: 99efefd302
2021-11-06 00:04:11 +00:00
Arthur Lu
14ed3cc507 Merge pull request #19 from titanscouting/modularize
Reflect modularization changes into v1

Former-commit-id: b0a0632b99
2021-11-05 16:22:04 -07:00
Arthur Lu
c90a35ff51 added empty modules for future additions
Former-commit-id: d234302cdc
2021-11-05 23:17:42 +00:00
Arthur Lu
60e88a5987 formalized module interface
Former-commit-id: 108284a94b
2021-11-02 05:37:27 +00:00
Arthur Lu
9c2946718f removed testing notebook,
anticipated removal of pandas requirement,
added pandas and matplotlib to pyinstaller exclusion,
removed unessasary option in build scripts


Former-commit-id: 0fd24d42e1
2021-10-30 03:51:03 +00:00
Arthur Lu
bd0c0d99f9 module replaces processing
Former-commit-id: d798bfe7d2
2021-10-30 03:11:10 +00:00
Arthur Lu
9ddf61621e fixed debug logging
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: bd87111e08
2021-10-28 21:12:28 +00:00
Arthur Lu
0af93238be implemented dynamic module loading/running,
fixed minor issue in metric module

Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 7be48af85e
2021-10-28 20:40:56 +00:00
Arthur Lu
6c8e738420 added Metric, Pit modules (theoretically working)
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 3ac4e96d2d
2021-10-28 20:13:57 +00:00
Arthur Lu
0eccc32096 changed Match module to use current data bindings
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: afab67f8ef
2021-10-22 01:40:56 +00:00
Arthur Lu
f8f8543ea2 changed sample json
Former-commit-id: 8966900341
2021-10-21 22:33:26 +00:00
Arthur Lu
a1fb295a1a minor fix
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 7f0d3fc753
2021-10-21 21:58:02 +00:00
Arthur Lu
4ded02b34f added sample initializaition loop
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: d16fe61a79
2021-10-21 21:56:44 +00:00
Arthur Lu
88282aa18b modified config validation for Match module
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: de81a03e3d
2021-10-21 21:51:14 +00:00
Arthur Lu
66bf3a3a3e removed autovivification (likely not required)
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: f40939d69d
2021-10-21 21:29:44 +00:00
Arthur Lu
375b550153 finished rough outline for Match module
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: d5ebb0348b
2021-10-21 21:27:35 +00:00
Arthur Lu
79dd2c3479 minor fix
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 72a14bfac9
2021-10-21 20:56:06 +00:00
Arthur Lu
d2ced284ea added working load_data for Match module
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 8e8c19c5fd
2021-10-21 20:42:47 +00:00
Arthur Lu
07965cba4b added working validate_config for Match module
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: b9e2de2dc6
2021-10-21 20:28:29 +00:00
Arthur Lu
36081e1239 removed keys
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 4a1d7f0db1
2021-10-21 20:05:07 +00:00
Dev Singh
de4d3d4967 Update README.md 2021-10-21 14:21:20 -05:00
Arthur Lu
49ee02d6fa Merge pull request #18 from titanscouting/daemon-fix
Fix messaging in daemon

Former-commit-id: 6a4d13a33f
2021-10-16 16:42:47 -07:00
Arthur Lu
08f5ba987a removed websockets from requirements.txr,
fixed return code of daemon,
removed websockets from hidden imports of superscript.spec


Former-commit-id: 828e45844e
2021-10-16 20:28:57 +00:00
Arthur Lu
dafd66447f moved sample zmq SUB client to test/
Former-commit-id: ab13e10271
2021-10-16 20:21:12 +00:00
Arthur Lu
6bcb3cbff4 verification of zmq solution working remotely
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 41fb81cfac
2021-10-15 06:20:05 +00:00
Arthur Lu
6cd092da37 solution using zmq PUB/SUB,
working locally


Former-commit-id: c0c46a1b9d
2021-10-15 06:12:50 +00:00
Arthur Lu
8540642bef works locally again
Former-commit-id: 7b05707e3b
2021-10-14 23:30:15 +00:00
Arthur Lu
5d0fbc06c6 possible fix: UDP broadcast,
not connecing from outside LAN

Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 531c8f80f3
2021-10-14 22:52:15 +00:00
Arthur Lu
a4a13c7cb5 set return value for profile option
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: e15f87b2e5
2021-10-14 19:45:28 +00:00
Arthur Lu
26d9d962c0 added close_all when exiting due to error
Former-commit-id: 9b614af85c
2021-10-13 22:32:42 +00:00
Arthur Lu
e20a212bd9 changes to stop() error reporting
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: bd77927421
2021-10-13 19:11:27 +00:00
Arthur Lu
1151541976 fixed key error in loop delay logic,
improved error logging to include stack trace

Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 3f32104c72
2021-10-12 22:16:34 +00:00
Arthur Lu
6f28e507a1 fixed errorlog path
Signed-off-by: Arthur Lu <learthurgo@gmail.com>

Former-commit-id: 0895eb3d74
2021-10-12 21:42:09 +00:00
Arthur Lu
1e6bc1926a fixed minor push_pit bug
Former-commit-id: 9603ca2be9
2021-10-11 02:59:49 +00:00
Arthur Lu
d89f997117 fixed sample config.json
Former-commit-id: 24e666a23b
2021-10-11 02:56:24 +00:00
Arthur Lu
31423d04d8 Merge pull request #17 from titanscouting/event-listener
Pull changes from event-listener to superscript-v1

Former-commit-id: 82b62924f6
2021-10-10 19:52:53 -07:00
Arthur Lu
44e9711b2d Merge branch 'superscript-v1' into event-listener
Former-commit-id: 3dbdcfbd35
2021-10-10 19:52:47 -07:00
Arthur Lu
f24d5163d7 Merge pull request #16 from titanscouting/database-config
Pull changes from database-config into superscript-v1

Former-commit-id: 790eb4aa03
2021-10-10 19:49:19 -07:00
Arthur Lu
06630b0dd4 Merge pull request #15 from titanscouting/automate-build
Update superscript-v1 with automate-build

Former-commit-id: 0fb586f13d
2021-10-10 19:47:48 -07:00
Arthur Lu
32ae4fd636 implemented event delay with dummy listener,
event delay listener in data.py unimplemented


Former-commit-id: dea5f3a374
2021-10-10 06:15:22 +00:00
Arthur Lu
96ebb82085 added opotion to pull config from database,
major code refactor


Former-commit-id: da913e639b
2021-09-23 07:10:01 +00:00
Arthur Lu
09b8cca884 Merge pull request #13 from titanscouting/superscript-v1
Pull recent changes from superscript-v1 to database-config

Former-commit-id: e18ddcec9d
2021-09-22 20:29:02 -07:00
Arthur Lu
fb1033e92c Merge branch 'databse-config' into superscript-v1
Former-commit-id: a64c2c71ca
2021-09-22 20:28:55 -07:00
Arthur Lu
16882a0a75 added debug option to usage description,
set generic error code to 1


Former-commit-id: 9163244e2c
2021-09-21 23:36:13 +00:00
Arthur Lu
2b8ecc5bee added databse config getter/setter to data.py,
fixed args for data.py functions


Former-commit-id: 74bcc116a2
2021-09-21 05:14:54 +00:00
Arthur Lu
8112effbce added debug option,
improved memory usage slightly,
changed errorlog.txt to errorlog.log,
updated .gitignore


Former-commit-id: bd51efc6c2
2021-09-21 04:43:32 +00:00
Arthur Lu
56d3a0adcd added event and time delay options
(event delay unimplemented)


Former-commit-id: fb4e5da1d4
2021-08-27 23:39:48 +00:00
Arthur Lu
d56411253c fixed badge url 2021-08-26 18:20:11 -07:00
Arthur Lu
c415225afe Update release badge 2021-08-26 18:11:25 -07:00
Arthur Lu
2444963af9 changed asset upload name
removed create criteria, must be publish


Former-commit-id: 73b5c393a0
2021-08-26 23:19:53 +00:00
Arthur Lu
8000a7314d add release asset automaticallt
Former-commit-id: 42ca74b4ab
2021-08-26 23:14:09 +00:00
Arthur Lu
3e212e4502 fixed typo
Former-commit-id: fc63d5d7e1
2021-08-26 22:57:15 +00:00
Arthur Lu
66e00987c4 fixed path
Former-commit-id: 1106a0ffb1
2021-08-26 22:53:21 +00:00
Arthur Lu
2f90e7d11a added prerequisite and build steps
Former-commit-id: 65a1720657
2021-08-26 22:48:48 +00:00
Arthur Lu
f0ef4fea5d Merge pull request #12 from titanscouting/superscript-v1
Merge current changes to build-superscript

Former-commit-id: 6b4de40c49
2021-08-26 15:45:24 -07:00
Arthur Lu
9be9008ae1 fixed and properly named build action
Former-commit-id: 694733700a
2021-08-26 22:26:29 +00:00
Arthur Lu
097fd2836b reorganized imports in superscript
Former-commit-id: 18c26a00b6
2021-08-22 05:35:04 +00:00
Arthur Lu
052788afb9 fixed verbose/profile output options
Former-commit-id: ca399cf350
2021-08-19 20:58:35 +00:00
Arthur Lu
e4eb824f51 Merge pull request #11 from titanscouting/daemonize-superscript
Merge changes in daemonize-superscript to refactor-superscript

Former-commit-id: 3e8d876f80
2021-08-18 17:36:55 -07:00
Arthur Lu
11d3db4b44 added profile option to superscript
Former-commit-id: ea07d7c709
2021-08-19 00:34:42 +00:00
Arthur Lu
76f78047b3 unified main_lin, main_win functions
Former-commit-id: 342dae3022
2021-08-18 22:35:42 +00:00
Arthur Lu
20f2040a1a changed websocket bind address to 0.0.0.0
Former-commit-id: e9258c831d
2021-08-18 05:58:21 +00:00
Arthur Lu
ffead9e240 fixed daemon no start issue
Former-commit-id: efc353dafb
2021-08-18 05:13:25 +00:00
Arthur Lu
0287b5c0e2 temporary workaround for missing ssl cert
Former-commit-id: 739a2f36f8
2021-08-18 03:26:47 +00:00
Arthur Lu
b6a1dfedb9 not working
Former-commit-id: e82177d17b
2021-08-18 03:04:01 +00:00
Arthur Lu
cafb773d8b replaced profile to verbose option
Former-commit-id: 055c296c00
2021-08-18 00:43:00 +00:00
Arthur Lu
871b313d95 fixed linux only import in windows exec
Former-commit-id: 61ae377e97
2021-08-17 14:17:40 -07:00
Arthur Lu
bcbb653696 daemonized, socketed superscript
improved runtime
removed superscript-socket.py


Former-commit-id: 6fa29f767d
2021-08-17 21:02:08 +00:00
Arthur Lu
b4c7365bf0 added *.pid files to git ignore
Former-commit-id: f8f8d6bf27
2021-08-13 23:32:59 +00:00
Arthur Lu
c2f35f4cb2 superscript v 0.9.2
Former-commit-id: e559ced751
2021-08-13 21:57:03 +00:00
Arthur Lu
2ebd2cba8a removed socket/daemon test scripts
Former-commit-id: d1a9e56703
2021-08-12 22:26:37 +00:00
Arthur Lu
6819aaf143 consolidated dataset/data imports
Former-commit-id: 6364746d7a
2021-08-12 21:58:41 +00:00
Arthur Lu
b3ab9156db consolidated datraset.py into datas.py
removed unessasary time import in data.py
added missing import to processing.py


Former-commit-id: f74cfb3ae3
2021-08-12 21:53:03 +00:00
Arthur Lu
30641e43d8 fixed delay in daemon stopping
Former-commit-id: 1d9fa99058
2021-08-11 23:37:57 +00:00
Arthur Lu
3a068654ed added start/stop/restart argument functionality
slightly buggy


Former-commit-id: ba793140af
2021-08-11 23:26:52 +00:00
Arthur Lu
962061007b removed time-test
Former-commit-id: c09f4d0897
2021-08-11 22:54:24 +00:00
Arthur Lu
91f34a8d74 daemonized superscript socket example
added python-daemon to requirements.txt


Former-commit-id: 922095ebe0
2021-08-11 22:28:24 +00:00
Arthur Lu
19bca6967c added websockets to requirements.txt
Former-commit-id: d9ba7bcb24
2021-08-11 18:21:34 +00:00
Arthur Lu
fb2ea60fea started socketing superscript statuses
Former-commit-id: a703fe4867
2021-08-11 18:10:04 +00:00
Arthur Lu
5d95913467 removed duplicate requirements
Former-commit-id: f258a768be
2021-08-11 00:33:06 +00:00
Arthur Lu
7e800c9004 added Pool.join to allow threads to exit safely
added keyboard interrupt signal ignore in threads


Former-commit-id: 836e9ca6be
2021-07-16 06:12:26 +00:00
Arthur Lu
3c7262498c superscript v 0.9.1
Former-commit-id: 246efd524b
2021-06-13 00:49:04 +00:00
Arthur Lu
4c65e88903 finished refactor
Former-commit-id: b2f2dfe2a4
2021-06-12 23:57:16 +00:00
Arthur Lu
b3c26ce2cf renamed cli_interface.py to interface.py
Former-commit-id: b94c9125b3
2021-06-12 07:16:11 +00:00
Arthur Lu
089ff7ec01 separated gui and cli dev files
began refactoring superscript in cli application


Former-commit-id: ee309b9f8b
2021-06-12 07:09:26 +00:00
Arthur Lu
d684813ee0 Merge pull request #10 from titanscouting/automate-build
Automate build
2021-06-09 14:58:21 -07:00
Arthur Lu
26079f3180 fixed pathing for build-CLI.*
added temp directory to gitignore
2021-04-27 07:26:14 +00:00
Arthur Lu
99e722c400 removed ThreadPoolExecutor import 2021-04-25 06:05:33 +00:00
Arthur Lu
f5a0e0fe8c added sample build-cli workflow 2021-04-25 03:51:01 +00:00
Arthur Lu
28e423942f added .gitattributes 2021-04-15 19:41:10 +00:00
Arthur Lu
8977f8c277 added compiled binaries with no file endings
to gitignore
2021-04-13 04:05:46 +00:00
Arthur Lu
2b0f718aa5 removed compiled binaries
added compiled binaries in /dist/ to gitignore
2021-04-13 04:03:07 +00:00
Arthur Lu
30469a3211 removed matplotlib import
removed plotting pit analysis
fixed warning supression for win exe
superscript v 0.8.6
2021-04-12 15:13:54 -07:00
Arthur Lu
391d4e1996 created batch script for windows compilation 2021-04-12 14:39:00 -07:00
Arthur Lu
224f64e8b7 better fix for devcontainer.json 2021-04-12 06:30:21 +00:00
Arthur Lu
aa7d7ca927 quick patch for devcontainer.json 2021-04-12 06:27:50 +00:00
Arthur Lu
d10c16d483 superscript v 0.8.5 2021-04-10 06:08:18 +00:00
Arthur Lu
f211d00f2d superscript v 0.8.4 2021-04-09 23:45:16 +00:00
Arthur Lu
69c707689b superscript v 0.8.3 2021-04-03 20:47:45 +00:00
Arthur Lu
d2f9c802b3 built and verified threading fixes 2021-04-02 22:04:06 +00:00
Arthur Lu
99e28f5e83 fixed .gitignore
added build-CLI script
fixed threading in superscript
2021-04-02 21:58:35 +00:00
Arthur Lu
18dbc174bd deleted config.json
changed superscript config lookup to relative path
added additional requirements to requirements.txt
added build spec file for superscript
2021-04-02 21:35:05 +00:00
Arthur Lu
79689d69c8 fixed spelling in default config,
added config to git ignore
2021-04-02 01:28:25 +00:00
Dev Singh
80c3f1224b Merge pull request #3 from titanscouting/superscript-main
Merge initial changes
2021-04-01 13:40:29 -05:00
Dev Singh
960a1b3165 fix ut and file structure 2021-04-01 13:38:53 -05:00
Arthur Lu
89fcd366d3 Merge branch 'master' into superscript-main 2021-04-01 11:34:44 -07:00
Dev Singh
79cde44108 Create SECURITY.md 2021-04-01 13:11:38 -05:00
Dev Singh
2b896db9a9 Create MAINTAINERS 2021-04-01 13:11:22 -05:00
Dev Singh
483897c011 Merge pull request #1 from titanscouting/add-license-1
Create LICENSE
2021-04-01 13:11:03 -05:00
Dev Singh
9287d98fe2 Create LICENSE 2021-04-01 13:10:50 -05:00
Dev Singh
991751a340 Create CONTRIBUTING.md 2021-04-01 13:10:14 -05:00
Dev Singh
9d2476b5eb Create README.md 2021-04-01 13:09:18 -05:00
20 changed files with 1191 additions and 884 deletions

View File

@@ -1,7 +1,6 @@
FROM ubuntu:20.04 FROM python:slim
WORKDIR / WORKDIR /
RUN apt-get -y update RUN apt-get -y update; apt-get -y upgrade
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata RUN apt-get -y install git binutils
RUN apt-get install -y python3 python3-dev git python3-pip python3-kivy python-is-python3 libgl1-mesa-dev build-essential COPY requirements.txt .
RUN ln -s $(which pip3) /usr/bin/pip RUN pip install -r requirements.txt
RUN pip install pymongo pandas numpy scipy scikit-learn matplotlib pylint kivy

View File

@@ -1,2 +0,0 @@
FROM titanscout2022/tra-analysis-base:latest
WORKDIR /

View File

@@ -1,28 +1,23 @@
{ {
"name": "TRA Analysis Development Environment", "name": "TRA Analysis Development Environment",
"build": { "build": {
"dockerfile": "dev-dockerfile", "dockerfile": "Dockerfile",
}, },
"settings": { "settings": {
"terminal.integrated.shell.linux": "/bin/bash", "terminal.integrated.shell.linux": "/bin/bash",
"python.pythonPath": "/usr/local/bin/python", "python.pythonPath": "/usr/local/bin/python",
"python.linting.enabled": true, "python.linting.enabled": true,
"python.linting.pylintEnabled": true, "python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", "python.linting.pylintPath": "/usr/local/bin/pylint",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black", "python.linting.pylintArgs": ["--indent-string", "\t"],
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", "python.testing.pytestPath": "/usr/local/bin/pytest",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit", "editor.tabSize": 4,
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", "editor.insertSpaces": false
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
"python.testing.pytestPath": "/usr/local/py-utils/bin/pytest"
}, },
"extensions": [ "extensions": [
"mhutchie.git-graph", "mhutchie.git-graph",
"ms-python.python", "ms-python.python",
"waderyan.gitblame" "waderyan.gitblame"
], ],
"postCreateCommand": "/usr/bin/pip3 install -r ${containerWorkspaceFolder}/src/requirements.txt && /usr/bin/pip3 install --no-cache-dir pylint && /usr/bin/pip3 install pytest" "postCreateCommand": ""
} }

View File

@@ -1,18 +1,13 @@
requests cerberus
pymongo
pandas
tra-analysis
dnspython dnspython
pyinstaller
requests
pymongo
numpy numpy
scipy pyinstaller
scikit-learn pylint
six pymongo
pyparsing pyparsing
pandas pytest
requests
kivy==2.0.0rc2 scikit-learn
scipy
six
tra-analysis

View File

@@ -1,7 +1,7 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions # 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 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Superscript Unit Tests name: Build Superscript Linux
on: on:
release: release:
@@ -11,7 +11,25 @@ jobs:
generate: generate:
name: Build Linux name: Build Linux
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout master - name: Checkout master
uses: actions/checkout@master uses: actions/checkout@master
- name: Install Dependencies
run: pip install -r requirements.txt
working-directory: src/
- name: Give Execute Permission
run: chmod +x build-CLI.sh
working-directory: build/
- name: Build Binary
run: ./build-CLI.sh
working-directory: build/
- name: Copy Binary to Root Dir
run: cp superscript ..
working-directory: dist/
- name: Upload Release Asset
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: superscript
asset_name: superscript
tag: ${{ github.ref }}

8
.gitignore vendored
View File

@@ -9,6 +9,10 @@
**/tra_analysis/ **/tra_analysis/
**/temp/* **/temp/*
**/*.pid
**/profile.*
**/*.log
**/errorlog.txt **/errorlog.txt
/dist/superscript.* /dist/*
/dist/superscript

View File

@@ -1,4 +1,4 @@
# Red Alliance Analysis &middot; ![GitHub release (latest by date)](https://img.shields.io/github/v/release/titanscout2022/tra-superscript) # Red Alliance Analysis &middot; ![GitHub release (latest by date)](https://img.shields.io/github/v/release/titanscout2022/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.

View File

@@ -1,5 +1,5 @@
set pathtospec="../src/superscript.spec" set pathtospec="superscript.spec"
set pathtodist="../dist/" set pathtodist="../dist/"
set pathtowork="temp/" set pathtowork="temp/"
pyinstaller --onefile --clean --distpath %pathtodist% --workpath %pathtowork% %pathtospec% pyinstaller --clean --distpath %pathtodist% --workpath %pathtowork% %pathtospec%

View File

@@ -1,5 +1,5 @@
pathtospec="../src/superscript.spec" pathtospec="superscript.spec"
pathtodist="../dist/" pathtodist="../dist/"
pathtowork="temp/" pathtowork="temp/"
pyinstaller --onefile --clean --distpath ${pathtodist} --workpath ${pathtowork} ${pathtospec} pyinstaller --clean --distpath ${pathtodist} --workpath ${pathtowork} ${pathtospec}

35
build/superscript.spec Normal file
View File

@@ -0,0 +1,35 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['../src/superscript.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=['dnspython', 'sklearn.utils._weight_vector', 'sklearn.utils._typedefs', 'sklearn.neighbors._partition_nodes', 'requests'],
hookspath=[],
hooksconfig={},
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 )

240
src/config.py Normal file
View File

@@ -0,0 +1,240 @@
import json
from exceptions import ConfigurationError
from cerberus import Validator
class Configuration:
path = None
config = {}
_sample_config = {
"persistent":{
"key":{
"database":"",
"tba":"",
"tra":{
"CLIENT_ID":"",
"CLIENT_SECRET":"",
"url": ""
}
},
"config-preference":"local",
"synchronize-config":False
},
"variable":{
"event-delay":False,
"loop-delay":0,
"competition": "2020ilch",
"modules":{
"match":{
"tests":{
"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":{
"tests":{
"elo":{
"score":1500,
"N":400,
"K":24
},
"gl2":{
"score":1500,
"rd":250,
"vol":0.06
},
"ts":{
"mu":25,
"sigma":8.33
}
}
},
"pit":{
"tests":{
"wheel-mechanism":True,
"low-balls":True,
"high-balls":True,
"wheel-success":True,
"strategic-focus":True,
"climb-mechanism":True,
"attitude":True
}
}
}
}
}
_validation_schema = {
"persistent": {
"type": "dict",
"required": True,
"require_all": True,
"schema": {
"key": {
"type": "dict",
"require_all":True,
"schema": {
"database": {"type":"string"},
"tba": {"type": "string"},
"tra": {
"type": "dict",
"require_all": True,
"schema": {
"CLIENT_ID": {"type": "string"},
"CLIENT_SECRET": {"type": "string"},
"url": {"type": "string"}
}
}
}
},
"config-preference": {"type": "string", "required": True},
"synchronize-config": {"type": "boolean", "required": True}
}
}
}
def __init__(self, path):
self.path = path
self.load_config()
self.validate_config()
def load_config(self):
try:
f = open(self.path, "r")
self.config.update(json.load(f))
f.close()
except:
self.config = self._sample_config
self.save_config()
f.close()
raise ConfigurationError("could not find config file at <" + self.path + ">, created new sample config file at that path")
def save_config(self):
f = open(self.path, "w+")
json.dump(self.config, f, ensure_ascii=False, indent=4)
f.close()
def validate_config(self):
v = Validator(self._validation_schema, allow_unknown = True)
isValidated = v.validate(self.config)
if not isValidated:
raise ConfigurationError("config validation error: " + v.errors)
def __getattr__(self, name): # better hashed lookup method for common multikey-value paths, TYPE UNSAFE
attr_lookup = {
"persistent": self.config["persistent"],
"key": self.config["persistent"]["key"],
"database": self.config["persistent"]["key"]["database"],
"tba": self.config["persistent"]["key"]["tba"],
"tra": self.config["persistent"]["key"]["tra"],
"priority": self.config["persistent"]["config-preference"],
"sync": self.config["persistent"]["synchronize-config"],
"variable": self.config["variable"],
"event_delay": self.config["variable"]["event-delay"],
"loop_delay": self.config["variable"]["loop-delay"],
"competition": self.config["variable"]["competition"],
"modules": self.config["variable"]["modules"]
}
try:
return attr_lookup[name]
except KeyError:
return None
def __getitem__(self, key):
return self.config[key]
def resolve_config_conflicts(self, logger, client): # needs improvement with new localization scheme
sync = self.sync
priority = self.priority
if sync:
if priority == "local" or priority == "client":
logger.info("config-preference set to local/client, loading local config information")
remote_config = client.get_database_config()
if remote_config != self.config["variable"]:
client.set_database_config(self.config["variable"])
logger.info("database config was different and was updated")
# no change to config
elif priority == "remote" or priority == "database":
logger.info("config-preference set to remote/database, loading remote config information")
remote_config = client.get_database_config()
if remote_config != self.config["variable"]:
self.config["variable"] = remote_config
self.save_config()
# change variable to match remote
logger.info("local config was different and was updated")
else:
raise ConfigurationError("persistent/config-preference field must be \"local\"/\"client\" or \"remote\"/\"database\"")
else:
if priority == "local" or priority == "client":
logger.info("config-preference set to local/client, loading local config information")
# no change to config
elif priority == "remote" or priority == "database":
logger.info("config-preference set to remote/database, loading database config information")
self.config["variable"] = client.get_database_config()
# change variable to match remote without updating local version
else:
raise ConfigurationError("persistent/config-preference field must be \"local\"/\"client\" or \"remote\"/\"database\"")

View File

@@ -1,123 +1,56 @@
import requests import requests
import pymongo
import pandas as pd import pandas as pd
import time import pymongo
from exceptions import APIError
def pull_new_tba_matches(apikey, competition, cutoff): class Client:
api_key= apikey
x=requests.get("https://www.thebluealliance.com/api/v3/event/"+competition+"/matches/simple", headers={"X-TBA-Auth_Key":api_key}) def __init__(self, config):
self.competition = config.competition
self.tbakey = config.tba
self.mongoclient = pymongo.MongoClient(config.database)
self.trakey = config.tra
def close(self):
self.mongoclient.close()
def pull_new_tba_matches(self, cutoff):
competition = self.competition
api_key= self.tbakey
x=requests.get("https://www.thebluealliance.com/api/v3/event/"+competition+"/matches/simple", headers={"X-TBA-Auth-Key":api_key})
json = x.json()
out = [] out = []
for i in x.json(): for i in json:
if i["actual_time"] != None and i["actual_time"]-cutoff >= 0 and i["comp_level"] == "qm": if i["actual_time"] != None and i["comp_level"] == "qm":
out.append({"match" : i['match_number'], "blue" : list(map(lambda x: int(x[3:]), i['alliances']['blue']['team_keys'])), "red" : list(map(lambda x: int(x[3:]), i['alliances']['red']['team_keys'])), "winner": i["winning_alliance"]}) out.append({"match" : i['match_number'], "blue" : list(map(lambda x: int(x[3:]), i['alliances']['blue']['team_keys'])), "red" : list(map(lambda x: int(x[3:]), i['alliances']['red']['team_keys'])), "winner": i["winning_alliance"]})
out.sort(key=lambda x: x['match'])
return out return out
def get_team_match_data(apikey, competition, team_num): def get_team_match_data(self, team_num):
client = pymongo.MongoClient(apikey) client = self.mongoclient
competition = self.competition
db = client.data_scouting db = client.data_scouting
mdata = db.matchdata mdata = db.matchdata
out = {} out = {}
for i in mdata.find({"competition" : competition, "team_scouted": team_num}): for i in mdata.find({"competition" : competition, "team_scouted": str(team_num)}):
out[i['match']] = i['data'] out[i['match']] = i['data']
return pd.DataFrame(out) return pd.DataFrame(out)
def get_team_pit_data(apikey, competition, team_num): def get_team_metrics_data(self, team_num):
client = pymongo.MongoClient(apikey) client = self.mongoclient
db = client.data_scouting competition = self.competition
mdata = db.pitdata
out = {}
return mdata.find_one({"competition" : competition, "team_scouted": team_num})["data"]
def get_team_metrics_data(apikey, competition, team_num):
client = pymongo.MongoClient(apikey)
db = client.data_processing db = client.data_processing
mdata = db.team_metrics mdata = db.team_metrics
return mdata.find_one({"competition" : competition, "team": team_num}) return mdata.find_one({"competition" : competition, "team": team_num})
def get_match_data_formatted(apikey, competition): def get_team_pit_data(self, team_num):
client = pymongo.MongoClient(apikey) client = self.mongoclient
competition = self.competition
db = client.data_scouting db = client.data_scouting
mdata = db.teamlist mdata = db.pitdata
x=mdata.find_one({"competition":competition}) return mdata.find_one({"competition" : competition, "team_scouted": str(team_num)})["data"]
out = {}
for i in x:
try:
out[int(i)] = unkeyify_2l(get_team_match_data(apikey, competition, int(i)).transpose().to_dict())
except:
pass
return out
def get_metrics_data_formatted(apikey, competition): def unkeyify_2l(self, layered_dict):
client = pymongo.MongoClient(apikey)
db = client.data_scouting
mdata = db.teamlist
x=mdata.find_one({"competition":competition})
out = {}
for i in x:
try:
out[int(i)] = d.get_team_metrics_data(apikey, competition, int(i))
except:
pass
return out
def get_pit_data_formatted(apikey, competition):
client = pymongo.MongoClient(apikey)
db = client.data_scouting
mdata = db.teamlist
x=mdata.find_one({"competition":competition})
out = {}
for i in x:
try:
out[int(i)] = get_team_pit_data(apikey, competition, int(i))
except:
pass
return out
def get_pit_variable_data(apikey, competition):
client = pymongo.MongoClient(apikey)
db = client.data_processing
mdata = db.team_pit
out = {}
return mdata.find()
def get_pit_variable_formatted(apikey, competition):
temp = get_pit_variable_data(apikey, competition)
out = {}
for i in temp:
out[i["variable"]] = i["data"]
return out
def push_team_tests_data(apikey, competition, team_num, data, dbname = "data_processing", colname = "team_tests"):
client = pymongo.MongoClient(apikey)
db = client[dbname]
mdata = db[colname]
mdata.replace_one({"competition" : competition, "team": team_num}, {"_id": competition+str(team_num)+"am", "competition" : competition, "team" : team_num, "data" : data}, True)
def push_team_metrics_data(apikey, competition, team_num, data, dbname = "data_processing", colname = "team_metrics"):
client = pymongo.MongoClient(apikey)
db = client[dbname]
mdata = db[colname]
mdata.replace_one({"competition" : competition, "team": team_num}, {"_id": competition+str(team_num)+"am", "competition" : competition, "team" : team_num, "metrics" : data}, True)
def push_team_pit_data(apikey, competition, variable, data, dbname = "data_processing", colname = "team_pit"):
client = pymongo.MongoClient(apikey)
db = client[dbname]
mdata = db[colname]
mdata.replace_one({"competition" : competition, "variable": variable}, {"competition" : competition, "variable" : variable, "data" : data}, True)
def get_analysis_flags(apikey, flag):
client = pymongo.MongoClient(apikey)
db = client.data_processing
mdata = db.flags
return mdata.find_one({flag:{"$exists":True}})
def set_analysis_flags(apikey, flag, data):
client = pymongo.MongoClient(apikey)
db = client.data_processing
mdata = db.flags
return mdata.replace_one({flag:{"$exists":True}}, data, True)
def unkeyify_2l(layered_dict):
out = {} out = {}
for i in layered_dict.keys(): for i in layered_dict.keys():
add = [] add = []
@@ -127,3 +60,239 @@ def unkeyify_2l(layered_dict):
add.sort(key = lambda x: x[0]) add.sort(key = lambda x: x[0])
out[i] = list(map(lambda x: x[1], add)) out[i] = list(map(lambda x: x[1], add))
return out return out
def get_match_data_formatted(self):
teams_at_comp = self.get_teams_at_competition()
out = {}
for team in teams_at_comp:
try:
out[int(team)] = self.unkeyify_2l(self.get_team_match_data(team).transpose().to_dict())
except:
pass
return out
def get_metrics_data_formatted(self):
competition = self.competition
teams_at_comp = self.get_teams_at_competition()
out = {}
for team in teams_at_comp:
try:
out[int(team)] = self.get_team_metrics_data(int(team))
except:
pass
return out
def get_pit_data_formatted(self):
client = self.mongoclient
competition = self.competition
x=requests.get("https://titanscouting.epochml.org/api/fetchAllTeamNicknamesAtCompetition?competition="+competition)
x = x.json()
x = x['data']
x = x.keys()
out = {}
for i in x:
try:
out[int(i)] = self.get_team_pit_data(int(i))
except:
pass
return out
def get_pit_variable_data(self):
client = self.mongoclient
db = client.data_processing
mdata = db.team_pit
return mdata.find()
def get_pit_variable_formatted(self):
temp = self.get_pit_variable_data()
out = {}
for i in temp:
out[i["variable"]] = i["data"]
return out
def push_team_tests_data(self, team_num, data, dbname = "data_processing", colname = "team_tests"):
client = self.mongoclient
competition = self.competition
db = client[dbname]
mdata = db[colname]
mdata.replace_one({"competition" : competition, "team": team_num}, {"_id": competition+str(team_num)+"am", "competition" : competition, "team" : team_num, "data" : data}, True)
def push_team_metrics_data(self, team_num, data, dbname = "data_processing", colname = "team_metrics"):
client = self.mongoclient
competition = self.competition
db = client[dbname]
mdata = db[colname]
mdata.replace_one({"competition" : competition, "team": team_num}, {"_id": competition+str(team_num)+"am", "competition" : competition, "team" : team_num, "metrics" : data}, True)
def push_team_pit_data(self, variable, data, dbname = "data_processing", colname = "team_pit"):
client = self.mongoclient
competition = self.competition
db = client[dbname]
mdata = db[colname]
mdata.replace_one({"competition" : competition, "variable": variable}, {"competition" : competition, "variable" : variable, "data" : data}, True)
def get_analysis_flags(self, flag):
client = self.mongoclient
db = client.data_processing
mdata = db.flags
return mdata.find_one({flag:{"$exists":True}})
def set_analysis_flags(self, flag, data):
client = self.mongoclient
db = client.data_processing
mdata = db.flags
return mdata.replace_one({flag:{"$exists":True}}, data, True)
def get_previous_time(self):
previous_time = self.get_analysis_flags("latest_update")
if previous_time == None:
self.set_analysis_flags("latest_update", 0)
previous_time = 0
else:
previous_time = previous_time["latest_update"]
return previous_time
def set_current_time(self, current_time):
self.set_analysis_flags("latest_update", {"latest_update":current_time})
def get_database_config(self):
remote_config = self.get_analysis_flags("config")
return remote_config["config"] if remote_config != None else None
def set_database_config(self, config):
self.set_analysis_flags("config", {"config": config})
def load_match(self):
return self.get_match_data_formatted()
def load_metric(self, match, group_name, metrics):
group = {}
for team in match[group_name]:
db_data = self.get_team_metrics_data(team)
if db_data == 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 load_pit(self):
return self.get_pit_data_formatted()
def push_match(self, results):
for team in results:
self.push_team_tests_data(team, results[team])
def push_metric(self, metric):
for team in metric:
self.push_team_metrics_data(team, metric[team])
def push_pit(self, pit):
for variable in pit:
self.push_team_pit_data(variable, pit[variable])
def check_new_database_matches(self):
return True
#----- API implementations below -----#
def get_team_competition(self):
trakey = self.trakey
url = self.trakey['url']
endpoint = '/api/fetchTeamCompetition'
params = {
"CLIENT_ID": trakey['CLIENT_ID'],
"CLIENT_SECRET": trakey['CLIENT_SECRET']
}
response = requests.request("GET", url + endpoint, params=params)
json = response.json()
if json['success']:
return json['competition']
else:
raise APIError(json)
def get_team(self):
trakey = self.trakey
url = self.trakey['url']
endpoint = '/api/fetchTeamCompetition'
params = {
"CLIENT_ID": trakey['CLIENT_ID'],
"CLIENT_SECRET": trakey['CLIENT_SECRET']
}
response = requests.request("GET", url + endpoint, params=params)
json = response.json()
if json['success']:
return json['team']
else:
raise APIError(json)
""" doesn't seem to be functional:
def get_team_match_data(self, team_num):
trakey = self.trakey
url = self.trakey['url']
competition = self.competition
endpoint = '/api/fetchAllTeamMatchData'
params = {
"competition": competition,
"teamScouted": team_num,
"CLIENT_ID": trakey['CLIENT_ID'],
"CLIENT_SECRET": trakey['CLIENT_SECRET']
}
response = requests.request("GET", url + endpoint, params=params)
json = response.json()
if json['success']:
return json['data'][team_num]
else:
raise APIError(json)"""
def get_teams_at_competition(self):
trakey = self.trakey
url = self.trakey['url']
competition = self.competition
endpoint = '/api/fetchAllTeamNicknamesAtCompetition'
params = {
"competition": competition,
"CLIENT_ID": trakey['CLIENT_ID'],
"CLIENT_SECRET": trakey['CLIENT_SECRET']
}
response = requests.request("GET", url + endpoint, params=params)
json = response.json()
if json['success']:
return list(json['data'].keys())
else:
raise APIError(json)

View File

@@ -1,151 +0,0 @@
<Launch>:
orientation: "vertical"
NavigationLayout:
ScreenManager:
id: screen_manager
HomeScreen:
name: "Home"
BoxLayout:
orientation: "vertical"
MDToolbar:
title: screen_manager.current
elevation: 10
left_action_items: [['menu', lambda x: nav_drawer.toggle_nav_drawer()]]
GridLayout:
cols: 1
padding: 15, 15
spacing: 20, 20
MDTextFieldRect:
hint_text: "Console Log"
# size_hint: .8, None
# align: 'center'
# Widget:
SettingsScreen:
name: "Settings"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: screen_manager.current
elevation: 10
left_action_items: [['menu', lambda x: nav_drawer.toggle_nav_drawer()]]
Widget:
InfoScreen:
name: "Info"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: screen_manager.current
elevation: 10
left_action_items: [['menu', lambda x: nav_drawer.toggle_nav_drawer()]]
# GridLayout:
# cols: 2
# padding: 15, 15
# spacing: 20, 20
BoxLayout:
orientation: "horizontal"
MDLabel:
text: "DB Key:"
halign: 'center'
MDTextField:
hint_text: "placeholder"
pos_hint: {"center_y": .5}
BoxLayout:
orientation: "horizontal"
MDLabel:
text: "TBA Key:"
halign: 'center'
MDTextField:
hint_text: "placeholder"
pos_hint: {"center_y": .5}
BoxLayout:
orientation: "horizontal"
MDLabel:
text: "CPU Use:"
halign: 'center'
MDLabel:
text: "placeholder"
halign: 'center'
BoxLayout:
orientation: "horizontal"
MDLabel:
text: "Network:"
halign: 'center'
MDLabel:
text: "placeholder"
halign: 'center'
Widget:
BoxLayout:
orientation: "horizontal"
MDLabel:
text: "Progress"
halign: 'center'
MDProgressBar:
id: progress
value: 50
StatsScreen:
name: "Stats"
MDCheckbox:
size_hint: None, None
size: "48dp", "48dp"
pos_hint: {'center_x': .5, 'center_y': .5}
on_active: Screen.test()
#Navigation Drawer -------------------------
MDNavigationDrawer:
id: nav_drawer
BoxLayout:
orientation: "vertical"
padding: "8dp"
spacing: "8dp"
MDLabel:
text: "Titan Scouting"
font_style: "Button"
size_hint_y: None
height: self.texture_size[1]
MDLabel:
text: "Data Analysis"
font_style: "Caption"
size_hint_y: None
height: self.texture_size[1]
ScrollView:
MDList:
OneLineAvatarListItem:
text: "Home"
on_press:
# nav_drawer.set_state("close")
# screen_manager.transition.direction = "left"
screen_manager.current = "Home"
IconLeftWidget:
icon: "home"
OneLineAvatarListItem:
text: "Settings"
on_press:
# nav_drawer.set_state("close")
# screen_manager.transition.direction = "right"
# screen_manager.fade
screen_manager.current = "Settings"
IconLeftWidget:
icon: "cog"
OneLineAvatarListItem:
text: "Info"
on_press:
# nav_drawer.set_state("close")
# screen_manager.transition.direction = "right"
# screen_manager.fade
screen_manager.current = "Info"
IconLeftWidget:
icon: "cog"
OneLineAvatarListItem:
text: "Stats"
on_press:
# nav_drawer.set_state("close")
# screen_manager.transition.direction = "right"
# screen_manager.fade
screen_manager.current = "Stats"
IconLeftWidget:
icon: "cog"

7
src/exceptions.py Normal file
View File

@@ -0,0 +1,7 @@
class APIError(Exception):
def __init__(self, str):
super().__init__(str)
class ConfigurationError (Exception):
def __init__(self, str):
super().__init__(str)

91
src/interface.py Normal file
View File

@@ -0,0 +1,91 @@
from logging import Logger as L
import datetime
import platform
import json
class Logger(L):
file = None
levels = {
0: "",
10:"[DEBUG] ",
20:"[INFO] ",
30:"[WARNING] ",
40:"[ERROR] ",
50:"[CRITICAL]",
}
targets = []
def __init__(self, verbose, profile, debug, file = None):
super().__init__("tra_logger")
self.file = file
if file is not None:
self.targets.append(self._send_file)
if profile:
self.targets.append(self._send_null)
elif verbose:
self.targets.append(self._send_scli)
elif debug:
self.targets.append(self._send_scli)
else:
self.targets.append(self._send_null)
def _send_null(self, msg):
pass
def _send_scli(self, msg):
print(msg)
def _send_file(self, msg):
f = open(self.file, 'a')
f.write(msg + "\n")
f.close()
def get_time_formatted(self):
return datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S %Z")
def log(self, level, msg):
for t in self.targets:
t(self.get_time_formatted() + "| " + self.levels[level] + ": " + msg)
def debug(self, msg):
self.log(10, msg)
def info(self, msg):
self.log(20, msg)
def warning(self, msg):
self.log(30, msg)
def error(self, msg):
self.log(40, msg)
def critical(self, msg):
self.log(50, msg)
def splash(self, version):
def hrule():
self.log(0, "#"+38*"-"+"#")
def box(s):
temp = "|"
temp += s
temp += (40-len(s)-2)*" "
temp += "|"
self.log(0, temp)
hrule()
box(" superscript version: " + version)
box(" os: " + platform.system())
box(" python: " + platform.python_version())
hrule()
def save_module_to_file(self, module, data, results):
f = open(module + ".log", "w")
json.dump({"data": data, "results":results}, f, ensure_ascii=False, indent=4)
f.close()

View File

@@ -1,58 +0,0 @@
from kivy.lang import Builder
from kivymd.uix.screen import Screen
from kivymd.uix.list import OneLineListItem, MDList, TwoLineListItem, ThreeLineListItem
from kivymd.uix.list import OneLineIconListItem, IconLeftWidget
from kivy.uix.scrollview import ScrollView
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.base import runTouchApp
from kivymd.uix.menu import MDDropdownMenu, MDMenuItem
from kivymd.app import MDApp
# import superscript as ss
# 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 matplotlib.pyplot as plt
from concurrent.futures import ThreadPoolExecutor
import time
import warnings
# global exec_threads
# Screens
class HomeScreen(Screen):
pass
class SettingsScreen(Screen):
pass
class InfoScreen(Screen):
pass
class StatsScreen(Screen):
pass
class MyApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Red"
return Builder.load_file("design.kv")
def test():
print("test")
if __name__ == "__main__":
MyApp().run()

313
src/module.py Normal file
View File

@@ -0,0 +1,313 @@
import abc
import signal
import numpy as np
from tra_analysis import Analysis as an
from tqdm import tqdm
class Module(metaclass = abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (hasattr(subclass, '__init__') and
callable(subclass.__init__) and
hasattr(subclass, 'validate_config') and
callable(subclass.validate_config) and
hasattr(subclass, 'run') and
callable(subclass.run)
)
@abc.abstractmethod
def __init__(self, *args, **kwargs):
raise NotImplementedError
@abc.abstractmethod
def validate_config(self, *args, **kwargs):
raise NotImplementedError
@abc.abstractmethod
def run(self, *args, **kwargs):
raise NotImplementedError
class Match (Module):
config = None
timestamp = None
client = None
data = None
results = None
def __init__(self, config, timestamp, client):
self.config = config
self.timestamp = timestamp
self.client = client
def validate_config(self):
return True, ""
def run(self):
self._load_data()
self._process_data()
self._push_results()
def _load_data(self):
self.data = self.client.load_match()
def _simplestats(self, data_test):
signal.signal(signal.SIGINT, signal.SIG_IGN)
data = np.array(data_test[3])
data = data[np.isfinite(data)]
ranges = list(range(len(data)))
test = data_test[2]
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 _process_data(self):
tests = self.config["tests"]
data = self.data
input_vector = []
for team in tqdm(data, desc = "Match Module ", unit = " team"):
for variable in data[team]:
if variable in tests:
for test in tests[variable]:
input_vector.append((team, variable, test, data[team][variable]))
self.data = input_vector
self.results = []
for test_var_data in self.data:
self.results.append(self._simplestats(test_var_data))
def _push_results(self):
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
result_filtered = self.results
input_vector = self.data
return_vector = AutoVivification()
i = 0
for result in result_filtered:
filtered = input_vector[i][2]
try:
short = short_mapping[filtered]
return_vector[input_vector[i][0]][input_vector[i][1]][input_vector[i][2]] = result[short]
except KeyError: # not in mapping
return_vector[input_vector[i][0]][input_vector[i][1]][input_vector[i][2]] = result
i += 1
self.results = return_vector
self.client.push_match(self.results)
class Metric (Module):
config = None
timestamp = None
client = None
data = None
results = None
def __init__(self, config, timestamp, client):
self.config = config
self.timestamp = timestamp
self.client = client
def validate_config(self):
return True, ""
def run(self):
self._load_data()
self._process_data()
self._push_results()
def _load_data(self):
self.data = self.client.pull_new_tba_matches(self.timestamp)
def _process_data(self):
self.results = {}
elo_N = self.config["tests"]["elo"]["N"]
elo_K = self.config["tests"]["elo"]["K"]
matches = self.data
red = {}
blu = {}
for match in tqdm(matches, desc = "Metric Module ", unit = " match"):
red = self.client.load_metric(match, "red", self.config["tests"])
blu = self.client.load_metric(match, "blue", self.config["tests"])
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)
self.results[match['match']] = temp_vector
self.client.push_metric(temp_vector)
def _push_results(self):
pass
class Pit (Module):
config = None
timestamp = None
client = None
data = None
results = None
def __init__(self, config, timestamp, client):
self.config = config
self.timestamp = timestamp
self.client = client
def validate_config(self):
return True, ""
def run(self):
self._load_data()
self._process_data()
self._push_results()
def _load_data(self):
self.data = self.client.load_pit()
def _process_data(self):
tests = self.config["tests"]
return_vector = {}
for team in tqdm(self.data, desc = "Pit Module ", unit = " team"):
for variable in self.data[team]:
if variable in tests:
if not variable in return_vector:
return_vector[variable] = []
return_vector[variable].append(self.data[team][variable])
self.results = return_vector
def _push_results(self):
self.client.push_pit(self.results)
class Rating (Module):
pass
class Heatmap (Module):
pass
class Sentiment (Module):
pass

View File

@@ -3,10 +3,42 @@
# Notes: # Notes:
# setup: # setup:
__version__ = "0.8.6" __version__ = "1.0.0"
# changelog should be viewed using print(analysis.__changelog__) # changelog should be viewed using print(analysis.__changelog__)
__changelog__ = """changelog: __changelog__ = """changelog:
1.0.0:
- superscript now runs in PEP 3143 compliant well behaved daemon on Linux systems
- removed daemon and socket functionality, user can implement using external software
- added verbose option to linux superscript to allow for interactive output
- moved pymongo import to superscript.py
- added profile option to linux superscript to profile runtime of script
- reduced memory usage slightly by consolidating the unwrapped input data
- added debug option, which performs one loop of analysis and dumps results to local files
- added event and time delay options to config
- event delay pauses loop until even listener recieves an update
- time delay pauses loop until the time specified has elapsed since the BEGINNING of previous loop
- added options to pull config information from database (reatins option to use local config file)
- config-preference option selects between prioritizing local config and prioritizing database config
- synchronize-config option selects whether to update the non prioritized config with the prioritized one
- divided config options between persistent ones (keys), and variable ones (everything else)
- generalized behavior of various core components by collecting loose functions in several dependencies into classes
- module.py contains classes, each one represents a single data analysis routine
- config.py contains the Configuration class, which stores the configuration information and abstracts the getter methods
0.9.3:
- improved data loading performance by removing redundant PyMongo client creation (120s to 14s)
- passed singular instance of PyMongo client as standin for apikey parameter in all data.py functions
0.9.2:
- removed unessasary imports from data
- minor changes to interface
0.9.1:
- fixed bugs in configuration item loading exception handling
0.9.0:
- moved printing and logging related functions to interface.py (changelog will stay in this file)
- changed function return files for load_config and save_config to standard C values (0 for success, 1 for error)
- added local variables for config location
- moved dataset getting and setting functions to dataset.py (changelog will stay in this file)
- moved matchloop, metricloop, pitloop and helper functions (simplestats) to processing.py
0.8.6: 0.8.6:
- added proper main function - added proper main function
0.8.5: 0.8.5:
@@ -114,514 +146,157 @@ __author__ = (
"Jacob Levine <jlevine@imsa.edu>", "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: # imports:
from tra_analysis import analysis as an import argparse, sys, time, traceback, warnings
import data as d from config import Configuration, ConfigurationError
from collections import defaultdict from data import Client
import json from interface import Logger
import math from module import Match, Metric, Pit
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(logger, verbose, profile, debug, config_path):
def main(): def close_all():
if "client" in locals():
global exec_threads client.close()
sys.stderr = open("errorlog.txt", "w")
warnings.filterwarnings("ignore") warnings.filterwarnings("ignore")
splash() logger.splash(__version__)
while (True): modules = {"match": Match, "metric": Metric, "pit": Pit}
while True:
try: try:
current_time = time.time() loop_start = time.time()
print("[OK] time: " + str(current_time))
config = load_config("config.json") logger.info("current time: " + str(loop_start))
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") config = Configuration(config_path)
cfg_max_threads = config["max-threads"]
sys_max_threads = os.cpu_count() logger.info("found and loaded config at <" + config_path + ">")
if cfg_max_threads > -sys_max_threads and cfg_max_threads < 0 :
alloc_processes = sys_max_threads + cfg_max_threads client = Client(config)
elif cfg_max_threads > 0 and cfg_max_threads < 1:
alloc_processes = math.floor(cfg_max_threads * sys_max_threads) logger.info("established connection to database")
elif cfg_max_threads > 1 and cfg_max_threads <= sys_max_threads:
alloc_processes = cfg_max_threads previous_time = client.get_previous_time()
elif cfg_max_threads == 0:
alloc_processes = sys_max_threads logger.info("analysis backtimed to: " + str(previous_time))
config.resolve_config_conflicts(logger, client)
config_modules, competition = config.modules, config.competition
client.competition = competition
for m in config_modules:
if m in modules:
start = time.time()
current_module = modules[m](config_modules[m], previous_time, client)
valid = current_module.validate_config()
if not valid:
continue
current_module.run()
logger.info(m + " module finished in " + str(time.time() - start) + " seconds")
if debug:
logger.save_module_to_file(m, current_module.data, current_module.results) # logging flag check done in logger
client.set_current_time(loop_start)
close_all()
logger.info("closed threads and database client")
logger.info("finished all tasks in " + str(time.time() - loop_start) + " seconds, looping")
if profile:
return 0
if debug:
return 0
event_delay = config["variable"]["event-delay"]
if event_delay:
logger.info("loop delayed until database returns new matches")
new_match = False
while not new_match:
time.sleep(1)
new_match = client.check_new_database_matches()
logger.info("database returned new matches")
else: else:
print("[ERROR] Invalid number of processes, must be between -" + str(sys_max_threads) + " and " + str(sys_max_threads)) loop_delay = float(config["variable"]["loop-delay"])
exit() remaining_time = loop_delay - (time.time() - loop_start)
exec_threads = Pool(processes = alloc_processes) if remaining_time > 0:
print("[OK] " + str(alloc_processes) + " threads started") logger.info("loop delayed by " + str(remaining_time) + " seconds")
time.sleep(remaining_time)
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: except KeyboardInterrupt:
print("\n[OK] caught KeyboardInterrupt, killing processes") close_all()
exec_threads.terminate() logger.info("detected KeyboardInterrupt, exiting")
print("[OK] processes killed, exiting") return 0
exit()
else: except ConfigurationError as e:
pass str_e = "".join(traceback.format_exception(e))
logger.error("encountered a configuration error: " + str(e))
logger.error(str_e)
close_all()
return 1
#clear() except Exception as e:
str_e = "".join(traceback.format_exception(e))
logger.error("encountered an exception while running")
logger.error(str_e)
close_all()
return 1
def clear(): def start(verbose, profile, debug, config_path, log_path):
# for windows logger = Logger(verbose, profile, debug, file = log_path)
if name == 'nt':
_ = system('cls')
# for mac and linux(here, os.name is 'posix') if profile:
else:
_ = system('clear')
def print_hrule(): import cProfile, pstats, io
profile = cProfile.Profile()
profile.enable()
exit_code = main(logger, verbose, profile, debug, config_path)
profile.disable()
f = open("profile.txt", "w+")
ps = pstats.Stats(profile, stream = f).sort_stats("cumtime")
ps.print_stats()
sys.exit(exit_code)
print("#"+38*"-"+"#") elif verbose:
def print_box(s): exit_code = main(logger, verbose, profile, debug, config_path)
sys.exit(exit_code)
temp = "|" elif debug:
temp += s
temp += (40-len(s)-2)*" "
temp += "|"
print(temp)
def splash(): exit_code = main(logger, verbose, profile, debug, config_path)
sys.exit(exit_code)
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: else:
previous_time = previous_time["latest_update"] pass # must be vebose, debug or profile
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 __name__ == "__main__":
if sys.platform.startswith('win'):
multiprocessing.freeze_support() parser = argparse.ArgumentParser(description = "TRA data processing application.")
main() parser.add_argument("mode", metavar = "MODE", type = str, nargs = 1, choices = ["verbose", "profile", "debug"], help = "verbose, debug, profile")
parser.add_argument("--config", dest = "config", default = "config.json", type = str, help = "path to config file")
parser.add_argument("--logfile", dest = "logfile", default = "logfile.log", type = str, help = "path to log file")
args = parser.parse_args()
mode = args.mode[0]
config_path = args.config
log_path = args.logfile
if mode == "verbose":
start(True, False, False, config_path = config_path, log_path = log_path)
elif mode == "profile":
start(False, True, False, config_path = config_path, log_path = log_path)
elif mode == "debug":
start(False, False, True, config_path = config_path, log_path = log_path)
exit(0)

View File

@@ -1,37 +0,0 @@
# -*- 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 )

14
test/test_zmq.py Normal file
View File

@@ -0,0 +1,14 @@
import signal
import zmq
signal.signal(signal.SIGINT, signal.SIG_DFL)
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect('tcp://localhost:5678')
socket.setsockopt(zmq.SUBSCRIBE, b'status')
while True:
message = socket.recv_multipart()
print(f'Received: {message}')