Allow publisher articles to have a publication date.

master
Pacman Ghost 2 years ago
parent 95e662c9f6
commit 49c608186c
  1. 28
      alembic/versions/702eeb219037_allow_articles_to_have_a_publication_.py
  2. 7
      asl_articles/articles.py
  3. 1
      asl_articles/models.py
  4. 11
      asl_articles/tests/fixtures/publisher-article-dates.json
  5. 72
      asl_articles/tests/test_articles.py
  6. 15
      web/src/ArticleSearchResult.js
  7. 64
      web/src/ArticleSearchResult2.js

@ -0,0 +1,28 @@
"""Allow articles to have a publication date.
Revision ID: 702eeb219037
Revises: a33edb7272a2
Create Date: 2021-11-16 20:41:37.454305
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '702eeb219037'
down_revision = 'a33edb7272a2'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('article', sa.Column('article_date', sa.String(length=100), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('article', 'article_date')
# ### end Alembic commands ###

@ -19,7 +19,7 @@ from asl_articles.utils import get_request_args, clean_request_args, clean_tags,
_logger = logging.getLogger( "db" )
_FIELD_NAMES = [ "*article_title", "article_subtitle", "article_snippet", "article_pageno",
_FIELD_NAMES = [ "*article_title", "article_subtitle", "article_date", "article_snippet", "article_pageno",
"article_url", "article_tags", "pub_id", "publ_id"
]
@ -51,6 +51,7 @@ def get_article_vals( article, deep ):
"article_subtitle": article.article_subtitle,
"article_image_id": article.article_id if article.article_image else None,
"article_authors": [ get_author_vals( a.parent_author ) for a in authors ],
"article_date": article.article_date,
"article_snippet": article.article_snippet,
"article_pageno": article.article_pageno,
"article_url": article.article_url,
@ -94,6 +95,8 @@ def create_article():
# create the new article
vals[ "time_created" ] = datetime.datetime.now()
if not vals.get( "publ_id" ):
vals.pop( "article_date", None )
article = Article( **vals )
db.session.add( article )
db.session.flush()
@ -223,6 +226,8 @@ def update_article():
_set_seqno( article, vals["pub_id"] )
vals[ "time_updated" ] = datetime.datetime.now()
apply_attrs( article, vals )
if not vals.get( "publ_id" ):
article.article_date = None
_save_authors( article )
_save_scenarios( article )
_save_image( article )

@ -63,6 +63,7 @@ class Article( db.Model ):
article_id = db.Column( db.Integer, primary_key=True )
article_title = db.Column( db.String(200), nullable=False )
article_subtitle = db.Column( db.String(200) )
article_date = db.Column( db.String(100) ) # nb: this is just a display string
article_snippet = db.Column( db.String(5000) )
article_seqno = db.Column( db.Integer )
article_pageno = db.Column( db.String(20) )

@ -0,0 +1,11 @@
{
"publisher": [
{ "publ_id": 1, "publ_name": "Avalon Hill" }
],
"publication": [
{ "pub_id": 20, "pub_name": "ASL Journal", "publ_id": 1 }
]
}

@ -484,6 +484,61 @@ def test_publisher_articles( webdriver, flask_app, dbconn ): #pylint: disable=to
# ---------------------------------------------------------------------
def test_publisher_article_dates( webdriver, flask_app, dbconn ):
"""Test "published" dates for publisher articles."""
# initialize
init_tests( webdriver, flask_app, dbconn, disable_constraints=False, fixtures="publisher-article-dates.json" )
# initialize
article_title, article_date = "test article", "1st January, 2000"
article_sr = None
def check_article_date( has_date ):
# check the article's publication date
def do_check():
elem = find_child( ".article_date", article_sr )
article_id = article_sr.get_attribute( "testing--article_id" )
row = get_article_row( dbconn, article_id, ["article_date"] )
if has_date:
return elem.text == article_date and row[0] == article_date
else:
return not elem and not row[0]
wait_for( 2, do_check )
# create an article associated with a publication
create_article( {
"title": article_title,
"publication": "ASL Journal",
"snippet": "This is a test article.",
"pageno": 42,
"authors": [ "+Joe Blow" ]
} )
article_sr = wait_for( 2, lambda: find_search_result( article_title ) )
check_article_date( False )
# change the article to be associated with a publisher
edit_article( article_sr, {
"publisher": "Avalon Hill"
}, expected_constraints = [
"The article date was not specified."
], accept_constraints=True )
check_article_date( False )
# give the article a published date
edit_article( article_sr, {
"article_date": article_date
} )
check_article_date( True )
# change the article back to the publication
edit_article( article_sr, {
"publication": "ASL Journal"
} )
check_article_date( False )
# ---------------------------------------------------------------------
def test_unicode( webdriver, flask_app, dbconn ):
"""Test Unicode content."""
@ -636,7 +691,10 @@ def test_article_ratings( webdriver, flask_app, dbconn ):
# ---------------------------------------------------------------------
def create_article( vals, toast_type="info", expected_error=None, expected_constraints=None, dlg=None ):
def create_article( vals, toast_type="info",
expected_error=None, expected_constraints=None, accept_constraints=False,
dlg=None
):
"""Create a new article."""
# initialize
@ -656,7 +714,9 @@ def create_article( vals, toast_type="info", expected_error=None, expected_const
return dlg # nb: the dialog is left on-screen
elif expected_constraints:
# we were expecting constraint warnings, confirm them
check_constraint_warnings( "Do you want to create this article?", expected_constraints, "cancel" )
check_constraint_warnings( "Do you want to create this article?",
expected_constraints, "ok" if accept_constraints else "cancel"
)
return dlg # nb: the dialog is left on-screen
else:
# we were expecting the create to work, confirm this
@ -668,7 +728,9 @@ def create_article( vals, toast_type="info", expected_error=None, expected_const
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def edit_article( sr, vals, toast_type="info", expected_error=None, expected_constraints=None ): #pylint: disable=too-many-branches
def edit_article( sr, vals, toast_type="info",
expected_error=None, expected_constraints=None, accept_constraints=False
): #pylint: disable=too-many-branches
"""Edit a article's details."""
# initialize
@ -690,7 +752,9 @@ def edit_article( sr, vals, toast_type="info", expected_error=None, expected_con
return dlg # nb: the dialog is left on-screen
elif expected_constraints:
# we were expecting constraint warnings, confirm them
check_constraint_warnings( "Do you want to update this article?", expected_constraints, "cancel" )
check_constraint_warnings( "Do you want to update this article?",
expected_constraints, "ok" if accept_constraints else "cancel"
)
return dlg # nb: the dialog is left on-screen
else:
# we were expecting the update to work, confirm this

@ -140,9 +140,18 @@ export class ArticleSearchResult extends React.Component
<div className="snippet" dangerouslySetInnerHTML={{__html: display_snippet}} />
</div>
<div className="footer">
{ authors.length > 0 && <div className="authors"> By {makeCommaList(authors)} </div> }
{ scenarios.length > 0 && <div className="scenarios"> Scenarios: {makeCommaList(scenarios)} </div> }
{ tags.length > 0 && <div className="tags"> Tags: {tags} </div> }
{ authors.length > 0 &&
<div className="authors"> By {makeCommaList(authors)} </div>
}
{ this.props.data.article_date &&
<div> <label>Published:</label> <span className="article_date"> {this.props.data.article_date} </span> </div>
}
{ scenarios.length > 0 &&
<div className="scenarios"> Scenarios: {makeCommaList(scenarios)} </div>
}
{ tags.length > 0 &&
<div className="tags"> Tags: {tags} </div>
}
</div>
</div> ) ;
}

@ -22,16 +22,19 @@ export class ArticleSearchResult2
let parentMode = vals.publ_id ? "publisher" : "publication" ;
let publicationParentRowRef = null ;
let publisherParentRowRef = null ;
let articleDateRef = null ;
function onPublicationParent() {
parentMode = "publication" ;
publicationParentRowRef.style.display = "flex" ;
publisherParentRowRef.style.display = "none" ;
articleDateRef.style.display = "none" ;
refs.pub_id.focus() ;
}
function onPublisherParent() {
parentMode = "publisher" ;
publicationParentRowRef.style.display = "none" ;
publisherParentRowRef.style.display = "flex" ;
articleDateRef.style.display = "flex" ;
refs.publ_id.focus() ;
}
@ -154,6 +157,7 @@ export class ArticleSearchResult2
// prepare the form content
/* eslint-disable jsx-a11y/img-redundant-alt */
const content = <div>
<div style={{display:"flex"}}>
<div className="image-container">
<div className="row image">
<img src={imageUrl} className="image"
@ -174,33 +178,40 @@ export class ArticleSearchResult2
/>
</div>
</div>
<div className="row title"> <label className="top"> Title: </label>
<input type="text" defaultValue={vals.article_title} autoFocus ref={r => refs.article_title=r} />
</div>
<div className="row subtitle"> <label className="top"> Subtitle: </label>
<input type="text" defaultValue={vals.article_subtitle} ref={r => refs.article_subtitle=r} />
</div>
<div className="row publication" style={{display:parentMode==="publication"?"flex":"none"}} ref={r => publicationParentRowRef=r} >
<label className="select top parent-mode"
title = "Click to associate this article with a publisher."
onClick = {onPublisherParent}
> Publication: </label>
<Select className="react-select" classNamePrefix="react-select" options={publications} isSearchable={true}
defaultValue = {currPub}
ref = { r => refs.pub_id=r }
/>
<input className="pageno" type="text" defaultValue={vals.article_pageno} ref={r => refs.article_pageno=r} title="Page number." />
</div>
<div className="row publisher" style={{display:parentMode==="publisher"?"flex":"none"}} ref={r => publisherParentRowRef=r} >
<label className="select top parent-mode"
title="Click to associate this article with a publication."
onClick = {onPublicationParent}
> Publisher: </label>
<Select className="react-select" classNamePrefix="react-select" options={publishers} isSearchable={true}
defaultValue = {currPubl}
ref = { r => refs.publ_id=r }
/>
<div style={{flexGrow:1}}>
<div className="row title"> <label className="top"> Title: </label>
<input type="text" defaultValue={vals.article_title} autoFocus ref={r => refs.article_title=r} />
</div>
<div className="row subtitle"> <label className="top"> Subtitle: </label>
<input type="text" defaultValue={vals.article_subtitle} ref={r => refs.article_subtitle=r} />
</div>
<div className="row publication" style={{display:parentMode==="publication"?"flex":"none"}} ref={r => publicationParentRowRef=r} >
<label className="select top parent-mode"
title = "Click to associate this article with a publisher."
onClick = {onPublisherParent}
> Publication: </label>
<Select className="react-select" classNamePrefix="react-select" options={publications} isSearchable={true}
defaultValue = {currPub}
ref = { r => refs.pub_id=r }
/>
<input className="pageno" type="text" defaultValue={vals.article_pageno} ref={r => refs.article_pageno=r} title="Page number." />
</div>
<div className="row publisher" style={{display:parentMode==="publisher"?"flex":"none"}} ref={r => publisherParentRowRef=r} >
<label className="select top parent-mode"
title="Click to associate this article with a publication."
onClick = {onPublicationParent}
> Publisher: </label>
<Select className="react-select" classNamePrefix="react-select" options={publishers} isSearchable={true}
defaultValue = {currPubl}
ref = { r => refs.publ_id=r }
/>
</div>
<div className="row article_date" style={{display:parentMode==="publisher"?"flex":"none"}}ref={r => articleDateRef=r} >
<label className="select top"> Date: </label>
<input className="article_date" type="text" defaultValue={vals.article_date} ref={r => refs.article_date=r} />
</div>
</div>
</div>
<div className="row snippet"> <label> Snippet: </label>
<textarea defaultValue={vals.article_snippet} ref={r => refs.article_snippet=r} />
</div>
@ -286,6 +297,7 @@ export class ArticleSearchResult2
[ () => newVals.article_pageno === "" && newVals.pub_id !== null, "No page number was specified.", refs.article_pageno ],
[ () => newVals.article_pageno !== "" && newVals.pub_id === null, "A page number was specified but no publication.", refs.pub_id ],
[ () => newVals.article_pageno !== "" && !isNumeric(newVals.article_pageno), "The page number is not numeric.", refs.article_pageno ],
[ () => newVals.publ_id && newVals.article_date === "", "The article date was not specified.", refs.article_date ],
[ () => newVals.article_snippet === "", "No snippet was provided.", refs.article_snippet ],
[ () => newVals.article_authors.length === 0, "No authors were specified.", refs.article_authors ],
[ () => newVals.article_tags && newVals.article_tags.length === 1 && newVals.article_tags[0] === "tips", "This tip has no other tags." ],

Loading…
Cancel
Save