Changeset 4877
- Timestamp:
- 11/26/08 13:57:40 (1 month ago)
- Files:
-
- estimatorplugin/0.11/estimatorplugin/api.py (modified) (4 diffs)
- estimatorplugin/0.11/estimatorplugin/estimator.py (modified) (2 diffs)
- estimatorplugin/0.11/estimatorplugin/htdocs/estimate.css (modified) (3 diffs)
- estimatorplugin/0.11/estimatorplugin/htdocs/estimate.js (modified) (5 diffs)
- estimatorplugin/0.11/estimatorplugin/macro_provider.py (modified) (1 diff)
- estimatorplugin/0.11/estimatorplugin/templates/estimate.html (modified) (6 diffs)
- estimatorplugin/0.11/estimatorplugin/webui.py (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
estimatorplugin/0.11/estimatorplugin/api.py
r3415 r4877 7 7 from trac.env import IEnvironmentSetupParticipant 8 8 9 dbversion = 19 dbversion = 2 10 10 dbkey = 'EstimatorPluginDbVersion' 11 11 … … 30 30 """ 31 31 ver = dbhelper.get_system_value(self.env, dbkey) 32 ans = (not ver) or ( ver< dbversion)33 self.log.debug('Estimator needs upgrade? %s [installed version:%s pluginversion:%s '%(ans, ver, dbversion))32 ans = (not ver) or (int(ver) < dbversion) 33 self.log.debug('Estimator needs upgrade? %s [installed version:%s pluginversion:%s ] '%(ans, ver, dbversion)) 34 34 return ans 35 35 … … 53 53 communication DECIMAL, 54 54 tickets VARCHAR(512), 55 comment VARCHAR(8000)55 comment text 56 56 )""",[]), 57 57 ("""CREATE TABLE estimate_line_item( … … 62 62 high DECIMAL 63 63 )""",[])) 64 65 if ver < 2: 66 self.log.debug('Creating Estimate and Estimate_Line_Item tables (Version 1)') 67 success = success and dbhelper.execute_in_trans(self.env, (""" ALTER TABLE estimate ADD COLUMN diffcomment text ; """,[])) 68 64 69 # SHOULD BE LAST IN THIS FUNCTION 65 70 if success: estimatorplugin/0.11/estimatorplugin/estimator.py
r3361 r4877 2 2 3 3 estimateUpdate = """ 4 UPDATE estimate SET rate=%s, variability=%s, communication=%s, tickets=%s, comment=%s 4 UPDATE estimate SET rate=%s, variability=%s, communication=%s, tickets=%s, comment=%s, diffcomment=%s 5 5 WHERE id=%s 6 6 """ 7 7 estimateInsert = """ 8 INSERT INTO estimate (rate, variability, communication, tickets, comment, id)9 VALUES(%s,%s,%s,%s,%s, %s)8 INSERT INTO estimate (rate, variability, communication, tickets, comment, diffcomment, id) 9 VALUES(%s,%s,%s,%s,%s,%s, %s) 10 10 """ 11 11 lineItemInsert = """ … … 42 42 return dbhelper.get_scalar(env, "SELECT COMMENT FROM estimate WHERE ID=%s", 0, id) 43 43 44 def getTextEstimate(env, id): 45 return dbhelper.get_scalar(env, "SELECT DIFFCOMMENT FROM estimate WHERE ID=%s", 0, id) 46 44 47 estimateChangeTicketComment = """ 45 48 INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) estimatorplugin/0.11/estimatorplugin/htdocs/estimate.css
r3054 r4877 1 . message {1 .infomessage { 2 2 color:green; 3 3 border:2px solid green; … … 7 7 } 8 8 9 .numberCell{ 9 table.estimateBody tr td { 10 border-top:1px solid #CCC; 11 } 12 13 table.estimateBody tr.lineItemFooter th { 14 border-top:1px solid #CCC; 15 } 16 17 .numberCell, table.estimateBody tr td.numberCell { 10 18 text-align:right; 11 19 border:1px solid blue; … … 25 33 text-decoration: line-through; 26 34 } 35 estimatorplugin/0.11/estimatorplugin/htdocs/estimate.js
r3059 r4877 24 24 } 25 25 26 function evenDeeperClone(node){ 26 function chromeCleanerPredicate ( node ){ 27 if(node && node.tagName){ 28 //its all text FF plugin adds chrome images 29 if (node.tagName.toLowerCase() == "img" && node.src.search('chrome://') == 0) return false; 30 } 31 return true; 32 } 33 34 function evenDeeperClone(node /* pred */){ 27 35 //firefox 2 has a bug where it wont clone textarea values sometimes 36 // Optional pred arg determines whether or not a node should be cloned 37 var pred = arguments[1] || chromeCleanerPredicate; 28 38 var kid, cloned; 39 if (pred && !pred(node)) return null; 29 40 var newNode = node.cloneNode(false); 30 41 for(var i=0 ; kid=node.childNodes[i] ; i++){ 31 42 if(kid.tagName && kid.tagName.toLowerCase()=='textarea'){ 32 cloned = cn(' textarea');33 cloned. value = kid.value;43 cloned = cn('div'); 44 cloned.innerHTML = kid.value.replace('\r\n','<br />', 'g').replace('\r','<br />', 'g').replace('\n','<br />', 'g'); 34 45 }else{ 35 46 cloned = evenDeeperClone(kid); 36 } 37 newNode.appendChild(cloned);47 } 48 if(cloned) newNode.appendChild(cloned); 38 49 } 39 50 return newNode; … … 51 62 var tr = cn('tr', {}, 52 63 cn('td', {}, 53 cn('textarea', {id:uid("description"), name:uid("description"), cols:30, style:"height: 34px;"},64 cn('textarea', {id:uid("description"), name:uid("description"), style:"height: 68px; width:100%;"}, 54 65 valFn('description'))), 55 66 cn('td', { valign:'top'}, … … 165 176 } 166 177 } 167 if (parent.id) parent.id = ""; 178 if (parent.id){ 179 parent.className = (parent.className || "") +" "+ parent.id; 180 parent.id = ""; 181 } 168 182 return parent; 169 183 } … … 183 197 return elem; 184 198 } 199 200 function prepareComment( preview ){ 201 var s = ""; 202 function walker ( node ){ 203 for (var i=0, kid ; kid = node.childNodes[i]; i++ ){ 204 if(kid && kid.tagName){ 205 var tn = kid.tagName.toLowerCase(); 206 if (tn == 'table'){ 207 var print_sep = false; 208 for(var row,j=0 ; row = kid.rows[j] ; j++){ 209 if (row.className == "lineItemheader") print_sep = true; 210 if (row.className == "lineItemFooter") print_sep = false; 211 for(var cell, k=0 ; cell = row.cells[k] ; k++){ 212 var val = (cell.textContent || cell.innerText); 213 if(val) s += val + ((k==0 && print_sep) ? "\n| " : 214 (print_sep ? " | " : "\t")); 215 } 216 s += "\n"; 217 if(print_sep)s+="---------------------------\n"; 218 } 219 s += "\n"; 220 } 221 } 222 } 223 } 224 walker(preview); 225 return s; 226 } 227 185 228 function preparePreview(){ 186 229 var preview = $$('estimateoutput'); … … 188 231 preview.appendChild(removeFirstRow(removeInputsAndIds(evenDeeperClone($$('estimateParams'))))); 189 232 preview.appendChild(removeInputsAndIds(evenDeeperClone($$('estimateBody')))); 233 //alert(prepareComment( preview )); 234 $$('diffcomment').value = prepareComment( preview ); 190 235 $$('comment').value = preview.innerHTML; 191 236 } estimatorplugin/0.11/estimatorplugin/macro_provider.py
r3361 r4877 22 22 if html: 23 23 add_stylesheet(req, "Estimate/estimate.css") 24 add_stylesheet(req, "common/css/diff.css") 24 25 html+= '<br /><a href="%s?id=%s">edit this estimate</a>' % (req.href.Estimate(), id) 25 26 else: estimatorplugin/0.11/estimatorplugin/templates/estimate.html
r3059 r4877 37 37 </tr> 38 38 <tr> 39 <td class="fieldLabel" ><label for="rate"> Rate:</label></td>39 <td class="fieldLabel" ><label for="rate"> Rate:</label></td> 40 40 <td><input id="rate" name="rate" type="text" onkeyup="runCalculation()" 41 41 value="${estimate.rate}" … … 43 43 </tr> 44 44 <tr> 45 <td class="fieldLabel" ><label for="variability"> Variability:</label></td>45 <td class="fieldLabel" ><label for="variability"> Variability:</label></td> 46 46 <td><input id="variability" name="variability" type="text" onkeyup="runCalculation()" 47 47 value="${estimate.variability}" … … 68 68 69 69 <tr id="lineItemFooter"> 70 <th class="fieldLabel" > Total:</th>70 <th class="fieldLabel" > Total:</th> 71 71 <td id="lowTotal" class="numberCell" ></td> 72 72 <td id="highTotal" class="numberCell"></td> … … 75 75 </tr> 76 76 <tr> 77 <th class="fieldLabel" >Adjusted Hours:</th>77 <th class="fieldLabel" >Adjusted Hours:</th> 78 78 <td id="lowAdjusted" class="numberCell" ></td> 79 79 <td id="highAdjusted" class="numberCell" ></td> … … 82 82 </tr> 83 83 <tr> 84 <th class="fieldLabel"> Cost:</th>84 <th class="fieldLabel"> Cost:</th> 85 85 <td id="lowCost" class="numberCell" ></td> 86 86 <td id="highCost" class="numberCell" ></td> … … 99 99 100 100 </div> 101 <textarea id="diffcomment" name="diffcomment" style="display:none;"></textarea> 101 102 <textarea id="comment" name="comment" style="display:none;"></textarea> 102 103 </div> estimatorplugin/0.11/estimatorplugin/webui.py
r3361 r4877 11 11 from trac.ticket import Ticket 12 12 import datetime 13 from trac.web.chrome import Chrome 13 14 from trac.util.datefmt import utc, to_timestamp 15 from trac.versioncontrol.diff import get_diff_options, diff_blocks 16 from genshi.template import TemplateLoader 17 from genshi.filters.transform import Transformer 18 from trac.web.api import ITemplateStreamFilter 19 20 21 # class EstimatorTicketStyleApplication(Component): 22 # implements(ITemplateStreamFilter) 23 24 # def __init__(self): 25 # pass 26 27 # # ITemplateStreamFilter 28 # def filter_stream(self, req, method, filename, stream, data): 29 # self.log.debug("EstimatorTicketStyleApplication executing") 30 # if not filename == 'ticket.html': 31 # self.log.debug("EstimatorTicketStyleApplication not the correct template") 32 # return stream 33 # #stream = stream | Transformer('//link[ends-with(@href,"trac.css")]').after( 34 # stream = stream | Transformer('//link[@href="/projects/test/chrome/common/css/trac.css")]').after( 35 36 # tag.link(type="text/css", rel="stylesheet", 37 # href=req.href.chrome("common", "css" , "diff.css"))() 38 # ) 39 # return stream 40 41 14 42 15 43 class EstimationsPage(Component): … … 17 45 def __init__(self): 18 46 pass 47 48 def get_diffs(self, req, old_text, new_text, id): 49 diff_style, diff_options, diff_data = get_diff_options(req) 50 diff_context = 3 51 for option in diff_options: 52 if option.startswith('-U'): 53 diff_context = int(option[2:]) 54 break 55 if diff_context < 0: 56 diff_context = None 57 diffs = diff_blocks(old_text.splitlines(), new_text.splitlines(), context=diff_context, 58 tabwidth=2, 59 ignore_blank_lines=True, 60 ignore_case=True, 61 ignore_space_changes=True) 62 63 chrome = Chrome(self.env) 64 loader = TemplateLoader(chrome.get_all_templates_dirs()) 65 tmpl = loader.load('diff_div.html') 66 67 title = "Estimate:%s Changed" %id 68 changes=[{'diffs': diffs, 'props': [], 69 'title': title, 'href': req.href('Estimate', id=id), 70 'new': {'path':title, 'rev':'', 'shortrev': '', 'href': req.href('Estimate', id=id)}, 71 'old': {'path':"", 'rev':'', 'shortrev': '', 'href': ''}}] 72 73 data = chrome.populate_data(req, 74 { 'changes':changes , 'no_id':True, 'diff':diff_data, 75 'longcol': '', 'shortcol': ''}) 76 # diffs = diff_blocks("Russ Tyndall", "Russ Foobar Tyndall", context=None, ignore_blank_lines=True, ignore_case=True, ignore_space_changes=True) 77 # data = c._default_context_data.copy () 78 diff_data['style']='sidebyside'; 79 data.update({ 'changes':changes , 'no_id':True, 'diff':diff_data, 80 'longcol': '', 'shortcol': ''}) 81 stream = tmpl.generate(**data) 82 return stream.render() 83 84 19 85 def load(self, id, addMessage, data): 20 86 try: … … 38 104 def line_item_hash_from_args(self, args): 39 105 not_line_items=['__FORM_TOKEN','tickets','variability','communication', 40 'rate', 'id', 'comment' ]106 'rate', 'id', 'comment', 'diffcomment'] 41 107 itemReg = re.compile(r"(\D+)(\d+)") 42 108 lineItems = {} … … 50 116 return lineItems 51 117 52 def notify_old_tickets(self, req, id, addMessage, changer ):53 try:118 def notify_old_tickets(self, req, id, addMessage, changer, new_text): 119 #try: 54 120 estimate_rs = getEstimateResultSet(self.env, id) 55 121 tickets = estimate_rs.value('tickets', 0) 56 comment = estimate_rs.value('comment', 0)122 old_text = estimate_rs.value('diffcomment', 0) 57 123 tickets = [int(t.strip()) for t in tickets.split(',')] 58 self.log.debug('Notifying old tickets of estimate change: %s' % tickets) 124 self.log.debug('About to render the diffs for tickets: %s ' % (tickets, )) 125 comment = """{{{ 126 #!html 127 %s 128 }}} """ % self.get_diffs(req, old_text, new_text, id) 129 self.log.debug('Notifying old tickets of estimate change: %s \n %s' % (tickets, comment)) 59 130 return [(estimateChangeTicketComment, 60 131 [t, … … 63 134 to_timestamp(datetime.datetime.now(utc)) - 1, 64 135 req.authname, 65 "{{{\n#!html\n<del>%s</del>\n}}}" % comment]) 136 comment 137 ]) 66 138 for t in tickets] 67 except Exception, e:139 #except Exception, e: 68 140 self.log.error("Error saving old ticket changes: %s" % e) 69 141 addMessage("Tickets must be numbers") … … 89 161 def save_from_form (self, req, addMessage): 90 162 #try: 91 92 163 args = req.args 93 164 tickets = args["tickets"] … … 103 174 else: 104 175 self.log.debug('Saving edited estimate') 105 old_tickets = self.notify_old_tickets(req, id, addMessage, req.authname )176 old_tickets = self.notify_old_tickets(req, id, addMessage, req.authname, args['diffcomment']) 106 177 sql = estimateUpdate 107 178 self.log.debug('Old Tickets to Update: %r' % old_tickets) 108 179 estimate_args = [args['rate'], args['variability'], 109 180 args['communication'], tickets, 110 args['comment'], id]181 args['comment'], args['diffcomment'], id] 111 182 saveEstimate = (sql, estimate_args) 112 183 saveLineItems = [] … … 148 219 if self.notify_new_tickets( req, id, tickets, addMessage): 149 220 addMessage("Estimate Saved!") 150 req.redirect(req.href.Estimate()+'?id=%s '%id)221 req.redirect(req.href.Estimate()+'?id=%s&justsaved=true'%id) 151 222 else: 152 223 addMessage("Failed to save! %s" % result) … … 167 238 # for tickets with only old estimates on them, we would still like to apply style 168 239 url = req.href.Estimate() 169 style = req.href.chrome('Estimate/estimate.css')240 #style = req.href.chrome('Estimate/estimate.css') 170 241 if req.perm.has_permission("TICKET_MODIFY"): 171 242 yield 'mainnav', "Estimate", \ 172 Markup('<a href="%s">%s</a><link type="text/css" href="%s" rel="stylesheet">' % 173 (url , "Estimate", style)) 174 yield 'mainnav', "Estimate-style", \ 175 Markup('<link type="text/css" href="%s" rel="stylesheet">' % 176 (style)) 243 Markup('<a href="%s">%s</a>' % 244 (url , "Estimate")) 177 245 178 246 # IRequestHandler methods … … 203 271 if req.args.has_key('id') and req.args['id'].strip() != '': 204 272 self.load(int(req.args['id']), addMessage, data) 205 273 274 if req.args.has_key('justsaved'): 275 tickets = ''.join( 276 ['<a href="%s/%s" >#%s</a>' % (req.href.ticket(), i.strip(), i.strip()) 277 for i in data['estimate']['tickets'].split(',')]) 278 addMessage("Estimate saved and added to tickets: "+tickets) 206 279 207 280 add_script(req, "Estimate/JSHelper.js")
