· 9 years ago · Jul 04, 2017, 08:34 AM
1import MySQLdb
2
3SQL_HOST = ""
4SQL_USER = ""
5SQL_PSWD = ""
6SQL_DTBS = ""
7
8class DatabaseManager(object):
9 """
10 This class manages all database connections. The main idea of this object
11 is to create an easy interface so we can easilly and safely query values
12 from a database witout worrying about convulent and complex sqlite syntax.
13
14 This interface allows us to safely call and remove objects from the database
15 so that users do not have to access the database directly, but through
16 a simple API structure.
17
18 We should only have one database instance so it is unnecesarry to create
19 another object to hold the instances of this object.
20 """
21 players = []
22 def __init__(self, host, user, password, database):
23 """
24 Default Constructor
25
26 Initialize all the default values and open a connection with the SQLite
27 database. Create the tables if they don't exist.
28
29 @PARAM host - Host of the database
30 @PARAM user - User of the database
31 @PARAM password - Password of the database
32 @PARAM database - Database to connect in
33 """
34 self.host = host
35 self.user = user
36 self.password = password
37 self.database = database
38 self.connection = MySQLdb.connect(self.host, self.user, self.password, self.database)
39 self.connection.text_factory = str
40 self.cursor = self.connection.cursor()
41
42 def __del__(self):
43 """
44 Default deconstructor.
45
46 Executed when a database instance is destroyed. Ensure that the database
47 is saved and closed.
48 """
49 self.save()
50 self.close()
51
52 def __contains__(self, key):
53 """
54 Executed automatically when we attempt to test to see if an element exists
55 within the database.
56
57 @PARAM key - The value to test for validity
58 @RETURN boolean - Whether or not the value exists
59 """
60 key = str(key)
61 self.execute("SELECT games FROM Player WHERE name=?", key)
62 result = self.cursor.fetchone()
63 if bool(result):
64 return True
65 return False
66
67 def __iter__(self):
68 """
69 Executed automatically when we attempt to iterate through an instance
70 of this class. Query all the names from the playerstats table and
71 yield each name as a seperate object.
72
73 @RETURN yield object - string objects which represent player's steamids
74 """
75 self.execute("SELECT * FROM Player")
76 self.cursor.fetchall().itervalues()
77
78 def execute(self, parseString, *args):
79 """
80 A wrapper function to simulate the execute() method of a cursor object.
81
82 @PARAM parseString - the string query line of a SQLite statement
83 """
84 self.cursor.execute(parseString, args)
85
86 def getPlayerInfo(self, userid, infoType):
87 """
88 Returns a players attribute from the playerstats table.
89
90 @PARAM userid - the integer userid of the player your wish to check
91 @PARAM infoType - the column name of the value you wish to return
92 @RETURN object - returns an object type of the value to which statType returns
93 """
94 if not isinstance(userid, int):
95 userid = self.getUserIdFromSteamId(userid)
96 infoType = str(infoType).replace("'", "''")
97 if hasattr(infoType, "__len__"):
98 query = "SELECT " + ",".join(map(str, infoType)) + " FROM Player WHERE name=?"
99 else:
100 query = "SELECT " + str(infoType) + " FROM Player WHERE name=?"
101 self.execute(query, userid)
102 return self.fetchone()
103
104 def query(self, table, primaryKeyName, primaryKeyValue, options):
105 """
106 Queries results from the table for one person and returns either a
107 tuple or a single value depending on the amount of values passed.
108
109 @PARAM table - the table of which this query should take place
110 @PARAM primaryKeyName - the name of the key you wish to test for equality
111 @PARAM primaryKeyValue - the options to update where the primaryKeyName's value equates to this value
112 @PARAM options - either a single value or a tuple which we will get the values of
113 """
114 if hasattr(options, "__len__"):
115 query = "SELECT " + ",".join(map(lambda x: str(x).replace("'", "''"), options)) + " FROM " + table \
116 + " WHERE " + primaryKeyName + "='" + primaryKeyValue + "'"
117 else:
118 query = "SELECT " + str(options).replace("'", "''") + " FROM " + table + \
119 " WHERE " + primaryKeyName + "='" + primaryKeyValue + "'"
120 self.execute(query)
121 return self.fetchone()
122
123 def fetchall(self):
124 """
125 Mimics the sqlite fetchall method which recides within a cursor object.
126 Ensures that the true values are added to a list so that we don't need
127 to index it if the value is only one item in length (e.g. item instead
128 of (item,)...)
129
130 @RETURN list - attributes from which the query returned
131 """
132 trueValues = []
133 for value in self.cursor.fetchall():
134 if isinstance(value, tuple):
135 if len(value) > 1:
136 tempValues = []
137 for tempValue in value:
138 if isinstance(tempValue, long):
139 tempValue = int(tempValue)
140 tempValues.append(tempValue)
141 trueValues.append(tempValues)
142 else:
143 if isinstance(value[0], long):
144 trueValues.append(int(value[0]))
145 else:
146 trueValues.append(value[0])
147 else:
148 if isinstance(value, long):
149 value = int(value)
150 trueValues.append(value)
151 return trueValues
152
153 def fetchone(self):
154 """
155 Mimics the sqlite fetchone method which recides within a cursor object.
156 Ensures that a single value is returned from the cursor object if only
157 one object exists within the tuple from the query, otherwise it returns
158 the query result
159
160 @RETURN Object - the result from the query command
161 """
162 result = self.cursor.fetchone()
163 if hasattr(result, "__iter__"):
164 if len(result) == 1:
165 trueResults = result[0]
166 if isinstance(trueResults, long):
167 trueResults = int(trueResults)
168 return trueResults
169 else:
170 trueResults = []
171 for trueResult in result:
172 if isinstance(trueResult, long):
173 trueResult = int(trueResult)
174 trueResults.append(trueResult)
175 return trueResults
176 if isinstance(result, long):
177 result = int(result)
178 return result
179
180 def close(self):
181 """
182 Closes the connections so that no further queries can be made.
183 """
184 self.cursor.close()
185 self.connection.close()
186
187class PlayerManager(object):
188 """
189 This class is used to manage players. This class itself simulates a
190 dictionary but gives additional functions which allow us to modify the way
191 the singelton is perceived. This will store individual Player objects by a
192 tag name and allows us to reference the singleton object by dictionary item
193 assignment / retrieval to retrieve the retrespective player object.
194 """
195 def __init__(self):
196 """ Default constructor, initialize variables """
197 self.players = {}
198
199 def __getitem__(self, name):
200 """
201 Executed when object[x] is executed on the singleton. As we wish to
202 simulate dictionary attributes, we want to return the player object
203 referenced by their name
204
205 @PARAM str name - The ID of the user we wish to return the Player object
206 @RETURN Player - The Player object referenced by ID
207 """
208 return self.players[name]
209
210 def __iter__(self):
211 """
212 This function executes automatically when "for x in object" is executed.
213 We want to return all the player objects inside the player dictionary.
214
215 @RETURN generator - A generator to a list of all the player objects
216 """
217 return self.players.itervalues()
218
219 def __contains__(self, name):
220 """
221 Executed automatically when we use the syntax "x in object". We will
222 test if the name in question exists within the players dictionary.
223
224 @PARAM str name - The name to test for validation
225 @RETURN boolean - Whether or not the name already exists
226 """
227 return name in self.players
228
229 def __delitem__(self, name):
230 """
231 Removes the player and object from memory so the RAM is cleared.
232 object.removePlayer(name) == object.__delitem__(name)
233
234 @PARAM str name - The name of the player to remove
235 """
236 self.removePlayer(name)
237
238 def addPlayer(self, name):
239 """
240 Add the player into memory as an Player object
241
242 @PARAM str name - The name of the player to add
243 """
244 self.players[name] = PlayerObject(name)
245
246 def removePlayer(self, name):
247 """
248 Removes the player and object from memory
249
250 @PARAM str name - The name of the player to remove
251 """
252 if name in self.players:
253 del self.players[name]
254
255class PlayerObject(object):
256 """
257 This is an object which will revolve around all active players. This should
258 contain all relevant information about a particular player, and cache all
259 their details so we don't need to query the database at regular intervals.
260 """
261 name = None
262 cache = None
263 def __init__(self, name):
264 """"
265 Default constructor, initialize variables
266 Set up all necessary objects and cache values here.
267
268 @PARAM str name - The name of the user who this object references
269 """
270 self.name = str(name)
271 self.cache = PlayerCache(self.name)
272
273 def __getattr__(self, attribute):
274 """
275 This object will allow us to get attributes from the cache object
276
277 @PARAM str attribute - The attribute of the PlayerCache we want to
278 @RETURN str attribute - The attribute if it he exist
279 """
280 if hasattr(self.cache, attribute):
281 return getattr(self.cache, attribute)
282 raise AttributeError("Player object has no attribute %s" % attribute)
283
284 def __setattr__(self, attribute, value):
285 """
286 This object will allow us to reassing values in the PlayerCache object
287
288 @PARAM str attribute - The attribute to change
289 @PARAM int|str value - The value to set to the attribute
290 @RETURN mixed attribute - setattr object if cache has attribute
291 """
292 if hasattr(self.cache, attribute):
293 return setattr(self.cache, attribute, value)
294 object.__setattr__(attribute, value)
295
296class PlayerCache(object):
297 """
298 This object should be used by each player and should store all relevant
299 information regarding their current stats. Only refresh if explicitly told to;
300 we never need to refresh on ticks. This means we can reduce the amount of
301 DB queries we need which improves read time and efficiency.
302 """
303 def __init__(self, name):
304 """"
305 Default constructor, initialize variables
306
307 @PARAM str name - The name of the player on the DB
308 """
309 self.name = name
310 self.update()
311
312 def update(self):
313 """
314 This method simply refreshes the local attributes
315 with the attributes from the dictionnary.
316 """
317 database.execute("SELECT games, scores, strikes, spares, splits, opens FROM Player WHERE name=?", self.name)
318 self.games, self.scores, self.strikes, self.spares, self.splits, self.opens = database.fetchone()
319 self.minimum, self.maximum, self.average = min(self.scores), max(self.scores), round(sum(map(int, self.scores)) / float(len(self.scores)))
320
321players = PlayerManager()
322database = DatabaseManager(SQL_HOST, SQL_USER, SQL_PSWD, SQL_DTBS)