Tag: Extension

Calendar Black Out Dates with Control Extensions

Calendar Black Out Dates with Control Extensions

The OPA Hub Website is always happy to hear from readers and learn about the things they are doing and trying to do. In this case, this article was inspired by our reader AF, from Adelaide. The question was; how can we implement a calendar control, to allow the users to select a date – but the calendar control must be able to black-out certain dates (public holidays, non-work days). It’s the sort of thing we probably can all relate to.

The Calendar control that Oracle Policy Automation uses is a very standard JavaScript dropdown, but there is little in the way of configuration in respect of the dates shown. We can style the calendar and we can offer two different ways to enter dates (either as the traditional calendar or the three-fields-in-a-row style that some applications use).

So it comes down to what can be done with an Extension. Regular reader will remember that we have spoken about calendar controls before, on the subject of Year-Only Selection. So that Extension will be the basis of this article.

Firstly, what are the tools we might need?

  • jQuery
  • JQuery UI, especially datepicker

The datepicker widget from jQuery supports a variety of user-related events, including one called BeforeShowDay, which is where we can come in a specify which days should not be clickable. They remain in the calendar display of course.

The basic concept therefore, for this demonstration is:

  1. The user can select a date from the control. Certain days are not available.
  2. The control must handle both adding a date when the date is not currently entered, as well as when the date is already entered and the user wants to correct it (for example, going back to a previous screen in the interview.
  3. The date must of course be saved to our chosen attribute.

As always this is without any warranty or guarantee of fitness for purpose. It’s a quick demonstration that you can then add to and correct yourselves.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
 * Richard Napier The OPA Hub Website March 2019
 * Educational Example of Custom Date Control Extension
 * I will remember this is for demonstration purposes only
 */
OraclePolicyAutomation.AddExtension({
	customInput: function (control, interview) {
		if (control.getProperty("name") == "xDate") {
			return {
				mount: function (el) {
 
					var myDatePicker = document.createElement('input');
					myDatePicker.setAttribute("id", "dateselect");
					var mySessionDate = interview.getValue("dt_session");
 
 
					el.appendChild(myDatePicker);
 
 
					$('#dateselect').datepicker({
						dateFormat: "yy-mm-dd",
						onSelect: function (dateText) {
							var RecoveredDate = dateText;
 
							interview.setInputValue("dt_circ", RecoveredDate);
 
						},
						beforeShowDay: function (date) {
							var array = ["2019-03-14", "2019-03-15", "2019-03-16"]
							var string = jQuery.datepicker.formatDate('yy-mm-dd', date);
 
							return [array.indexOf(string) == -1]
						}
					});
 
					var mySelectedDate = "";
 
					if (interview.getValue("dt_circ") == null) {
 
						$('#dateselect').datepicker("setDate", mySessionDate);
						var RecoveredDate = mySessionDate;
 
						interview.setInputValue("dt_circ", RecoveredDate);
 
 
					} else {
 
						var myPreviouslySelectedDate = new
							Date(interview.getValue("dt_circ"));
 
 
						var myPreviouslySelectedDateAsDate = new
							Date(myPreviouslySelectedDate);
 
						var myDayPrefix = "";
						var myMonthPrefix = "";
						if (myPreviouslySelectedDateAsDate.getDate() < 10) {
							myDayPrefix = "0"
						}
						if (myPreviouslySelectedDateAsDate.getMonth() < 10) {
							myMonthPrefix = "0"
						}
						var myConvertedYear = myPreviouslySelectedDateAsDate.getFullYear();
						var myConvertedMonth = myPreviouslySelectedDateAsDate.getMonth() + 1;
						var myConvertedDay = myPreviouslySelectedDateAsDate.getDate();
						var myPreviouslySelectedDateOnly =
							myConvertedYear + "-" + myMonthPrefix + (myConvertedMonth) + "-" + myDayPrefix + myConvertedDay;
 
 
						mySelectedDate = myPreviouslySelectedDateOnly;
 
 
						$('#dateselect').datepicker("setDate", mySelectedDate);
 
 
						var RecoveredDate = $('#dateselect').datepicker("getDate");
 
						interview.setInputValue("dt_circ", new Date(RecoveredDate));
					}
				},
				update: function (el) {},
				unmount: function (el) {
					var RecoveredDate = $('#dateselect').datepicker("getDate");
 
					interview.setInputValue("dt_circ", new Date(RecoveredDate));
 
					var myPicker = $('#dateselect');
					myPicker.remove();
 
 
				}
			}
		}
	}
});

Calendar Black Out Dates with Control Extensions – About the Code

The first part of the Mount is basically setting up the jQuery datepicker to be able to hide some days. We also set the date format to the international YYYY-MM-DD that we all know. Of course a more sophisticated approach would check the region and apply the correct format.

Line 22 Sets up the Select Event and Line 29 the BeforeShowDay  Event.

We attempt to grab the value of our user-entered date (dt_circ) and place it in the date control. If that is unknown or null, then we will default to the current date.

When the user selects a valid date, we will of course copy it into the attribute again.
Finally, when the Unmount fires, we will clean up.

The End Result

 

In a later post we will look at using an entity to store the blackout dates. Stay tuned! For the time being the Project is in the OPA Hub Shop.

Dynamic Charts in Container Controls

Dynamic Charts in Container Controls

As many of you know, we try to provide JavaScript Extension examples for you to experiment with in your own Projects. A while ago, we looked at an interesting question about using Dynamic Charts in Container Controls – basically to display a chart (which is the sort of thing it was designed for),  but to also include the capability to update the chart live as the data is edited on the same Screen. A quick animation will give you the idea:

The example is an interesting one, on account of it allowing us to talk about the difference between viewing data that exists already, versus using data that has just been created.

To keep it simple, we can think of server-side data and client-side data. Server-side data, for example used in Container Controls, represents the data that has been collected in a previous Screen. It’s already been seen by the determination server.

Client-side data is information that has been entered on the current Screen before the Next button has been clicked. So as far as the determination process is concerned it is “not quite there yet”.

The same concepts exist in the world of JavaScript extensions. For example, consider the difference between these two keys : mount and update from the updated  Container Control example that is available here.

 return {
                mount: function(el) {
                    console.log("Starting customContainer Mount");
                    if (document.readyState == 'complete') {
                        var rows = [];
                        for (i = 0; i < entities.length; i++) {
                            entity = entities[i];
                            if (entity.entityId === "payment") {
                                break;
                            }
                        }
                        rows = entity.instances;
                        var myFlatList = [];
                        var myObject;
                        var width = 300,
                            height = 300,
                            radius = 150,
                            color = d3.scaleOrdinal(d3.schemeCategory10);
                        var size = rows.length;
                        for (i = 1; i < size + 1; i++) {
                            myObject = new Object();
                            myObject.label = control._source.screen.serverState.payment['the payment' + i].payment;
                            myObject.value = control._source.screen.serverState.payment['the payment' + i].amount;
                            myFlatList.push(myObject);
                        }
                        data = myFlatList;
                        var vis = d3.select(el);
                        vis.select('svg').remove();
                        var vis = d3.select(el)
                            .append('svg')
                            .data([data])
                            .attr('width', width)
                            .attr('height', height)
                            .append('g')
                            .attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');
                        var arc = d3.arc().outerRadius(radius)
                            .innerRadius(0);
                        var pie = d3.pie()
                            .value(function(d) {
                                return d.value;
                            });
                        var arcs = vis.selectAll('g.slice')
                            .data(pie)
                            .enter()
                            .append('svg:g')
                            .attr('class', 'slice');
                        arcs.append('svg:path')
                            .attr('fill', function(d, i) {
                                return color(i);
                            })
                            .attr("d", arc);
                        arcs.append('svg:text')
                            .attr('transform', function(d) {
                                d.innerRadius = 0;
                                d.outerRadius = radius;
                                return "translate(" + arc.centroid(d) + ")";
                            })
                            .attr('text-anchor', 'middle')
                            .text(function(d, i) {
                                return data[i].label;
                            });
                        console.log("Ending customContainer Update");
                    }
                    console.log("Ending customContainer Mount");
                },
                update: function(el) {
                    console.log("Starting customContainer Update");
                    var myFlatList = [];
                    var myObject;
                    entity = control._source.publicInterface._source.screen.clientState.payment;
                    var rows = [];
                    rows = entity;
                    for (var key in entity) {
                        if (!rows.hasOwnProperty(key)) continue;
                        var obj = rows[key];
                        for (var prop in obj) {
                            if (!obj.hasOwnProperty(prop)) continue;
                            myObject = new Object();
                            myObject.label = obj.payment;
                            myObject.value = obj.amount;
                        }
                        if (myObject.label != "") {
                            myFlatList.push(myObject);
                        }
                    }
                    var width = 300,
                        height = 300,
                        radius = 150,
                        color = d3.scaleOrdinal(d3.schemeCategory10);
                    data = myFlatList;
                    var vis = d3.select(el);
                    vis.select('svg').remove();
                    var vis = d3.select(el)
                        .append('svg')
                        .data([data])
                        .attr('width', width)
                        .attr('height', height)
                        .append('g')
                        .attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');
                    var arc = d3.arc().outerRadius(radius)
                        .innerRadius(0);
                    var pie = d3.pie()
                        .value(function(d) {
                            return d.value;
                        });
                    var arcs = vis.selectAll('g.slice')
                        .data(pie)
                        .enter()
                        .append('svg:g')
                        .attr('class', 'slice');
                    arcs.append('svg:path')
                        .attr('fill', function(d, i) {
                            return color(i);
                        })
                        .attr("d", arc);
                    arcs.append('svg:text')
                        .attr('transform', function(d) {
                            d.innerRadius = 0;
                            d.outerRadius = radius;
                            return "translate(" + arc.centroid(d) + ")";
                        })
                        .attr('text-anchor', 'middle')
                        .text(function(d, i) {
                            return data[i].label;
                        });
                    console.log("Ending customContainer Update");
                },

Notice that the mount key uses the server-side data : when we draw the Control, we can only draw what is already available to us. But in the update, we want to take into account the new data entered by the user, so we look for the client-side data. So now we can be more specific : you can access clientstate and serverstate information which can help you work with previously entered or just-entered dynamic information.

Hope you have fun building your Dynamic Charts in Container Controls! We’ve updated the Shop example to include the complete Zip File.

Dynamic Charts in Container Controls Example Image

Extension Code Generators for JavaScript

Extension Code Generators for JavaScript

Over on my other home (https://siebelhub.com) we use and promote the use of Code Generators : because most of the JavaScript people need to create is in fact very structured, because of the underlying extension architecture. You don’t write just any code, you write code the way you have to write it. So how to write Extension Code?

[Update 1 November 2018 : I added fullCustomInput as an option. I also added more explicit logging – so if you use multiple files you can see which is doing what. I also tidied up a few things]

It struck me the other day that this is equally true now for Oracle Policy Modeling – JavaScript extensions have a very well defined structure, and as I said above : you don’t write just any code – you write it the way you have to write it, in order to fit into the underlying architecture of our beloved interview.js.

Since I find myself often repeating the same JavaScript task over and over again – for example I need to create 2 custom Labels for 2 different projects or customers – I do a lot of cutting and pasting. But that often goes wrong as I forget I’ve renamed something or changed a variable somewhere.

So I decided to create a Code Generator – a sort of tool to create templates of code that I can use to create new files. And I decided to use Oracle Policy Automation for the job. Here is the sort of thing I am talking about:

Extension Code

By answering a few questions about the code you want to write, the Project generates a template for you to copy and paste into a new Text File and there you have your code to start writing something interesting. There is a Copy to Clipboard button at the bottom of the code.

In the end, I decided to add a few things to the examples to act as learning tools:

  1. The Input Control has Validate and Update options that help understand the way these Keys actually are supposed to work. Validate throws an error message to the user if the text entered does not match a specific validation rule.
  2. The Update Key shows how update is fired whenever anything on the Screen is updated. Useful for updating your Control in response to other activity on the Screen. As an example, the color of the Input changes each time Update is fired.

The Extension Code Generator also includes the custom Search as templates with the following options:

  1. Search lets you choose from a series of standard searches (Airlines, Railways Stations, etc) as well as allowing you to configure your own REST call and lets you add any Headers you need to make your call.

Here is a video that will hopefully shed some more light on what this Project does (or tries to do)!

This seemed like a fun thing to do in Oracle Policy Automation – if you would like a copy of the (18C) project then please leave a comment and I will tell you all about it’s foibles :).

Detail Pop-up in Oracle Policy Automation

Detail Pop-up in Oracle Policy Automation

The other day someone came to me with a curious and interesting problem. They had a number of entity instances, inferred in an Excel spreadsheet. These had to be displayed in the usual way with an Entity Container in a Screen of Oracle Policy Modeller. So far so good, I hear you say.

Detail Pop-up in Oracle Policy Automation 1

The inferred attributes (which in the end would probably be coming from a data source, but for now come from Excel as inferred instances) included a Text attribute that was frankly far too large to display correctly in the Screen, given that there might be a few rows of instances in the final result.

We experimented with lots of different layouts, dynamically hiding and showing items based on various criteria in order to make room, but in the end we came to the conclusion that we needed some sort of Detail Pop-up in Oracle Policy Automation – something that could be called upon by the user as and when they needed it, but ignored (and invisible) when not needed.

Here is the starting point for our adventure.

The Screen above is populated with all the instances of the student’s scholarship. On the left is a set of information that we need to display on request. Normally we will only display the name of the Scholarship. You will see in the list of attributes there is an overview ( a text attribute), a deduction ( a currency attribute) and a renewable status (a boolean attribute). There are a few others, including the contact details (a text attribute, which is a URL).

So we are going to customise the label control I have just described. It is “inside” the Entity instance loop : so in reality there will be one per instance. We need our label to contain, as you would expect, the correct information. We wish to avoid pop-ups (nasty!) and dialogs (too complex for a simple window) and tool-tips are too small and simple for the data we have. The label shown will have a custom property called “name” and will be used as a hook for our JavaScript Extension.

We experimented with all sort of things before we went for this option : JavaScript is a last resort. We were frustrated by the behaviour of hidden items that appeared slowly (we could see “uncertain” appearing before the out of the box JavaScript populated the bound controls). So it was going to be JavaScript.

So,  our Detail Pop-up in Oracle Policy Automation needs to meet these challenges. We are going, as a simple demonstration, to use a CSS toggle to show or hide the relevant data for our instances. We can set the scene as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.area {
position:relative;
color:#0645AD;
font-weight:400;
}
 
.area:hover {
text-decoration:underline;
text-decoration-color:blue;
cursor: pointer;
}
 
.fixed {
position:absolute;
top:190px;
left:465px;
border:0;
z-index:9999;
background-color:#FFF;
display:none;
height:300px;
width:420px;
float:right;
text-decoration:none;
text-decoration-color:#000;
text-color:#000;
padding:5px;
}

The above CSS file sets up two main classes and a pseudo-class for a mouse hovering over our row. The area is the area we will normally show – with just the name – and the fixed area is a fixed DIV which will display (or not) when we decide we want to visualise the information. Now for the main content of our customLabel extension. Here is the mount key with as usual, my simplistic attempts at coding : for the umpteenth time I’m not a professional – but my job is to show something can be done, and let other’s get on with making it happen.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
 
if (control.getProperty("name") == "xLabel") {
return {
mount: function (el) {
var maindiv = document.createElement("div");
maindiv.style.visibility = 'visible';
maindiv.innerHTML = interview.getValue("scholarship_award", "scholarship", control.instance);
maindiv.classList.add("area");
maindiv.setAttribute("id", interview.getValue("scholarship_award", "scholarship", control.instance));
el.appendChild(maindiv);
 
var tooltipdiv = document.createElement("div");
tooltipdiv.style.visibility = 'visible';
tooltipdiv.classList.add("fixed");
tooltipdiv.innerHTML = control.getCaption();
tooltipdiv.setAttribute("id", interview.getValue("scholarship_award", "scholarship", control.instance));
el.appendChild(tooltipdiv);
 
$("div [id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").click(function () {
$(".fixed").hide();
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").html("<h6>Name : " + interview.getValue("scholarship_award", "scholarship", control.instance) + "</h6>");
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").append("<h6>Overview : " + interview.getValue("scholarship_overview", "scholarship", control.instance) + "</h6>");
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").append("<h6><b>Renewable</b>: " + interview.getValue("scholarship_renewable", "scholarship", control.instance) + "</h6>");
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").append("<h6><b>Maximum Value</b>: " + interview.getValue("scholarship_deduction", "scholarship", control.instance) + "</h6>");
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").append("<h6><b>Eligibility</b>: " + interview.getValue("scholarship_eligibility", "scholarship", control.instance) + "</h6>");
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").append("<h6><b>Minimum Grade</b>: " + interview.getValue("scholarship_min_grade", "scholarship", control.instance) + "</h6>");
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").append("<h6><b>Contact</b>: <a href='" + interview.getValue("scholarship_contact", "scholarship", control.instance) + "' target='_blank'>SFAS Office</a></h6>");
$(".fixed[id='" + interview.getValue("scholarship_award", "scholarship", control.instance) + "']").toggle();
});
 
}

Here is the review of the Detail Pop-up in Oracle Policy Automation code shown above, starting with the usual check to make sure we are only working with our label and not any other label on the Screen. In lines 6 to 11 we add a new DIV (to replace the one that contained all those %substitution%s , and we populate it with only one attribute. Since that attribute is also unique, we use it as a handy way to populate the HTML id attribute.

In lines 13 to 18 we create a second, hidden DIV that uses the fixed class shown above.

In lines 20 to 29 we set up the click event so that the user can click on the label and the hidden text will be revealed. The code preview (I notice) has eaten the ending part of the line, so use the PDF version instead. Notice the use of getValue, with the entity name and the instance, to ensure that we create a DIV with a unique name and unique content. The content will be toggled (hidden or shown) when you click on it.

Finally the unmount is very standard, cleaning up our various pieces by removing them.

1
2
3
4
if (control.getProperty("name") == "xLabel") {
$(".area").remove();
$(".fixed").remove();
}

The end result looks like this, before the click:

Detail Pop-up in Oracle Policy Automation 3And after the click:

Detail Pop-up in Oracle Policy Automation 4

Our Detail Pop-up in Oracle Policy Automation prototype gives the user the information they need when they need it. Of course it can be extended and transformed into something much less ugly, but this is a good start. You can find the PDF in the Shop, it’s free!

The OPA Hub Snap Poll Results

The OPA Hub Snap Poll Results

As you know, the OPA Hub Website runs short-term polls or “Snap Polls” in an effort to collect and share information about Oracle Policy Automation that may hopefully be of value to the Community. The OPA Hub Snap Poll Results concern the question we asked in March 2018, specifically “Are you going to be using the new JavaScript Extension in your OPA Interviews?”.

The most recent versions of Oracle Policy Automation have pretty much consolidated JavaScript as the client-side platform for delivering just about any visual changes you might wish for. Many of us are also pretty hopeful that the JavaScript library in interviews.js is a forerunner of a future REST client, and hopefully the basis for some sophisticated integrations as well.

Of course there are other avenues of development of Oracle Policy Automation, notably the experimental RuleScript, based on the output of the Oracle Labs and the graal library. Anyway, The OPA Hub Snap Poll Results were quite definitely in favour of the JavaScript extensions. You can find the results below, and I have included a link to a dynamic version of the graphic hosted by our friends at easel.ly.

 New OPA Snap Poll

As the Snap Poll on the subject of JavaScript has now closed, a new Snap Poll has been opened, this time in an effort to get more information about the needs of the Community in respect of training and advanced workshops. Please take a moment to answer the OPA Hub Snap Poll on this subject.

You’ve got to be in it, to win it

A reminder : when we close this Snap Poll, one lucky voter will get a free copy of Getting Started with Oracle Policy Automation 2018 Edition, so don’t hesitate to vote today. The Snap Poll will close on the 31st April 2018, and results will be published on this website soon afterwards.

 

Worldwide
Logo by Southpaw Projects LLC