Skip to content

Commit

Permalink
polish code
Browse files Browse the repository at this point in the history
  • Loading branch information
Celthi committed May 28, 2023
1 parent e3e148c commit dbc16a5
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 153 deletions.
9 changes: 5 additions & 4 deletions src/rally/api/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ pub async fn get_tasks(ut: &UserToken, wp: &models::ObjectModel) -> Result<Vec<m
let tasks = match wp {
models::ObjectModel::HierarchicalRequirement(models::HierarchicalRequirement {
Tasks,
.. }) => Tasks,
models::ObjectModel::Defect(models::Defect{Tasks,..}) => Tasks,
models::ObjectModel::TestSet(models::TestSet{Tasks, ..}) => Tasks,
_ => return Err(anyhow!("Not have task!"))
..
}) => Tasks,
models::ObjectModel::Defect(models::Defect { Tasks, .. }) => Tasks,
models::ObjectModel::TestSet(models::TestSet { Tasks, .. }) => Tasks,
_ => return Err(anyhow!("Not have task!")),
};
if tasks.is_none() {
return Ok(vec![]);
Expand Down
12 changes: 7 additions & 5 deletions src/rally/api/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ pub async fn fetch_rally_user(ut: &UserToken, name: &str) -> Result<Vec<User>> {
match res {
RallyResult::QueryResult(qr) => {
let results = qr.Results;
Ok(results.into_iter().filter_map(|i| match i {
ObjectModel::User(u) => Some(u),
_ => None,
}).collect::<Vec<User>>())
Ok(results
.into_iter()
.filter_map(|i| match i {
ObjectModel::User(u) => Some(u),
_ => None,
})
.collect::<Vec<User>>())
}
_ => Err(anyhow!(format!(
"No Rally user for {} or the Rally token is invalid.\r\n",
name
))),
}

}
168 changes: 24 additions & 144 deletions src/rally/op/ts.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
mod entry;
mod task_op;
use crate::msg::message::TimeSpent;
use crate::rally::api;
use crate::rally::api::task::{create_task, update_task};
use crate::rally::api::user::fetch_rally_user;
use crate::rally::api::task::update_task;
use crate::rally::api::wp::get_wp;
use crate::rally::models::task::CreateTask;
use crate::rally::models::time::UpdateValue;
use crate::rally::models::{ObjectModel, SingleObjectModel, Task, TimeEntryItem, User};
use crate::rally::models::ObjectModel;
use crate::token::tokens::UserToken;
use anyhow::{bail, Result};
use chrono::prelude::*;

use tracing::{error, info};

fn get_task_name(date: &DateTime<Utc>) -> String {
let s = "Code review ".to_string();
let date = date.format("%Y-%m-%d").to_string();
s + &date
}
use tracing::info;

pub async fn add_time_spent(ut: &UserToken, tp: &TimeSpent) -> Result<()> {
let Some(wp_id) = tp.get_wp_formatted_id() else {
Expand All @@ -28,149 +19,38 @@ pub async fn add_time_spent(ut: &UserToken, tp: &TimeSpent) -> Result<()> {
add_time_sheet(ut, &wp_id, tp).await
}

/// Add time sheet to the work product.
/// If the work product is accepted, it will not add the time sheet.
/// If the work product is not accepted, it will add the time sheet to the task.
async fn add_time_sheet(ut: &UserToken, wp_id: &str, tp: &TimeSpent) -> Result<(), anyhow::Error> {
let work_product = get_wp(ut, wp_id).await?;
// todo: handle feature and US without task
let schedule_state = work_product.get_schedule_state();
if schedule_state == "Accepted" {
bail!("Accepted item {} cannot add task.", wp_id,);
}
let task_date: DateTime<Utc> = Utc::now();
let (task, item) =
get_task_and_time_entry_item(ut, task_date, wp_id, &work_product, tp).await?;
if item.is_some() && task.is_some() {
let task = task.unwrap();
let item = item.unwrap();
add_time_entry_value(&item, ut, task_date, tp, work_product).await?;
let todo = task.ToDo.map(|t| t - tp.get_time_spent()).unwrap_or(0.0);
update_task(ut, &task, todo).await?;
} else {
info!("No task and time item for {wp_id}");
}
add_to_work_product(ut, task_date, wp_id, work_product, tp).await?;
Ok(())
}

async fn add_time_entry_value(
item: &TimeEntryItem,
/// Add time sheet to the work product.
async fn add_to_work_product(
ut: &UserToken,
task_date: DateTime<Utc>,
tp: &TimeSpent,
wp_id: &str,
work_product: ObjectModel,
tp: &TimeSpent,
) -> Result<(), anyhow::Error> {
let update_value = create_update_value(item, ut, task_date, tp).await?;
if update_value.object_id.is_none() {
api::time::add_time_entry_value(ut, &work_product.get_project(), &update_value).await?;
let (task, item) =
entry::get_task_and_time_entry_item(ut, task_date, wp_id, &work_product, tp).await?;
Ok(if item.is_some() && task.is_some() {
let task = task.unwrap();
let item = item.unwrap();
entry::add_time_entry_value(&item, ut, task_date, tp, work_product).await?;
let todo = task.ToDo.map(|t| t - tp.get_time_spent()).unwrap_or(0.0);
update_task(ut, &task, todo).await?;
} else {
api::time::update_time_entry_value(ut, &work_product.get_project(), &update_value).await?;
}
Ok(())
}

async fn create_update_value(
item: &TimeEntryItem,
ut: &UserToken,
task_date: DateTime<Utc>,
tp: &TimeSpent,
) -> Result<UpdateValue, anyhow::Error> {
let values = api::time::get_time_entry_values(ut, &item._ref).await?;
let mut update_value = UpdateValue::new(task_date, tp.get_time_spent(), item._ref.clone());
values.iter().for_each(|i| {
if i.DateVal.year() == task_date.year()
&& i.DateVal.month() == task_date.month()
&& i.DateVal.day() == task_date.day()
{
update_value.set_object_id(i.ObjectID);
update_value.add_hours(i.Hours);
}
});
Ok(update_value)
}

async fn get_uncompleted_task_and_entry_item(
ut: &UserToken,
tis: Vec<TimeEntryItem>,
) -> Result<Option<(Task, TimeEntryItem)>> {
for i in tis {
if let SingleObjectModel::Task(t) =
api::fetch_object::<SingleObjectModel>(ut, &i.Task._ref).await?
{
if t.State != "Completed" {
return Ok(Some((t, i)));
}
}
}
Ok(None)
}

async fn get_task_and_time_entry_item(
ut: &UserToken,
task_date: DateTime<Utc>,
wp_id: &str,
work_product: &ObjectModel,
tp: &TimeSpent,
) -> Result<(Option<Task>, Option<TimeEntryItem>)> {
let tis = api::time::get_time_entry_items(ut, &task_date, wp_id).await?;
let uncompleted_task_and_entry_item = get_uncompleted_task_and_entry_item(ut, tis).await?;
if let Some((task, item)) = uncompleted_task_and_entry_item {
return Ok((Some(task), Some(item)));
}
if let Some(task) = select_or_create_task(ut, work_product, tp).await? {
let item = Some(
api::time::create_time_entry_item(
ut,
&work_product.get_project(),
work_product,
&task_date,
&task,
)
.await?,
);
return Ok((Some(task), item));
}
error!("cannot find task end item for {}", wp_id);
Ok((None, None))
}

async fn select_or_create_task(
ut: &UserToken,
work_product: &ObjectModel,
tp: &TimeSpent,
) -> Result<Option<Task>> {
let tasks = api::task::get_tasks(ut, work_product).await?;
let owners = fetch_rally_user(ut, &ut.name).await?;
for owner in owners.iter() {
let t = select_task_for_owner(tasks, owner);
if t.is_some() {
return Ok(t);
}

let task_date: DateTime<Utc> = Utc::now();
let task_name = tp
.task_name
.clone()
.unwrap_or_else(|| get_task_name(&task_date));
let ct = CreateTask::new(
task_name,
owner._ref.clone(),
tp.get_time_spent(),
work_product,
);
return Ok(Some(create_task(ut, &ct).await?));
}
Ok(None)
}

fn select_task_for_owner(tasks: Vec<Task>, owner: &User) -> Option<Task> {
for t in tasks {
if t.State != "Completed"
&& t.Owner
.as_ref()
.map(|o| o._refObjectUUID.as_deref())
.flatten()
.map(|o| o == owner._refObjectUUID)
.unwrap_or(false)
{
return Some(t);
}
}
None
info!("Cannot add task and time item for {wp_id}");
})
}
97 changes: 97 additions & 0 deletions src/rally/op/ts/entry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use super::task_op::select_or_create_task;
use crate::msg::message::TimeSpent;
use crate::rally::api;
use crate::rally::models::time::UpdateValue;
use crate::rally::models::ObjectModel;
use crate::rally::models::SingleObjectModel;
use crate::rally::models::Task;
use crate::token::tokens::UserToken;
use anyhow::Result;
use chrono::prelude::*;
use tracing::error;

use crate::rally::models::TimeEntryItem;

pub async fn add_time_entry_value(
item: &TimeEntryItem,
ut: &UserToken,
task_date: DateTime<Utc>,
tp: &TimeSpent,
work_product: ObjectModel,
) -> Result<(), anyhow::Error> {
let update_value = create_update_value(item, ut, task_date, tp).await?;
if update_value.object_id.is_none() {
api::time::add_time_entry_value(ut, &work_product.get_project(), &update_value).await?;
} else {
api::time::update_time_entry_value(ut, &work_product.get_project(), &update_value).await?;
}
Ok(())
}

pub async fn create_update_value(
item: &TimeEntryItem,
ut: &UserToken,
task_date: DateTime<Utc>,
tp: &TimeSpent,
) -> Result<UpdateValue, anyhow::Error> {
let values = api::time::get_time_entry_values(ut, &item._ref).await?;
let mut update_value = UpdateValue::new(task_date, tp.get_time_spent(), item._ref.clone());
values.iter().for_each(|i| {
if i.DateVal.year() == task_date.year()
&& i.DateVal.month() == task_date.month()
&& i.DateVal.day() == task_date.day()
{
update_value.set_object_id(i.ObjectID);
update_value.add_hours(i.Hours);
}
});
Ok(update_value)
}

pub async fn get_uncompleted_task_and_entry_item(
ut: &UserToken,
tis: Vec<TimeEntryItem>,
) -> Result<Option<(Task, TimeEntryItem)>> {
for i in tis {
if let SingleObjectModel::Task(t) =
api::fetch_object::<SingleObjectModel>(ut, &i.Task._ref).await?
{
if t.State != "Completed" {
return Ok(Some((t, i)));
}
}
}
Ok(None)
}
/// get task and time entry item
/// if there is a uncompleted task, return it
/// if there is no uncompleted task, create a new task and time entry item
/// if there is no task, return None
pub async fn get_task_and_time_entry_item(
ut: &UserToken,
task_date: DateTime<Utc>,
wp_id: &str,
work_product: &ObjectModel,
tp: &TimeSpent,
) -> Result<(Option<Task>, Option<TimeEntryItem>)> {
let tis = api::time::get_time_entry_items(ut, &task_date, wp_id).await?;
let uncompleted_task_and_entry_item = get_uncompleted_task_and_entry_item(ut, tis).await?;
if let Some((task, item)) = uncompleted_task_and_entry_item {
return Ok((Some(task), Some(item)));
}
if let Some(task) = select_or_create_task(ut, work_product, tp).await? {
let item = Some(
api::time::create_time_entry_item(
ut,
&work_product.get_project(),
work_product,
&task_date,
&task,
)
.await?,
);
return Ok((Some(task), item));
}
error!("cannot find task end item for {}", wp_id);
Ok((None, None))
}
62 changes: 62 additions & 0 deletions src/rally/op/ts/task_op.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::msg::message::TimeSpent;
use crate::rally::api;
use crate::rally::api::task::create_task;
use crate::rally::api::user::fetch_rally_user;
use crate::rally::models::task::CreateTask;
use crate::rally::models::{ObjectModel, Task, User};
use crate::token::tokens::UserToken;
use anyhow::Result;
use chrono::prelude::*;

fn get_task_name(date: &DateTime<Utc>) -> String {
let s = "Code review ".to_string();
let date = date.format("%Y-%m-%d").to_string();
s + &date
}

/// Select a task for the owner. If the owner has a task that is not completed, then
/// return that task. Otherwise, create a new task for the owner.
pub async fn select_or_create_task(
ut: &UserToken,
work_product: &ObjectModel,
tp: &TimeSpent,
) -> Result<Option<Task>> {
let tasks = api::task::get_tasks(ut, work_product).await?;
let owners = fetch_rally_user(ut, &ut.name).await?;
for owner in owners.iter() {
let t = select_task_for_owner(tasks, owner);
if t.is_some() {
return Ok(t);
}

let task_date: DateTime<Utc> = Utc::now();
let task_name = tp
.task_name
.clone()
.unwrap_or_else(|| get_task_name(&task_date));
let ct = CreateTask::new(
task_name,
owner._ref.clone(),
tp.get_time_spent(),
work_product,
);
return Ok(Some(create_task(ut, &ct).await?));
}
Ok(None)
}

pub fn select_task_for_owner(tasks: Vec<Task>, owner: &User) -> Option<Task> {
for t in tasks {
if t.State != "Completed"
&& t.Owner
.as_ref()
.map(|o| o._refObjectUUID.as_deref())
.flatten()
.map(|o| o == owner._refObjectUUID)
.unwrap_or(false)
{
return Some(t);
}
}
None
}

0 comments on commit dbc16a5

Please sign in to comment.