Skip to content

Commit

Permalink
Merge pull request #594 from thoth-pub/feature/583_add_withdrawn_date
Browse files Browse the repository at this point in the history
Feature/583 add withdrawn date
  • Loading branch information
brendan-oconnell authored Apr 25, 2024
2 parents 07879d1 + 1aaf824 commit 965f46a
Show file tree
Hide file tree
Showing 31 changed files with 476 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- [583](https://github.com/thoth-pub/thoth/issues/583) - Add new field, Permanently Withdrawn Date, to Work for Out-of-print or Withdrawn from Sale Works.

### Changed
- [218](https://github.com/thoth-pub/thoth/issues/218) - Make series ISSN optional

Expand Down
7 changes: 7 additions & 0 deletions thoth-api/migrations/v0.12.3/down.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@ ALTER TABLE series

ALTER TABLE series
ALTER COLUMN issn_digital SET NOT NULL;

ALTER TABLE work
DROP CONSTRAINT work_active_withdrawn_date_check,
DROP CONSTRAINT work_inactive_no_withdrawn_date_check,
DROP CONSTRAINT work_withdrawn_date_after_publication_date_check,
DROP COLUMN withdrawn_date;

19 changes: 19 additions & 0 deletions thoth-api/migrations/v0.12.3/up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,22 @@ ALTER TABLE series
ALTER TABLE series
ALTER COLUMN issn_digital DROP NOT NULL;

ALTER TABLE work
ADD COLUMN withdrawn_date DATE;

UPDATE work
SET withdrawn_date = updated_at
WHERE (work_status = 'withdrawn-from-sale'
OR work_status = 'out-of-print');

ALTER TABLE work
ADD CONSTRAINT work_active_withdrawn_date_check CHECK
((work_status = 'withdrawn-from-sale' OR work_status = 'out-of-print')
OR (work_status NOT IN ('withdrawn-from-sale', 'out-of-print') AND withdrawn_date IS NULL)),

ADD CONSTRAINT work_inactive_no_withdrawn_date_check CHECK
(((work_status = 'withdrawn-from-sale' OR work_status = 'out-of-print') AND withdrawn_date IS NOT NULL)
OR (work_status NOT IN ('withdrawn-from-sale', 'out-of-print'))),

ADD CONSTRAINT work_withdrawn_date_after_publication_date_check CHECK
(withdrawn_date IS NULL OR (publication_date < withdrawn_date));
15 changes: 14 additions & 1 deletion thoth-api/src/graphql/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::model::publisher::*;
use crate::model::reference::*;
use crate::model::series::*;
use crate::model::subject::*;
use crate::model::work::crud::WorkValidation;
use crate::model::work::*;
use crate::model::work_relation::*;
use crate::model::Convert;
Expand Down Expand Up @@ -1516,6 +1517,8 @@ impl MutationRoot {
.account_access
.can_edit(publisher_id_from_imprint_id(&context.db, data.imprint_id)?)?;

data.validate()?;

Work::create(&context.db, &data).map_err(|e| e.into())
}

Expand Down Expand Up @@ -1705,17 +1708,20 @@ impl MutationRoot {
work.can_be_chapter(&context.db)?;
}

data.validate()?;
let account_id = context.token.jwt.as_ref().unwrap().account_id(&context.db);
// update the work and, if it succeeds, synchronise its children statuses and pub. date
match work.update(&context.db, &data, &account_id) {
Ok(w) => {
// update chapters if their pub. data or work_status doesn't match the parent's
// update chapters if their pub. data, withdrawn_date or work_status doesn't match the parent's
for child in work.children(&context.db)? {
if child.publication_date != w.publication_date
|| child.work_status != w.work_status
|| child.withdrawn_date != w.withdrawn_date
{
let mut data: PatchWork = child.clone().into();
data.publication_date = w.publication_date;
data.withdrawn_date = w.withdrawn_date;
data.work_status = w.work_status.clone();
child.update(&context.db, &data, &account_id)?;
}
Expand Down Expand Up @@ -2279,6 +2285,13 @@ impl Work {
self.publication_date
}

#[graphql(
description = "Date a work was withdrawn from publication. Only applies to out of print and withdrawn from sale works."
)]
pub fn withdrawn_date(&self) -> Option<NaiveDate> {
self.withdrawn_date
}

pub fn place(&self) -> Option<&String> {
self.place.as_ref()
}
Expand Down
23 changes: 21 additions & 2 deletions thoth-api/src/model/work/crud.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
NewWork, NewWorkHistory, PatchWork, Work, WorkField, WorkHistory, WorkOrderBy, WorkStatus,
WorkType,
NewWork, NewWorkHistory, PatchWork, Work, WorkField, WorkHistory, WorkOrderBy, WorkProperties,
WorkStatus, WorkType,
};
use crate::graphql::model::TimeExpression;
use crate::graphql::utils::{Direction, Expression};
Expand Down Expand Up @@ -176,6 +176,10 @@ impl Crud for Work {
Direction::Asc => query.order(dsl::publication_date.asc()),
Direction::Desc => query.order(dsl::publication_date.desc()),
},
WorkField::WithdrawnDate => match order.direction {
Direction::Asc => query.order(dsl::withdrawn_date.asc()),
Direction::Desc => query.order(dsl::withdrawn_date.desc()),
},
WorkField::Place => match order.direction {
Direction::Asc => query.order(dsl::place.asc()),
Direction::Desc => query.order(dsl::place.desc()),
Expand Down Expand Up @@ -399,6 +403,21 @@ impl DbInsert for NewWorkHistory {
db_insert!(work_history::table);
}

pub trait WorkValidation
where
Self: WorkProperties,
{
fn validate(&self) -> ThothResult<()> {
self.withdrawn_date_error()?;
self.no_withdrawn_date_error()?;
self.withdrawn_date_before_publication_date_error()
}
}

impl WorkValidation for NewWork {}

impl WorkValidation for PatchWork {}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
101 changes: 101 additions & 0 deletions thoth-api/src/model/work/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
use std::fmt;
use strum::Display;
use strum::EnumString;
use thoth_errors::{ThothError, ThothResult};
use uuid::Uuid;

use crate::graphql::utils::Direction;
Expand Down Expand Up @@ -98,6 +99,7 @@ pub enum WorkField {
#[strum(serialize = "DOI")]
Doi,
PublicationDate,
WithdrawnDate,
Place,
PageCount,
PageBreakdown,
Expand Down Expand Up @@ -144,6 +146,7 @@ pub struct Work {
pub imprint_id: Uuid,
pub doi: Option<Doi>,
pub publication_date: Option<NaiveDate>,
pub withdrawn_date: Option<NaiveDate>,
pub place: Option<String>,
pub page_count: Option<i32>,
pub page_breakdown: Option<String>,
Expand Down Expand Up @@ -184,6 +187,7 @@ pub struct WorkWithRelations {
pub edition: Option<i32>,
pub doi: Option<Doi>,
pub publication_date: Option<String>,
pub withdrawn_date: Option<String>,
pub place: Option<String>,
pub page_count: Option<i32>,
pub page_breakdown: Option<String>,
Expand Down Expand Up @@ -234,6 +238,7 @@ pub struct NewWork {
pub imprint_id: Uuid,
pub doi: Option<Doi>,
pub publication_date: Option<NaiveDate>,
pub withdrawn_date: Option<NaiveDate>,
pub place: Option<String>,
pub page_count: Option<i32>,
pub page_breakdown: Option<String>,
Expand Down Expand Up @@ -275,6 +280,7 @@ pub struct PatchWork {
pub imprint_id: Uuid,
pub doi: Option<Doi>,
pub publication_date: Option<NaiveDate>,
pub withdrawn_date: Option<NaiveDate>,
pub place: Option<String>,
pub page_count: Option<i32>,
pub page_breakdown: Option<String>,
Expand Down Expand Up @@ -326,6 +332,93 @@ pub struct WorkOrderBy {
pub direction: Direction,
}

impl WorkStatus {
fn is_withdrawn_out_of_print(&self) -> bool {
matches!(self, WorkStatus::OutOfPrint | WorkStatus::WithdrawnFromSale)
}
}

pub trait WorkProperties {
fn work_status(&self) -> &WorkStatus;
fn publication_date(&self) -> &Option<NaiveDate>;
fn withdrawn_date(&self) -> &Option<NaiveDate>;

fn is_withdrawn_out_of_print(&self) -> bool {
self.work_status().is_withdrawn_out_of_print()
}

fn has_withdrawn_date(&self) -> bool {
self.withdrawn_date().is_some()
}

fn withdrawn_date_error(&self) -> ThothResult<()> {
if !self.is_withdrawn_out_of_print() && self.has_withdrawn_date() {
return Err(ThothError::WithdrawnDateError);
}
Ok(())
}

fn no_withdrawn_date_error(&self) -> ThothResult<()> {
if self.is_withdrawn_out_of_print() && !self.has_withdrawn_date() {
return Err(ThothError::NoWithdrawnDateError);
}
Ok(())
}

fn withdrawn_date_before_publication_date_error(&self) -> ThothResult<()> {
if let (Some(withdrawn_date), Some(publication_date)) =
(self.withdrawn_date(), self.publication_date())
{
if withdrawn_date < publication_date {
return Err(ThothError::WithdrawnDateBeforePublicationDateError);
}
}
Ok(())
}
}

impl WorkProperties for Work {
fn work_status(&self) -> &WorkStatus {
&self.work_status
}

fn withdrawn_date(&self) -> &Option<NaiveDate> {
&self.withdrawn_date
}

fn publication_date(&self) -> &Option<NaiveDate> {
&self.publication_date
}
}

impl WorkProperties for NewWork {
fn work_status(&self) -> &WorkStatus {
&self.work_status
}

fn withdrawn_date(&self) -> &Option<NaiveDate> {
&self.withdrawn_date
}

fn publication_date(&self) -> &Option<NaiveDate> {
&self.publication_date
}
}

impl WorkProperties for PatchWork {
fn work_status(&self) -> &WorkStatus {
&self.work_status
}

fn withdrawn_date(&self) -> &Option<NaiveDate> {
&self.withdrawn_date
}

fn publication_date(&self) -> &Option<NaiveDate> {
&self.publication_date
}
}

impl Work {
pub fn compile_fulltitle(&self) -> String {
if let Some(subtitle) = &self.subtitle.clone() {
Expand Down Expand Up @@ -376,6 +469,7 @@ impl From<Work> for PatchWork {
imprint_id: w.imprint_id,
doi: w.doi,
publication_date: w.publication_date,
withdrawn_date: w.withdrawn_date,
place: w.place,
page_count: w.page_count,
page_breakdown: w.page_breakdown,
Expand Down Expand Up @@ -480,6 +574,7 @@ fn test_workfield_display() {
assert_eq!(format!("{}", WorkField::Edition), "Edition");
assert_eq!(format!("{}", WorkField::Doi), "DOI");
assert_eq!(format!("{}", WorkField::PublicationDate), "PublicationDate");
assert_eq!(format!("{}", WorkField::WithdrawnDate), "WithdrawnDate");
assert_eq!(format!("{}", WorkField::Place), "Place");
assert_eq!(format!("{}", WorkField::PageCount), "PageCount");
assert_eq!(format!("{}", WorkField::PageBreakdown), "PageBreakdown");
Expand Down Expand Up @@ -621,6 +716,10 @@ fn test_workfield_fromstr() {
WorkField::from_str("PublicationDate").unwrap(),
WorkField::PublicationDate
);
assert_eq!(
WorkField::from_str("WithdrawnDate").unwrap(),
WorkField::WithdrawnDate
);
assert_eq!(WorkField::from_str("Place").unwrap(), WorkField::Place);
assert_eq!(
WorkField::from_str("PageCount").unwrap(),
Expand Down Expand Up @@ -727,6 +826,7 @@ fn test_work_into_patchwork() {
imprint_id: Uuid::parse_str("00000000-0000-0000-BBBB-000000000002").unwrap(),
doi: Some(Doi::from_str("https://doi.org/10.00001/BOOK.0001").unwrap()),
publication_date: chrono::NaiveDate::from_ymd_opt(1999, 12, 31),
withdrawn_date: None,
place: Some("León, Spain".to_string()),
page_count: Some(123),
page_breakdown: None,
Expand Down Expand Up @@ -766,6 +866,7 @@ fn test_work_into_patchwork() {
assert_eq!(work.imprint_id, patch_work.imprint_id);
assert_eq!(work.doi, patch_work.doi);
assert_eq!(work.publication_date, patch_work.publication_date);
assert_eq!(work.withdrawn_date, patch_work.withdrawn_date);
assert_eq!(work.place, patch_work.place);
assert_eq!(work.page_count, patch_work.page_count);
assert_eq!(work.page_breakdown, patch_work.page_breakdown);
Expand Down
1 change: 1 addition & 0 deletions thoth-api/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ table! {
imprint_id -> Uuid,
doi -> Nullable<Text>,
publication_date -> Nullable<Date>,
withdrawn_date -> Nullable<Date>,
place -> Nullable<Text>,
page_count -> Nullable<Int4>,
page_breakdown -> Nullable<Text>,
Expand Down
Loading

0 comments on commit 965f46a

Please sign in to comment.