Skip to content

Instantly share code, notes, and snippets.

@julianh2o
Forked from pplante/README.md
Last active April 13, 2017 00:26
Show Gist options
  • Save julianh2o/619b8620ecb11e890caaa134813fcb64 to your computer and use it in GitHub Desktop.
Save julianh2o/619b8620ecb11e890caaa134813fcb64 to your computer and use it in GitHub Desktop.
Rentlytics - Lease Visualizer (Julian Hartline)

Rentlytics Exercise - Lease Visualizer

Using the dataset below, build a lease history gantt chart. The X-Axis should be time, with the Y-Axis representing unique "unitName" values. On the gantt chart, each lease is represented by a rectangle, in the row corresponding to the unit. The graph should also highlight the month with the highest aggregated rent.

Please fork this CodePen to submit your result, you may use any HTML/CSS/JS preprocessors or framework you wish.

DO NOT USE A CHARTING/GRAPHING LIBRARY (excluding D3).

Implementation Notes (Julian Hartline)

  • For convenience and elegance, I'm using $.getJSON which requires that this code is run using an http server and cannot simply be loaded with file://
  • Much of this code was adapted from the Gantt Chart example from D3
  • While working on this, I realized that the example above uses an extension to D3 which may or may not be considered legal for the purposes of this example
  • Partway through using this library, I (of course) realized that it lacked a feature I needed, so I forked it to my own Github repository and included it in the GIST through Rawgit
  • While implementing the "highest aggregated rent" feature, I made a few observations that lead to slight changes in implementation
    • First of all, leases don't necessarily begin on the first of the month, so highlighting the month with the highest aggregated rent doesn't really tell the whole story. Instead, I've implemented a date range during which the total income from current leases is highest. This is not constrained to a month boundary. (though with the given data, it still effectively highlights a month)
    • Second, the instructions recommend highlighting the month with the highest aggregated rent, but it's very possible (likely even) that more than one month will have the same aggregate rent. This can happen in both a jointed and disjointed fashion. The implementation I went with finds the maximum amount of rent money being earned at a given time and then highlights all ranges that equal that income.
[
{
"unitName": "A101",
"beginTimestamp": 1328256000000,
"endTimestamp": 1359978400000,
"rent": 1200
},
{
"unitName": "B201",
"beginTimestamp": 1298966400000,
"endTimestamp": 1398966400000,
"rent": 1300
},
{
"unitName": "A301",
"beginTimestamp": 1275721200000,
"endTimestamp": 1298966400000,
"rent": 1500
},
{
"unitName": "A101",
"beginTimestamp": 1298966400000,
"endTimestamp": 1310664000000,
"rent": 1100
},
{
"unitName": "A301",
"beginTimestamp": 1357878400000,
"endTimestamp": 1369878400000,
"rent": 2000
}
]
<!DOCTYPE html>
<html>
<head>
<title>Rentlytics Exercise - Lease Visualizer</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js" integrity="sha256-jCRPoAgIIooCTnLmaSyKMPrFgFh6/T0e8c3i+KkZZ6U=" crossorigin="anonymous"></script>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="https://rawgit.com/julianh2o/Gantt-Chart/master/gantt-chart-d3.js"></script>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
function findBestRentalPeriods(data) {
var events = {};
_.each(data,function(o) { //this creates an object keyed by timestamps with the rental data for each timestamp stored as the value
var start = events[o.startDate.getTime()] || [];
var end = events[o.endDate.getTime()] || [];
start.push(o);
end.push(o);
events[o.startDate.getTime()] = start;
events[o.endDate.getTime()] = end;
});
var timestamps = _.keys(events).sort();
var incomeAfterTimestamp = [];
var currentIncome = 0;
_.each(timestamps,function(ts,i) { //now we can iterate through all of the events to assign a resulting income for each event
_.each(events[ts],function(event) { //we iterate over the events occuring at a given time, subtracting lease ends and adding lease beginnings
if (event.startDate.getTime() == ts) { //this event started at this timestamp
currentIncome += event.rent;
} else if (event.endDate.getTime() == ts) { //this event ended at this timestamp
currentIncome -= event.rent;
}
incomeAfterTimestamp[i] = currentIncome;
});
});
var maxIncome = _.max(incomeAfterTimestamp);
var allBinsWithMaxIncome = _.reject(_.map(incomeAfterTimestamp,function(income,i) { return income == maxIncome ? i : undefined }),_.isUndefined);
var rangesWithMaxIncome = _.map(allBinsWithMaxIncome,function(index) {
return [new Date(parseInt(timestamps[index])),new Date(parseInt(timestamps[index+1]))]; //we shouldnt need to check for the end of the list.. the last item with valid input data will always be 0
});
return rangesWithMaxIncome;
}
function createLeaseGantt(leaseInfoArray) {
var minExtent = null;
var maxExtent = null;
//massage data into the required format and get the extents
var ganttData = _.map(leaseInfoArray,function(tenancy) {
var out = {
"startDate":new Date(tenancy.beginTimestamp),
"endDate":new Date(tenancy.endTimestamp),
"taskName":tenancy.unitName,
"rent":tenancy.rent,
};
//The extents of our gantt chart will be based on the earliest start date and the latest end date
if (!minExtent || out.startDate < minExtent) minExtent = out.startDate;
if (!maxExtent || out.endDate > maxExtent) maxExtent = out.endDate;
return out;
});
var bestRentalPeriods = findBestRentalPeriods(ganttData);
var taskNames = _.uniq(_.map(ganttData,"taskName")).sort();
var tickFormat = "%b '%y";
var gantt = d3.gantt().taskTypes(taskNames).tickFormat(tickFormat).height(500).width(900);
gantt.highlightRanges(bestRentalPeriods);
gantt.timeDomainMode("fixed");
gantt.timeDomain([minExtent,maxExtent]);
gantt(ganttData);
}
$(document).ready(function() {
$.getJSON("./dataset.json",function(data) {
createLeaseGantt(data);
});
});
.bar {
fill: #008FBE;
}
.highlight {
fill: #ED913B;
fill-opacity: .9;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment