web: more register chart improvements
- show a background color for future and less-than-zero regions - show points for transactions, not all line corners - hovering over point shows balance, date, posted amount and transaction - clicking a point scrolls towards that date
This commit is contained in:
		
							parent
							
								
									470835adc2
								
							
						
					
					
						commit
						f2d9c6e9c1
					
				| @ -75,6 +75,7 @@ module Hledger.Data.Amount ( | ||||
|   mixed, | ||||
|   amounts, | ||||
|   filterMixedAmount, | ||||
|   filterMixedAmountByCommodity, | ||||
|   normaliseMixedAmountPreservingFirstPrice, | ||||
|   normaliseMixedAmountPreservingPrices, | ||||
|   -- ** arithmetic | ||||
| @ -429,6 +430,17 @@ amounts (Mixed as) = as | ||||
| filterMixedAmount :: (Amount -> Bool) -> MixedAmount -> MixedAmount | ||||
| filterMixedAmount p (Mixed as) = Mixed $ filter p as | ||||
| 
 | ||||
| -- | Return an unnormalised MixedAmount containing exactly one Amount | ||||
| -- with the specified commodity and the quantity of that commodity | ||||
| -- found in the original. NB if Amount's quantity is zero it will be | ||||
| -- discarded next time the MixedAmount gets normalised. | ||||
| filterMixedAmountByCommodity :: Commodity -> MixedAmount -> MixedAmount | ||||
| filterMixedAmountByCommodity c (Mixed as) = Mixed as' | ||||
|   where | ||||
|     as' = case filter ((==c) . acommodity) as of | ||||
|             []   -> [nullamt{acommodity=c}] | ||||
|             as'' -> [sum as''] | ||||
| 
 | ||||
| -- | Convert a mixed amount's component amounts to the commodity of their | ||||
| -- assigned price, if any. | ||||
| costOfMixedAmount :: MixedAmount -> MixedAmount | ||||
|  | ||||
| @ -11,9 +11,12 @@ a some base account.  They are used by hledger-web. | ||||
| module Hledger.Reports.TransactionsReports ( | ||||
|   TransactionsReport, | ||||
|   TransactionsReportItem, | ||||
|   triOrigTransaction, | ||||
|   triDate, | ||||
|   triAmount, | ||||
|   triBalance, | ||||
|   triSimpleBalance, | ||||
|   triCommodityAmount, | ||||
|   triCommodityBalance, | ||||
|   journalTransactionsReport, | ||||
|   accountTransactionsReport, | ||||
|   transactionsReportByCommodity | ||||
| @ -51,11 +54,12 @@ type TransactionsReportItem = (Transaction -- the original journal transaction, | ||||
|                               ,MixedAmount -- the running balance for the current account(s) after this transaction | ||||
|                               ) | ||||
| 
 | ||||
| triDate (t,_,_,_,_,_) = tdate t | ||||
| triOrigTransaction (torig,_,_,_,_,_) = torig | ||||
| triDate (_,tacct,_,_,_,_) = tdate tacct | ||||
| triAmount (_,_,_,_,a,_) = a | ||||
| triBalance (_,_,_,_,_,a) = a | ||||
| triSimpleBalance (_,_,_,_,_,Mixed a) = case a of [] -> "0" | ||||
|                                                  (Amount{aquantity=q}):_ -> show q | ||||
| triCommodityAmount c = filterMixedAmountByCommodity c  . triAmount | ||||
| triCommodityBalance c = filterMixedAmountByCommodity c  . triBalance | ||||
| 
 | ||||
| ------------------------------------------------------------------------------- | ||||
| 
 | ||||
| @ -237,9 +241,5 @@ filterTransactionsReportByCommodity c (label,items) = | ||||
|         go bal ((t,t2,s,o,amt,_):is) = (t,t2,s,o,amt,bal'):go bal' is | ||||
|           where bal' = bal + amt | ||||
| 
 | ||||
| -- | Filter out all but the specified commodity from this amount. | ||||
| filterMixedAmountByCommodity :: Commodity -> MixedAmount -> MixedAmount | ||||
| filterMixedAmountByCommodity c (Mixed as) = Mixed $ filter ((==c). acommodity) as | ||||
| 
 | ||||
| ------------------------------------------------------------------------------- | ||||
| 
 | ||||
|  | ||||
| @ -134,6 +134,7 @@ instance Yesod App where | ||||
|             addScript $ StaticR js_jquery_hotkeys_js | ||||
|             addScript $ StaticR js_jquery_flot_min_js | ||||
|             addScript $ StaticR js_jquery_flot_time_min_js | ||||
|             addScript $ StaticR js_jquery_flot_tooltip_min_js | ||||
|             toWidget [hamlet| \<!--[if lte IE 8]> <script type="text/javascript" src="@{StaticR js_excanvas_min_js}"></script> <![endif]--> |] | ||||
|             addStylesheet $ StaticR hledger_css | ||||
|             addScript $ StaticR hledger_js | ||||
|  | ||||
| @ -4,6 +4,7 @@ module Handler.RegisterR where | ||||
| 
 | ||||
| import Import | ||||
| 
 | ||||
| import Data.List | ||||
| import Data.Maybe | ||||
| import Safe | ||||
| 
 | ||||
| @ -105,31 +106,69 @@ registerChartHtml percommoditytxnreports = | ||||
| <div#register-chart style="width:85%; height:150px; margin-bottom:1em; display:block;"> | ||||
| <script type=text/javascript> | ||||
|  \$(document).ready(function() { | ||||
|    var chartdiv = $('#register-chart'); | ||||
|    if (chartdiv.is(':visible')) { | ||||
|    var $chartdiv = $('#register-chart'); | ||||
|    if ($chartdiv.is(':visible')) { | ||||
|      \$('#register-chart-label').text('#{charttitle}'); | ||||
|      registerChart( | ||||
|        chartdiv, | ||||
|        [ | ||||
|          $forall (comm,(_,items)) <- percommoditytxnreports | ||||
|      var seriesData = [ | ||||
|       $forall (c,(_,items)) <- percommoditytxnreports | ||||
|        /* we render each commodity using two series:  | ||||
|         * one with extra data points added to show a stepped balance line */ | ||||
|        { | ||||
|         data: [ | ||||
|           $forall i <- reverse items | ||||
|               [#{dayToJsTimestamp $ triDate i}, #{triSimpleBalance i}], | ||||
|            [ | ||||
|             #{dayToJsTimestamp $ triDate i}, | ||||
|             #{simpleMixedAmountQuantity $ triCommodityBalance c i}, | ||||
|            ], | ||||
|           /* [] */ | ||||
|         ], | ||||
|            label: '#{comm}', | ||||
|            color: #{colorForCommodity comm}, | ||||
|         label: '#{c}', | ||||
|         color: #{colorForCommodity c}, | ||||
|         lines: { | ||||
|           show: true, | ||||
|           steps: true, | ||||
|         }, | ||||
|         points: {  | ||||
|           show: false, | ||||
|         }, | ||||
|         clickable: false, | ||||
|         hoverable: false, | ||||
|        }, | ||||
|        /* and one with the original data, showing one clickable, hoverable point per transaction */ | ||||
|        { | ||||
|         data: [ | ||||
|           $forall i <- reverse items | ||||
|            [ | ||||
|             #{dayToJsTimestamp $ triDate i}, | ||||
|             #{simpleMixedAmountQuantity $ triCommodityBalance c i}, | ||||
|             '#{show $ triCommodityAmount c i}', | ||||
|             '#{show $ triCommodityBalance c i}', | ||||
|             '#{concat $ intersperse "\\n" $ lines  $ show $ triOrigTransaction i}', | ||||
|            ], | ||||
|           /* [] */ | ||||
|         ], | ||||
|         label: '', | ||||
|         color: '#{colorForCommodity c}', | ||||
|         lines: { | ||||
|           show: false, | ||||
|         }, | ||||
|         points: {  | ||||
|           show: true, | ||||
|           color: '#{colorForCommodity c}', | ||||
|         }, | ||||
|        }, | ||||
|      ] | ||||
|      ); | ||||
|      var plot = registerChart($chartdiv, seriesData); | ||||
|      \$chartdiv.bind("plotclick", registerChartClick); | ||||
|    }; | ||||
|  }); | ||||
| |] | ||||
|            -- [#{dayToJsTimestamp $ ltrace "\ndate" $ triDate i}, #{ltrace "balancequantity" $ simpleMixedAmountQuantity $ triCommodityBalance c i}, '#{ltrace "balance" $ show $ triCommodityBalance c i}, '#{ltrace "amount" $ show $ triCommodityAmount c i}''], | ||||
|  where | ||||
|    charttitle = case maybe "" (fst.snd) $ headMay percommoditytxnreports | ||||
|            of "" -> "" | ||||
|               s  -> s++":" | ||||
|    colorForCommodity = fromMaybe 0 . flip lookup commoditiesIndex | ||||
|    commoditiesIndex = zip (map fst percommoditytxnreports) [0..] :: [(Commodity,Int)] | ||||
|    simpleMixedAmountQuantity = maybe 0 aquantity . headMay . amounts | ||||
| 
 | ||||
|  | ||||
| @ -36,61 +36,93 @@ function registerChart($container, series) { | ||||
|   return $container.plot( | ||||
|     series, | ||||
|     { /* general chart options */ | ||||
|       series: { | ||||
|         points: {  | ||||
|           show: true, | ||||
|         }, | ||||
|         lines: { | ||||
|           show: true, | ||||
|           steps: true, | ||||
|         }, | ||||
|         bars: { | ||||
|           // show: true,
 | ||||
|           // barWidth: 1000 * 60 * 60, // ms
 | ||||
|         }, | ||||
|       }, | ||||
|       yaxis: { | ||||
|         /* mode: "time", */ | ||||
|         /* timeformat: "%y/%m/%d", */ | ||||
|         /* ticks: 6, */ | ||||
|       }, | ||||
|       // series: {
 | ||||
|       // },
 | ||||
|       // yaxis: {
 | ||||
|       //   /* ticks: 6, */
 | ||||
|       // },
 | ||||
|       xaxis: { | ||||
|         mode: "time", | ||||
|         timeformat: "%Y/%m/%d" | ||||
|         /* ticks: 6, */ | ||||
|       }, | ||||
|       grid: { | ||||
|         // clickable: true,
 | ||||
|         // hoverable: true,
 | ||||
|         // autoHighlight: true,
 | ||||
|         markings: | ||||
|          function (axes) { | ||||
|           // console.log(axes);
 | ||||
|           // var markings = [];
 | ||||
|           // for (var x = Math.floor(axes.xaxis.min); x < axes.xaxis.max; x += 2)
 | ||||
|           //   markings.push({ xaxis: { from: x, to: x + 1 } });
 | ||||
|           // midx = Math.floor(axes.xaxis.min + (axes.xaxis.max - axes.xaxis.min) / 2);
 | ||||
|           var now = Date.now(); | ||||
|           var markings = [ | ||||
|             // {
 | ||||
|             //   xaxis: { to: now },        // past
 | ||||
|             //   yaxis: { from: 0, to: 0 }, // =0
 | ||||
|             //  color: '#d88',
 | ||||
|             //  lineWidth:1
 | ||||
|             // },
 | ||||
|             { | ||||
|               xaxis: { from: now, to: now },  | ||||
| 							color: '#888', | ||||
| 							lineWidth:1 | ||||
|               xaxis: { to: now }, // past
 | ||||
|               yaxis: { to: 0 },   // <0
 | ||||
|               color: '#ffdddd', | ||||
|             }, | ||||
| 
 | ||||
|             // {
 | ||||
|             //   xaxis: { from: now, to: now }, // now
 | ||||
|             //  color: '#bbb',
 | ||||
|             // },
 | ||||
| 
 | ||||
|             { | ||||
|               xaxis: { from: now }, // future
 | ||||
|               yaxis: { from: 0 },   // >0
 | ||||
|               // color: '#dddddd',
 | ||||
|               color: '#e0e0e0', | ||||
|             }, | ||||
|             { | ||||
|               yaxis: { from: 0, to: 0 }, | ||||
|               xaxis: { from: now }, // future
 | ||||
|               yaxis: { to: 0 },     // <0
 | ||||
|               // color: '#ddbbbb',
 | ||||
|               color: '#e8c8c8', | ||||
|             }, | ||||
|             { | ||||
|               // xaxis: { from: now },      // future
 | ||||
|               yaxis: { from: 0, to: 0 }, // =0
 | ||||
|               color: '#bb0000', | ||||
|               lineWidth:1 | ||||
|             }, | ||||
|           ]; | ||||
|           // console.log(markings);
 | ||||
|           return markings; | ||||
|         } | ||||
|         }, | ||||
|         hoverable: true, | ||||
|         autoHighlight: true, | ||||
|         clickable: true, | ||||
|       }, | ||||
|       /* https://github.com/krzysu/flot.tooltip */ | ||||
|       tooltip: true, | ||||
|       tooltipOpts: { | ||||
|         xDateFormat: "%Y/%m/%d", | ||||
|         content: | ||||
|           function(label, x, y, flotitem) { | ||||
|             var data = flotitem.series.data[flotitem.dataIndex]; | ||||
|             return data[3]+" balance on %x after "+data[2]+" posted by transaction:<pre>"+data[4]+"</pre>"; | ||||
|           }, | ||||
|         onHover: function(flotitem, $tooltipel) { | ||||
|           $tooltipel.css('border-color',flotitem.series.color); | ||||
|         }, | ||||
|       }, | ||||
|     } | ||||
|   ).data("plot"); | ||||
| } | ||||
| 
 | ||||
| function registerChartClick(ev, pos, item) { | ||||
|   if (item) { | ||||
|     var date = $.plot.dateGenerator(item.datapoint[0], {}); | ||||
|     var dateid = $.plot.formatDate(date, '%Y-%m-%d'); | ||||
|     $target = $('#'+dateid); | ||||
|     if ($target.length) | ||||
|       $('html, body').animate({ | ||||
|         scrollTop: $target.offset().top | ||||
|       }, 1000); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| //----------------------------------------------------------------------
 | ||||
| // ADD FORM
 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user