| [2390] | 1 | from trac.core import * |
|---|
| 2 | |
|---|
| 3 | |
|---|
| [2776] | 4 | |
|---|
| [2390] | 5 | class CustomReportManager: |
|---|
| 6 | """A Class to manage custom reports""" |
|---|
| 7 | version = 1 |
|---|
| 8 | name = "custom_report_manager_version" |
|---|
| 9 | env = None |
|---|
| 10 | log = None |
|---|
| [2774] | 11 | TimingAndEstimationKey = "Timing and Estimation Plugin" |
|---|
| [2390] | 12 | |
|---|
| 13 | def __init__(self, env, log): |
|---|
| 14 | self.env = env |
|---|
| 15 | self.log = log |
|---|
| 16 | self.upgrade() |
|---|
| 17 | |
|---|
| 18 | def upgrade(self): |
|---|
| 19 | # Check to see what version we have |
|---|
| 20 | db = self.env.get_db_cnx() |
|---|
| 21 | cursor = db.cursor() |
|---|
| 22 | cursor.execute("SELECT value FROM system WHERE name=%s", (self.name,)) |
|---|
| 23 | try: |
|---|
| 24 | version = int(cursor.fetchone()[0]) |
|---|
| 25 | except: |
|---|
| 26 | version = 0 |
|---|
| 27 | cursor.execute("INSERT INTO system (name,value) VALUES(%s,%s)", |
|---|
| 28 | (self.name, version)) |
|---|
| 29 | |
|---|
| 30 | if version > self.version: |
|---|
| 31 | raise TracError("Fatal Error: You appear to be running two plugins with conflicting versions " |
|---|
| 32 | "of the CustomReportManager class. Please ensure that '%s' is updated to " |
|---|
| 33 | "version %s of the file reportmanager.py (currently using version %s)." |
|---|
| 34 | % (__name__, str(version), str(self.version))) |
|---|
| 35 | |
|---|
| 36 | # Do the staged updates |
|---|
| 37 | try: |
|---|
| 38 | if version < 1: |
|---|
| 39 | cursor.execute("CREATE TABLE custom_report (" |
|---|
| 40 | "id INTEGER," |
|---|
| 41 | "uuid VARCHAR(64)," |
|---|
| 42 | "maingroup VARCHAR(255)," |
|---|
| 43 | "subgroup VARCHAR(255)," |
|---|
| 44 | "version INTEGER," |
|---|
| 45 | "ordering INTEGER)") |
|---|
| 46 | |
|---|
| 47 | #if version < 2: |
|---|
| 48 | # cursor.execute("...") |
|---|
| 49 | |
|---|
| 50 | # Updates complete, set the version |
|---|
| 51 | cursor.execute("UPDATE system SET value=%s WHERE name=%s", |
|---|
| 52 | (self.version, self.name)) |
|---|
| 53 | db.commit() |
|---|
| 54 | db.close() |
|---|
| 55 | |
|---|
| 56 | except Exception, e: |
|---|
| 57 | self.log.error("CustomReportManager Exception: %s" % (e,)); |
|---|
| 58 | db.rollback() |
|---|
| [2774] | 59 | |
|---|
| 60 | def get_report_id_and_version (self, uuid): |
|---|
| [5493] | 61 | sql = "SELECT custom_report.id, custom_report.version FROM custom_report "\ |
|---|
| 62 | "JOIN report ON report.id = custom_report.id " \ |
|---|
| 63 | "WHERE uuid=%s" |
|---|
| [2785] | 64 | tpl = self.get_first_row(sql, uuid) |
|---|
| [2774] | 65 | return tpl or (None, 0) |
|---|
| 66 | |
|---|
| 67 | def get_new_report_id (self): |
|---|
| [2776] | 68 | """find the next available report id """ |
|---|
| [2813] | 69 | rtn = self.get_scalar("SELECT MAX(id) FROM report") |
|---|
| 70 | return (rtn and rtn+1) or 1 |
|---|
| 71 | |
|---|
| [2774] | 72 | def get_max_ordering(self, maingroup, subgroup): |
|---|
| [2776] | 73 | """ Find the maximum ordering value used for this group of the custom_report table""" |
|---|
| [2785] | 74 | return self.get_scalar("SELECT MAX(ordering) FROM custom_report WHERE maingroup=%s AND subgroup=%s", |
|---|
| 75 | 0, maingroup, subgroup) or 0 |
|---|
| [2390] | 76 | |
|---|
| [2774] | 77 | def _insert_report (self, next_id, title, author, description, query, |
|---|
| 78 | uuid, maingroup, subgroup, version, ordering): |
|---|
| [2776] | 79 | """ Adds a row the custom_report_table """ |
|---|
| [2774] | 80 | self.log.debug("Inserting new report '%s' with uuid '%s'" % (title,uuid)) |
|---|
| [5493] | 81 | self.execute_in_trans(("DELETE FROM custom_report WHERE uuid=%s", (uuid,)), |
|---|
| 82 | ("INSERT INTO report (id, title, author, description, query) " \ |
|---|
| [2785] | 83 | "VALUES (%s, %s, %s, %s, %s)", |
|---|
| 84 | (next_id, title, author, description, query)), |
|---|
| 85 | ("INSERT INTO custom_report (id, uuid, maingroup, subgroup, version, ordering) " \ |
|---|
| 86 | "VALUES (%s, %s, %s, %s, %s, %s)", |
|---|
| 87 | (next_id, uuid, maingroup, subgroup, version, ordering))) |
|---|
| [5493] | 88 | self.log.debug("Attempting to increment sequence (only works in postgres)") |
|---|
| [4020] | 89 | try: |
|---|
| 90 | self.execute_in_trans(("SELECT nextval('report_id_seq');",[])); |
|---|
| 91 | self.log.debug("Sequence updated"); |
|---|
| 92 | except: |
|---|
| 93 | self.log.debug("Sequence failed to update, perhaps you are not running postgres?"); |
|---|
| [2774] | 94 | |
|---|
| 95 | def _update_report (self, id, title, author, description, query, |
|---|
| 96 | maingroup, subgroup, version): |
|---|
| [2776] | 97 | """Updates a report and its row in the custom_report table """ |
|---|
| [2785] | 98 | self.log.debug("Updating report '%s' with to version %s" % (title, version)) |
|---|
| 99 | self.execute_in_trans(("UPDATE report SET title=%s, author=%s, description=%s, query=%s " \ |
|---|
| 100 | "WHERE id=%s", (title, author, description, query, id)), |
|---|
| 101 | ("UPDATE custom_report SET version=%s, maingroup=%s, subgroup=%s " |
|---|
| 102 | "WHERE id=%s", (version, maingroup, subgroup, id))) |
|---|
| 103 | |
|---|
| [2774] | 104 | def add_report(self, title, author, description, query, uuid, version, |
|---|
| 105 | maingroup, subgroup="", force=False): |
|---|
| 106 | """ |
|---|
| [2776] | 107 | We add/update a report to the system. We will not overwrite unchanged versions |
|---|
| 108 | unless force is set. |
|---|
| 109 | """ |
|---|
| [2390] | 110 | # First check to see if we can load an existing version of this report |
|---|
| [2774] | 111 | (id, currentversion) = self.get_report_id_and_version(uuid) |
|---|
| [3960] | 112 | self.log.debug("add_report %s (ver:%s) | id: %s currentversion: %s" % (uuid , version, id, currentversion)) |
|---|
| [2390] | 113 | try: |
|---|
| 114 | if not id: |
|---|
| [2774] | 115 | next_id = self.get_new_report_id() |
|---|
| 116 | ordering = self.get_max_ordering(maingroup, subgroup) + 1 |
|---|
| 117 | self._insert_report(next_id, title, author, description, query, |
|---|
| 118 | uuid, maingroup, subgroup, version, ordering) |
|---|
| [2390] | 119 | return True |
|---|
| [2774] | 120 | if currentversion < version or force: |
|---|
| 121 | self._update_report(id, title, author, description, query, |
|---|
| 122 | maingroup, subgroup, version) |
|---|
| [2390] | 123 | return True |
|---|
| 124 | except Exception, e: |
|---|
| [2785] | 125 | self.log.error("CustomReportManager.add_report Exception: %s, %s" % (e,(title, author, uuid, version, |
|---|
| 126 | maingroup, subgroup, force))); |
|---|
| [3179] | 127 | self.log.debug("report %s not upgraded (a better version already exists)" % uuid) |
|---|
| [2390] | 128 | return False |
|---|
| 129 | |
|---|
| 130 | def get_report_by_uuid(self, uuid): |
|---|
| [2774] | 131 | sql = "SELECT report.id,report.title FROM custom_report "\ |
|---|
| 132 | "LEFT JOIN report ON custom_report.id=report.id "\ |
|---|
| 133 | "WHERE custom_report.uuid=%s" |
|---|
| [2785] | 134 | return self.get_first_row(sql,uuid) |
|---|
| [5493] | 135 | |
|---|
| [2390] | 136 | def get_reports_by_group(self, group): |
|---|
| [2785] | 137 | """Gets all of the reports for a given group""" |
|---|
| [2390] | 138 | db = self.env.get_db_cnx() |
|---|
| 139 | cursor = db.cursor() |
|---|
| 140 | rv = {} |
|---|
| 141 | try: |
|---|
| [2785] | 142 | cursor.execute("SELECT custom_report.subgroup,report.id,report.title, custom_report.version, custom_report.uuid " |
|---|
| [2390] | 143 | "FROM custom_report " |
|---|
| 144 | "LEFT JOIN report ON custom_report.id=report.id " |
|---|
| 145 | "WHERE custom_report.maingroup=%s " |
|---|
| 146 | "ORDER BY custom_report.subgroup,custom_report.ordering", (group,)) |
|---|
| [2785] | 147 | for subgroup, id, title, version, uuid in cursor: |
|---|
| [2390] | 148 | if not rv.has_key(subgroup): |
|---|
| 149 | rv[subgroup] = { "title": subgroup, |
|---|
| 150 | "reports": [] } |
|---|
| [2785] | 151 | rv[subgroup]["reports"].append( { "id": int(id), "title": title, "version":version, "uuid":uuid } ) |
|---|
| [2390] | 152 | except: |
|---|
| 153 | pass |
|---|
| 154 | return rv |
|---|
| [5493] | 155 | |
|---|
| 156 | def get_version_hash_by_group(self, group): |
|---|
| 157 | """Gets all of the reports for a given group as a uuid=>version hash""" |
|---|
| 158 | db = self.env.get_db_cnx() |
|---|
| 159 | cursor = db.cursor() |
|---|
| 160 | rv = {} |
|---|
| 161 | try: |
|---|
| 162 | cursor.execute("SELECT custom_report.subgroup,report.id,report.title, custom_report.version, custom_report.uuid " |
|---|
| 163 | "FROM custom_report " |
|---|
| 164 | "LEFT JOIN report ON custom_report.id=report.id " |
|---|
| 165 | "WHERE custom_report.maingroup=%s " |
|---|
| 166 | "ORDER BY custom_report.subgroup,custom_report.ordering", (group,)) |
|---|
| 167 | for subgroup, id, title, version, uuid in cursor: |
|---|
| 168 | rv[uuid] = version |
|---|
| 169 | except: |
|---|
| 170 | pass |
|---|
| 171 | return rv |
|---|
| [2390] | 172 | |
|---|
| [2785] | 173 | # similar functions are found in dbhelper, but this file should be fairly |
|---|
| 174 | # stand alone so that it can be copied and pasted around |
|---|
| 175 | def get_first_row(self, sql,*params): |
|---|
| 176 | """ Returns the first row of the query results as a tuple of values (or None)""" |
|---|
| 177 | db = self.env.get_db_cnx() |
|---|
| 178 | cur = db.cursor() |
|---|
| 179 | data = None; |
|---|
| 180 | try: |
|---|
| 181 | cur.execute(sql, params) |
|---|
| 182 | data = cur.fetchone(); |
|---|
| 183 | db.commit(); |
|---|
| 184 | except Exception, e: |
|---|
| 185 | self.log.error('There was a problem executing sql:%s \n \ |
|---|
| 186 | with parameters:%s\nException:%s'%(sql, params, e)); |
|---|
| 187 | db.rollback() |
|---|
| 188 | try: |
|---|
| 189 | db.close() |
|---|
| 190 | except: |
|---|
| 191 | pass |
|---|
| 192 | return data; |
|---|
| [2776] | 193 | |
|---|
| [2785] | 194 | def get_scalar(self, sql, col=0, *params): |
|---|
| 195 | """ Gets a single value (in the specified column) from the result set of the query""" |
|---|
| [2823] | 196 | data = self.get_first_row(sql, *params); |
|---|
| [2785] | 197 | if data: |
|---|
| 198 | return data[col] |
|---|
| 199 | else: |
|---|
| 200 | return None; |
|---|
| [2776] | 201 | |
|---|
| [2785] | 202 | def execute_in_trans(self, *args): |
|---|
| 203 | success = True |
|---|
| 204 | db = self.env.get_db_cnx() |
|---|
| 205 | cur = db.cursor() |
|---|
| 206 | try: |
|---|
| 207 | for sql, params in args: |
|---|
| 208 | cur.execute(sql, params) |
|---|
| 209 | db.commit() |
|---|
| 210 | except Exception, e: |
|---|
| 211 | self.log.error('There was a problem executing sql:%s \n \ |
|---|
| 212 | with parameters:%s\nException:%s'%(sql, params, e)); |
|---|
| 213 | db.rollback(); |
|---|
| 214 | success = False |
|---|
| 215 | try: |
|---|
| 216 | db.close() |
|---|
| 217 | except: |
|---|
| 218 | pass |
|---|
| 219 | return success |
|---|
| 220 | |
|---|
| 221 | |
|---|