Toggle navigation OptaPlanner logo
  • Home
  • Download
  • Learn
    • Documentation
    • Videos
    • Slides
    • Training
    • Use cases
    • Compatibility
    • Testimonials and case studies
  • Get help
  • Source
  • Team
  • Services
  • Star
  • @OptaPlanner
  • Fb
Fork me on GitHub
  • Mechanic scheduling (part 3) - Simulation and l...
  • How to plan (and optimize) a Secret Santa

Exploring the new OptaWeb Employee Rostering backend

Tue 22 October 2019

Avatar Julian Cui

Julian Cui


GitHub

OptaWeb Employee Rostering developer

To help you solve employee shift rostering challenges, we maintain an end-to-end web application called OptaWeb Employee Rostering. It is a web application that optimizes shift scheduling for maximum efficiency and fairness for employees. Scheduling guards is very different from scheduling nurses, therefore you can fork and customize the code to your requirements.

Recently, we rewrote the frontend and backend in modern technologies. In this post I’ll give an overview of the new backend and the key features that were added.

The Problem

Let’s start with the problem we’re trying to solve. Here is a visual overview of the Employee Rostering problem:

employeeRosteringValueProposition

Many company workplaces need solutions to this problem, from hospitals to call centers. There are many factors to consider when assigning shifts to employees, such as required skills, employee availabilities, and employee fairness. So how do we find the best solution? With OptaPlanner, of course!

Domain Model

Before we dive into the code, we first need a domain model. A domain model represents the entities (Java objects) and how they relate with other entities in the application. It tells us the behaviour of the objects and how they change during solving. Here’s a class diagram of the Employee Rostering model, where employees are nurses:

optaWebEmployeeRosteringClassDiagram

Each of the blocks above represents a Java entity object in our application, which are saved in a database for solving. We use JPA (Hibernate) as our persistence layer. The arrows show how certain entities map to others, so changing one during the solving process may affect another. The * and 1 symbols on the arrows represent a many-to-one mapping of the entities in the database.

Here’s a simple example of the Skill entity:

@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"tenantId", "name"}))
public class Skill {

    @NotNull
    @Size(min = 1, max = 120)
    @Pattern(regexp = "^(?!\\s).*(?<!\\s)$", message = "Name should not contain any leading or trailing whitespaces")
    private String name;

    @SuppressWarnings("unused")
    public Skill() {

    }

    public Skill(Integer tenantId, String name) {
        super(tenantId);
        this.name = name;
    }

    // Getters and setters
}

This Java object stores the name of the skill and the id of the Tenant (the user, such as a hospital or a call center) it belongs to. The @Entity annotation represents a table in the JPA database, where each instance of the Skill entity has its own row. The @Table annotation specifies the columns of the table (we’ll see this in the next section).

H2 Database

To test the database, we use the H2 Database Console to run SQL statements and view the saved entities on a user interface. Here’s an example of executing a SQL statement that selects every Skill entity:

skillsConsole

Notice that the autogenerated demo data is saved in the H2 database when the application starts up. This console makes it convenient to test and modify the in-memory database.

Ok, so having data generated on startup is nice for demoing the application. But what if we wanted to use the application in production and start with no demo data? To solve this, we added a feature that gives the user the option to start the application with no persisted data. This was implemented by adding an additional system property called InitialData:

public static final String INITIAL_DATA_PROPERTY = "optaweb.generator.initial.data";

public enum InitialData {
    EMPTY,
    DEMO_DATA // default
}

In RosterGenerator.java, we use the system property to determine whether to generate data or not. Notice that if initialData = EMPTY, the method returns without generating any data:

SystemPropertiesRetriever.InitialData initialData = SystemPropertiesRetriever.determineInitialData();

switch (initialData) {
    case EMPTY:
        return;
    case DEMO_DATA:
        // Generate demo data
}

To configure the system properties and start with an empty database, add this to the applications.properties file:

optaweb.generator.initial.data=EMPTY

This sets the optaweb.generator.initial.data system property for the application when it starts.

REST API

Now let’s actually interact with the backend application. An easy way to test the REST API is to use the Swagger framework, which OptaWeb Employee Rostering supports.

As an example, here are the Tenant REST methods shown in the Swagger user interface:

tenantMethods

As we can see, each method has documentation, the relative path URL, and its corresponding HTTP request. The Swagger UI makes it very easy to test any method in the backend. Let’s say we wanted a list of all the Tenants in the application. We start by selecting the corresponding method to display the method details:

getTenantsMethod

Here, the Example Value field shows a sample value of a Tenant list, serialized to JSON format. We see that each Tenant contains the id, name, and version fields, which are saved in the database whenever a Tenant is added. Also note the Try it out button in the top right corner. We can use it to send HTTP requests with ease for testing purposes. Let’s try it out:

executeGetTenants

Sending the GET request returns all the saved Tenants. We can see them in JSON format above.

Nice, we just sent our first HTTP request! But the Employee Rostering application does much more than just getting entities. There are methods for creating, deleting, and updating various entities (remember the Domain Model above), which the application uses to modify the database, as well as methods that interact with the OptaPlanner API.

Let’s try a more interesting method:

solveMethod

This method is the core of the OptaWeb Employee Rostering application. It starts the OptaPlanner solver that assigns work shifts to employees for a specific Tenant. The solver uses heuristics and algorithms to find the most optimal solution under time and resource constraints. To learn more about how OptaPlanner finds better solutions, read about Local Search in the OptaPlanner docs.

Let’s try out the solver for the Tenant with id = 1:

executeSolverMethod

After clicking Execute, the solver starts evaluating possible solutions for arranging shifts, and finds the shift roster with the best score. The score is calculated using the Drools business constraint rules that we configured in the application. As the algorithm finds new solutions, the engine evaluates the score of each solution and compares it with the score of the current best solution. If the score is higher, the new solution becomes the new best solution. Notice the log messages in the console during solving:

solverLog

Notice the medium score when solving starts versus when solving ends. In our application, each unassigned shift has a score of -1medium, which means for every unassigned shift a particular solution has, the medium score of that solution would decrease by 1. In this case, the starting shift arrangement had a medium score of -660, so there were 660 shifts that were not assigned to any employee. After solving, the shift arrangement had a medium score of 0, which means every shift was assigned to an employee.

Congratulations! You’ve just found an optimal employee shift arrangement given the time and business constraints for this Tenant.

Conclusion

In the process of migrating the OptaWeb Employee Rostering backend application to another Java framework, we added and used various tools for better user and developer experience. The most notable tools included the H2 Database Console for JPA persistence testing, adding the InitialData system property to configure the startup demo data, and Swagger for REST API testing and documentation.

Of course, we don’t expect users to interact with the application through H2 and Swagger, that’s what a user interface is for! The UI for this application was implemented in the Employee Rostering Frontend Application, written in React.js.

To build and run the application for yourself, check out the source code.


Comments Permalink
 tagged as employee rostering coding algorithm production

Comments

Visit our forum to comment
  • Mechanic scheduling (part 3) - Simulation and l...
  • How to plan (and optimize) a Secret Santa
Atom News feed
Don't want to miss a single blog post?
Follow us on
  • T
  • Fb
Blog archive
Latest release
  • 8.3.0.Final released
    Fri 5 March 2021
Upcoming events
  • KIE Live
    Worldwide - Tue 9 March 2021
    • Testing your constraints with OptaPlanner by Lukáš Petrovický, Karina Varela, Alex Porcelli
  • Javaland
    Worldwide - Tue 16 March 2021
    • AI on Quarkus: I love it when an OptaPlan comes together by Geoffrey De Smet
  • Red Hat Webinar
    Worldwide - Wed 24 March 2021
    • AI planning: Top 3 use cases and benefits by Ronald Meuwsen, Geoffrey De Smet
  • SouJava MOTU
    Worldwide - Thu 15 April 2021
    • Planejamento de Recursos com OptaPlanner by Karina Varela, Otávio Santana
Add event / Archive
Latest blog posts
  • Optimizing COVID-19 vaccination appointment scheduling
    Thu 4 March 2021
     Paul Brown
  • How much faster is Java 15?
    Tue 26 January 2021
     Michal Tomčo
  • Solve the facility location problem
    Fri 9 October 2020
     Jiří Locker
  • OptaPlanner Week 2020 recordings
    Mon 7 September 2020
     Geoffrey De Smet
  • Let’s OptaPlan your jBPM tasks (part 1) - Integrating the two worlds
    Fri 3 July 2020
     Walter Medvedeo
  • AI versus Covid-19: How Java helps nurses and doctors in this fight
    Fri 8 May 2020
     Christopher Chianelli
  • Workflow processes with AI scheduling
    Tue 5 May 2020
     Christopher Chianelli
Blog archive
Latest videos
  • YT Maintenance scheduling
    Wed 24 February 2021
     Julian Cui
  • YT Vaccination appointment scheduling
    Wed 3 February 2021
     Geoffrey De Smet
  • YT Shadow variables
    Tue 19 January 2021
     Geoffrey De Smet
  • YT Domain modeling and design patterns
    Tue 17 November 2020
     Geoffrey De Smet
  • YT Quarkus insights: AI constraint solving
    Tue 20 October 2020
     Geoffrey De Smet
  • YT AI in kotlin
    Wed 23 September 2020
     Geoffrey De Smet
  • YT Planning agility: continuous planning, real-time planning and more
    Thu 3 September 2020
     Geoffrey De Smet
Video archive

KIE projects

  • Drools rule engine
  • OptaPlanner constraint solver
  • jBPM workflow engine

Community

  • Blog
  • Get Help
  • Team
  • Governance
  • Academic research

Code

  • Build from source
  • Submit a bug
  • License (Apache-2.0)
  • Release notes
  • Upgrade recipes
Sponsored by
Red Hat
More coder content at
Red Hat Developers
© Copyright 2006-2021, Red Hat, Inc. or third-party contributors - Privacy statement - Terms of use - Website info