Maximo

Maximo

Come for answers, stay for best practices. All we're missing is you.

 View Only
  • 1.  Using Maximo Calendar in an automation script

    Posted Tue August 15, 2023 09:31 AM

    Hi,

    i am trying to calculate a date based on a defined maximo calendar in a script. In this calendar are (like normal) our working hours defined.

    I want to take a defined date, add x hours working time and retrieve the finish date. So far so normal, i could do this with a normal Java-Calendar. But if i take e.g. 01.08.2023 12:00 am as date and add 6 hours working time, Java would give me 01.08.2023 8 pm. Assuming i have a working period from 8 am to 3 pm each working day, i want to get 02.08.2023 11 am as result.

    Does anyone know how to achieve this in a simple way? I think Maximo must be able to do this internally, is there any possibility to use this from my script?



    ------------------------------
    Andreas Brieke
    IT Service Management Consultant
    SVA System Vertrieb Alexander GmbH
    ------------------------------


  • 2.  RE: Using Maximo Calendar in an automation script

    Posted Tue August 15, 2023 02:10 PM

    Here you go.  This will calculate the end date with a specified start time and work hours. It also includes calculating multiple days if necessary.

    If you have any questions feel free to reach out.  If you need in Jython I can add that too.

    Calendar = Java.type("java.util.Calendar");
    
    // start the day at 8:00 AM
    var startHour = 8;
    
    // work hours, 8 hours a day. 9 if there is an hour lunch
    var workHours = 8;
    
    main();
    
    function main() {
        var calendar = Calendar.getInstance();
    
        // reset hour, minutes, seconds and millis to midnight.
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
    
        var resultDate =  addHours(calendar, 13);
    }
    
    function addHours(start, hours) {
        
        var leftOverHours = hours % workHours;
    
        var fullDays = 0;
    
        if (leftOverHours > 0) {
            fullDays = (hours - leftOverHours) / workHours;
        }
    
        start.add(Calendar.DAY_OF_YEAR, fullDays);
        start.add(Calendar.HOUR_OF_DAY, startHour + leftOverHours);
    
        return start.getTime();
    }


    ------------------------------
    Jason VenHuizen
    https://sharptree.io
    https://opqo.io
    ------------------------------



  • 3.  RE: Using Maximo Calendar in an automation script

    Posted Wed August 16, 2023 10:44 AM

    Hi Jason,

    many thanks, when i read your snippet i thought "damn, how stupid i can be, this is a so simple solution". But thinking a bit more about it, this does not take weekends and holidays into account, so i could end up with a date for example "this should be finished on Sunday 1pm". And i think adding this would make it a bit more complex.

    Relying on the Maximo Calendar should be the easier way, because it already knows on which days there will be no work.



    ------------------------------
    Andreas Brieke
    IT Service Management Consultant
    SVA System Vertrieb Alexander GmbH
    ------------------------------



  • 4.  RE: Using Maximo Calendar in an automation script

    Posted Wed August 16, 2023 03:57 AM

    Hi Andreas,

    Some time ago, I had implemented a Java code with calendar, shift and work periods to report actual work time to status tracking. I think you may reuse or convert to Jython. Please check the link below. Hope it helps.

    Tracking Ticket Status by Taking Calendar and Shift into Account

    https://www.ibm.com/support/pages/node/1128855



    ------------------------------
    YALCIN KUMBASAR
    ------------------------------



  • 5.  RE: Using Maximo Calendar in an automation script

    Posted Wed August 16, 2023 11:13 AM

    Hi Yalcin,

    this is great! This is exactly what i need. I am currently porting this to jython and will show my final result here.

    As i want to return a java.util.Calendar or a java.util.Date, i am trying to translate your returns to the needed changes. On one of these i am a bit helpless when this could occur.

    Could you explain it to me please? I am hanging on this one:

    else if ((DateUtility.compareTime(start, end) >= 0) || (DateUtility.compareTime(end, reportDate) > 0))
            {
              if (DateUtility.compareTime(start, end) == 0) {
                  getMboValue("WORKTIMETRACKING")
                            .setValue(PmUtility.elapseTime(totalMinutes*60000L));
                  return;
              }

    I think this is the comparison for a 24h shift, but why do you just write the totalMinutes and do no comparison between accumulatedMinutes and totalMinutes??



    ------------------------------
    Andreas Brieke
    IT Service Management Consultant
    SVA System Vertrieb Alexander GmbH
    ------------------------------



  • 6.  RE: Using Maximo Calendar in an automation script

    Posted Wed August 16, 2023 01:37 PM

    Hi Andreas,

    This is a development from 2015 while I was working for IBM. So, I do not have all the environment or requirement details now. I believe the system was SCCD not ICD / Maximo. As far as I remember, the client had simple shifts like DAY and NIGHT. So it is possible that a shift can start yesterday and end today. There is also lunch break so there can be multiple daily records on workperiod table if I am not mistaken. 

    For the one you ask, there is no non-working minutes if shift >= 24 hours or the status change occurs within shift again that means there is no non-working hours. That's why totalminutes can be used directly. I hope it makes sense.

    I think it will be more clear when you translate to Automation script and test.



    ------------------------------
    YALCIN KUMBASAR
    ------------------------------



  • 7.  RE: Using Maximo Calendar in an automation script

    Posted Thu August 17, 2023 02:09 AM

    Hi Yalcin,

    i was already translating to Automation Script, that was were my question came from.

    Thanks for your tip, i think i got it sorted.

    So, this is my function (i do not guarantee that there are no errors :)):

    def getTargetDateShift(orgid,calendar,shift,startDate,workTimeMinutes):
        '''
        Calculates a TargetTime based on Calendar/Shift
    
        orgid:            The ORGID the Shift belongs to
        calendar:         The CALENDAR the Shift belongs to
        shift:            The Shift with the time data (needs to be filled with working times)
        startDate:        The Date to start calculation on
        workTimeMinutes:  The Minutes that should be added
    
        returns a java.util.Calendar with the Calculated Time
        '''
        from psdi.util.logging import MXLoggerFactory
        log = MXLoggerFactory.getLogger("maximo.script")
        log.info("getTargetDateShift - START")
        log.info("orgid {} calendar {} shift {} startDate {} workTimeMinutes {}".format(orgid,calendar,shift,startDate,workTimeMinutes))
        from psdi.mbo import SqlFormat
        from psdi.app.common import DateUtility
        from psdi.server import MXServer
        from java.util import Calendar
    
        returnCal = Calendar.getInstance()
        returnCal.setTime(startDate)
    
        todayDate = DateUtility.getDate(startDate)
        yesterdayDate = DateUtility.addDays(todayDate,-1)
        log.debug("Today {} Yesterday {}".format(todayDate,yesterdayDate))
    
        sqf = SqlFormat("calnum = :1 and shiftnum = :2 and orgid = :3 and workdate >= :4")
        sqf.setObject(1,"WORKPERIOD","calnum",calendar)
        sqf.setObject(2,"WORKPERIOD","shiftnum",shift)
        sqf.setObject(3,"WORKPERIOD","orgid",orgid)
        sqf.setDate(4, yesterdayDate)
        log.debug("SQL {}".format(sqf.format()))
        workPeriodSet = MXServer.getMXServer().getMboSet("WORKPERIOD",MXServer.getMXServer().getSystemUserInfo())
        workPeriodSet.setWhere(sqf.format())
        workPeriodSet.reset()
        workPeriodSet.setOrderBy("WORKDATE")
    
    
        if workPeriodSet.isEmpty():
            workPeriodSet.close()
            workPeriodSet.cleanup()
            log.error("No Shift can be found with the supplied Data - ORGID {} CALENDAR {} SHIFT {}".format(orgid,calendar,shift))
            raise Exception("No Shift can be found with the supplied Data - ORGID {} CALENDAR {} SHIFT {}".format(orgid,calendar,shift))
    
        accumulatedMinutes = 0
        workdate = None
        periodStart = None
        periodEnd = None
    
        workPeriodMbo = workPeriodSet.moveFirst()
        while workPeriodMbo:
            workdate = workPeriodMbo.getDate('WORKDATE')
            periodStart = workPeriodMbo.getDate("STARTTIME")
            periodEnd = workPeriodMbo.getDate("ENDTIME")
            workHours = workPeriodMbo.getDouble("WORKHOURS")
            workMinutes = int(workHours * 60)
            log.debug("workdate {} start {} end {} workHours {} workMinutes {}".format(workdate,periodStart,periodEnd,workHours,workMinutes))
            log.debug("workdate.getTime {} todayDate.getTime {}".format(workdate.getTime(),todayDate.getTime()))
    
            if workdate.getTime() < todayDate.getTime():
                log.debug("DateUtility.compareTime(periodStart,periodEnd) {}".format(DateUtility.compareTime(periodStart,periodEnd)))
                if DateUtility.compareTime(periodStart,periodEnd) > 0: #Nightwork!
                    diff = DateUtility.timeDiff(periodEnd,startDate)
                    log.debug("DateUtility.timeDiff(periodEnd,startDate) {}".format(diff))
                    if diff > 0:
                        accumulatedMinutes += diff
                        log.debug("accumulatedMinutes {} workTimeMinutes {}".format(accumulatedMinutes,workTimeMinutes))
                        if accumulatedMinutes >= workTimeMinutes:
                            returnCal.add(Calendar.MINUTE,diff)
                            log.debug("exit 1")
                            break
                        
            elif workdate.getTime() == todayDate.getTime():
                log.debug("DateUtility.compareTime(periodStart,startDate) {}".format(DateUtility.compareTime(periodStart,startDate)))
                log.debug("DateUtility.compareTime(periodStart,periodEnd) {}".format(DateUtility.compareTime(periodStart,periodEnd)))
                log.debug("DateUtility.compareTime(periodEnd,startDate) {}".format(DateUtility.compareTime(periodEnd,startDate)))
                if DateUtility.compareTime(periodStart,startDate) >= 0:
                    accumulatedMinutes += workMinutes
                    if accumulatedMinutes >= workTimeMinutes:
                        date = DateUtility.combineDate(workdate,periodStart)
                        t = workMinutes - (accumulatedMinutes - workTimeMinutes)
                        log.debug("date {} t {}".format(date,t))
                        returnCal.setTime(date)
                        returnCal.add(Calendar.MINUTE,t)
                        log.debug("exit 2")
                        break
                elif DateUtility.compareTime(periodStart,periodEnd) >= 0 or DateUtility.compareTime(periodEnd,startDate) > 0:
                    if DateUtility.compareTime(periodStart,periodEnd) == 0: #24h shift?
                        diff = DateUtility.timeDiff(periodStart,periodEnd)
                        if diff > 0:
                            accumulatedMinutes += diff
                            if accumulatedMinutes >= workTimeMinutes:
                                date = DateUtility.combineDate(workdate,periodStart)
                                t = workMinutes - (accumulatedMinutes - workTimeMinutes)
                                returnCal.setTime(date)
                                returnCal.add(Calendar.MINUTE,t)
                                log.debug("date {} t {}".format(date,t))
                                log.debug("exit 3")
                                break
                    diff = DateUtility.timeDiff(periodEnd,startDate)
                    log.debug("DateUtility.timeDiff(periodEnd,startDate) {}".format(diff))
                    if DateUtility.compareTime(periodStart,periodEnd) > 0:
                        diff += 1440
                    log.debug("diffAfter {}".format(diff))
                    if diff > 0:
                        accumulatedMinutes += diff
                        log.debug("accumulatedMinutes {} workTimeMinutes {}".format(accumulatedMinutes,workTimeMinutes))
                        if accumulatedMinutes >= workTimeMinutes:
                            returnCal.add(Calendar.MINUTE,workTimeMinutes)
                            log.debug("exit 4")
                            break
            else:
                accumulatedMinutes += workMinutes
                log.debug("accumulatedMinutes {}".format(accumulatedMinutes))
                if accumulatedMinutes >= workTimeMinutes:
                    date = DateUtility.combineDate(workdate,periodStart)
                    t = workMinutes - (accumulatedMinutes - workTimeMinutes)
                    returnCal.setTime(date)
                    returnCal.add(Calendar.MINUTE,t)
                    log.debug("date {} t {}".format(date,t))
                    log.debug("exit 5")
                    break
            workPeriodMbo = workPeriodSet.moveNext()
        
        workPeriodSet.close()
        workPeriodSet.cleanup()
    
        log.info("getTargetDateShift - END - returning {}".format(returnCal.getTime()))
        return returnCal

    I hope this helps others if they need it



    ------------------------------
    Andreas Brieke
    IT Service Management Consultant
    SVA System Vertrieb Alexander GmbH
    ------------------------------



  • 8.  RE: Using Maximo Calendar in an automation script

    Posted Thu August 17, 2023 03:45 AM

    Hi Andreas,

    Thank you for sharing the python version, I am sure others will make use of it. Greetings...



    ------------------------------
    YALCIN KUMBASAR
    ------------------------------