| 14 | | SCHEMA = [ |
| 15 | | Table('ticket_status_dt', key = ('ticket', 'status'))[ |
| 16 | | Column('ticket'), |
| 17 | | Column('status'), |
| 18 | | Column('time', type = 'integer'), |
| 19 | | Index(['status', 'time'])] |
| 20 | | ] |
| 21 | | |
| 22 | | # IEnvironmentSetupParticipant methods |
| 23 | | def environment_created(self): |
| 24 | | self._upgrade_db(self.env.get_db_cnx()) |
| 25 | | |
| 26 | | def environment_needs_upgrade(self, db): |
| 27 | | cursor = db.cursor() |
| 28 | | #if self._need_migration(db): |
| 29 | | # return True |
| 30 | | try: |
| 31 | | cursor.execute("select count(*) from ticket_status_dt") |
| 32 | | |
| 33 | | cursor.fetchone() |
| 34 | | return False |
| 35 | | except: |
| 36 | | db.rollback() |
| 37 | | return True |
| 38 | | |
| 39 | | def upgrade_environment(self, db): |
| 40 | | self._upgrade_db(db) |
| 41 | | |
| 42 | | # def _need_migration(self, db): |
| 43 | | # cursor = db.cursor() |
| 44 | | # try: |
| 45 | | # cursor.execute("select count(*) from wiki_namespace") |
| 46 | | # cursor.fetchone() |
| 47 | | # self.env.log.debug("tractags needs to migrate old data") |
| 48 | | # return True |
| 49 | | # except: |
| 50 | | # db.rollback() |
| 51 | | # return False |
| 52 | | |
| 53 | | def _upgrade_db(self, db): |
| 54 | | try: |
| 55 | | try: |
| 56 | | from trac.db import DatabaseManager |
| 57 | | db_backend, _ = DatabaseManager(self.env)._get_connector() |
| 58 | | except ImportError: |
| 59 | | db_backend = self.env.get_db_cnx() |
| 60 | | |
| 61 | | cursor = db.cursor() |
| 62 | | for table in self.SCHEMA: |
| 63 | | for stmt in db_backend.to_sql(table): |
| 64 | | self.env.log.debug(stmt) |
| 65 | | cursor.execute(stmt) |
| 66 | | |
| 67 | | db.commit() |
| 68 | | |
| 69 | | cursor = db.cursor() |
| 70 | | cursor.execute("SELECT id from ticket") |
| 71 | | for id, in cursor: |
| 72 | | rebuild_status_log(self.env,id,db) |
| 73 | | db.commit() |
| 74 | | |
| 75 | | #TODO:データを集計する |
| 76 | | |
| 77 | | # # Migrate old data |
| 78 | | # if self._need_migration(db): |
| 79 | | # cursor = db.cursor() |
| 80 | | # cursor.execute("INSERT INTO tags (tagspace, name, tag) SELECT " |
| 81 | | # "'wiki', name, namespace FROM wiki_namespace") |
| 82 | | # cursor.execute("DROP TABLE wiki_namespace") |
| 83 | | # db.commit() |
| 84 | | except: |
| 85 | | db.rollback() |
| 86 | | raise |
| 87 | | |
| 88 | | """Extension point interface for components that require notification |
| 89 | | when tickets are created, modified, or deleted.""" |
| 90 | | |
| 91 | | def ticket_created(self,ticket): |
| 92 | | """Called when a ticket is created.""" |
| 93 | | |
| 94 | | def ticket_changed(self,ticket, comment, author, old_values): |
| 95 | | """Called when a ticket is modified. |
| | 19 | def validate_ticket(self,req, ticket): |
| | 20 | """If status chaned, then set date to custom field.""" |
| | 21 | status_dt = set_status_dt(self.env,ticket.id,ticket['status'],ticket['changetime']) |
| | 22 | if ticket._old.has_key('status'): |
| | 23 | for m in status_dt: |
| | 24 | ticket[m] = status_dt[m] |
| 120 | | delete_status_log(env,ticket_id,db) |
| | 45 | cursor = db.cursor() |
| | 46 | cursor.execute("SELECT newvalue,time,ticket ,field from ticket_change where ticket=%s" |
| | 47 | " and field=%s" |
| | 48 | " order by time",(ticket_id,'status')) |
| | 49 | history=[(row[0],to_datetime(row[1])) for row in cursor] |
| | 50 | if new_status: |
| | 51 | history.append((new_status,new_time)) |
| | 52 | |
| | 53 | result ={} |
| | 54 | for new_status,time in history: |
| | 55 | #orderの順に日付が入ることを規定する |
| | 56 | #古い更新から見ていき、 |
| | 57 | #新しいステータスより前のステータスが空なら日付を入れる |
| | 58 | #新しいステータスより後のステータスは強制的に空にする |
| | 59 | idx = order.index(new_status) |
| | 60 | formated_date = format_date(to_datetime(time)) |
| | 61 | |
| | 62 | for m_idx in range(len(order)-1, 0,-1): |
| | 63 | if not order[m_idx] in custom_fields: |
| | 64 | continue |
| | 65 | |
| | 66 | m_field = custom_fields[order[m_idx]] |
| | 67 | if not m_field in result: |
| | 68 | result[m_field] = None |
| | 69 | |
| | 70 | if idx==m_idx: |
| | 71 | result[m_field]=formated_date |
| | 72 | elif idx<m_idx: |
| | 73 | result[m_field]=None |
| | 74 | else: |
| | 75 | if result[m_field]==None: |
| | 76 | result[m_field]=formated_date |
| | 77 | else: |
| | 78 | formated_date=result[m_field] |
| | 79 | return result |
| 123 | | cursor = db.cursor() |
| 124 | | cursor.execute("SELECT ticket,field,newvalue,oldvalue,time from ticket_change where ticket=%s" |
| 125 | | " and field=%s" |
| 126 | | " order by time",(ticket_id,'status')) |
| 127 | | |
| 128 | | _assigned='assigned' |
| 129 | | _accepted='accepted' |
| 130 | | _closed='closed' |
| 131 | | _reopened ='reopened' |
| 132 | | # see http://trac.edgewall.org/wiki/TracWorkflow |
| 133 | | |
| 134 | | #member={_assigned:0,_accepted:1,_repoened:2,_closed:3} |
| 135 | | order=[_assigned,_accepted,_reopened,_closed] |
| 136 | | member=[_assigned,_accepted,_closed] |
| 137 | | dates = {_assigned:None,_accepted:None,_closed:None,_reopened:None} |
| 138 | | #orderの順に日付が入ることを規定する |
| 139 | | #古い更新から見ていき、 |
| 140 | | #新しいステータスより前のステータスが空なら日付を入れる |
| 141 | | # ただし、skip_enabled はからになることを許す。 |
| 142 | | #新しいステータスより後のステータスは強制的に空にする |
| 143 | | for ticket,field,newvalue,oldvalue,time in cursor: |
| 144 | | if newvalue in order: |
| 145 | | idx = order.index(newvalue) |
| 146 | | dates[newvalue] = time |
| 147 | | for after in order[idx+1:]: |
| 148 | | dates[after]=None |
| 149 | | for target in member: |
| 150 | | if dates[target]: |
| 151 | | idx = order.index(target) |
| 152 | | for pre in order[:idx]: |
| 153 | | if dates[pre]==None or dates[pre]> dates[target]: |
| 154 | | if pre in member: |
| 155 | | dates[pre]=dates[target] |
| 156 | | |
| 157 | | cursor = db.cursor() |
| 158 | | for m in dates: |
| 159 | | if dates[m] != None: |
| 160 | | cursor.execute("insert into ticket_status_dt (ticket,status,time) " |
| 161 | | "values(%s,%s,%s)", (ticket_id,m,dates[m])) |
| 162 | | |
| 163 | | |
| 164 | | for m in dates: |
| 165 | | field_name ="last_"+m |
| 166 | | formated_date = format_date(to_datetime(dates[m])) |
| 167 | | cursor.execute("SELECT value from ticket_custom " |
| 168 | | "where ticket=%s and name=%s",(ticket_id,field_name)) |
| 169 | | row = cursor.fetchone() |
| 170 | | if row: |
| 171 | | if dates[m] != None: |
| 172 | | if row[0]!=formated_date: |
| 173 | | cursor.execute("UPDATE ticket_custom SET value=%s " |
| 174 | | "where ticket=%s and name=%s", |
| 175 | | (formated_date,ticket_id,field_name)) |
| 176 | | |
| 177 | | else: |
| 178 | | cursor.execute("DELETE FROM ticket_custom " |
| 179 | | "where ticket=%s and name=%s", |
| 180 | | (ticket_id,field_name)) |
| 181 | | else: |
| 182 | | if dates[m] != None: |
| 183 | | cursor.execute("INSERT into ticket_custom (ticket,name,value)" |
| 184 | | "values (%s,%s,%s) ", |
| 185 | | (ticket_id,field_name,formated_date)) |
| 186 | | |
| 187 | | |
| 188 | | if handle_ta: |
| 189 | | db.commit() |
| 190 | | |
| 191 | | |
| 192 | | def delete_status_log(env,ticket_id,db=None): |
| 193 | | if not db: |
| 194 | | db = env.get_db_cnx() |
| 195 | | handle_ta = True |
| 196 | | else: |
| 197 | | handle_ta = False |
| 198 | | |
| 199 | | #db, handle_ta = self._get_db_for_write(db) |
| 200 | | cursor = db.cursor() |
| 201 | | cursor.execute("DELETE FROM ticket_status_dt WHERE ticket=%s", (ticket_id,)) |
| 202 | | |
| 203 | | if handle_ta: |
| 204 | | db.commit() |
| 205 | | |