1+ import json
2+ from exceptions import ConfigurationError
3+ from cerberus import Validator
4+
5+ from data import set_database_config , get_database_config
6+
7+ class Configuration :
8+
9+ path = None
10+ config = {}
11+
12+ _sample_config = {
13+ "persistent" :{
14+ "key" :{
15+ "database" :"" ,
16+ "tba" :"" ,
17+ "tra" :{
18+ "CLIENT_ID" :"" ,
19+ "CLIENT_SECRET" :"" ,
20+ "url" : ""
21+ }
22+ },
23+ "config-preference" :"local" ,
24+ "synchronize-config" :False
25+ },
26+ "variable" :{
27+ "event-delay" :False ,
28+ "loop-delay" :0 ,
29+ "competition" : "2020ilch" ,
30+ "modules" :{
31+ "match" :{
32+ "tests" :{
33+ "balls-blocked" :[
34+ "basic_stats" ,
35+ "historical_analysis" ,
36+ "regression_linear" ,
37+ "regression_logarithmic" ,
38+ "regression_exponential" ,
39+ "regression_polynomial" ,
40+ "regression_sigmoidal"
41+ ],
42+ "balls-collected" :[
43+ "basic_stats" ,
44+ "historical_analysis" ,
45+ "regression_linear" ,
46+ "regression_logarithmic" ,
47+ "regression_exponential" ,
48+ "regression_polynomial" ,
49+ "regression_sigmoidal"
50+ ],
51+ "balls-lower-teleop" :[
52+ "basic_stats" ,
53+ "historical_analysis" ,
54+ "regression_linear" ,
55+ "regression_logarithmic" ,
56+ "regression_exponential" ,
57+ "regression_polynomial" ,
58+ "regression_sigmoidal"
59+ ],
60+ "balls-lower-auto" :[
61+ "basic_stats" ,
62+ "historical_analysis" ,
63+ "regression_linear" ,
64+ "regression_logarithmic" ,
65+ "regression_exponential" ,
66+ "regression_polynomial" ,
67+ "regression_sigmoidal"
68+ ],
69+ "balls-started" :[
70+ "basic_stats" ,
71+ "historical_analyss" ,
72+ "regression_linear" ,
73+ "regression_logarithmic" ,
74+ "regression_exponential" ,
75+ "regression_polynomial" ,
76+ "regression_sigmoidal"
77+ ],
78+ "balls-upper-teleop" :[
79+ "basic_stats" ,
80+ "historical_analysis" ,
81+ "regression_linear" ,
82+ "regression_logarithmic" ,
83+ "regression_exponential" ,
84+ "regression_polynomial" ,
85+ "regression_sigmoidal"
86+ ],
87+ "balls-upper-auto" :[
88+ "basic_stats" ,
89+ "historical_analysis" ,
90+ "regression_linear" ,
91+ "regression_logarithmic" ,
92+ "regression_exponential" ,
93+ "regression_polynomial" ,
94+ "regression_sigmoidal"
95+ ]
96+ }
97+ },
98+ "metric" :{
99+ "tests" :{
100+ "elo" :{
101+ "score" :1500 ,
102+ "N" :400 ,
103+ "K" :24
104+ },
105+ "gl2" :{
106+ "score" :1500 ,
107+ "rd" :250 ,
108+ "vol" :0.06
109+ },
110+ "ts" :{
111+ "mu" :25 ,
112+ "sigma" :8.33
113+ }
114+ }
115+ },
116+ "pit" :{
117+ "tests" :{
118+ "wheel-mechanism" :True ,
119+ "low-balls" :True ,
120+ "high-balls" :True ,
121+ "wheel-success" :True ,
122+ "strategic-focus" :True ,
123+ "climb-mechanism" :True ,
124+ "attitude" :True
125+ }
126+ }
127+ }
128+ }
129+ }
130+
131+ _validation_schema = {
132+ "persistent" : {
133+ "type" : "dict" ,
134+ "required" : True ,
135+ "require_all" : True ,
136+ "schema" : {
137+ "key" : {
138+ "type" : "dict" ,
139+ "require_all" :True ,
140+ "schema" : {
141+ "database" : {"type" :"string" },
142+ "tba" : {"type" : "string" },
143+ "tra" : {
144+ "type" : "dict" ,
145+ "require_all" : True ,
146+ "schema" : {
147+ "CLIENT_ID" : {"type" : "string" },
148+ "CLIENT_SECRET" : {"type" : "string" },
149+ "url" : {"type" : "string" }
150+ }
151+ }
152+ }
153+ },
154+ "config-preference" : {"type" : "string" , "required" : True },
155+ "synchronize-config" : {"type" : "boolean" , "required" : True }
156+ }
157+ }
158+ }
159+
160+ def __init__ (self , path ):
161+ self .path = path
162+ self .load_config ()
163+ self .validate_config ()
164+
165+ def load_config (self ):
166+ try :
167+ f = open (self .path , "r" )
168+ self .config .update (json .load (f ))
169+ f .close ()
170+ except :
171+ self .config = self ._sample_config
172+ self .save_config ()
173+ f .close ()
174+ raise ConfigurationError ("could not find config file at <" + self .path + ">, created new sample config file at that path" )
175+
176+ def save_config (self ):
177+ f = open (self .path , "w+" )
178+ json .dump (self .config , f , ensure_ascii = False , indent = 4 )
179+ f .close ()
180+
181+ def validate_config (self ):
182+ v = Validator (self ._validation_schema , allow_unknown = True )
183+ isValidated = v .validate (self .config )
184+
185+ if not isValidated :
186+ raise ConfigurationError ("config validation error: " + v .errors )
187+
188+ def __getattr__ (self , name ): # simple linear lookup method for common multikey-value paths, TYPE UNSAFE
189+ if name == "persistent" :
190+ return self .config ["persistent" ]
191+ elif name == "key" :
192+ return self .config ["persistent" ]["key" ]
193+ elif name == "database" :
194+ # soon to be deprecated
195+ return self .config ["persistent" ]["key" ]["database" ]
196+ elif name == "tba" :
197+ return self .config ["persistent" ]["key" ]["tba" ]
198+ elif name == "tra" :
199+ return self .config ["persistent" ]["key" ]["tra" ]
200+ elif name == "priority" :
201+ return self .config ["persistent" ]["config-preference" ]
202+ elif name == "sync" :
203+ return self .config ["persistent" ]["synchronize-config" ]
204+ elif name == "variable" :
205+ return self .config ["variable" ]
206+ elif name == "event_delay" :
207+ return self .config ["variable" ]["event-delay" ]
208+ elif name == "loop_delay" :
209+ return self .config ["variable" ]["loop-delay" ]
210+ elif name == "competition" :
211+ return self .config ["variable" ]["competition" ]
212+ elif name == "modules" :
213+ return self .config ["variable" ]["modules" ]
214+ else :
215+ return None
216+
217+ def __getitem__ (self , key ):
218+ return self .config [key ]
219+
220+ def resolve_config_conflicts (self , logger , client ): # needs improvement with new localization scheme
221+ sync = self .sync
222+ priority = self .priority
223+
224+ if sync :
225+ if priority == "local" or priority == "client" :
226+ logger .info ("config-preference set to local/client, loading local config information" )
227+ remote_config = get_database_config (client )
228+ if remote_config != self .config ["variable" ]:
229+ set_database_config (client , self .config ["variable" ])
230+ logger .info ("database config was different and was updated" )
231+ # no change to config
232+ elif priority == "remote" or priority == "database" :
233+ logger .info ("config-preference set to remote/database, loading remote config information" )
234+ remote_config = get_database_config (client )
235+ if remote_config != self .config ["variable" ]:
236+ self .config ["variable" ] = remote_config
237+ self .save_config ()
238+ # change variable to match remote
239+ logger .info ("local config was different and was updated" )
240+ else :
241+ raise ConfigurationError ("persistent/config-preference field must be \" local\" /\" client\" or \" remote\" /\" database\" " )
242+ else :
243+ if priority == "local" or priority == "client" :
244+ logger .info ("config-preference set to local/client, loading local config information" )
245+ # no change to config
246+ elif priority == "remote" or priority == "database" :
247+ logger .info ("config-preference set to remote/database, loading database config information" )
248+ self .config ["variable" ] = get_database_config (client )
249+ # change variable to match remote without updating local version
250+ else :
251+ raise ConfigurationError ("persistent/config-preference field must be \" local\" /\" client\" or \" remote\" /\" database\" " )
0 commit comments