Create scheduled task programmatically without using management portal "Task Scheduler Wizard"

Primary tabs

Is it possible to create a scheduled task programmatically without using the management portal "Task Scheduler Wizard"?

Once created the scheduled task  would then be editable in the Wizard as normal for other users.

Thanks,

Conor

Replies

Yes, it's possible. You can use %SYS.Task for a Task API. Its methods and properties are well documented in its superclass %SYS.TaskSuper.

Yes, it's possible. For this you need to create class of the task. I used it to create server-monitoring sheduled task, that send me eamil. For example, i have class, that contains parameters of futured monitoring tasks (like task-name, period, timestart, timefinish, activity, description and so on), and contain method to create task:

Method generateTask(ByRef out)
{
#define crlf $c(13,10)
#define quote $C(34)
err = ""
try {
className = ..#taskPath_"."_##this.Name
##class(%ClassDefinition).%ExistsId(className){
ObjClass = ##class(%Dictionary.ClassDefinition).%OpenId(className) ; Если такая задача есть - открываем и чистим
ObjClass.Parameters.Clear(),ObjClass.Properties.Clear(),ObjClass.Methods.Clear()
}
else {
ObjClass = ##class(%Dictionary.ClassDefinition).%New(className) ; иначе создаем новую
}
ObjClass.Super = "%SYS.Task.Definition"   Param = ##class(%Dictionary.ParameterDefinition).%New(ObjClass.Name_":TaskName") ; Параметр: имя задачи
Param.Default = ##this.Name
Param.Description = "Имя задачи"
ObjClass.Parameters.Insert(Param) Prop = ##class(%Dictionary.PropertyDefinition).%New(ObjClass.Name_":Active") ; Свойство: активность
Prop.Type ="%Boolean"
Prop.Description = "Флаг активности задачи"
Prop.Required = 1
ObjClass.Properties.Insert(Prop) Prop = ##class(%Dictionary.PropertyDefinition).%New(ObjClass.Name_":Email") ; Свойство: Почтовые адреса для рассылки
Prop.Type = "%String"
Prop.Description = "Почтовые адреса для рассылки"
Prop.Parameters.SetAt(200,"MAXLEN")
ObjClass.Properties.Insert(Prop)
 
  Prop = ##class(%Dictionary.PropertyDefinition).%New(ObjClass.Name_":MethodName") ; Свойство: Метод для запуска
Prop.Type = "%String"
Prop.Description = "Метод Cache из класса для запуска"
ObjClass.Properties.Insert(Prop) Prop = ##class(%Dictionary.PropertyDefinition).%New(ObjClass.Name_":ClassName") ; Свойство: Класс Cache с методом для запуска
Prop.Type = "%String"
Prop.Description = "Класс Cache с методом для запуска"
ObjClass.Properties.Insert(Prop) Prop = ##class(%Dictionary.PropertyDefinition).%New(ObjClass.Name_":Params") ; Свойство: Параметры запускаемого метода
Prop.Type = "%String"
Prop.Description = "Параметры запускаемого метода"
ObjClass.Properties.Insert(Prop) method = ##class(%Dictionary.MethodDefinition).%New(className_":OnTask") ; Метод. Основной метод задачи
method.Name = "OnTask"
method.ReturnType = "%Status"
code = ##class(%Stream.TmpCharacter).%New()
code.Rewind()
code.Write(" #define quote $C(34)"_$$$crlf)
code.Write(" q:'..Active $$$OK"_$$$crlf)
code.Write(" s st=$$$OK"_$$$crlf)
code.Write(" try {"_$$$crlf)
code.Write(" s err="""""_$$$crlf)
code.Write(" s msg="""""_$$$crlf)
code.Write(" ;формирование параметров выполнения метода:_____"_$$$crlf)
code.Write(" s jstring=""{"""_$$$crlf)
code.Write(" for i=1:1:$l(..Params,""~"") {"_$$$crlf)
code.Write(" s buf=$p(..Params,""~"",i)"_$$$crlf)
code.Write(" i i<$l(..Params,""~""){"_$$$crlf)
code.Write(" s:buf'="""" jstring=jstring_$$$quote_($p(buf,""="",1))_$$$quote_"":""_$p(buf,""="",2)_"","" "_$$$crlf)
code.Write(" }else{"_$$$crlf)
code.Write(" s:buf'="""" jstring=jstring_$$$quote_($p(buf,""="",1))_$$$quote_"":""_$p(buf,""="",2)"_$$$crlf)
code.Write(" }"_$$$crlf)
code.Write(" }"_$$$crlf)
code.Write(" s jstring=jstring_""}"""_$$$crlf)
code.Write(" s in={}.%FromJSON(jstring)"_$$$crlf)
code.Write(" s out={""error"":"""",""message"":"""",""subject"":""""}"_$$$crlf)
code.Write(" d $CLASSMETHOD(..ClassName,..MethodName,.in,.out)"_$$$crlf)
code.Write(" ;отправка письма:________________________________"_$$$crlf)
code.Write(" i ..Email'="""" {"_$$$crlf)
code.Write(" s mailIn={}"_$$$crlf)
code.Write(" s mailIn.subject= """""_$$$crlf)
code.Write(" s mailIn.to = ..Email"_$$$crlf)
code.Write(" s mailIn.message= ""<h3>Статус выполнения задачи по расписанию ""_..#TaskName_"" : ""_$Case(out.error,"""":""Выполнено"",$$$OK:""Выполнено"",:""Выполнено с ошибками"")_"" </h3>"""_$$$crlf)
code.Write(" s:(out.message'="""") mailIn.message=mailIn.message_""Результат: </br>""_out.message"_$$$crlf)
code.Write(" s mailIn.message= mailIn.message_""Класс: ""_..ClassName_"", метод: ""_..MethodName_""</br>"""_$$$crlf)
code.Write(" s:((out.error'="""")&(out.error'=$$$OK)) mailIn.message=mailIn.message_""Ошибка: ""_out.error_""</br>"""_$$$crlf)
code.Write(" s:(out.subject'="""") mailIn.subject=out.subject"_$$$crlf)
code.Write(" s:(out.from'="""") mailIn.from=out.from"_$$$crlf)
code.Write(" s mailIn.isHTML = 1"_$$$crlf)
code.Write(" s mailIn.attach = """""_$$$crlf)
code.Write(" d ##class(Lib.Util.Email).Send(.mailIn,.out)"_$$$crlf)
code.Write(" }"_$$$crlf)
code.Write(" } // try"_$$$crlf)
code.Write(" catch {s err=$ze}"_$$$crlf)
code.Write(" if err'="""" {s st=$$$ERROR($$$GeneralError,err)}"_$$$crlf)
code.Write(" q st"_$$$crlf)
method.Implementation=code
ObjClass.Methods.Insert(method) ObjClass.%Save() status = $system.OBJ.Compile(className,"UBR-D",.errlog,1)
s:$System.Status.IsError(status) err=$SYSTEM.Status.GetErrorText(status)
}
catch exp {
err = ..ErrorDetect(.exp)
}
out.error = err
q
}

Alexandr,

why did you prefer generating the task class instead of just writing it in some text editor? Looks like overkill for such a small task.

Because it is a part of admin-panel of our web-application, that could generate any task.

it is a part of admin-panel of our web-application

I guessed it was not ad hoc writing ;)

Even with some universal tool like your admin panel, you can generate only those tasks which code you preliminary prepared with code.Write() commands. The reasons of preparing code this way in this very case are still unclear for me: I can hardly imagine extra functionality you can add to the traditional approach demonstrated in Evgeny's reply.

Another example which I use in Covid-19 online dashboard to update the data:

ClassMethod CreateTask() As %Status
{
    Set task=##class(%SYS.Task).%New()
    Set task.Name = "Update data"
    Set task.NameSpace=$Namespace
    Set task.TimePeriod=0 // Daily
    Set task.TimePeriodEvery=1 // Every 1 day
    Set task.DailyFrequency=1 // Run Several times in a day
    Set task.DailyFrequencyTime=0 // Run every x minutes
    Set task.DailyIncrement=60 // # of minutes between runs
    Set task.DailyStartTime = 0 // Start at 00:00:00
    Set task.DailyEndTime = 86399 // End at 23:59:59
    Set task.StartDate = $p($H,",",1) // Start today
    
    Set taskdef = ##class(Covid19.UpdateTask).%New()
    Do task.AssignSettings(taskdef)
    Set task.TaskClass=$classname(taskdef)
    
    Set st = task.%Save()
    Return:$$$ISERR(st) st
    Return ##class(%SYS.Task).RunNow(task.%Id())
}

But you need to have the class,  Covid19.UpdateTask in this case:

Class Covid19.UpdateTask Extends %SYS.Task.Definition
{

Method OnTask() As %Status
{
    return ##class(Covid19.Utils).DailyUpdate()
}

}

HTH

One question,

If my %SYS.Task.Definition class needs a parameter, how to add it in the %SYS.Task definition?

this is my task

Class MyTasks.LabsTask Extends %SYS.Task.Definition
{

Parameter PROPERTYVALIDATION = 1;

Parameter BPDOPROCESS= "MyLabs.BS.Process";

Parameter TaskName = "Check process of labs";

Property Laboratory As %String [ Required ];

Method OnTask() As %Status
{
    #Dim tService As Service.class
    set tSC = $$$OK
    set message = ##class(Ens.StringContainer).%New(..Laboratory)
    $$$ThrowOnError(##class(Ens.Director).CreateBusinessService(..#BPDOPROCESS, .tService))
    If ($IsObject(tService)) {
        Set tSC = tService.ProcessInput(message,.output)
    }
    
    Quit tSC
}

}

Regards,
Francisco Lopez

I've found the solution.

ClassMethod CreateTask() As %Status
{
    Set task=##class(%SYS.Task).%New()
    Set task.Name = "Check laboratory process"
    Set task.NameSpace=$Namespace
    Set task.TimePeriod=0 // Daily
    Set task.TimePeriodEvery=1 // Every 1 day
    Set task.DailyFrequency=0 // Run once
    Set taskdef = ##class(MyTasks.LabsTask).%New()
    Do task.AssignSettings(taskdef)
    Set task.TaskClass=$classname(taskdef)
    Set task.Settings=$lb("Laboratory","LABS1")
    
    Set st = task.%Save()
    Return:$$$ISERR(st) st
    Return ##class(%SYS.Task).RunNow(task.%Id())
}