From b49c08aed3ed4e137809c42326c47abb0dfc1223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 22 May 2024 23:05:43 +0200 Subject: [PATCH] Added planning load module and updated dependencies A new module 'planning_load' has been added to the 'planning' directory, handling workload planning tasks. Updates have also been made to project dependencies in Cargo.lock and Cargo.toml. The added dependencies are necessary for the new functionalities in the 'planning_load' module. Also, a few adjustments were made to allow better access and error handling across packages. --- Cargo.lock | 83 ++++++++++++++++++++++++ Cargo.toml | 2 + src/cli.rs | 1 + src/main.rs | 1 + src/openproject/client.rs | 4 +- src/openproject/work.rs | 116 +++++++++++++++++++++++++++++++++- src/planning/mod.rs | 1 + src/planning/planning_load.rs | 15 +++++ 8 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 src/planning/planning_load.rs diff --git a/Cargo.lock b/Cargo.lock index c05560c..5634e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,12 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "byteorder" version = "1.4.3" @@ -110,10 +116,12 @@ version = "0.1.0" dependencies = [ "clap", "gitlab", + "iso8601", "log", "reqwest", "serde", "simple-home-dir", + "tabled", "tokio", "toml", "url", @@ -750,6 +758,16 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", + "serde", +] + [[package]] name = "itertools" version = "0.10.5" @@ -959,6 +977,17 @@ version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +[[package]] +name = "papergrid" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -983,6 +1012,30 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.79" @@ -1335,6 +1388,30 @@ dependencies = [ "libc", ] +[[package]] +name = "tabled" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e" +dependencies = [ + "papergrid", + "tabled_derive", + "unicode-width", +] + +[[package]] +name = "tabled_derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c138f99377e5d653a371cdad263615634cfc8467685dfe8e73e2b8e98f44b17" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tempfile" version = "3.9.0" @@ -1586,6 +1663,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "void" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index 2cd682c..c247c84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,5 @@ url = "2.3.1" tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros"] } simple-home-dir = "0.2.1" log = "0.4.17" +tabled = "0.15.0" +iso8601 = { version = "0.6.1" , features = ["serde"]} diff --git a/src/cli.rs b/src/cli.rs index 44edd08..1b4fed9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -21,6 +21,7 @@ pub(crate) enum Commands { #[derive(Subcommand)] pub(crate) enum Planning { I2work(Issue2Work), + Load, } #[derive(Args, Debug)] diff --git a/src/main.rs b/src/main.rs index 19789f7..351f91c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,7 @@ async fn main() { let result = match cli.command { Some(Planning(I2work(args))) => planning::issue2work::issue2work(config, &args).await, + Some(Planning(cli::Planning::Load)) => planning::planning_load::planning_load(config).await, Some(Test) => debug::debug(config).await, None => Err(GeneralError { description: "No command launched".to_string(), diff --git a/src/openproject/client.rs b/src/openproject/client.rs index 0cfb407..abd20d9 100644 --- a/src/openproject/client.rs +++ b/src/openproject/client.rs @@ -6,8 +6,8 @@ use serde::Deserialize; use std::error::Error; #[derive(Deserialize, Debug)] -pub(crate) struct OpenProjectError { - pub(crate) description: String, +pub struct OpenProjectError { + pub description: String, } impl From for OpenProjectError { diff --git a/src/openproject/work.rs b/src/openproject/work.rs index d9aca46..2f2bc28 100644 --- a/src/openproject/work.rs +++ b/src/openproject/work.rs @@ -1,3 +1,6 @@ +use crate::openproject::client::{handle_response_status, Client, OpenProjectError}; +use crate::openproject::hal::Link; +use iso8601::Duration; use serde::{Deserialize, Serialize}; #[derive(Serialize, Debug)] @@ -19,8 +22,119 @@ pub struct DescriptionWriter { pub(crate) raw: String, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Clone)] pub struct WorkPackage { pub id: u64, pub subject: String, + #[serde(alias = "estimatedTime")] + pub estimated_time: Option, + #[serde(alias = "spentTime")] + pub spent_time: Option, +} + +#[derive(Deserialize, Debug)] +pub struct WorkPackageCollectionLinks { + #[serde(alias = "nextByOffset")] + pub next_by_offset: Option, +} + +#[derive(Deserialize, Debug)] +pub struct WorkPackagesElements { + pub elements: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct WorkPackageCollection { + pub total: u64, + pub count: u64, + pub offset: u64, + #[serde(alias = "pageSize")] + pub page_size: u64, + #[serde(alias = "_links")] + pub links: WorkPackageCollectionLinks, + #[serde(alias = "_embedded")] + pub embedded: WorkPackagesElements, +} + +impl WorkPackageCollection { + pub fn has_next(&self) -> bool { + self.links.next_by_offset.is_some() + } + + pub fn is_last(&self) -> bool { + !self.has_next() + } +} + +pub trait WorkPackageCollectionClient { + async fn work_package(&self) -> Result, OpenProjectError>; +} + +trait GetWorkPackageCollectionClient { + async fn first_work_packages_collection( + &self, + ) -> Result; + async fn next_work_packages_collection( + &self, + current: &WorkPackageCollection, + ) -> Result; +} + +impl GetWorkPackageCollectionClient for Client { + async fn first_work_packages_collection( + &self, + ) -> Result { + let client = reqwest::Client::new(); + + let response = client + .get(format!("{}/api/v3/work_packages", self.base_url)) + .basic_auth("apikey", Some(&self.token)) + .send() + .await?; + + let collection = handle_response_status(response, "could not find work packages").await?; + + Ok(collection) + } + + async fn next_work_packages_collection( + &self, + current: &WorkPackageCollection, + ) -> Result { + let client = reqwest::Client::new(); + + let response = client + .get(format!( + "{}/{}", + self.base_url, + current.links.next_by_offset.clone().unwrap().href + )) + .basic_auth("apikey", Some(&self.token)) + .send() + .await?; + + let collection = handle_response_status(response, "could not find work packages").await?; + + Ok(collection) + } +} + +impl WorkPackageCollectionClient for Client { + async fn work_package(&self) -> Result, OpenProjectError> { + let mut work_packages: Vec = vec![]; + let mut collection = self.first_work_packages_collection().await?; + + for w in collection.embedded.elements.iter_mut() { + work_packages.push(w.clone()); + } + + while !collection.is_last() { + collection = self.next_work_packages_collection(&collection).await?; + for w in collection.embedded.elements.iter() { + work_packages.push(w.clone()); + } + } + + Ok(work_packages) + } } diff --git a/src/planning/mod.rs b/src/planning/mod.rs index dd9b3f9..0be28d8 100644 --- a/src/planning/mod.rs +++ b/src/planning/mod.rs @@ -1 +1,2 @@ pub(crate) mod issue2work; +pub(crate) mod planning_load; diff --git a/src/planning/planning_load.rs b/src/planning/planning_load.rs new file mode 100644 index 0000000..3cdb5bb --- /dev/null +++ b/src/planning/planning_load.rs @@ -0,0 +1,15 @@ +use crate::config::Config; +use crate::error::GeneralError; +use crate::openproject::client::Client; +use crate::openproject::work::WorkPackageCollectionClient; + +pub async fn planning_load(config: Config) -> Result<(), GeneralError> { + let open_project_client = Client::from_config(&config.openproject); + let work_packages = open_project_client.work_package().await?; + + for w in work_packages { + println!("{:?}", w); + } + + Ok(()) +}