LoadBucket#

One time bucket of a resource's load profile: when it starts, how much work is booked in it, and how much the resource can take. Buckets come in ResourceLoad::buckets, returned by Project::load_profile and Project::preview inside a LoadProfile.

Declaration#

#[derive(Clone, Debug, PartialEq)]
pub struct LoadBucket {
    pub start: DateTime,
    pub load: f64,
    pub capacity: f64,
    pub tasks: Option<Vec<Id>>,
}

With the serde feature, LoadBucket implements Serialize and Deserialize with camelCase field names. tasks is left out of the output when it's None.

Fields#

Field Type Description
start DateTime Start of the bucket. The bucket length is the profile's LoadProfile::basis.
load f64 Work booked on the resource in this bucket, in assignment value units (1.0 = 100 %).
capacity f64 What the resource can take in this bucket, in the same units. ResourceLoad::capacity_mode says where the value comes from.
tasks Option<Vec<Id>> Ids of the tasks booked in the bucket, in input order. None unless the query's ProfileTasks asks for task lists; Some with an empty list when the bucket isn't listed.

The bucket is overallocated when load > capacity.

Example#

use plen_core::{
    AssignmentData, Basis, DateTime, ProfileQuery, ProfileTasks, Project, ProjectData,
    ResourceData, SolveOptions, TaskData,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let day = |d| DateTime::from_ymd_hm(2026, 1, d, 0, 0).ok_or("invalid date");

    let mut task = TaskData::new("a");
    task.start_date = Some(day(5)?);
    task.duration = Some(3.0);

    let data = ProjectData {
        tasks: vec![task],
        resources: vec![ResourceData { id: "dev".into(), capacity: None, calendar_id: None }],
        assignments: vec![AssignmentData {
            id: None,
            task: "a".into(),
            resource: "dev".into(),
            value: 1.0,
            offset: None,
            duration: None,
        }],
        ..Default::default()
    };

    let mut project = Project::default();
    project.solve(Some(data), &SolveOptions::default())?;

    let mut query = ProfileQuery::new(day(5)?, day(10)?);
    query.basis = Some(Basis::Day);
    query.tasks = ProfileTasks::Over;

    let profile = project.load_profile(&query.into())?;
    for resource in &profile.resources {
        for bucket in &resource.buckets {
            if bucket.load > bucket.capacity {
                println!("{:?} overallocated by {}: {:?}", bucket.start, bucket.load - bucket.capacity, bucket.tasks);
            }
        }
    }
    Ok(())
}