DNN Scheduled task - last run date - dotnetnuke-5

I have created DNN scheduled task on my website to generate a report of all users created since the last run of the task. I want to do this so that the report can be configured to generate daily, weekly, monthly or any other duration, just by changing the properties of the scheduled task in DNN.
My problem is that I am not sure how to get the "last run date" of a task inside my dll. It is not clear if this is possible, and if it is, then which property of the ScheduleHistoryItem object I should use.
(DNN v5.6.2)

Yes it is possible. Once you have pulled the list of ScheduleHistoryItems you want via the SchedulingProvider.Instance().GetScheduleHistory function, you can sort the list by the built in ScheduleHistorySortStartDate IComparer. The function below will return the last ScheduledHistoryItem that ran, which you can then check the EndDate property of the result to determine when the task last completed.
public DotNetNuke.Services.Scheduling.ScheduleHistoryItem GetLastScheduleHistoryItem(int ScheduleId = -1)
{
System.Collections.ArrayList scheduleHistory = DotNetNuke.Services.Scheduling.SchedulingProvider.Instance().GetScheduleHistory(ScheduleId);
if (scheduleHistory != null)
{
scheduleHistory.Sort(new DotNetNuke.Services.Scheduling.ScheduleHistorySortStartDate()); //Sort the returned results by the Start Date
if (scheduleHistory.Count > 0)
return (DotNetNuke.Services.Scheduling.ScheduleHistoryItem)scheduleHistory[0];
}
return null;
}

Related

How to trigger an email notification when cell value is modified by function

I would like to create a Google Sheets with event triggers. I'm using Google Apps Script.
I succeeded, thanks to Stack Overflow, to create a Google Sheets with an automatic mail notification when a cell is modified by a user.
Now I would like to know if this is possible when cell is modified by a function (not user's modification), such as :
if (today() >= B3 ; "late" ; "not late")
The function checks date, and give result "late" or "not late".
When deadlines are reached, the function would return "late" and a mail would be sent to warn me. The body mail would have the value of the cell in the B, D and E column and in the same row of the cell modified (I know how to do this using e.source, getRange and getRow)
So far, i've tried this, but it's not working
function sendNotification(e) {
if("F" == e.range.getA1Notation().charAt(0)) {
if(e.value == "Late") {
//Define Notification Details
var recipients = "user#example.com";
var subject = "Deadlines" ;
var body = "deadline reached";
//Send the Email
MailApp.sendEmail(recipients, subject, body);
}
}
}
How can I set up mail notifications when cells in F column have the "late" value (with "late" being the result of a function) ?
You can use a simple script that runs on a timer trigger and checks for any modification in a specific column in your sheet.
I use script like that for a lot of tasks, including calendar and sheets monitoring.
Below is a test code that works on column F, you have to run it once manually to create the scriptProperties value that I use to detect changes.
Then create a time trigger to run it every hour or any other timer value you find useful.
The only issue would be if you have a very long sheet, you could reach the length limit of the properties... (right now I don't remember the max length, will have to check ;-)
Code :
function checkColumnF() {
var sh = SpreadsheetApp.getActiveSheet();
var values = sh.getRange('F1:F').getValues().join('-');
if(PropertiesService.getScriptProperties().getKeys().length==0){ // first time you run the script
PropertiesService.getScriptProperties().setProperty('oldValues', values);
return;
}
var oldValues = PropertiesService.getScriptProperties().getProperty('oldValues').split('-');
var valuesArray = values.split('-');
while (valuesArray.length>oldValues.length){
oldValues.push('x'); // if you append some rows since last exec
}
Logger.log('oldValues = '+oldValues)
Logger.log('current values = '+valuesArray)
for(var n=0;n<valuesArray.length;n++){
if(oldValues[n] != valuesArray[n]){ // check for any difference
sendMail(n+1,valuesArray[n]);
}
}
PropertiesService.getScriptProperties().setProperty('oldValues', values);
}
function sendMail(row,val){
Logger.log('value changed on row '+row+' value = '+val+' , mail sent');
// uncomment below when you are sure everything runs fine to avoid sending dozens of emails while you test !
//MailApp.sendEmail(Session.getActiveUser().getEmail(),'value changed in your sheet','Row '+row+' is now '+val);
}

Crontab style scheduling in Play 2.4.x?

Technically I can install cron on the machine and curl the url, but I'm trying to avoid that. Any way to accomplish this?
Reason I want to avoid cron is so I can easily change the schedule or stop it completely without also ssh'ing into the machine to do so.
Take a look at: https://github.com/enragedginger/akka-quartz-scheduler.
Refer to http://quartz-scheduler.org/api/2.1.7/org/quartz/CronExpression.html for valid CronExpressions and examples.
An example taken from the docs:
An example schedule called Every-30-Seconds which, aptly, fires-off every 30 seconds:
akka {
quartz {
schedules {
Every30Seconds {
description = "A cron job that fires off every 30 seconds"
expression = "*/30 * * ? * *"
calendar = "OnlyBusinessHours"
}
}
}
}
You can integrate this into your Play! application (probably in your Global application)
You can use the Akka scheduler.
val scheduler = Akka.system(app).scheduler
scheduler.schedule(0 seconds, 1 hour) {
// run this block every hour
}
The first parameter is a delay, so if you wanted to delay to a specific time you could easily calculate the target time with some simple date arithmetic.
Check out https://github.com/philcali/cronish
Some example code from README.md:
val payroll = task {
println("You have just been paid... Finally!")
}
// Yes... that's how you run it
payroll executes "every last Friday in every month"
val greetings = job (println("Hello there")) describedAs "General Greetings"
// give a delayed start
val delayed = greetings runs "every day at 7:30" in 5.seconds
// give an exact time to start
val exact = greetings runs "every day at noon" starting now + 1.week
// resets a job to its definition
val reseted = exact.reset()
reseted starting now + 1.day

Utilities.formatDate() in Google Apps Script outputs previous date (e.g. input 25.05.2015 -> output 24.05.2015)

I have a problem with Google Docs' Utilities.formatDate() function.
I have a spreadsheet that contains all of the orders we place in the lab. When an order is delivered our lab manager enters the delivery date in the relevant cell in such a spreadsheet, in the following format: dd.MM.yyyy.
I created a script that, provided certain conditions, will email whoever placed that order alerting them that the order has been delivered on that particular date. Here is the code:
function DeliveryAlerts() {
try {
var email_dict = {"Y":"Y#Z.com"}
var spreadsheet = SpreadsheetApp.openById("ABC");
SpreadsheetApp.setActiveSpreadsheet(spreadsheet);
var sheet = spreadsheet.getSheetByName("Orders");
var values = sheet.getRange("A2:Q251").getValues();
var bgcolours = sheet.getRange("A2:Q251").getBackgrounds();
for(var i=0;i<=249;i++)
{
var j = i + 2;
if (values[i][16]=="Yes" && values[i][11]!="" && bgcolours[i][16]!="#b8b8b8")
{
var email_address = email_dict[values[i][13]];
var cur_date = Utilities.formatDate(values[i][11], "GMT+1", "EEE dd.MM.yyyy");
var message = "Hello there,\n\nYour order of " + values[i][4] + " has been delivered on "+ cur_date +".\n\nBest wishes";
var subject = "Delivery alert";
MailApp.sendEmail(email_address, subject, message,{replyTo:"abc#abc.com", name:"ABC"});
sheet.getRange("Q"+j).setBackground("#b8b8b8");
}
}
} catch (err) {
MailApp.sendEmail("abc#abc.com", "Delivery Alerts Script in Order Master List", err);
}
}
I use
Utilities.formatDate(values[i][11], "GMT+1", "EEE dd.MM.yyyy") to reformat the date from, say, 25.05.2015 (that is, the value in the cell) to Mon 25.05.2015. However, what I get instead is Sun 24.05.2015.
Does anybody know what is going on?
Thank you in advance.
Nicola
Check the time zone setting in the script editor. Under the FILE menu, choose PROJECT PROPERTIES in the script editor. It's possible to have a different time zone setting in Apps Script, than is in the spreadsheet. This is a common issue that arises. Apps Script allows a separate time zone setting from the spreadsheet. Also, even if the time is only off by one minute, if the time setting of the date is all zeros, it's common to get the problem that you are having. When a user enters a date, it's possible that no time setting is made. So the time is set to all zeros. The date is correct, but the time is all zeros. Even if the date was typed in at 3 in the afternoon, for example, and the date is correct, the time setting can be midnight of that day. So, even if you subtracted one second from that date, it would now be the day before.

Trigger works but test doesn't cover 75% of the code

I have a trigger which works in the sandbox. The workflow checks the field in the campaign level and compares it with the custom setting. If it matches, then it returns the target to the DS Multiplier field. The trigger looks as follows
trigger PopulateTarget on Campaign (before insert, before update)
{
for(Campaign campaign : Trigger.new)
{
if (String.isNotBlank(campaign.Apex_Calculator__c) == true)
{
DSTargets__c targetInstance = DSTargets__c.getInstance(campaign.Apex_Calculator__c);
{
String target = targetInstance .Target__c;
campaign.DS_Target_Multiplier__c = Target;
}
}
}
}
However, I had problems to write a proper test to this and asked for the help on the internet. I received the test
#isTest
private class testPopulateTarget{
static testMethod void testMethod1(){
// Load the Custom Settings
DSTargets__c testSetting = new DSTargets__c(Name='Africa - 10 Weeks; CW 10',Target__c='0.1538', SetupOwnerId = apexCalculatorUserId);
insert testSetting;
// Create Campaign. Since it would execute trigger, put it in start and stoptests
Test.startTest();
Campaign testCamp = new Campaign();
// populate all reqd. fields.
testCamp.Name = 'test DS campaign';
testCamp.RecordTypeId = '012200000001b3v';
testCamp.Started_Campaign_weeks_before_Event__c = '12 Weeks';
testCamp.ParentId= '701g0000000EZRk';
insert testCamp;
Test.stopTest();
testCamp = [Select ID,Apex_Calculator__c,DS_Target_Multiplier__c from Campaign where Id = :testCamp.Id];
system.assertEquals(testCamp.DS_Target_Multiplier__c,testSetting.Target__c);// assert that target is populated right
}
}
Such test returns the error "Compile Error: Variable does not exist: apexCalculatorUserId at line 6 column 122". If I remove that ApexCalculator part System.assertEquals then the test passes. However it covers 4/6 part of the code (which is 66%)
Could anyone help me how should I amend the code to make the coverage of 75%?
Yes, apexCalculatorUserId has not been defined. The code you were given appears to be incomplete. You'll need to look at the constructor DSTargets__c and see what kind of ID it is expecting there.
At a guess, you could try UserInfo.getUserId() to get the ID of the current user, but that may not be the ID that's expected in the constructor. It would be worth trying it to see if the test coverage improves.
1) Replace apexCalculatorUserId with UserInfo.getUserId()
2) I'm not sure what kind of field is Apex_Calculator__c on campaign. If its not a formula you want to insert a new line before "insert testCamp". Something like:
testCamp.Apex_Calculator__c = UserInfo.getUserId();

Firing Maximo workflow event from code

In our Maximo workflow we have a few schemas in which work order reaches a Condition node with a check on a startdate. If current date is less than it's startdate then work order goes to a Wait node with "maximo.workorder.update" condition. So when the scheduled date for WO comes people need to go to WO tracking and save this WO manually. Only then it continues it's way through the workflow. Otherwise WO will sit on that Wait node till the end of time.
What I want to do is to trigger this update event by crontask everyday so when the right date comes WO will wake up by itself.
I inspected source code for a Save button in WO tracking application and found that no matter what there's MboRemoteSet.save() method call. I assumed that you need to get some changes done and then call save() on the right MboSet. Also I know that in DB there's table called EVENTRESPONSE that keeps track of WOs sitting on the Wait nodes in workflow.
My crontask class contains this code:
MXServer mxServer = MXServer.getMXServer();
UserInfo userInfo = mxServer.getUserInfo("maxadmin");
woSet = mxServer.getMboSet("WORKORDER", userInfo);
...
String query = "select sourceid as WORKORDERID from EVENTRESPONSE"
+ " where eventname = 'maximo.workorder.update'"
+ " and sourcetable = 'WORKORDER'";
SqlFormat sqf = new SqlFormat("workorderid IN (" + query + ")");
woSet.setWhere(sqf.format());
MboRemote wo;
Date currentDate = new Date();
for (int i = 0; (wo = woSet.getMbo(i)) != null; i++) {
System.err.println(wo.getString("description"));
wo.setValue("CHANGEDATE", currentDate);
}
woSet.save();
workorder.changedate successfully refreshes but "maximo.workorder.update" event doesn't proc and WO stays on the Wait node.
So, how should I fire maximo.workorder.update?
This response comes a year late, I understand, but it may help others.
It is possible to use an "Escalation" to identify all work orders that have had their time to come and use an action on the escalation to update something on the work order. This will result in Maximo saving the change, thereby triggering the wait node of the workflow, all without any code, just configurations.
I have done something similar in the past and usually I end up flipping a YORN field that I had created for this purpose.