diff --git a/vizgrimoire/GrimoireSQL.py b/vizgrimoire/GrimoireSQL.py index fac40b5e..784a92f3 100644 --- a/vizgrimoire/GrimoireSQL.py +++ b/vizgrimoire/GrimoireSQL.py @@ -87,17 +87,78 @@ def SetDBChannel (user=None, password=None, database=None, cursor = db.cursor() cursor.execute("SET NAMES 'utf8'") -def ExecuteQuery (sql): - result = {} +def ExecuteQuery (sql, enforce = None): + """Execute query, return results. + + Returns the results of executing the specified SQL query, as a + dictionary. In this dictionary, keys are field names for the query + results, values are the corresponding value(s) for the corresponding + field in all rows. + + Depending on the value of the parameter 'enforce', results will + be enforced to be a list, or (default behavior, for compatibility), + a list if results are more than one row, or single values if the + result is only one row. If result is 0 rows, an empty list will always + be returned for each key. + + For example, if the following values are returned by the query + (assuming each line is a row, first line is the name of fields): + + "Field A", "Field B" + "String 1", 1 + "String 2", 2 + + the returning dictionary will be + + {"Field A": ["String 1", "String 2"], + "Field B": [1, 2] + } + + If query results is just one row, and enforce == "list", the result + is as follows: + + {"Field A": ["String 1"], + "Field B": [1] + } + + In the same case, enforce == None, the result is: + + {"Field A": "String 1", + "Field B": 1 + } + + If query results is 0 rows: + + {"Field A": [], + "Field B": [] + } + + If for some reason the the description of the fields cannot be obtained, + it returns None (if enforce =) "list") or empty directory + (if enforce == None). Probably it should raise an exception. + + :param sql: SQL string to execute + :param enforce: enforce some kind of result, "list" or None (default) + + :returns result of executing the SQL string + + """ + cursor.execute(sql) rows = cursor.rowcount columns = cursor.description - if columns is None: return result + if columns is None: + if enforce == "list": + return None + else: + return {} + + result = {} for column in columns: result[column[0]] = [] - if rows > 1: + if rows > 1 or ((rows == 1) and (enforce == "list")): for value in cursor.fetchall(): for (index,column) in enumerate(value): result[columns[index][0]].append(column) diff --git a/vizgrimoire/analysis/top_questions_qaforums.py b/vizgrimoire/analysis/top_questions_qaforums.py index 6c56bf15..7445f82c 100644 --- a/vizgrimoire/analysis/top_questions_qaforums.py +++ b/vizgrimoire/analysis/top_questions_qaforums.py @@ -63,7 +63,7 @@ def top_commented(self): group by q.question_identifier order by comments desc limit %s """ % (self.filters.startdate, self.filters.enddate, self.filters.npeople) - return self.db.ExecuteQuery(query) + return self.db.ExecuteQuery(query, enforce = "list") def top_visited(self): # The top visited questions are those questions with the diff --git a/vizgrimoire/metrics/query_builder.py b/vizgrimoire/metrics/query_builder.py index 5bb2a680..b208d921 100644 --- a/vizgrimoire/metrics/query_builder.py +++ b/vizgrimoire/metrics/query_builder.py @@ -222,19 +222,88 @@ def __SetDBChannel__ (self, user=None, password=None, database=None, return db - def ExecuteQuery (self, sql): - if sql is None: return {} + def ExecuteQuery (self, sql, enforce = None): + """Execute query, return results. + + Returns the results of executing the specified SQL query, as a + dictionary. In this dictionary, keys are field names for the query + results, values are the corresponding value(s) for the corresponding + field in all rows. + + Depending on the value of the parameter 'enforce', results will + be enforced to be a list, or (default behavior, for compatibility), + a list if results are more than one row, or single values if the + result is only one row. If result is 0 rows, an empty list will always + be returned for each key. + + For example, if the following values are returned by the query + (assuming each line is a row, first line is the name of fields): + + "Field A", "Field B" + "String 1", 1 + "String 2", 2 + + the returning dictionary will be + + {"Field A": ["String 1", "String 2"], + "Field B": [1, 2] + } + + If query results is just one row, and enforce == "list", the result + is as follows: + + {"Field A": ["String 1"], + "Field B": [1] + } + + In the same case, enforce == None, the result is: + + {"Field A": "String 1", + "Field B": 1 + } + + If query results is 0 rows: + + {"Field A": [], + "Field B": [] + } + + If for some reason the the description of the fields cannot be obtained, + it returns None (if enforce =) "list") or empty directory + (if enforce == None). Probably it should raise an exception. + + :param sql: SQL string to execute + :param enforce: enforce some kind of result, "list" or None (default) + + :returns result of executing the SQL string + + :raises ValueError: when sql is None (and enforce is not None) + + """ + + + if sql is None: + if enforce is not None: + raise ValueError ('sql string cannot be none') + else: + return {} # print sql - result = {} self.cursor.execute(sql) rows = self.cursor.rowcount columns = self.cursor.description - if columns is None: return result + if columns is None: + # Description failed + if enforce is not None: + return None + else: + return {} + + result = {} for column in columns: result[column[0]] = [] - if rows > 1: + if rows > 1 or ((rows == 1) and (enforce == "list")): for value in self.cursor.fetchall(): for (index,column) in enumerate(value): result[columns[index][0]].append(column)