0

I've been familiarizing myself with Optaplanner recently - more specifically, with its nurse rostering example - and have been attempting to add a new hard constraint but, so far, haven't had much success.

I'm endeavoring to add a DayUnavailableRequest constraint (ie. employees are unavailable to work on specific days). I'm using the DayOffRequest as a template. The only difference between the two contraints is that the DayUnavailableRequest constraint would he "hard".

To do so, I added a new file - DayUnavailableRequest.Java - to the request folder:

package org.optaplanner.examples.nurserostering.domain.request;
import com.thoughtworks.xstream.annotations.XStreamAlias;

import org.optaplanner.examples.common.domain.AbstractPersistable;
import org.optaplanner.examples.nurserostering.domain.Employee;
import org.optaplanner.examples.nurserostering.domain.ShiftDate;

@XStreamAlias("DayUnavailableRequest")
public class DayUnavailableRequest extends AbstractPersistable {

private Employee employee;
private ShiftDate shiftDate;

public Employee getEmployee() {
    return employee;
}

public void setEmployee(Employee employee) {
    this.employee = employee;
}

public ShiftDate getShiftDate() {
    return shiftDate;
}

public void setShiftDate(ShiftDate shiftDate) {
    this.shiftDate = shiftDate;
}

@Override
public String toString() {
    return shiftDate + "_OFF_" + employee;
}

}

I then added the following to nurseRosteringScoreRules.drl:

Availability day on/off
rule "dayUnavailableRequest"
when
    $dayUnavailableRequest : DayUnavailableRequest($employee : employee, $shiftDate :  shiftDate)
    $assignment : ShiftAssignment(employee == $employee, shiftDate == $shiftDate)
then
    scoreHolder.addHardConstraintMatch(kcontext, - 1);
end`

In nurseRoster.java, I added:

public List<DayUnavailableRequest> getDayUnavailableRequestList() {
return dayUnavailableRequestList;
}

public void setDayUnavailableRequestList(List<DayUnavailableRequest> dayUnavailableRequestList) {
    this.dayUnavailableRequestList = dayUnavailableRequestList;
}    

as well as:

facts.addAll(dayUnavailableRequestList);

In Employee.java, I added:

private Map<ShiftDate, DayUnavailableRequest> dayUnavailableRequestMap;

And...

public Map<ShiftDate, DayUnavailableRequest> getDayUnavailableRequestMap() {
    return dayUnavailableRequestMap;
}

public void setDayUnavailableRequestMap(Map<ShiftDate, DayUnavailableRequest> dayUnavailableRequestMap) {
    this.dayUnavailableRequestMap = dayUnavailableRequestMap;
}

And finally, in NurseRosteringImporter.java, I included:

On line 115:

readShiftOnRequestList(nurseRoster, schedulingPeriodElement.getChild("ShiftOnRequests"));

On line 131:

nurseRoster.getDayUnavailableRequestList().size(),

And...

private void readDayUnavailableRequestList(NurseRoster nurseRoster, Element dayUnavailableRequestsElement) throws JDOMException {
        List<DayUnavailableRequest> dayUnavailableRequestList;
        if (dayUnavailableRequestsElement == null) {
            dayUnavailableRequestList = Collections.emptyList();
        } else {
            List<Element> dayUnavailableElementList = (List<Element>) dayUnavailableRequestsElement.getChildren();
            dayUnavailableRequestList = new ArrayList<DayUnavailableRequest>(dayUnavailableElementList.size());
            long id = 0L;
            for (Element element : dayUnavailableElementList) {
                assertElementName(element, "DayUnavailable");
                DayUnavailableRequest dayUnavailableRequest = new DayUnavailableRequest();
                dayUnavailableRequest.setId(id);

                Element employeeElement = element.getChild("EmployeeID");
                Employee employee = employeeMap.get(employeeElement.getText());
                if (employee == null) {
                    throw new IllegalArgumentException("The shiftDate (" + employeeElement.getText()
                            + ") of dayUnavailableRequest (" + dayUnavailableRequest + ") does not exist.");
                }
                dayUnavailableRequest.setEmployee(employee);

                Element dateElement = element.getChild("Date");
                ShiftDate shiftDate = shiftDateMap.get(dateElement.getText());
                if (shiftDate == null) {
                    throw new IllegalArgumentException("The date (" + dateElement.getText()
                            + ") of dayUnavailableRequest (" + dayUnavailableRequest + ") does not exist.");
                }
                dayUnavailableRequest.setShiftDate(shiftDate);

                dayUnavailableRequestList.add(dayUnavailableRequest);
                employee.getDayUnavailableRequestMap().put(shiftDate, dayUnavailableRequest);
                id++;
            }
        }
        nurseRoster.setDayUnavailableRequestList(dayUnavailableRequestList);
    }

I'm using sprint01_1week.xml for testing purpose. When I add the constraint data, I either receive an error, or the application works but the availability constraints themselves don't take effect.

For example:

<DaysUnavailable>   
 <DayUnavailable>
  <EmployeeID>4</EmployeeID>
  <Date>2014-10-24</Date>
 </DayUnavailable>
</DaysUnavailable>

returns a long "uncaught exception" error.

<DayUnavailableRequest>
 <DayUnavailable>
  <EmployeeID>4</EmployeeID>
  <Date>2014-10-24</Date>
 </DayUnavailable>
</DayUnavailableRequest>  

doesn't return an error but also isn't applied to the application.

Any help would be greatly appreciated. Also, if there is a more efficient way to add employee availability, please share.

Thanks.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52

2 Answers2

0

You added, in NurseRosteringImporter.java:

On line 115:

readShiftOnRequestList(nurseRoster, schedulingPeriodElement.getChild("ShiftOnRequests"));

And you must to add this, instead:

readDayUnavailableRequestList(nurseRoster, schedulingPeriodElement.getChild("DayUnavailableRequest"));
-1

On Line 777 of NurseRosteringImporter.java you need to add the lines that are the same as line 776, 775, 773 etc but for DayUnavailableRequest.

You also need to do what the guy above said too.

It worked for me with those changes.

Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
Aaron
  • 1
  • 1