Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Manages a sqlite3 database to store the results.
4"""
5import datetime
6from uuid import uuid4
7import numpy
8import pandas
9from .dbengine import Database
10from .options_helpers import read_options, read_users
11from .competition import Competition
14class DatabaseCompetition(Database):
15 """
16 Holds the data used for competitions. Tables:
18 Competitions
20 * cpt_id
21 * cpt_name
22 * metric
23 * datafile
24 * description
25 * expected_values
27 Teams
29 * team_id
30 * team_name
32 Players
34 * player_id
35 * team_id
36 * player_name
37 * mail
38 * login
39 * pwd
41 Submission
43 * cpt_id
44 * player_id
45 * date
46 * filename
47 * metric_value
48 """
50 def __init__(self, dbfile):
51 """
52 @param dbfile filename or ``:memory:``
53 """
54 Database.__init__(self, dbfile)
55 self._init()
57 def _init(self):
58 """
59 Creates the tables if not present.
60 """
61 self.connect()
62 tables = self.get_table_list()
63 adds = dict(competitions=DatabaseCompetition._col_competitions,
64 teams=DatabaseCompetition._col_teams,
65 players=DatabaseCompetition._col_players,
66 submissions=DatabaseCompetition._col_submissions)
67 for k, v in adds.items():
68 if k not in tables:
69 self.create_table(k, v())
70 self.commit()
71 self.close()
73 def init_from_options(self, filename):
74 """
75 Initializes the database. It skips a table if
76 it exists.
78 @param filename filename
79 """
80 opt = read_options(filename)
81 if opt is None:
82 raise ValueError("No option in '{0}'.".format(filename))
83 users = read_users(opt["allowed_users"])
84 key = "tmpl_competitions" if "tmpl_competitions" in opt else "competitions"
85 competitions = [(d if isinstance(d, Competition)
86 else Competition(**d)) for d in opt[key]]
88 if not self.has_rows("teams"):
89 teams = map(lambda x: x[1]['team'], users.items())
90 tdf = pandas.DataFrame({"team_name": list(teams)})
91 tdf.reset_index(drop=False, inplace=True)
92 tdf.columns = ["team_id", "team_name"]
93 tdf.to_sql("teams", self.Connection,
94 if_exists="append", index=False)
96 if not self.has_rows("players"):
97 players = list(map(lambda x: x[1], users.items()))
98 pdf = pandas.DataFrame(players)
99 pdf.reset_index(drop=False, inplace=True)
100 tdf = self.to_df("teams")
101 pdf["player_name"] = pdf["name"]
102 pdf["player_id"] = pdf["index"]
103 pdf = pdf.merge(tdf, left_on="team", right_on="team_name")
104 pdf = pdf.drop(["name", "team_name", "index", "team"], axis=1)
105 pdf.to_sql("players", self.Connection,
106 if_exists="append", index=False)
108 if not self.has_rows("competitions"):
109 pdf = pandas.DataFrame(Competition.to_records(competitions))
110 if "cpt_id" in pdf.columns:
111 pdf.reset_index(drop=True, inplace=True)
112 else:
113 pdf.reset_index(drop=False, inplace=True)
114 # tdf = self.to_df("competitions")
115 pdf["cpt_id"] = pdf["index"]
116 pdf = pdf.drop("index", axis=1)
117 pdf.to_sql("competitions", self.Connection,
118 if_exists="append", index=False)
120 if not self.has_rows("submissions"):
121 pdf = DatabaseCompetition._dummy_submissions()
122 pdf.reset_index(drop=True, inplace=True)
123 pdf.to_sql("submissions", self.Connection,
124 if_exists="append", index=False)
126 df = self.to_df("players")
127 logins = set(df['login'])
128 if None in logins:
129 raise ValueError("One login is wrong: {0}".format(logins))
131 def get_competitions(self):
132 """
133 Returns the list of competitions as list of ``(cpt_id, cpt_name)``.
134 """
135 return self.execute("SELECT cpt_id, cpt_name FROM competitions")
137 def to_df(self, table):
138 """
139 Returns the content of a table as a dataframe.
140 """
141 return pandas.read_sql("SELECT * FROM {0}".format(table), self.Connection)
143 @property
144 def Connection(self):
145 """
146 Returns the connexion.
147 """
148 self._check_connection()
149 return self._connection
151 @staticmethod
152 def _col_competitions():
153 return [('cpt_id', int), ('link', str), ('cpt_name', str), ('description', str),
154 ('metric', str), ('datafile', str), ('expected_values', str)]
156 @staticmethod
157 def _col_teams():
158 return [('team_id', int), ('team_name', str)]
160 @staticmethod
161 def _col_players():
162 return [('player_id', int), ('team_id', int), ('metric', str), ('player_name', str),
163 ('mail', str), ('login', str), ('pwd', str)]
165 @staticmethod
166 def _col_submissions():
167 return [('sub_id', str), ('cpt_id', int), ('player_id', int), ('date', str),
168 ('data', str), ('metric', str), ('metric_value', float)]
170 @staticmethod
171 def _dummy_submissions():
172 return pandas.DataFrame([dict(sub_id=str(uuid4()), cpt_id=-1, player_id=-1, date=datetime.datetime.now(),
173 data='', metric='rse', metric_value=numpy.nan)])
175 def get_cpt_id(self):
176 """
177 Returns the list of competation id.
178 """
179 return list(_[0] for _ in self.execute("SELECT cpt_id FROM competitions"))
181 def get_player_id(self):
182 """
183 Returns the list of competation id.
184 """
185 return list(_[0] for _ in self.execute("SELECT player_id FROM players"))
187 def submit(self, cpt_id, player_id, data, date=datetime.datetime.now()):
188 """
189 Adds a submission to the database.
191 @param cpt_id competition id
192 @param player_id player who did the submission
193 @param data data of the submission
195 The function computes the metric associated to the submission.
196 """
197 if not isinstance(data, str):
198 raise TypeError("data must be str not {0}".format(type(data)))
199 cp = list(self.execute(
200 "SELECT cpt_id, metric, expected_values FROM competitions WHERE cpt_id={0}".format(cpt_id)))
201 if len(cp) == 0:
202 raise ValueError("Unable to find cpt_id={0} in\n{1}".format(
203 cpt_id, self.get_cpt_id()))
204 pid = list(self.execute(
205 "SELECT player_id FROM players WHERE player_id={0}".format(player_id)))
206 if len(pid) == 0:
207 raise ValueError("Unable to find player_id={0} in\n{1}".format(
208 player_id, self.get_player_id()))
209 metrics = [_[1:] for _ in cp]
211 sub = []
212 for met, exp in metrics:
213 cp = Competition(cpt_id=0, link='', name='', description='',
214 metric=met, expected_values=exp)
215 dres = cp.evaluate(data)
216 res = dres[met]
217 if not isinstance(res, float):
218 res = float(res)
219 rec = dict(sub_id=str(uuid4()), cpt_id=cpt_id, player_id=player_id,
220 data=data, metric=met, metric_value=res)
221 sub.append(rec)
223 df = pandas.DataFrame(sub)
224 df.to_sql("submissions", self._connection,
225 if_exists="append", index=False)
226 self.commit()
228 def get_competition(self, cpt_id):
229 """
230 Retrieves a competition.
232 @param cpt_id competition id
233 @return @see cl Competition
234 """
235 res = list(self.execute(
236 "SELECT * FROM competitions WHERE cpt_id=={0}".format(cpt_id)))
237 if len(res) == 0:
238 raise KeyError("No competition for cpt_id=={0}".format(cpt_id))
239 if len(res) != 1:
240 raise KeyError(
241 "Too many competitions for cpt_id=={0}".format(cpt_id))
242 args = list(res[0])
243 return Competition(*args)
245 def get_results(self, cpt_id):
246 """
247 Retrieves the results of a competition.
249 @param cpt_id competition id
250 @return a data frame
251 """
252 res = pandas.read_sql("""SELECT A.*, B.player_name AS player_name, C.team_name
253 FROM submissions AS A
254 INNER JOIN players AS B ON A.player_id == B.player_id
255 INNER JOIN teams AS C ON B.team_id == C.team_id
256 WHERE cpt_id == {0}
257 """.format(cpt_id), self.Connection)
258 return res