Changeset 3019
- Timestamp:
- 01/10/08 14:23:12 (1 year ago)
- Files:
-
- estimatorplugin/0.11/estimatorplugin/api.py (modified) (1 diff)
- estimatorplugin/0.11/estimatorplugin/dbhelper.py (modified) (1 diff)
- estimatorplugin/0.11/estimatorplugin/estimator.py (added)
- estimatorplugin/0.11/estimatorplugin/htdocs/estimate.css (modified) (1 diff)
- estimatorplugin/0.11/estimatorplugin/htdocs/estimate.js (modified) (5 diffs)
- estimatorplugin/0.11/estimatorplugin/templates/estimate.html (modified) (5 diffs)
- estimatorplugin/0.11/estimatorplugin/webui.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
estimatorplugin/0.11/estimatorplugin/api.py
r3013 r3019 48 48 success &= dbhelper.execute_in_trans( 49 49 ("""CREATE TABLE estimate( 50 id integer ,50 id integer PRIMARY KEY, 51 51 rate DECIMAL, 52 52 variability DECIMAL, 53 communication _overheadDECIMAL,54 ticket _id integer53 communication DECIMAL, 54 tickets VARCHAR(512) 55 55 )""",[]), 56 56 ("""CREATE TABLE estimate_line_item( 57 id integer ,57 id integer PRIMARY KEY, 58 58 estimate_id integer, 59 59 description VARCHAR(2048), 60 low _hoursDECIMAL,61 high _hoursDECIMAL60 low DECIMAL, 61 high DECIMAL 62 62 )""",[])) 63 63 # SHOULD BE LAST IN THIS FUNCTION estimatorplugin/0.11/estimatorplugin/dbhelper.py
r3014 r3019 168 168 def json_out(self): 169 169 return "[%s]" % ','. join( 170 [("{%s}" % ','.join(["'%s':%r" %(key, s elf.value(val, row))171 for (key, val) in columnMap.items()]))172 for row in rows])170 [("{%s}" % ','.join(["'%s':%r" %(key, str(self.value(val, row))) 171 for (key, val) in self.columnMap.items()])) 172 for row in self.rows]) estimatorplugin/0.11/estimatorplugin/htdocs/estimate.css
r3014 r3019 6 6 margin:5px; 7 7 } 8 9 .numberCell{ 10 text-align:right; 11 border:1px solid blue; 12 } 13 14 .fieldLabel{ 15 font-weight:bold; 16 text-align:right; 17 } estimatorplugin/0.11/estimatorplugin/htdocs/estimate.js
r3013 r3019 14 14 else return ""; 15 15 } 16 var $$ = document.getElementById; 16 17 17 var tr = cn('tr', {}, 18 18 cn('td', {}, 19 cn('textarea', {id:uid("description"), rows:2, cols:30},19 cn('textarea', {id:uid("description"), name:uid("description"), cols:30, style:"height: 34px;"}, 20 20 valFn('description'))), 21 21 cn('td', { valign:'top'}, 22 cn('input', {id:uid('low'), type:'text', style:"width:80px;",22 cn('input', {id:uid('low'),name:uid('low'), type:'text', style:"width:80px;", 23 23 value: valFn('low'), onkeyup:'runCalculation()'})), 24 24 cn('td', {valign:'top'}, 25 cn('input', {id:uid('high'), type:'text', style:"width:80px;"25 cn('input', {id:uid('high'), name:uid('high'),type:'text', style:"width:80px;" 26 26 , value: valFn('high'), onkeyup:'runCalculation()'})), 27 cn('td', {id:uid('ave'), valign:'top', style:"width:80px;"}),27 cn('td', {id:uid('ave'), 'class':"numberCell", valign:'top', style:"width:80px;"}), 28 28 cn('td', {id:uid('buttons'),valign:'top'}, 29 29 cn('button',{onclick:'removeLineItem(this);return false;'},'remove'))); … … 42 42 } 43 43 44 function make MultiplierAccessor(id){44 function makeNumberAccessor(id, def){ 45 45 return function(){ 46 46 var str = $$(id).value.trim(); 47 if (str.length == 0) return 1;47 if (str.length == 0) return def; 48 48 var val = Number(str); 49 if (isNaN(val)) return 1;49 if (isNaN(val)) return def; 50 50 return val; 51 51 } 52 52 } 53 53 54 var rate = make MultiplierAccessor('rate');55 var variability = make MultiplierAccessor('variability');56 var communication = make MultiplierAccessor('communication');54 var rate = makeNumberAccessor('rate', 1); 55 var variability = makeNumberAccessor('variability', 1); 56 var communication = makeNumberAccessor('communication', 1); 57 57 58 var lineItemValue 58 var ave_no_zero = function (x, y){ 59 if(x!=0 && y!=0){ 60 var val = (x+y)/2; 61 return Math.round(val * 1000)/1000; 62 } 63 else if (x !=0) return x; 64 else return y; 65 } 66 67 59 68 function runCalculation(){ 60 69 var item, lowTotal=0, highTotal=0, lowAdjusted, highAdjusted, … … 65 74 } 66 75 var valFn = function(str){ 67 var str = $$(uid(str)).value.trim(); 68 if (str.length == 0) return 0; 69 var val = Number(str); 70 if (isNaN(val)) return 0; 71 return val; 76 return makeNumberAccessor(uid(str), 0)(); 72 77 } 73 78 var low = valFn('low'); … … 76 81 lowTotal+=low; 77 82 highTotal+=high; 78 $$(uid('ave')).innerHTML = (low+high)/2;83 $$(uid('ave')).innerHTML = ave_no_zero(low, high); 79 84 } 80 lowAdjusted = variability() * communication() * lowTotal ; 81 highAdjusted = variability() * communication() * highTotal ; 82 lowCost = rate() * lowAdjusted; 83 highCost = rate() * highAdjusted; 85 var adjust = function(num){ 86 return Math.round(num*1000)/1000; 87 } 88 lowTotal = adjust(lowTotal); 89 highTotal = adjust(highTotal); 90 lowAdjusted = adjust(variability() * communication() * lowTotal); 91 highAdjusted = adjust(variability() * communication() * highTotal); 92 lowCost = adjust(rate() * lowAdjusted); 93 highCost = adjust(rate() * highAdjusted); 84 94 $$('lowTotal').innerHTML = lowTotal; 85 95 $$('highTotal').innerHTML = highTotal; 86 $$('aveTotal').innerHTML = (lowTotal+highTotal) / 2;96 $$('aveTotal').innerHTML = ave_no_zero(lowTotal, highTotal); 87 97 $$('lowAdjusted').innerHTML = lowAdjusted; 88 98 $$('highAdjusted').innerHTML = highAdjusted; 89 $$('aveAdjusted').innerHTML = (lowAdjusted+highAdjusted) / 2;99 $$('aveAdjusted').innerHTML = ave_no_zero(lowAdjusted, highAdjusted); 90 100 $$('lowCost').innerHTML = lowCost; 91 101 $$('highCost').innerHTML = highCost; 92 $$('aveCost').innerHTML = (lowCost+highCost) / 2;102 $$('aveCost').innerHTML = ave_no_zero(lowCost, highCost); 93 103 }; 94 104 … … 97 107 lineItems.removeItem(row.item); 98 108 row.parentNode.removeChild(row); 109 runCalculation(); 99 110 } 100 111 estimatorplugin/0.11/estimatorplugin/templates/estimate.html
r3014 r3019 9 9 10 10 <head> 11 <title> Time Tracking Managementfor Trac.11</title>11 <title>Advanced Estimations for Trac.11</title> 12 12 <script type="text/javascript" src="${chrome.htdocs_location}js/wikitoolbar.js"></script> 13 13 <script type="text/javascript" py:choose=""> … … 20 20 <body> 21 21 <form method="post" action="${estimate.href}" > 22 22 <input type="hidden" name="id" value="${estimate.id}"/> 23 23 24 <div id="content" class="estimate"> 24 25 <div id="messages" > … … 30 31 <table border="0" cellpadding="3" cellspacing="0" > 31 32 <tr> 32 <td ><label for="tickets">Ticket Numbers:</label></td> 33 <td><input id="tickets" type="text" /></td> 33 <td class="fieldLabel" ><label for="tickets">Ticket Numbers:</label></td> 34 <td><input id="tickets" name="tickets" type="text" 35 value="${estimate.tickets}" /></td> 34 36 </tr> 35 37 <tr> 36 <td ><label for="rate">Rate</label></td> 37 <td><input id="rate" type="text" onkeyup="runCalculation()" value="${estimate.rate}" /></td> 38 <td class="fieldLabel" ><label for="rate">Rate:</label></td> 39 <td><input id="rate" name="rate" type="text" onkeyup="runCalculation()" 40 value="${estimate.rate}" /></td> 38 41 </tr> 39 42 <tr> 40 <td ><label for="variability">Variability</label></td> 41 <td><input id="variability" type="text" onkeyup="runCalculation()" value="${estimate.variability}" /></td> 43 <td class="fieldLabel" ><label for="variability">Variability:</label></td> 44 <td><input id="variability" name="variability" type="text" onkeyup="runCalculation()" 45 value="${estimate.variability}" /></td> 42 46 </tr> 43 47 <tr> 44 <td ><label for="communication">Communication</label></td> 45 <td><input id="communication" type="text" onkeyup="runCalculation()" value="${estimate.communication}" /></td> 48 <td class="fieldLabel" ><label for="communication">Communication:</label></td> 49 <td><input id="communication" name="communication" type="text" onkeyup="runCalculation()" 50 value="${estimate.communication}" /></td> 46 51 </tr> 47 52 </table> 48 53 49 54 50 <table border="0" cellpadding="3" cellspacing=" 0" >51 <tr id=" header">55 <table border="0" cellpadding="3" cellspacing="1" > 56 <tr id="lineItemheader" > 52 57 <th>Description</th> 53 58 <th>Low </th> … … 60 65 61 66 <tr id="lineItemFooter"> 62 <th >Total:</th>63 <td id="lowTotal" ></td>64 <td id="highTotal" ></td>65 <td id="aveTotal" ></td>67 <th class="fieldLabel" >Total:</th> 68 <td id="lowTotal" class="numberCell" ></td> 69 <td id="highTotal" class="numberCell"></td> 70 <td id="aveTotal" class="numberCell"></td> 66 71 <td> </td> 67 72 </tr> 68 73 <tr> 69 <th >Adjusted Hours:</th>70 <td id="lowAdjusted" ></td>71 <td id="highAdjusted" ></td>72 <td id="aveAdjusted" ></td>74 <th class="fieldLabel">Adjusted Hours:</th> 75 <td id="lowAdjusted" class="numberCell" ></td> 76 <td id="highAdjusted" class="numberCell" ></td> 77 <td id="aveAdjusted" class="numberCell" ></td> 73 78 <td> </td> 74 79 </tr> 75 80 <tr> 76 <th >Cost:</th>77 <td id="lowCost" ></td>78 <td id="highCost" ></td>79 <td id="aveCost" ></td>81 <th class="fieldLabel">Cost:</th> 82 <td id="lowCost" class="numberCell" ></td> 83 <td id="highCost" class="numberCell" ></td> 84 <td id="aveCost" class="numberCell" ></td> 80 85 <td> </td> 81 86 </tr> … … 83 88 <script language="javascript" > 84 89 var lineItems = ${estimate.lineItems}; 85 loadLineItems( );90 loadLineItems(); 86 91 newLineItem(); 87 92 </script > estimatorplugin/0.11/estimatorplugin/webui.py
r3014 r3019 8 8 INavigationContributor, ITemplateProvider 9 9 from trac.web.href import Href 10 from estimator import * 11 10 12 11 13 class EstimationsPage(Component): … … 13 15 def __init__(self): 14 16 pass 17 def load(self, req, addMessage, data): 18 try: 19 id = int(req.args['id']) 20 data["estimate"]["id"] = id 21 estimate_rs = getEstimateResultSet(id) 22 if estimate_rs: 23 data["estimate"]["id"] = id 24 data["estimate"]["rate"] = estimate_rs.value("rate", 0) 25 data["estimate"]["tickets"] = estimate_rs.value("tickets", 0) 26 data["estimate"]["variability"] = estimate_rs.value("variability", 0) 27 data["estimate"]["communication"] = estimate_rs.value("communication", 0) 28 rs = getEstimateLineItemsResultSet(id) 29 if rs: 30 data["estimate"]["lineItems"] = rs.json_out() 31 else: 32 addMessage('Cant Find Estimate Id: %s' % id) 33 except Exception, e: 34 addMessage('Invalid Id: %s' % req.args['id']) 35 addMessage('Error: %s' % e) 36 def line_item_hash_from_args(self, args): 37 not_line_items=['__FORM_TOKEN','tickets','variability','communication','rate', 'id'] 38 itemReg = re.compile(r"(\D+)(\d+)") 39 lineItems = {} 40 def lineItemHasher( value, name, id ): 41 if not lineItems.has_key(id): 42 lineItems[id] = {} 43 lineItems[id][name] = value 44 [lineItemHasher( item[1], *itemReg.match( item[0] ).groups()) 45 for item in args.items() 46 if not(not_line_items.__contains__(item[0]))] 47 return lineItems 48 49 def save_from_form (self, args, addMessage): 50 #try: 51 tickets = args["tickets"] 52 id = args["id"] 53 if id == None or id == '' : 54 sql = estimateInsert 55 id = nextEstimateId () 56 else: 57 sql = estimateUpdate 15 58 59 estimate_args = [args['rate'], args['variability'], args['communication'], tickets, id] 60 saveEstimate = (sql, estimate_args) 61 saveLineItems = [] 62 newLineItemId = nextEstimateLineItemId () 63 64 # we want to delete any rows that were not included in the form request 65 # we will not use -1 as a valid id, so this will allow us to use the same sql reguardless of anything else 66 ids = ['-1'] 67 lineItems = self.line_item_hash_from_args(args).items() 68 lineItems.sort() 69 for item in lineItems: 70 itemId = item[0] 71 if int(itemId) < 400000000:# new ids on the HTML are this number and above 72 ids.append(str(itemId)) 73 sql = lineItemUpdate 74 else: 75 itemId = newLineItemId 76 newLineItemId += 1 77 sql = lineItemInsert 78 itemargs = [id, 79 item[1]['description'], 80 item[1]['low'], 81 item[1]['high'], 82 itemId] 83 saveLineItems.append((sql, itemargs)) 84 85 sql = removeLineItemsNotInListSql % ','.join(ids) 86 addMessage("Deleting NonExistant Estimate Rows: %r - %s" % (sql , id)) 87 result = dbhelper.execute_in_trans(saveEstimate, 88 (sql, id), 89 *saveLineItems) 90 if not result: 91 addMessage("Failed to save!") 92 else: 93 addMessage("Estimate Saved!") 94 95 #except Exception, e: 96 # raise e 97 # addMessage("Error Saving Estimate: %s" % e) 98 99 16 100 # INavigationContributor methods 17 101 def get_active_navigation_item(self, req): … … 30 114 def match_request(self, req): 31 115 return req.path_info.startswith('/Estimate') 32 33 def load(self, req, addMessage, data): 34 try: 35 id = int(req.args['id']) 36 data["estimate"]["id"] = id 37 estimate_rs = dbhelper.get_result_set("SELECT * FROM estimate WHERE estimate_id=%s", id) 38 if estimate_rs: 39 data["estimate"]["rate"] = estimate_rs.get_value("rate", 0) 40 data["estimate"]["variability"] = estimate_rs.get_value("variability", 0) 41 data["estimate"]["communication"] = estimate_rs.get_value("communication", 0) 42 rs = dbhelper.get_result_set("SELECT * FROM estimate_line_item WHERE estimate_id=%s", id) 43 if rs: 44 data["estimate"]["lineItems"] = rs.json_out() 45 else: 46 addMessage('Cant Find Estimate Id: %s' % id) 47 except Exception, e: 48 addMessage('Invalid Id: %s' % id) 49 addMessage('Error: %s' % e) 50 116 51 117 def process_request(self, req): 52 118 messages = [] 53 119 def addMessage(s): 54 120 messages.extend([s]); 121 addMessage("Post Args: %s"% req.args.items()) 55 122 if req.method == 'POST': 56 pass123 self.save_from_form(req.args, addMessage) 57 124 data = {} 58 data["estimate"]={"href": req.href.Estimate(), 59 "messages": messages, 60 "lineItems": '[]', 61 "rate": self.config.get( 'estimator','default_rate') or 200, 62 "variability": self.config.get( 'estimator','default_variability') or 1, 63 "communication": self.config.get( 'estimator','default_communication') or 1, 64 } 125 data["estimate"]={ 126 "href": req.href.Estimate(), 127 "messages": messages, 128 "id": None, 129 "lineItems": '[]', 130 "tickets": '', 131 "rate": self.config.get( 'estimator','default_rate') or 200, 132 "variability": self.config.get( 'estimator','default_variability') or 1, 133 "communication": self.config.get( 'estimator','default_communication') or 1, 134 } 65 135 66 if req.args.has_key('id') :136 if req.args.has_key('id') and req.args['id'].strip() != '': 67 137 self.load(req, addMessage, data) 68 138 … … 88 158 rtn = [resource_filename(__name__, 'templates')] 89 159 return rtn 160
