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,
|
mixed,
|
||||||
amounts,
|
amounts,
|
||||||
filterMixedAmount,
|
filterMixedAmount,
|
||||||
|
filterMixedAmountByCommodity,
|
||||||
normaliseMixedAmountPreservingFirstPrice,
|
normaliseMixedAmountPreservingFirstPrice,
|
||||||
normaliseMixedAmountPreservingPrices,
|
normaliseMixedAmountPreservingPrices,
|
||||||
-- ** arithmetic
|
-- ** arithmetic
|
||||||
@ -429,6 +430,17 @@ amounts (Mixed as) = as
|
|||||||
filterMixedAmount :: (Amount -> Bool) -> MixedAmount -> MixedAmount
|
filterMixedAmount :: (Amount -> Bool) -> MixedAmount -> MixedAmount
|
||||||
filterMixedAmount p (Mixed as) = Mixed $ filter p as
|
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
|
-- | Convert a mixed amount's component amounts to the commodity of their
|
||||||
-- assigned price, if any.
|
-- assigned price, if any.
|
||||||
costOfMixedAmount :: MixedAmount -> MixedAmount
|
costOfMixedAmount :: MixedAmount -> MixedAmount
|
||||||
|
|||||||
@ -11,9 +11,12 @@ a some base account. They are used by hledger-web.
|
|||||||
module Hledger.Reports.TransactionsReports (
|
module Hledger.Reports.TransactionsReports (
|
||||||
TransactionsReport,
|
TransactionsReport,
|
||||||
TransactionsReportItem,
|
TransactionsReportItem,
|
||||||
|
triOrigTransaction,
|
||||||
triDate,
|
triDate,
|
||||||
|
triAmount,
|
||||||
triBalance,
|
triBalance,
|
||||||
triSimpleBalance,
|
triCommodityAmount,
|
||||||
|
triCommodityBalance,
|
||||||
journalTransactionsReport,
|
journalTransactionsReport,
|
||||||
accountTransactionsReport,
|
accountTransactionsReport,
|
||||||
transactionsReportByCommodity
|
transactionsReportByCommodity
|
||||||
@ -51,11 +54,12 @@ type TransactionsReportItem = (Transaction -- the original journal transaction,
|
|||||||
,MixedAmount -- the running balance for the current account(s) after this 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
|
triAmount (_,_,_,_,a,_) = a
|
||||||
triBalance (_,_,_,_,_,a) = a
|
triBalance (_,_,_,_,_,a) = a
|
||||||
triSimpleBalance (_,_,_,_,_,Mixed a) = case a of [] -> "0"
|
triCommodityAmount c = filterMixedAmountByCommodity c . triAmount
|
||||||
(Amount{aquantity=q}):_ -> show q
|
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
|
go bal ((t,t2,s,o,amt,_):is) = (t,t2,s,o,amt,bal'):go bal' is
|
||||||
where bal' = bal + amt
|
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_hotkeys_js
|
||||||
addScript $ StaticR js_jquery_flot_min_js
|
addScript $ StaticR js_jquery_flot_min_js
|
||||||
addScript $ StaticR js_jquery_flot_time_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]--> |]
|
toWidget [hamlet| \<!--[if lte IE 8]> <script type="text/javascript" src="@{StaticR js_excanvas_min_js}"></script> <![endif]--> |]
|
||||||
addStylesheet $ StaticR hledger_css
|
addStylesheet $ StaticR hledger_css
|
||||||
addScript $ StaticR hledger_js
|
addScript $ StaticR hledger_js
|
||||||
|
|||||||
@ -4,6 +4,7 @@ module Handler.RegisterR where
|
|||||||
|
|
||||||
import Import
|
import Import
|
||||||
|
|
||||||
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Safe
|
import Safe
|
||||||
|
|
||||||
@ -105,31 +106,69 @@ registerChartHtml percommoditytxnreports =
|
|||||||
<div#register-chart style="width:85%; height:150px; margin-bottom:1em; display:block;">
|
<div#register-chart style="width:85%; height:150px; margin-bottom:1em; display:block;">
|
||||||
<script type=text/javascript>
|
<script type=text/javascript>
|
||||||
\$(document).ready(function() {
|
\$(document).ready(function() {
|
||||||
var chartdiv = $('#register-chart');
|
var $chartdiv = $('#register-chart');
|
||||||
if (chartdiv.is(':visible')) {
|
if ($chartdiv.is(':visible')) {
|
||||||
\$('#register-chart-label').text('#{charttitle}');
|
\$('#register-chart-label').text('#{charttitle}');
|
||||||
registerChart(
|
var seriesData = [
|
||||||
chartdiv,
|
$forall (c,(_,items)) <- percommoditytxnreports
|
||||||
[
|
/* we render each commodity using two series:
|
||||||
$forall (comm,(_,items)) <- percommoditytxnreports
|
* one with extra data points added to show a stepped balance line */
|
||||||
{
|
{
|
||||||
data: [
|
data: [
|
||||||
$forall i <- reverse items
|
$forall i <- reverse items
|
||||||
[#{dayToJsTimestamp $ triDate i}, #{triSimpleBalance i}],
|
[
|
||||||
|
#{dayToJsTimestamp $ triDate i},
|
||||||
|
#{simpleMixedAmountQuantity $ triCommodityBalance c i},
|
||||||
|
],
|
||||||
/* [] */
|
/* [] */
|
||||||
],
|
],
|
||||||
label: '#{comm}',
|
label: '#{c}',
|
||||||
color: #{colorForCommodity comm},
|
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
|
where
|
||||||
charttitle = case maybe "" (fst.snd) $ headMay percommoditytxnreports
|
charttitle = case maybe "" (fst.snd) $ headMay percommoditytxnreports
|
||||||
of "" -> ""
|
of "" -> ""
|
||||||
s -> s++":"
|
s -> s++":"
|
||||||
colorForCommodity = fromMaybe 0 . flip lookup commoditiesIndex
|
colorForCommodity = fromMaybe 0 . flip lookup commoditiesIndex
|
||||||
commoditiesIndex = zip (map fst percommoditytxnreports) [0..] :: [(Commodity,Int)]
|
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(
|
return $container.plot(
|
||||||
series,
|
series,
|
||||||
{ /* general chart options */
|
{ /* general chart options */
|
||||||
series: {
|
// series: {
|
||||||
points: {
|
// },
|
||||||
show: true,
|
// yaxis: {
|
||||||
},
|
// /* ticks: 6, */
|
||||||
lines: {
|
// },
|
||||||
show: true,
|
|
||||||
steps: true,
|
|
||||||
},
|
|
||||||
bars: {
|
|
||||||
// show: true,
|
|
||||||
// barWidth: 1000 * 60 * 60, // ms
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
/* mode: "time", */
|
|
||||||
/* timeformat: "%y/%m/%d", */
|
|
||||||
/* ticks: 6, */
|
|
||||||
},
|
|
||||||
xaxis: {
|
xaxis: {
|
||||||
mode: "time",
|
mode: "time",
|
||||||
timeformat: "%Y/%m/%d"
|
timeformat: "%Y/%m/%d"
|
||||||
/* ticks: 6, */
|
/* ticks: 6, */
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
// clickable: true,
|
|
||||||
// hoverable: true,
|
|
||||||
// autoHighlight: true,
|
|
||||||
markings:
|
markings:
|
||||||
function (axes) {
|
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 now = Date.now();
|
||||||
var markings = [
|
var markings = [
|
||||||
|
// {
|
||||||
|
// xaxis: { to: now }, // past
|
||||||
|
// yaxis: { from: 0, to: 0 }, // =0
|
||||||
|
// color: '#d88',
|
||||||
|
// lineWidth:1
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
xaxis: { from: now, to: now },
|
xaxis: { to: now }, // past
|
||||||
color: '#888',
|
yaxis: { to: 0 }, // <0
|
||||||
lineWidth:1
|
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',
|
color: '#bb0000',
|
||||||
lineWidth:1
|
lineWidth:1
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
// console.log(markings);
|
// console.log(markings);
|
||||||
return 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");
|
).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
|
// ADD FORM
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user