use super::*;
pub const NO_REVIEW_ITEM_TYPE_ERR: &str = "No Supervision (review item) type found";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Reviewed {
Yes,
No,
NoSubmission,
Inapplicable,
}
impl Reviewed {
pub fn verdict<'a>(seq: impl Iterator<Item = &'a Self>) -> Self {
let mut iter = seq.into_iter();
match iter.next() {
None => {
Self::Inapplicable
}
Some(reviewed) => match reviewed {
Self::Inapplicable => {
Self::Inapplicable
}
Self::Yes => {
Self::Yes
}
Self::No => {
Self::No
}
Self::NoSubmission => {
Self::verdict(iter)
}
},
}
}
fn from_notice_option(notice: Option<&Notice>, reviewer_role: &ProductionRole) -> Self {
match notice {
Some(notice) => notice.is_reviewed_by(reviewer_role),
None => Reviewed::NoSubmission,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DailyNotice {
#[serde(rename = "wip_review", skip_serializing_if = "Option::is_none")]
pub(crate) wip: Option<Notice>,
#[serde(rename = "feedback_review", skip_serializing_if = "Option::is_none")]
pub(crate) feedback: Option<Notice>,
#[serde(rename = "approval_review", skip_serializing_if = "Option::is_none")]
pub(crate) approval: Option<Notice>,
#[serde(rename = "client_feedback", skip_serializing_if = "Option::is_none")]
pub(crate) client: Option<Notice>,
}
impl DailyNotice {
pub fn review_type(&self, typ: &Supervision) -> Option<&Notice> {
match typ {
Supervision::Wip => self.wip.as_ref(),
Supervision::Feedback => self.feedback.as_ref(),
Supervision::Approval => self.approval.as_ref(),
Supervision::Client => self.client.as_ref(),
}
}
pub fn is_empty(&self) -> bool {
self.wip.is_none()
&& self.feedback.is_none()
&& self.approval.is_none()
&& self.client.is_none()
}
pub fn trim(&mut self, evaluation_date: &NaiveDate, trim_options: &NaiveTrimOptions) {
Notice::trim(&mut self.wip, evaluation_date, trim_options.wip.as_ref());
Notice::trim(
&mut self.feedback,
evaluation_date,
trim_options.feedback.as_ref(),
);
Notice::trim(
&mut self.approval,
evaluation_date,
trim_options.approval.as_ref(),
);
Notice::trim(
&mut self.client,
evaluation_date,
trim_options.client.as_ref(),
);
}
pub fn is_reviewed_by(&self, reviewer_role: &ProductionRole) -> DailyReview {
DailyReview {
wip: Reviewed::from_notice_option(self.wip.as_ref(), reviewer_role),
feedback: Reviewed::from_notice_option(self.feedback.as_ref(), reviewer_role),
approval: Reviewed::from_notice_option(self.approval.as_ref(), reviewer_role),
client: Reviewed::from_notice_option(self.client.as_ref(), reviewer_role),
}
}
pub fn parse_submit_time(&mut self, fmt: &str) {
if let Some(n) = self.wip.as_mut() {
n.parse_submit_time(fmt);
};
if let Some(n) = self.feedback.as_mut() {
n.parse_submit_time(fmt);
};
if let Some(n) = self.approval.as_mut() {
n.parse_submit_time(fmt);
};
if let Some(n) = self.client.as_mut() {
n.parse_submit_time(fmt);
};
}
}
#[derive(Debug, Clone)]
pub struct DailyReview {
pub wip: Reviewed,
pub feedback: Reviewed,
pub approval: Reviewed,
pub client: Reviewed,
}
impl DailyReview {
pub fn review_item(&self, typ: &Supervision) -> &Reviewed {
match typ {
Supervision::Wip => &self.wip,
Supervision::Feedback => &self.feedback,
Supervision::Approval => &self.approval,
Supervision::Client => &self.client,
}
}
}
pub struct NoticeWriteBuilder(Notice);
impl NoticeWriteBuilder {
pub fn new(notice: Notice) -> Self {
Self(notice)
}
fn created_at(mut self, submit_time: &DateTime<Local>) -> AnyResult<Self> {
self.0.created_at_str = Some(submit_time_str(submit_time)?);
Ok(self)
}
pub fn finish(self, submit_time: &DateTime<Local>) -> AnyResult<Notice> {
Ok(self.created_at(submit_time)?.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Notice {
#[serde(skip)]
pub typ: Option<Supervision>,
#[serde(rename = "submit_enabled")]
enabled: Option<bool>,
#[serde(rename = "submit_time")]
created_at_str: Option<String>,
#[serde(skip)]
created_at: Option<NaiveTime>,
#[serde(rename = "viewed_by_AD")]
ad_viewed: Option<bool>,
}
impl Notice {
pub fn draft(typ: Supervision) -> Self {
Self {
typ: Some(typ),
enabled: Some(false),
created_at_str: None,
created_at: None,
ad_viewed: Some(false),
}
}
#[cfg(feature = "gui")]
pub fn draft_unwrap_ui(&mut self, ui: &mut egui::Ui) -> egui::Response {
let typ = self.typ.as_ref().unwrap();
let enabled = self.enabled.as_mut().unwrap();
ui.toggle_value(enabled, typ.signaling_label())
.on_hover_text(typ.draft_tooltip())
}
pub fn enabled(typ: Supervision, target: bool) -> Self {
Self {
typ: Some(typ),
enabled: Some(target),
created_at_str: None,
created_at: None,
ad_viewed: Some(false),
}
}
pub fn reviewed(
typ: Supervision,
reviewer_role: &ProductionRole,
target: bool,
) -> Option<Self> {
match reviewer_role {
ProductionRole::ArtDirector | ProductionRole::TechSupport => Some(Self {
typ: Some(typ),
enabled: Some(true),
created_at_str: None,
created_at: None,
ad_viewed: Some(target),
}),
_ => {
None
}
}
}
pub fn reviewed_enabled(
mut self,
reviewer_role: &ProductionRole,
target: bool,
) -> Option<Self> {
match reviewer_role {
ProductionRole::ArtDirector | ProductionRole::TechSupport => {
self.enabled = Some(true);
self.ad_viewed = Some(target);
Some(self)
}
_ => {
None
}
}
}
pub fn is_enabled_or_false(&self) -> bool {
match &self.enabled {
Some(enabled) => *enabled,
None => false,
}
}
pub fn enabled_mut(&mut self, notice: Result<Self, DatabaseError>) {
match notice {
Err(_) => {
self.enabled = Some(false);
}
Ok(notice) => {
self.enabled = Some(notice.enabled.unwrap_or(false));
}
}
}
fn review_type_as_str(&self) -> AnyResult<&str> {
Ok(self.typ.as_ref().context(NO_REVIEW_ITEM_TYPE_ERR)?.as_ref())
}
pub fn submit_target_desc(&self) -> AnyResult<String> {
let desc = match self
.enabled
.as_ref()
.context("No \"submit_enabled\" target found")?
{
true => "Enabled Successfully",
false => "Disabled Successfully",
};
Ok(desc.to_string())
}
pub fn submit_type_desc(&self) -> AnyResult<String> {
Ok(format!("Review item type: {}", self.review_type_as_str()?,))
}
fn trim(
notice: &mut Option<Self>,
evaluation_date: &NaiveDate,
range: Option<&AnyResult<Vec<NaiveDate>>>,
) {
if let Some(_) = notice {
match range {
Some(Ok(range)) => {
if !range.contains(evaluation_date) {
notice.take();
};
}
Some(Err(_)) => {
notice.take();
}
None => {} };
};
}
fn parse_submit_time(&mut self, fmt: &str) {
if let Some(created_at) = &self.created_at_str {
self.created_at = NaiveTime::parse_from_str(&created_at, fmt).ok();
}
}
pub fn is_reviewed_by(&self, reviewer_role: &ProductionRole) -> Reviewed {
match self.enabled {
Some(enabled) => {
if !enabled {
return Reviewed::NoSubmission;
};
match reviewer_role {
ProductionRole::ArtDirector => {
match self.ad_viewed {
Some(ad_viewed) => match ad_viewed {
true => Reviewed::Yes,
false => Reviewed::No,
},
None => {
Reviewed::No
}
}
}
_ => Reviewed::Inapplicable,
}
}
None => {
Reviewed::Yes
}
}
}
}
#[derive(Debug)]
pub struct NoticeReadBuilder(Notice);
impl NoticeReadBuilder {
pub fn new(notice: Notice) -> Self {
Self(notice)
}
pub fn finish(mut self, _typ: &Supervision) -> AnyResult<Notice> {
self.0.parse_submit_time(time_fmt_cfg()?);
Ok(self.0)
}
}
pub struct NaiveTrimOptions {
wip: Option<AnyResult<Vec<NaiveDate>>>,
feedback: Option<AnyResult<Vec<NaiveDate>>>,
approval: Option<AnyResult<Vec<NaiveDate>>>,
client: Option<AnyResult<Vec<NaiveDate>>>,
}
impl From<&TrimOptions> for NaiveTrimOptions {
fn from(trim: &TrimOptions) -> Self {
Self {
wip: trim.wip.as_ref().map(|r| r.dates_between()),
feedback: trim.feedback.as_ref().map(|r| r.dates_between()),
approval: trim.approval.as_ref().map(|r| r.dates_between()),
client: trim.client.as_ref().map(|r| r.dates_between()),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct TrimOptions {
pub wip: Option<DateRange>,
pub feedback: Option<DateRange>,
pub approval: Option<DateRange>,
pub client: Option<DateRange>,
}
impl TrimOptions {
pub fn none() -> Self {
Self::default()
}
pub fn client_special(client: DateRange, others: Option<DateRange>) -> Self {
Self {
wip: others.clone(),
feedback: others.clone(),
approval: others,
client: Some(client),
}
}
pub fn homogeneous(range: Option<DateRange>) -> Self {
Self {
wip: range.clone(),
feedback: range.clone(),
approval: range.clone(),
client: range,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reviewed_verdicts() {
let seq = [Reviewed::NoSubmission, Reviewed::NoSubmission, Reviewed::No];
let reviewed = Reviewed::verdict(seq.iter());
assert_eq!(reviewed, Reviewed::No);
let seq = [
Reviewed::NoSubmission,
Reviewed::NoSubmission,
Reviewed::Yes,
Reviewed::Inapplicable,
];
let reviewed = Reviewed::verdict(seq.iter());
assert_eq!(reviewed, Reviewed::Yes);
let seq = [
Reviewed::No,
Reviewed::NoSubmission,
Reviewed::NoSubmission,
Reviewed::Yes,
];
let reviewed = Reviewed::verdict(seq.iter());
assert_eq!(reviewed, Reviewed::No);
let seq = [];
let reviewed = Reviewed::verdict(seq.iter());
assert_eq!(reviewed, Reviewed::Yes);
let seq = [
Reviewed::NoSubmission,
Reviewed::NoSubmission,
Reviewed::NoSubmission,
Reviewed::NoSubmission,
Reviewed::NoSubmission,
];
let reviewed = Reviewed::verdict(seq.iter());
assert_eq!(reviewed, Reviewed::Yes);
}
}