Skip to content

Commit

Permalink
Code reorg. Moved HTML to dedicated file.
Browse files Browse the repository at this point in the history
Fixed issue with new windows being blocked as a popup.

Created a bash setup script to setup configuration variables.
  • Loading branch information
Adam Fish committed Mar 29, 2017
1 parent 567f873 commit f732752
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 232 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -10,8 +10,9 @@ node_modules
*.realm.lock
*.realm.management
/realm-object-server
/config/config.js

# Ignore all logfiles and tempfiles.
/log/*.log
/tmp
/.DS_Store
*.DS_Store
47 changes: 46 additions & 1 deletion README.md
@@ -1,2 +1,47 @@
# github-gantt
# Github-Gantt
Generate Gantt Charts From Github Issues!

For organizations, especially open-source teams, that spend their day in Github there is no easy way to visualize the time
table on work outlined in issues. Instead, this requires syncing Github issues with other charting or roadmapping tools,
creating an unnecessary burden to use multiple tools.

This project seeks to simplify this disconnect and bring Gantt charting together with Github issues. The initial issue comment
is parsed for specific search strings to identify the start/end dates, which label to use for bar coloring, and progress. The data is aggregated in a Realm accessed by a Node.js Express server.

## Setup
First you will need to configure the server, run:
```
sh ./setup.sh
```
This will ask for your Github API token, organization name, and repo name. It will then generate a config file at
```
/config/config.js
```
Additional configuration options are available to customize the strings used to search on in the Github issues:
```
// Your Github Access Token
GITHUB_API_TOKEN: ""
// The name of the Github organization
GITHUB_ORG_NAME: ""
// The repo name in the Github organization
GITHUB_REPO_NAME: ""
// Configuration for the labels in Github issues to search for
START_DATE_STRING: "#### 🗓 Start Date:"
DUE_DATE_STRING: "#### 🗓 Expected Date:"
LABEL_STRING: "#### 💪 Team:"
PROGRESS_STRING: "#### 📈 Progress (0-1):"
```
## Start the server
```
node index.js
// Specify a port
PORT=80 node index.js
```

### Additional Work
- [ ] Support editing in the chart itself, passing back date changes to the Github issue
- [ ] Support listing of dependencies to display in the chart
- [ ] UI improvements to show who the task is assigned to
15 changes: 15 additions & 0 deletions config/config-example.js
@@ -0,0 +1,15 @@
module.exports = {
// Your Github Access Token
GITHUB_API_TOKEN: "",

// The name of the Github organization
GITHUB_ORG_NAME: "",
// The repo name in the Github organization
GITHUB_REPO_NAME: "",

// Configuration for the labels in Github issues to search for
START_DATE_STRING: "#### 🗓 Start Date:",
DUE_DATE_STRING: "#### 🗓 Expected Date:",
LABEL_STRING: "#### 💪 Team:",
PROGRESS_STRING: "#### 📈 Progress (0-1):",
}
205 changes: 205 additions & 0 deletions index.html
@@ -0,0 +1,205 @@
<!DOCTYPE html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
</head>
<script src="/static/dhtmlxgantt.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" href="/static/dhtmlxgantt.css" type="text/css" charset="utf-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style type="text/css">
html, body{ height:100%; padding:0px; margin:0px; overflow: hidden;}
.weekend{ background: #BD7990!important; color:white !important;}
.buttonload {
background-color: #4CAF50;
border: none;
color: white;
padding: 12px 24px;
font-size: 16px;
outline:none;
}

.fa {
margin-left: -12px;
margin-right: 8px;
}
</style>
<body onresize="zoomToFit()">
<div style="text-align: right;height: 40px;line-height: 40px;">
<button class="buttonload" onclick="refresh(this)" id="refreshButton">
Refresh
</button>
</div>
<div id="gantt_here" style='width:100%; height:100%;'></div>
<script type="text/javascript">
var HttpClient = function() {
this.get = function(aUrl, aCallback) {
var anHttpRequest = new XMLHttpRequest();
anHttpRequest.onreadystatechange = function() {
if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200)
aCallback(anHttpRequest.responseText);
}

anHttpRequest.open( "GET", aUrl, true );
anHttpRequest.send( null );
}
};
var client = new HttpClient();
gantt.config.xml_date = "%m-%d-%Y";
gantt.config.readonly = true;
gantt.config.grid_width = 400;
gantt.config.columns = [
{name:"text",
label:"Task name",
tree:true,
width:'*' },
];
gantt.attachEvent("onTaskClick", function(id, e) {
var newWin = window.open('', '_blank');
var url = "/getIssueURL?id="+id;
client.get(url, function(response) {
newWin.location.replace(response);
});
});

gantt.config.scale_unit = "week";
gantt.config.date_scale = "%F, %d";

gantt.config.subscales = [
{unit:"month", step:1, date:"%M"}
];
gantt.config.scale_height = 54;

gantt.init("gantt_here");
gantt.load("/data");

refresh(document.getElementById("refreshButton"));

function refresh(toggle) {
toggle.innerHTML = '<i class="fa fa-refresh fa-spin"></i>Working';

client.get("/refreshData", function(response) {
gantt.clearAll();
gantt.load("/data");
toggle.innerText = "Refresh";
});
};

function zoomToFit() {
var project = gantt.getSubtaskDates();
var areaWidth = gantt.$task.offsetWidth;

for (var i = 0; i < scaleConfigs.length; i++) {
var columnCount = getUnitsBetween(project.start_date, project.end_date, scaleConfigs[i].unit, scaleConfigs[i].step);
if ((columnCount + 2) * gantt.config.min_column_width <= areaWidth) {
break;
}
}

if (i == scaleConfigs.length) {
i--;
}
applyConfig(scaleConfigs[i], project);
gantt.render();
};

// Zoom to fit functionality
function applyConfig(config, dates) {
gantt.config.scale_unit = config.scale_unit;
if (config.date_scale) {
gantt.config.date_scale = config.date_scale;
gantt.templates.date_scale = null;
}
else {
gantt.templates.date_scale = config.template;
}

gantt.config.step = config.step;
gantt.config.subscales = config.subscales;

if (dates) {
gantt.config.start_date = gantt.date.add(dates.start_date, -1, config.unit);
gantt.config.end_date = gantt.date.add(gantt.date[config.unit + "_start"](dates.end_date), 2, config.unit);
}
else {
gantt.config.start_date = gantt.config.end_date = null;
}
};

// get number of columns in timeline
function getUnitsBetween(from, to, unit, step) {
var start = new Date(from);
var end = new Date(to);
var units = 0;
while (start.valueOf() < end.valueOf()) {
units++;
start = gantt.date.add(start, step, unit);
}
return units;
};

//Setting available scales
var scaleConfigs = [
// minutes
{ unit: "minute", step: 1, scale_unit: "hour", date_scale: "%H", subscales: [
{unit: "minute", step: 1, date: "%H:%i"}
]
},
// hours
{ unit: "hour", step: 1, scale_unit: "day", date_scale: "%j %M",
subscales: [
{unit: "hour", step: 1, date: "%H:%i"}
]
},
// days
{ unit: "day", step: 1, scale_unit: "month", date_scale: "%F",
subscales: [
{unit: "day", step: 1, date: "%j"}
]
},
// weeks
{unit: "week", step: 1, scale_unit: "month", date_scale: "%F",
subscales: [
{unit: "week", step: 1, template: function (date) {
var dateToStr = gantt.date.date_to_str("%d %M");
var endDate = gantt.date.add(gantt.date.add(date, 1, "week"), -1, "day");
return dateToStr(date) + " - " + dateToStr(endDate);
}}
]},
// months
{ unit: "month", step: 1, scale_unit: "year", date_scale: "%Y",
subscales: [
{unit: "month", step: 1, date: "%M"}
]},
// quarters
{ unit: "month", step: 3, scale_unit: "year", date_scale: "%Y",
subscales: [
{unit: "month", step: 3, template: function (date) {
var dateToStr = gantt.date.date_to_str("%M");
var endDate = gantt.date.add(gantt.date.add(date, 3, "month"), -1, "day");
return dateToStr(date) + " - " + dateToStr(endDate);
}}
]},
// years
{unit: "year", step: 1, scale_unit: "year", date_scale: "%Y",
subscales: [
{unit: "year", step: 5, template: function (date) {
var dateToStr = gantt.date.date_to_str("%Y");
var endDate = gantt.date.add(gantt.date.add(date, 5, "year"), -1, "day");
return dateToStr(date) + " - " + dateToStr(endDate);
}}
]},
// decades
{unit: "year", step: 10, scale_unit: "year", template: function (date) {
var dateToStr = gantt.date.date_to_str("%Y");
var endDate = gantt.date.add(gantt.date.add(date, 10, "year"), -1, "day");
return dateToStr(date) + " - " + dateToStr(endDate);
},
subscales: [
{unit: "year", step: 100, template: function (date) {
var dateToStr = gantt.date.date_to_str("%Y");
var endDate = gantt.date.add(gantt.date.add(date, 100, "year"), -1, "day");
return dateToStr(date) + " - " + dateToStr(endDate);
}}
]}
];
</script>
</body>

0 comments on commit f732752

Please sign in to comment.