22 days ago

Building a roadmap in jArchi

Link: https://easandbox.wordpress.com/2023/01/06/building-a-roadmap-in-jarchi/

First, Something Personal…

As some of you know, my main reason for building the EA sandbox to begin with, was primarily because I wanted to share my passion, which is EA. Reading through the posts in the past, they were written for very specific people in very specific situations, and sometimes I almost feel like going back and formalizing some of them – to bring them more fully in line with standards like ArchiMate. There’s always someone telling me that some view could be done better – and I do refine things over time but one of the things I often teach – is to make sure that views are telling a story, and get the story out there first, you can worry about the details later. Sometimes some of my posts may come across a little like i am preaching because sometimes my posts are also a vent for frustrations I am having to deal with. I apologize for that.

Some of you may know, in my long and distant past, I was a programmer, and that’s a passion that never goes away. When working with EA it often takes a long time to see the benefits of the things we do, but with programming its right there, with instant reward. I’ve avoided sharing scripts and being part of different communities because a lot of the scripts I have written could be done better – I know this, but I am not a programmer these days, and I just do not have time to build perfect code. So, this blog is a first – i am going to present some code and if there is sufficient interest in this kind of post, let me know, and I may just do it again. I am a bit nervous that i may get flack for this.

This is one of my first scripts I have written in jArchi – I’ve had it installed at home about two weeks now.

Why did I do this?

I needed a roadmap view – I work in a very complex environment with many projects and dependencies, and showing a roadmap is really important for my stakeholders. I dont like having to do this kind of thing by hand. End to end, this was under 8 hrs of fun coding, and i would trade 8 hours of fun coding, for 1 hr per month of boring work in PowerPoint, or any other tool. Within a year, this script pays for the effort in time saved.

What does it do?

When I execute this script, it creates a view named “YYYY-MM-DD: My Roadmap”. The contents of that view looks like this:

Its iterating through all my work packages in my model, sorting them by start date and if they are in this year puts them on this map. It works by looking for these properties:

What doesn’t it do

Right now, the script is restricted to showing a single year. Thats mostly because of the way I wrote the header code, with a little effort it could be rewritten to be multi year.

It’s also relying on the YYYY-MM-DD time format for sorting. This is a personal preference that just happens to work with string sorting, but I recognize the fact this would have been much better if i worked in a better way with the time functions. For some reason though, it doesn’t seem like javascript functions like .getFullYear() are available for use. It could be something I am doing wrong, but i had real troubles finding good examples of working with dates in Nashorn/jArchi. I didn’t have time to figure it out.

When there are a lot of work package elements this view may be a bit long, as its one work package per line. Later on if i find that I may well come and optimize the code a bit so i can have several on a single line.

The Code

 /*
 * Create Roadmap Sorted
 *
 * This creates a simple roadmap in the current model
 * Owen Richardson 2023-01-06
 * For more info see https://easandbox.wordpress.com/2023/01/06/building-a-roadmap-in-jarchi/
 */

//variables for dates
var todayDate = new Date().toISOString().slice(0, 10); //get todays date formatted YYYY-MM-DD
var yearString="2023"; //i wish i knew how to do a .getFullYear() in jArchi
var startOfYear=new Date(yearString + "-01-01 00:00").getTime();
var endOfYear=new Date(yearString + "-12-31 23:59").getTime();
//variables for graphics
var leftPadding=10; //number of pixels for left padding
var topPadding=10; //number of pixels for top padding
var monthWidth=120; //the size of each month on the roadmap
var oneTick=((endOfYear - startOfYear) / (monthWidth*12)); //its the value for aligning scales
//where we will store our work packages
var elementList = [] ;
// Simple function to take a number and turn it into month name. There are other ways to do this in Javascript,
// but i couldnt find one that worked.
function getMonthName(monthNumber) {
const month = ["January","February","March","April","May","June","July","August","September","October","November","December"];
return month[monthNumber-1];
}
//Sorts an array of objects by the property specified in strKey
function bubbleSortElement(arr,strKey){
    for(let i = 0; i < arr.length; i++){
        for(let j = 0; j < arr.length - i - 1; j++){
            if(arr[j + 1].prop(strKey) < arr[j].prop(strKey)){
                //Swapping
                [arr[j + 1],arr[j]] = [arr[j],arr[j + 1]]
            }
        }
    };
    return arr;
};
//let people know we are doing this in the console
console.log("Creating A roadmap "+todayDate);
//Create the view
var archimateView = model.createArchimateView(todayDate+": My Roadmap");
//add the year
var yearNote=archimateView.createObject("note", leftPadding, topPadding, 100, 30);
yearNote.borderType= BORDER.NONE;
yearNote.fontStyle="bold";
yearNote.setText(yearString);
yearNote.fontSize=14;
//build the scale
for (var i=0;i<12;i++)
{
	//creating the month labels
	var note = archimateView.createObject("note", leftPadding+(i*monthWidth), topPadding+30, monthWidth, 30);
	note.borderType= BORDER.RECTANGLE;
	note.fontStyle="bold";
	note.setText(getMonthName(i+1));
}
//putting all the work package elements from this model into an array, so we can sort them.
$("work-package").each(function(workpackage) {
	elementList.push(workpackage);
	});
//sorting the elementList Array by the property start date. The sort is string based which works because my date format
//is YYYY-MM-DD.
bubbleSortElement(elementList,"Start Date");
var count=0; //count how many elements we have going down.
for (var i=0;i<elementList.length;i++)
{
	//Lazy. I know there are a whole bunch of things that can go wrong with date conversions, and rather than check them
	//all i am putting things in try catch. it either works or it doesnt. If my computer studies teacher wasn't dead already
	//this would kill him.
	try 
 	{
	var wp=elementList[i]; //lazy. shortcutting having to type elementList[i] in the inner code.
	var elementStart=new Date(wp.prop("Start Date")).getTime();
	var elementEnd=new Date(wp.prop("End Date")).getTime();
	if (elementEnd>=startOfYear && elementStart<=endOfYear) 
		{
		if (elementStart < startOfYear) elementStart=startOfYear;
		if (elementEnd > endOfYear) elementEnd=endOfYear;
		var calcX= leftPadding + ((elementStart-startOfYear) / oneTick);
		var calcW= ((elementEnd-elementStart) /oneTick);
		var o=archimateView.add(wp,calcX,topPadding+66+(count*40),calcW,38);
		o.fontSize=8;
		o.textPosition = TEXT_POSITION.CENTER;
		count++;
		}
	}
	catch {}
}

How does it work?

Its reasonably commented, and without going through it line by line, there are some things its good to know. startOfYear & endOfYear effectively are establishing our scale. The getTime() method on a date saves us a lot of headaches that we would have in doing our math, because the calendar isn’t metric and months have different numbers of days. The function turns our date into a single number that can be used for scaling.

The positioning of each work package needs to be aligning to the topbar – we know how many pixels each month is – its set in the variable monthWidth. You can change this safely without breaking the script.

Graphically scaling I have an offset from the left (leftPadding) which is 10 pixels. So in graphical coordinates because I know I am doing 12 months, and my width for each month is 120, i know that my on screen coordinates need to start at 10 and end at 1450. I need to fit the dates of my work packages into that scale.

One pixel has to align with our units of time. to figure that out i created the variable oneTick:

(endOfYear – startOfYear) / (monthWidth*12)

To put this another way: The number of time units / the number of graphics units, gives us our scale. The final parts of that puzzle –

var calcX= leftPadding + ((elementStart-startOfYear) / oneTick);
var calcW= ((elementEnd-elementStart) /oneTick);

calcX is the calculated X position of the work package. calcW is the calculated width, which are needed when we create the objects, when we are doing the archimateView.add on the next line.

Summing it up

There’s some pretty good documentation for jArchi out there, and some really good code examples. I was really surprised how quickly this came together, which is good, because coding isn’t my job – I do this to save myself time!

I would be really happy to hear from anyone who wants to improve this script – when I get time, if the script proves useful I probably will. If anyone is interested in more blogs of this type let me know too. Thanks for your time!