Skip to content

Instantly share code, notes, and snippets.

@MayeulC
Created March 22, 2021 08:56
Show Gist options
  • Save MayeulC/e954c16cfead59e2e41732496be710ca to your computer and use it in GitHub Desktop.
Save MayeulC/e954c16cfead59e2e41732496be710ca to your computer and use it in GitHub Desktop.
Kanboard to frappe.io/gantt example
[{"type":"task","id":"281","title":"Simulations 1T1C pour plate-Forme de benchmark","start":[2020,8,26],"end":[2020,9,30],"column_title":"Ready","assignee":null,"progress":"25%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=281","color":{"name":"Teal","background":"#80cbc4","border":"#00695c"},"not_defined":true,"date_started_not_defined":true,"date_due_not_defined":false},{"type":"task","id":"285","title":"D\u00e9but PFE","start":[2018,3,15],"end":[2018,3,15],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=285","color":{"name":"Pink","background":"#f48fb1","border":"#d81b60"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"286","title":"Fin PFE","start":[2018,9,20],"end":[2018,9,20],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=286","color":{"name":"Pink","background":"#f48fb1","border":"#d81b60"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"287","title":"D\u00e9but th\u00e8se","start":[2018,10,1],"end":[2018,10,1],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=287","color":{"name":"Pink","background":"#f48fb1","border":"#d81b60"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"288","title":"CST 1A","start":[2019,7,11],"end":[2019,7,11],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=288","color":{"name":"Pink","background":"#f48fb1","border":"#d81b60"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"194","title":"Lift Prise en main et IPC","start":[2019,10,1],"end":[2019,11,14],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=194","color":{"name":"Blue","background":"rgb(219, 235, 255)","border":"rgb(168, 207, 255)"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"200","title":"Am\u00e9lioration plate-forme simulation","start":[2019,12,23],"end":[2020,1,30],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=200","color":{"name":"Teal","background":"#80cbc4","border":"#00695c"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"195","title":"Design GloFo","start":[2020,1,31],"end":[2020,4,10],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=195","color":{"name":"Yellow","background":"rgb(245, 247, 196)","border":"rgb(223, 227, 45)"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"283","title":"Vacances \u00e9t\u00e9 2020","start":[2020,7,25],"end":[2020,8,23],"column_title":"Done","assignee":null,"progress":"100%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=283","color":{"name":"Brown","background":"#d7ccc8","border":"#4e342e"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"280","title":"Design MAD200v4","start":[2020,9,6],"end":[2020,9,17],"column_title":"Work in progress","assignee":null,"progress":"50%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=280","color":{"name":"Deep Orange","background":"#ffab91","border":"#e64a19"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"289","title":"CST 2A","start":[2020,9,7],"end":[2020,9,7],"column_title":"Work in progress","assignee":null,"progress":"50%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=289","color":{"name":"Pink","background":"#f48fb1","border":"#d81b60"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"196","title":"Carac GloFo","start":[2020,9,30],"end":[2020,10,29],"column_title":"Backlog","assignee":null,"progress":"0%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=196","color":{"name":"Yellow","background":"rgb(245, 247, 196)","border":"rgb(223, 227, 45)"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"199","title":"D\u00e9monstrateur hybride","start":[2020,10,18],"end":[2021,1,16],"column_title":"Backlog","assignee":null,"progress":"0%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=199","color":{"name":"Green","background":"rgb(189, 244, 203)","border":"rgb(74, 227, 113)"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"197","title":"Carac MAD200v3","start":[2021,1,17],"end":[2021,2,15],"column_title":"Backlog","assignee":null,"progress":"0%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=197","color":{"name":"Orange","background":"rgb(255, 215, 179)","border":"rgb(255, 172, 98)"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"198","title":"R\u00e9daction manuscrit th\u00e8se","start":[2021,4,30],"end":[2021,9,30],"column_title":"Backlog","assignee":null,"progress":"0%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=198","color":{"name":"Red","background":"rgb(255, 187, 187)","border":"rgb(255, 151, 151)"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"284","title":"Vacances \u00e9t\u00e9 2021","start":[2021,7,24],"end":[2021,8,22],"column_title":"Backlog","assignee":null,"progress":"0%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=284","color":{"name":"Brown","background":"#d7ccc8","border":"#4e342e"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false},{"type":"task","id":"282","title":"Soutenance","start":[2021,10,17],"end":[2021,10,31],"column_title":"Backlog","assignee":null,"progress":"0%","link":"\/tableau\/?controller=TaskViewController&action=show&project_id=12&task_id=282","color":{"name":"Pink","background":"#f48fb1","border":"#d81b60"},"not_defined":false,"date_started_not_defined":false,"date_due_not_defined":false}]
[{"name": "Lift Prise en main et IPC", "id": "194", "progress": 100, "custom_class": "kanboard-color-Blue", "start": "2019-10-01", "end": "2019-11-14"}, {"name": "Am\u00e9lioration plate-forme simulation", "id": "200", "progress": 100, "custom_class": "kanboard-color-Teal", "start": "2019-12-23", "end": "2020-01-30"}, {"name": "Design GloFo", "id": "195", "progress": 100, "custom_class": "kanboard-color-Yellow", "start": "2020-01-31", "end": "2020-04-10"}, {"name": "Vacances \u00e9t\u00e9 2020", "id": "283", "progress": 100, "custom_class": "kanboard-color-Brown", "start": "2020-07-25", "end": "2020-08-23"}, {"name": "Simulations 1T1C pour plate-Forme de benchmark", "id": "281", "progress": 25, "custom_class": "kanboard-color-Teal", "dependencies": "194,200", "start": "2020-08-26", "end": "2020-09-30"}, {"name": "Design MAD200v4", "id": "280", "progress": 50, "custom_class": "kanboard-color-Deep-Orange", "start": "2020-09-06", "end": "2020-09-17"}, {"name": "Carac GloFo", "id": "196", "progress": 0, "custom_class": "kanboard-color-Yellow", "dependencies": "195", "start": "2020-09-30", "end": "2020-10-29"}, {"name": "D\u00e9monstrateur hybride", "id": "199", "progress": 0, "custom_class": "kanboard-color-Green", "dependencies": "196", "start": "2020-10-18", "end": "2021-01-16"}, {"name": "Carac MAD200v3", "id": "197", "progress": 0, "custom_class": "kanboard-color-Orange", "start": "2021-01-17", "end": "2021-02-15"}, {"name": "R\u00e9daction manuscrit th\u00e8se", "id": "198", "progress": 0, "custom_class": "kanboard-color-Red", "dependencies": "289", "start": "2021-04-30", "end": "2021-09-30"}, {"name": "Vacances \u00e9t\u00e9 2021", "id": "284", "progress": 0, "custom_class": "kanboard-color-Brown", "start": "2021-07-24", "end": "2021-08-22"}, {"name": "Soutenance", "id": "282", "progress": 0, "custom_class": "kanboard-color-Pink", "dependencies": "198", "start": "2021-10-17", "end": "2021-10-31"}]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple Gantt</title>
<style>
body {
font-family: sans-serif;
background: #ccc;
}
.container {
/* width: 80%; */
margin: 0 auto;
}
/* custom class */
.gantt .bar-milestone .bar {
fill: tomato;
}
.btn-group {
display: block;
}
</style>
<link rel="stylesheet" href="dist/frappe-gantt.css" />
<link rel="stylesheet" href="kanboard-gantt-tasks.css" />
<script src="dist/frappe-gantt.js"></script>
</head>
<body>
<div class="container">
<div class="btn-group" role="group" onclick="gantt_chart.change_view_mode(event.target.textContent)">
<!-- <button type="button" class="btn btn-sm btn-light">Quarter Day</button>
<button type="button" class="btn btn-sm btn-light">Half Day</button> !-->
<button type="button" class="btn btn-sm btn-light">Day</button>
<button type="button" class="btn btn-sm btn-light">Week</button>
<button type="button" class="btn btn-sm btn-light active" >Month</button>
<button type="button" class="btn btn-sm btn-light active" >Year</button>
</div>
<div class="gantt-target"></div>
</div>
<script>
fetch("gantt.json")
.then(response => response.json())
.then(json => newgantt(json));
var gantt_chart = [];
function newgantt(tasks) {
gantt_chart = new Gantt(".gantt-target", tasks, {
on_click: function (task) {
console.log(task);
},
on_date_change: function(task, start, end) {
console.log(task, start, end);
},
on_progress_change: function(task, progress) {
console.log(task, progress);
},
on_view_change: function(mode) {
console.log(mode);
},
view_mode: 'Year',
language: 'fr'
});
console.log(gantt_chart);
return gantt_chart;
}
</script>
</body>
</html>
.kanboard-color-Teal .bar {
fill: #80cbc4
}
.kanboard-color-Teal .bar-progress {
fill: #00695c
}
.kanboard-color-Pink .bar {
fill: #f48fb1
}
.kanboard-color-Pink .bar-progress {
fill: #d81b60
}
.kanboard-color-Blue .bar {
fill: rgb(219, 235, 255)
}
.kanboard-color-Blue .bar-progress {
fill: rgb(168, 207, 255)
}
.kanboard-color-Yellow .bar {
fill: rgb(245, 247, 196)
}
.kanboard-color-Yellow .bar-progress {
fill: rgb(223, 227, 45)
}
.kanboard-color-Brown .bar {
fill: #d7ccc8
}
.kanboard-color-Brown .bar-progress {
fill: #4e342e
}
.kanboard-color-Deep-Orange .bar {
fill: #ffab91
}
.kanboard-color-Deep-Orange .bar-progress {
fill: #e64a19
}
.kanboard-color-Green .bar {
fill: rgb(189, 244, 203)
}
.kanboard-color-Green .bar-progress {
fill: rgb(74, 227, 113)
}
.kanboard-color-Orange .bar {
fill: rgb(255, 215, 179)
}
.kanboard-color-Orange .bar-progress {
fill: rgb(255, 172, 98)
}
.kanboard-color-Red .bar {
fill: rgb(255, 187, 187)
}
.kanboard-color-Red .bar-progress {
fill: rgb(255, 151, 151)
}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 26 13:35:44 2020
@author: mcantan
"""
import json
""" Add dependencies missing from input data """
custom_deps = {
#'280': '197', # Carac mad200v3 > design mad200v4
'196': '195', # Carac glofo > design glofo
'282': '198', # Soutenance > manuscrit
'199': '196', # Démonstrateur > Carac Glofo
'281': '194,200', # Sim 1T1C > Amélioration plate-forme, lift
'198': '289', # manuscrit > CST 2A
'289': '288', # CST 2A > CST 1A
'288': '287', # CST 1A > Début thèse
'287': '286', # Début thèse > Fin PFE
'286': '285', # Fin PFE > Début PFE
}
excluded_ids = [
'292', # 4h enseignement
]
milestones = [
'début pfe',
'fin pfe',
'début thèse',
'cst 1a',
'cst 2a']
exclude_milestones = True
def arr_to_date(array):
return f"{array[0]:04}-{array[1]:02}-{array[2]:02}"
""" Load file """
with open("gantt-kanboard-dump.json") as fp:
j = json.load(fp)
backgrounds=dict();
newtasks = []
for jtask in j:
if jtask['id'] in excluded_ids:
continue
task = {
"name": jtask['title'],
"id": jtask['id'],
"progress": int(jtask['progress'].replace('%','')),
}
c = jtask['color'];
c['name'] = c['name'].replace(' ', '-')
task['custom_class'] = "kanboard-color-" + c['name']
backgrounds[task['custom_class']] = [c['background'],c['border']]
if task['id'] in custom_deps:
task['dependencies'] = custom_deps[task['id']]
is_milestone = False
for milestone in milestones:
if milestone in task['name'].lower():
is_milestone = True
task['custom_class']+=',kanboard-milestone'
task['start'] = arr_to_date(jtask['start'])
task['end'] = arr_to_date(jtask['end'])
if exclude_milestones and is_milestone:
continue
newtasks.append(task)
newtasks.sort(key=lambda k: k['start'])
""" Generate CSS for colours """
css = ""
for b in backgrounds:
css = css + """
.%s .bar {
fill: %s
}
.%s .bar-progress {
fill: %s
}
""" % (b, backgrounds[b][0], b, backgrounds[b][1])
""" Save files """
with open('gantt.json','w') as fp:
json.dump(newtasks,fp)
with open('kanboard-gantt-tasks.css','w') as fd:
fd.write(css)
@MayeulC
Copy link
Author

MayeulC commented Mar 22, 2021

How to use:

  1. Clone https://github.com/frappe/gantt. Everything will take place in that folder.
  2. Open page source for kanboard's gantt page, and copy the data-records field into a gantt-kanboard-dump.json file
  3. Download the above kanboardtochart.py python code
  4. Download the above index.html if you don't want to write your own.
  5. Optinally adjust custom_deps in the python code to draw dependency arrows between tasks (left depends on right).
  6. Run kanboardtochart.py. It will generate two few files, which I included here as a reference: kanboard-gantt-tasks.css, gantt.json.
  7. Open index.html in a web browser. For local development, due to CORS policy, I find it simpler to run python -m http.server in the folder.

Screens:

Kanboard

image

Exported version

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment