Biweekly View with DHTMLX Gantt Chart - javascript

In my dhtmlx gantt chart, I've configured my scales to show the year and Quarter (3 PIs per quarter)
gantt.config.scales = [
{ name: 'year', format: '%Y' },
{ name: 'quarter', template: date => `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1 )}` },
]
This gives me the result
| 2022 |
| PI-1 | PI-2 | PI-3 |
I now want to add 2-week increments to my scale to represent sprints per Quarter (6 sprints per quarter, 24 sprints per year)
| 2022 |
| PI-1 | PI-2 | PI-3 |
| S1 | S2 | S3 | S4 | S5 | S6 |
I can't figure out how to structure my template to achieve this. Any thoughts?

It should be noted that if the increment is 2 weeks, then there will be more than 24 sprints in a year. The same can be said about months, one quarter is equal to 3 months, which contain a non-integer number of weeks, so the boundaries of the quarter and sprint scales cannot coincide.
To set sprints in the scale, you need to use Custom time units.
And so I can offer the following solution:
If the increment is to be strictly 2 weeks, first determine the start date of each sprint. It is also necessary to set the start for the first sprint, for example, I set it to start on Monday of the first week of the year:
// specify the start date of the first sprint
gantt.config.project_start = new Date(2022, 0, 3);
// find a sprint for a given date
function findSprint(date) {
const firstSprint = gantt.date.week_start(new Date(gantt.config.project_start));
let currentDate = firstSprint;
let direction = 1;
if (date < firstSprint) {
direction = -1;
}
const increment = 2 * direction;
let nextDate = gantt.date.add(currentDate, increment, "week");
let num = 0;
while (!(currentDate.valueOf() <= date.valueOf() && nextDate.valueOf() > date.valueOf())) {
if (increment > 0) {
currentDate = nextDate;
nextDate = gantt.date.add(currentDate, increment, "week");
} else {
nextDate = currentDate;
currentDate = gantt.date.add(currentDate, increment, "week");
}
num += 1 * direction;
}
return {
sprintStart: currentDate,
sprintEnd: nextDate,
sprintNumber: num
}
}
// custom scale unit definition
gantt.date.sprint_start = function (date) {
return findSprint(date).sprintStart;
};
The next step is to specify that the increment will be two weeks:
gantt.date.add_sprint = function (date, inc) {
return gantt.date.add(gantt.date.sprint_start(date), inc * 2, "week");
};
And finally, add a new unit to the scale:
gantt.config.scales = [
{ unit: "year", step: 1, format: "%Y" },
{
unit: 'quarter',
format: date => {
return `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1)}`
}
},
{ unit: 'sprint', step: 1, template: function (date) {
const sprintInfo = findSprint(date);
return `Sprint ${sprintInfo.sprintNumber + 1}, (${gantt.templates.date_grid(sprintInfo.sprintStart)} - ${gantt.templates.date_grid(new Date(sprintInfo.sprintEnd - 1))})`
}
}
];
Please see an example: https://snippet.dhtmlx.com/15u2bd85.
Sometimes for some reason the borders of the quarter and year scales do not match, and this is most likely a bug, if this happens to you, please contact dhtmlx technical support.
If you want to have 24 sprints in a year, you can set specific dates for each sprint, it might look something like this:
const sprints = [
{ name: 'S1', start_date: new Date(2022,00,01), end_date: new Date(2022,00,15) },
{ name: 'S2', start_date: new Date(2022,00,15), end_date: new Date(2022,01,01) },
{ name: 'S3', start_date: new Date(2022,01,01), end_date: new Date(2022,01,15) },
{ name: 'S4', start_date: new Date(2022,01,15), end_date: new Date(2022,02,01) },
{ name: 'S5', start_date: new Date(2022,02,01), end_date: new Date(2022,02,15) },
{ name: 'S6', start_date: new Date(2022,02,15), end_date: new Date(2022,03,01) },
...
];
You need to add the given sprints to the unit:
gantt.date.sprints_start = function(date) {
return date;
};
function getSprint(date,type) {
const tempDate = new Date(date);
for (let i = 0; i < sprints.length; i++) {
if (+tempDate >= +sprints[i].start_date && +tempDate < +sprints[i].end_date) {
if (type == 'scaleUnit') {
return sprints[i].end_date;
}
if (type == 'template') {
return "<div class='sprint'>"+sprints[i].name+"</div>";
}
}
}
if (type == 'scaleUnit') {
const newDate = gantt.date.add(date, 1,'day');
return newDate;
}
if (type == 'template') {
return gantt.date.date_to_str("%m-%d")(date);
}
}
gantt.date.add_sprints = function(date, inc) {
return getSprint(date, 'scaleUnit');
};
const sprintsTemplate = function(date) {
return getSprint(date,'template');
}
and also add a new unit to the scale:
gantt.config.scales = [
{ unit: "year", step: 1, format: "%Y" },
{
unit: 'quarter',
format: date => {
return `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1 )}`
}
},
{ unit: 'month', step: 1, format: "%M" },
{ unit: 'sprints', step: 1, template: sprintsTemplate },
];
Here is an example: https://snippet.dhtmlx.com/0xznw5m9

Related

How to add the values ​in an array of objects depending on the date value of each object

I have this array:
[{start_date: "2022-12-05T04:00:00Z" ,distance: 1000, time: 3600}
,{start_date: "2022-02-07T04:00:00Z" ,distance: 1500, time: 6400},
{start_date: "2022-12-08T04:00:00Z" ,distance: 1000, time: 1300}]
I want to add the distance and time values ​​grouping them by the month indicated by the start_date value. For example, if two start_dates have the same month 2022-12-01 and 2022-12-08, how can I add the distance and time values ​​of those two months?
so i get a new array like this:
[{month: 12 ,total distance: 2000, total time: 4900},
{month: 02 , total distance: 1500, total time: 6400} ]
you can use reduce to group them by month which will give an object like
{
12: {
distance: 2000,
month: 12,
time: 4900
},
2: {
distance: 1500,
month: 2,
time: 6400
}
}
and using Object.values get the values array of it
let x = [{start_date: "2022-12-05T04:00:00Z" ,distance: 1000, time: 3600},{start_date: "2022-02-07T04:00:00Z" ,distance: 1500, time: 6400},{start_date: "2022-12-08T04:00:00Z" ,distance: 1000, time: 1300}]
let res = Object.values(x.reduce((acc,{start_date,distance,time})=> {
let month = new Date(start_date).getMonth()+1
if(!acc[month])acc[month] = {totalDistance:0,totalTime:0,month:month}
acc[month].totalDistance+=distance
acc[month].totalTime+=time
return acc;
},{}))
console.log(res)
You can use a object as a dictionary and save a accumulated value of time and distance per month key. Then, reduce all keys to an array with the requested format.
const groupPerMonth = (list) => {
const extractMonth = (stringDate) => {
const month = new Date(stringDate).getMonth() + 1;
return month < 10 ? `0${month}` : `${month}`;
}
const months = {};
for (const item of list) {
const month = extractMonth(item.start_date);
if (!(month in months)) {
months[month] = {
distance: 0,
total_time: 0,
};
}
months[month].distance += item.distance;
months[month].total_time += item.time;
}
const result = [];
for (const month in months) {
result.push({
month,
...months[month]
});
}
return result;
};
And test it:
console.log(
groupPerMonth([
{ start_date: "2022-12-05T04:00:00Z", distance: 1000, time: 3600 },
{ start_date: "2022-02-07T04:00:00Z", distance: 1500, time: 6400 },
{ start_date: "2022-12-08T04:00:00Z", distance: 1000, time: 1300 },
])
);
Output:
[
{ month: '12', distance: 2000, total_time: 4900 },
{ month: '02', distance: 1500, total_time: 6400 }
]
There might be different solutions to this, but one way to solve this is by using the lodash library to solve this. We can first group by month, followed by mapping each grouped item and adding the distance and time values in each group using reduce:
const list = [
{start_date: "2022-12-05T04:00:00Z" ,distance: 1000, time: 3600},
{start_date: "2022-02-07T04:00:00Z" ,distance: 1500, time: 6400},
{start_date: "2022-12-08T04:00:00Z" ,distance: 1000, time: 1300}
]
const grouped = _.groupBy(list, item => {
const date = new Date(item.start_date)
return date.getMonth() + 1
})
const groupedAndMapped = _.map(grouped, function(groupedList, date){
return {
month: date,
total_distance: _.reduce(groupedList, (total, current) => {
return total + current.distance
}, 0),
total_time:_.reduce(groupedList, (total, current) => {
return total + current.time
}, 0)
}
})
One improvement you could do is to format the month to an "MM-YYYY" format or something similar as its possible your data set can include items with different years.

How to get time difference from ZoneDateTime in javascript

How can I get the time difference in seconds between two date-times one is from the backend in the form of ZoneDateTime and the other will be the current time at the frontend while response get received.
Response time from backend is -
callStartTime:{
chronology: {id: 'ISO', calendarType: 'iso8601'}
dayOfMonth: 8
dayOfWeek: "TUESDAY"
dayOfYear: 39
hour: 16
minute: 15
month: "FEBRUARY"
monthValue: 2
nano: 240000000
offset: {totalSeconds: 19800, id: '+05:30', rules: {…}}
second: 29
year: 2022
zone: {id: 'Asia/Calcutta', rules: {…}}
From this How can I find the difference between current time and this time in seconds ?
Calculate the equivalent ECMAScript time value from the data. Subtract that from the current time value and you have the difference in milliseconds, e.g.
function getTimeDiff(data, date = new Date()) {
let utc = new Date(Date.UTC(
data.year,
data.monthValue - 1,
data.dayOfMonth,
data.hour,
data.minute,
data.second
) - data.offset.totalSeconds*1e3);
return date - utc;
}
function msToTime(ms) {
let days = ms / 8.64e7 | 0;
let hrs = (ms%8.64e7) / 3.6e6 | 0;
let mins = (ms%3.6e6) / 6e4 | 0;
let secs = (ms%6e4) / 1e3 | 0;
let z = n => (n<10? '0':'') + n;
return `${days? days + ' day' + (days == 1? ' ' : 's ') : ''}${z(hrs)}:` +
`${z(mins)}:${z(secs)}`;
}
let callStartTime = {
chronology: {id: 'ISO', calendarType: 'iso8601'},
dayOfMonth: 8,
dayOfWeek: "TUESDAY",
dayOfYear: 39,
hour: 16,
minute: 15,
month: "FEBRUARY",
monthValue: 2,
nano: 240000000,
offset: {totalSeconds: 19800, id: '+05:30', rules: {}},
second: 29,
year: 2022,
zone: {id: 'Asia/Calcutta', rules: {}}
};
['2022-02-08T16:15:29+05:30', // 0
null // now
].forEach(ts => {
let data = callStartTime;
let compDate = ts? new Date(ts) : new Date();
let diffMs = getTimeDiff(data, compDate);
console.log(`${ts || compDate.toISOString()} : ` +
`${diffMs} ms (${msToTime(diffMs)})`
);
});

Reduce list of value / date array to last x days and add missing days

I have the following JSON and I would like to get an array only including the last x days, and fill the gaps (days without any values) with the days and completed and started as 0:
[
{
"date":"2019-04-14T22:00:00.000Z",
"completed":0,
"started":6
},
{
"date":"2019-04-18T22:00:00.000Z",
"completed":3,
"started":1
},
{
"date":"2019-05-19T22:00:00.000Z",
"completed":0,
"started":1
},
{
"date":"2019-09-16T22:00:00.000Z",
"completed":1,
"started":1
},
{
"date":"2019-08-31T22:00:00.000Z",
"completed":0,
"started":2
},
{
"date":"2019-08-10T22:00:00.000Z",
"completed":0,
"started":1
},
{
"date":"2019-06-20T22:00:00.000Z",
"completed":0,
"started":1
},
{
"date":"2019-07-28T22:00:00.000Z",
"completed":0,
"started":4
},
{
"date":"2019-07-30T22:00:00.000Z",
"completed":0,
"started":1
},
{
"date":"2019-07-16T22:00:00.000Z",
"completed":0,
"started":1
},
{
"date":"2019-08-27T22:00:00.000Z",
"completed":0,
"started":1
}
]
I don't find a way to go on. I was inititially coming from an object where I had several entries for the same date and managed to do this consolidation, but now I am lost. Any ideas? I thought about filter or reduce but I don't know how to apply them.
Thanks!
Desired output:
[
{
"date": ((today - 7 days)),
"completed": ...,
"started": ...
},
{
"date": ((today - 6 days)),
"completed": ...,
"started": ...
},
{
"date": ((today - 5 days)),
"completed": ...,
"started": ...
}
]
Where ((today - x days)) is the date of the last 7 days and "completed" and started" contain the values from above or if not available each 0.
You can use filter to keep only the entries from the last x days (I'm using x in this code as a variable to represent that number of days):
const lastXDays = data.filter(entry => {
const daysSince = (Date.now() - (new Date(entry.date)).getTime())/1000/60/60/24;
return daysSince <= x;
});
Then to fill in the extra days, since these timestamps are all at the same time of day (22:00:00), you can sort by date and just add 1 day's worth of milliseconds to the minimum until you reach the maximum, inserting when there's no match to the currently checked time:
const sortedDays = lastXDays.sort((a,b) => a.date.getTime() - b.date.getTime());
const maxTime = sortedDays[sortedDays.length - 1].date.getTime();
const minTime = sortedDays[0].date.getTime();
const output = [];
for (let timeToCheck = minTime; timeToCheck <= maxtime; timeToCheck += 1000 * 60 * 60 * 24) {
const existing = sortedDays.find(day => day.date.getTime() === timeToCheck);
if (existing) {
output.push(existing);
} else {
output.push({
date: new Date(timeToCheck),
completed: 0,
started: 0
});
}
}

Function to filter JSON Object Javascript/Node.js

I have an array of JSON Objects like this:
[{
id: "1"
times:{
start: '2018-05-09T06:05:28.144Z',
end: '2018-05-09T06:10:21.564Z'
},
},
{
id: "2"
times:{
start: '2018-06-09T06:10:25.144Z',
end: '2018-06-09T06:20:20.564Z'
},
}]
I want to write a function say that will return only the objects that the total minutes between "start" and "end" timestamps are in a specific range.
example: return all objects that the total minutes between "start" and "end" timestamps is in the range 3 to 5 minutes.
Thanks in advance for your Help.
This should be it :
var timeArray = [{
id: "1",
times: {
start: '2018-05-09T06:06:21.564Z',
end: '2018-05-09T06:10:21.564Z'
}
},
{
id: "2",
times: {
start: '2018-06-09T06:10:25.144Z',
end: '2018-06-09T06:20:20.564Z'
}
}
]
function inRange(arr) {
const max = 5 // 5 minutes
const min = 3 // 3 minutes
const filteredArray = arr.filter((obj) => {
const range = (new Date(obj.times.end) - new Date(obj.times.start)) / 60000 // in minutes
return range > min && range < max ? obj : ''
})
return filteredArray
}
console.log(inRange(timeArray))

how to split the array list in separate array by day wise using javascript

how to split the array list by days
const days = [
{'name':'Mon','value':1},
{'name':'Tue','value':5},
{'name':'Wed','value':10},
{'name':'Wed','value':30},
{'name':'Fri','value':18},
{'name':'Sat','value':80},
{'name':'Sat','value':90},
{'name':'Sun','value':20},
]
I having the above array list by days i wed and Sat i am having two values for thu I am not having values. i need to split the repeated key values into separate array if day are not in the list i need to add zero value for that for example my out put will be
const result = [
[1,5,10,0,18,80,20],
[0,0,30,0,0,90,0]
]
I need like this result is it possible to do in javascript.
You can do this using native javascript.
The algorithm is very simple:
For each day in daysArray you should search it in your given array and just remove first occurence from days.
Do step 1 until days is empty. With the other words, execute step 1 until days.length == 0 condition is satisfied.
let days = [ {'name':'Mon','value':1}, {'name':'Tue','value':5}, {'name':'Wed','value':10}, {'name':'Wed','value':30}, {'name':'Fri','value':18}, {'name':'Sat','value':80}, {'name':'Sat','value':90}, {'name':'Sun','value':20} ], daysArray = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
let result = [];
while(days.length){
sub_array = [];
daysArray.forEach(function(item){
let index = days.findIndex(a => a.name == item);
if(index == -1)
sub_array.push(0);
else{
sub_array.push(days[index].value);
days.splice(index, 1);
}
});
result.push(sub_array);
}
console.log(result);
Add an array of days in the order that you want - daysList.
Group the day objects into a Map using the name as key - daysMap.
Reduce the daysList, and get the days objects from the map by the current day. Iterate the array of days with Array.forEach(), and for each add a week array filled with 0s if missing, and assign the day's value to the day index di.
const daysList = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
const days = [{"name":"Mon","value":1},{"name":"Tue","value":5},{"name":"Wed","value":10},{"name":"Wed","value":30},{"name":"Fri","value":18},{"name":"Sat","value":80},{"name":"Sat","value":90},{"name":"Sun","value":20}]
// group the days by name into a map
const daysMap = days.reduce((m, o) => {
m.has(o.name) || m.set(o.name, [])
m.get(o.name).push(o)
return m
}, new Map)
// iterate the daysList
const result = daysList.reduce((r, d, di) => {
//get the array of the days with the same name from the group
const daysObjs = daysMap.get(d) || []
//iterate the daysObjs array
daysObjs.forEach(({ name, value }, wi) => {
// add a new week array filled with 0 if the row is missing
r[wi] || r.push(new Array(daysList.length).fill(0))
// assign the day value to the week array
r[wi][di] = value
})
return r
}, [])
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
You could take an object for the day indices and for the ouiter indixec which is incremented by every inset of data.
var days = [{ name: 'Mon', value: 1 }, { name: 'Tue', value: 5 }, { name: 'Wed', value: 10 }, { name: 'Wed', value: 30 }, { name: 'Fri', value: 18 }, { name: 'Sat', value: 80 }, { name: 'Sat', value: 90 }, { name: 'Sun', value: 20 }],
indices = { Mon: { day: 0, index: 0 }, Tue: { day: 1, index: 0 }, Wed: { day: 2, index: 0 }, Thu: { day: 3, index: 0 }, Fri: { day: 4, index: 0 }, Sat: { day: 5, index: 0 }, Sun: { day: 6, index: 0 } },
result = days.reduce((r, { name, value }) => {
r[indices[name].index] = r[indices[name].index] || Array.from({ length: 7 }).fill(0);
r[indices[name].index++][indices[name].day] = value;
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use array#reduce and a lookup object of day and create an array of value based on a given day.
const days = [{'name':'Mon','value':1}, {'name':'Tue','value':5}, {'name':'Wed','value':10}, {'name':'Wed','value':30}, {'name':'Fri','value':18}, {'name':'Sat','value':80}, {'name':'Sat','value':90}, {'name':'Sun','value':20}, ],
day = {'Mon':0, 'Tue':1, 'Wed':2, 'Thu': 3, 'Fri': 4, 'Sat': 5, 'Sun': 6},
result = days.reduce((r,o) => {
var index = 0;
if(r[index][day[o.name]]) {
while(r[index] && r[index][day[o.name]]) {
index++;
}
if(!r[index]) {
r[index] = Array.from({length: 7}, _=> 0);
}
}
r[index][day[o.name]] = o.value;
return r;
},[Array.from({length: 7}, _=> 0)]);
console.log(result);
another solution:
const res = _.chain(days)
.map('name') // get days name
.uniq() // uniq days name
.map(day => _.filter(days, { name: day })) // iterate days name and get all day items
.map(sameDays => _.map(sameDays, 'value')) // get value for each day items
.map(vs => [vs[0], vs[1] || 0]) // set 0 value if only one item for day
.thru(_.spread(_.zip)) // transpose the array
.value()

Categories

Resources