Performance activity assignments


Performance activity

Describes the main entity for a specific performance activity. Content (e.g. elements and questions) and configuration are tied to a performance activity. Performance activities are a type of course module, living within a dedicated container to provide context. Activities can be in a draft or active state.


A business decides that they want to introduce a bi-weekly check-in between the managers and their individual staff members. Therefore they create a new activity "Bi-weekly check-in" and configure it to have one section with the "Subject" and the "Manager" as a responding participants. Additionally, they add the "Manager's Manager" as a view-only participant. They configure a set of questions for both participants to answer.


Tracks provide a way to assign users to activities and control how those assignments result in subject instances for the users. Currently each activity has a single track created automatically, but in the future it will be possible to create activities with multiple separate tracks. When there are multiple tracks a single user will be able to end up with multiple assignments to the same activity via different tracks. Each track can have one or more track assignments.

Tracks contain configuration settings which controls the assignment behaviour of the track. This includes window period dates (which determines when instances can be created, see below) as well as job assignment behaviour (which can result in multiple instances for the same user when they have multiple jobs).

Tracks can also specify repeating behaviour, which again can lead to multiple subject instances for the same user over time.


They set the track schedule to have an open-ended window period starting beginning of the year and enable repeating every 2 weeks. They also enable multiple job assignments in the track settings so that for every job assignment there will be separate instances.

Track assignment

A track assignment defines a method of assigning a group of users to a track. Track assignments have user group types - for example 'Position', 'Organisation' or 'Audience'. The assignment dynamically represents a group of users which will be continuously resolved to a list of actual users via a syncing method. Although a single user can be part of the multiple track assignments, they will only ever be assigned to a single track once.


Now they assign several positions to the track as they want to start running the bi-weekly check-ins for specific positions in the company only.

Track user assignment

Track user assignments represent a per-subject-user (or per-job-per-subject-user) instance of a specific track. The code takes the track window settings (such as relative periods) and store user-specific values in the user assignment. When one instance per job assignment is selected, the code will create one track user assignment per job a user has.

On the other hand if a track is configured to repeat, the code will still only create one track user assignment per user (or per-job-per-subject-user). Repeating is managed by creating separate subject instances per track user assignment.


After activating the activity a scheduled task creates individual user assignments for all users in those positions, one per user and job assignment. So if a user has multiple job assignments they also get multiple track_user_assignment records.

Subject instance

A subject instance represents a specific instance of the activity for a single subject-user (or job-per-subject-user). If a track is not repeating the system will ensure there is a one-to-one relation between track user assignments and subject instances. If a track is not repeating there will always be only one subject instance per track user assignment. Each individual subject instance has its own due date.


Immediately after the track_user_assignment records are updated the system will ensure the appropriate subject instances are created.

Participant instance

For any given subject instance, every participant who has a relationship in the activity at the time when the subject instance is created will get their own participant instance. The participant instance ties a specific user, in a specific relationship to a specific subject instance. Creating participant instances makes it possible to quickly find all instances a user is involved in, optionally by relationship.

Responses are tied to participant instances, but participant instances are created for both answering and view-only relationship as they are also used for tracking access.


For each subject instance, the system will resolve the relationships and create participant instances for each user in each relationship. These will show up as activities on the participant's 'Activities' page.

Participant section

Participant sections represent a participants progress in a specific section of the activity. While sections contain multiple elements, participant sections are the most fine-grained unit of tracking progress, since a section is submitted all at once (you can't submit one element without submitting the whole section).


The new approach is designed to solve some specific issues that we had with the previous appraisals. One of the big ones was the failure to cope with the inevitable change that would happen within an organisation during an appraisal period (people changing roles, managers and other roles changing).

In the new design there are two parts to solving those issues:

  1. Delay creating of instances until the time they are needed. This reduces the window of time during which changes can occur. E.g. instead of the manager role being filled with a user's manager at the start of an appraisal, for all stages, we split the stages into activities and create them just in time. Therefore the mid-year or end-of-year activity doesn't come into being until right before it's going to be used. That makes it much more likely that whoever is the manager at that point in time is the right person to do the activity.

  2. Inevitably there will still be cases where people still want to make exceptions - with the old approach everything was synced so there was no easy way to support exceptions. With the new system, because we create the instances we can easily allow manual creation of new instances, independent from the automated syncing.

So instead of tightly syncing we are focused on the creation of instances at the right time, and less on the removal of instances when they are no longer relevant. Of course there still may be times when something gets created which is no longer needed. Initially our plan is to not automatically do anything but give the flexibility to let users do what they want, such as closing the old one if it's no longer required. There is nothing precluding us from adding automation to that in the future - e.g. add rules that define when instances should be automatically closed, but that will come later.

Data model

Click the image below to enlarge it. 

Dtata model flowchart

Assignment process

Discussion of the logical process used by code to sync assignments.

The process of getting actual subject instances created for a user involves a series of steps. Steps should be processed in the order listed below to most efficiently manage the work, as later steps are dependent on up to date data from earlier steps.

  1. Expand list of active track user assignments.
  2. Create subject instances.
  3. Create participant instances.

Expand list of active track user assignments

The logic of the process is as follows:

  • Loop through all active track assignments (excludes tracks in draft activities and track assignments that are not active).
    • Take the list of user assignments currently associated with the track assignment (A).
    • Calculate the list of users who currently meet the criteria for this track assignment (B).
    • Expand the list B to include a user's job assignments if the activity has Job assignment based instances enabled. The new list contains user_id and job_assignment_id (C).
    • Compare C to the list of existing user assignments from A and remove any that are already assigned, leaving a new list of user assignments that need to be created (D).
    • Obtain a list of existing user assignments for the current track from all assignments (not just the one we are processing) (E).
    • Loop through all the user assignments that need to be created (D). For each one:
      • Check to see if there is an existing user assignment within (E).
      • If so:
        • Link this assignment to it also.
        • If it's deleted, reactivate it.
      • If not:
        • Create the user assignment and link this assignment to it.
    • Trigger an eventFor each track:Unlink all records that are not in the current assignment any more.
  • At the end check for orphaned user assignments and delete them:
    • Locate track_user_assignment records that are no longer linked to a track assignment.
    • Leave the record but set the 'deleted' flag to true.
    • Trigger a track_user_unassigned event.
  • Separate to the sync task we need to ensure calculated dates (window period, due dates) are kept in sync with their original source. For example if 'window period' was defined relative to a user profile date field we would need to listen for events that set or modified the field value and update any absolute date values.
  • If a user meets the criteria for multiple tracks, they will get multiple perform_track_user_assignment records. Currently we only have one track per activity however.
  • If multiple jobs are enabled a single user may get multiple perform_track_user_assignment records for a single activity track.
  • Conversely, aside from the two reasons above a user will only ever get one perform_track_user_assignment record (even if repeating is enabled). Repeats are handled via subject instances. See below.

Create subject instances

This step is dependent on the one above being completed recently so the activity track user assignment records are up to date (e.g. the previous step should have run shortly before this one).

The logic of the process is as follows:

  • Identify all active perform_track_user_assignment records where the current time is within the window defined in the record.
  • If the track is not repeating:
    • Find all perform_track_user_assignment records where there isn't already a corresponding subject_instance record and create a new active record. Calculate and set an absolute due date based on the due date setting on the track.
  • If the track is repeating:
    • Find the most recently created subject_instance record. Determine if the repeating criteria is met relative to the most recent record at the current time, and whether the last activity reached the maximum number of repeats. If more repeats are required and the time criteria is met create a new active activity_instance record as above.

This step does not look for cases where there is a subject_instance but no active subject_track_user_assignment. Subject instances are not automatically closed.

Create participant instances

This step is completed at the same time as the subject_instance records are created. It is a one-off process, there is no active syncing (e.g. no adding or removal of unwanted participant instances if criteria changes). The reason for not actively syncing is that people will want different behaviour in different circumstances. For example if a user's manager changes very close to the end of an appraisal cycle, they are probably happy to let the old manager finish it off, not expect the new manager to complete all sections. By not syncing, we leave it to be a manual process where they can choose to either close the subject instance or add the new participant manually. In future we may offer an option to sync, but it wouldn't be on by default.

The logic of the process is as follows:

  • For each new subject_instance record that has just been created:
    • Identify the subject user.
    • Calculate the participant relationship for this activity by looking at the perform_section_relationship table.
    • For each participant relationship calculate the user or users who fulfil the role, relative to the subject user.
    • Create perform_participant_instance records for each participant.