diff --git a/alembic/versions/3d58e8ebf8c6_added_a_seq_for_publications.py b/alembic/versions/3d58e8ebf8c6_added_a_seq_for_publications.py new file mode 100644 index 0000000..0f23d0c --- /dev/null +++ b/alembic/versions/3d58e8ebf8c6_added_a_seq_for_publications.py @@ -0,0 +1,28 @@ +"""Added a seq# for publications. + +Revision ID: 3d58e8ebf8c6 +Revises: 41cfc117c809 +Create Date: 2020-03-18 10:26:25.801673 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '3d58e8ebf8c6' +down_revision = '41cfc117c809' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('publication', sa.Column('pub_seqno', sa.Integer(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('publication', 'pub_seqno') + # ### end Alembic commands ### diff --git a/asl_articles/models.py b/asl_articles/models.py index 1a63e97..fc4dc40 100644 --- a/asl_articles/models.py +++ b/asl_articles/models.py @@ -38,6 +38,7 @@ class Publication( db.Model ): pub_date = db.Column( db.String(100) ) # nb: this is just a display string pub_description = db.Column( db.String(1000) ) pub_url = db.Column( db.String(500) ) + pub_seqno = db.Column( db.Integer ) pub_tags = db.Column( db.String(1000) ) publ_id = db.Column( db.Integer, db.ForeignKey( Publisher.__table__.c.publ_id, ondelete="CASCADE" ) diff --git a/asl_articles/publications.py b/asl_articles/publications.py index 517fdff..9f63a7a 100644 --- a/asl_articles/publications.py +++ b/asl_articles/publications.py @@ -5,6 +5,7 @@ import base64 import logging from flask import request, jsonify, abort +from sqlalchemy.sql.expression import func from asl_articles import app, db from asl_articles.models import Publication, PublicationImage, Article @@ -57,6 +58,7 @@ def get_publication_vals( pub, include_articles, add_type=False ): "pub_date": pub.pub_date, "pub_description": pub.pub_description, "pub_url": pub.pub_url, + "pub_seqno": pub.pub_seqno, "pub_image_id": pub.pub_id if pub.pub_image else None, "pub_tags": decode_tags( pub.pub_tags ), "publ_id": pub.publ_id, @@ -72,7 +74,16 @@ def get_publication_vals( pub, include_articles, add_type=False ): def get_publication_sort_key( pub ): """Get a publication's sort key.""" # NOTE: This is used to sort publications within their parent publisher. - return int( pub.time_created.timestamp() ) if pub.time_created else 0 + # FUDGE! We used to sort by time_created, but later added a seq#, so we now want to + # sort by seq# first, then fallback to time_created. + # NOTE: We assume that the seq# values are all small, and less than any timestamp. + # This means that any seq# value will appear before any timestamp in the sort order. + if pub.pub_seqno: + return pub.pub_seqno + elif pub.time_created: + return int( pub.time_created.timestamp() ) + else: + return 0 # --------------------------------------------------------------------- @@ -97,6 +108,7 @@ def create_publication(): vals[ "time_created" ] = datetime.datetime.now() pub = Publication( **vals ) db.session.add( pub ) + _set_seqno( pub, pub.publ_id ) _save_image( pub, updated ) db.session.commit() _logger.debug( "- New ID: %d", pub.pub_id ) @@ -109,6 +121,24 @@ def create_publication(): extras[ "tags" ] = do_get_tags() return make_ok_response( updated=updated, extras=extras, warnings=warnings ) +def _set_seqno( pub, publ_id ): + """Set a publication's seq#.""" + if publ_id: + # NOTE: Since we currently don't provide a way to set the seq# in the UI, + # we leave a gap between the seq# we assign here, to allow the user to manually + # insert publications into the gap at a later time. + max_seqno = db.session.query( func.max( Publication.pub_seqno ) ) \ + .filter( Publication.publ_id == publ_id ) \ + .scalar() + if max_seqno is None: + pub.pub_seqno = 1 + elif max_seqno == 1: + pub.pub_seqno = 10 + else: + pub.pub_seqno = max_seqno + 10 + else: + pub.pub_seqno = None + def _save_image( pub, updated ): """Save the publication's image.""" @@ -158,6 +188,8 @@ def update_publication(): pub = Publication.query.get( pub_id ) if not pub: abort( 404 ) + if vals["publ_id"] != pub.publ_id: + _set_seqno( pub, vals["publ_id"] ) vals[ "time_updated" ] = datetime.datetime.now() apply_attrs( pub, vals ) _save_image( pub, updated ) diff --git a/asl_articles/publishers.py b/asl_articles/publishers.py index 5168ca4..3e0a3e2 100644 --- a/asl_articles/publishers.py +++ b/asl_articles/publishers.py @@ -131,7 +131,7 @@ def update_publisher(): warnings = [] updated = clean_request_args( vals, _FIELD_NAMES, warnings, _logger ) - # update the publication + # update the publisher publ = Publisher.query.get( publ_id ) if not publ: abort( 404 ) diff --git a/web/src/PublisherSearchResult.js b/web/src/PublisherSearchResult.js index ac5e533..a2f2ee9 100644 --- a/web/src/PublisherSearchResult.js +++ b/web/src/PublisherSearchResult.js @@ -28,7 +28,16 @@ export class PublisherSearchResult extends React.Component if ( pub[1].publ_id === this.props.data.publ_id ) pubs.push( pub[1] ) ; } - pubs.sort( (lhs,rhs) => rhs.time_created - lhs.time_created ) ; + pubs.sort( (lhs,rhs) => { + if ( lhs.pub_seqno && rhs.pub_seqno ) + return rhs.pub_seqno - lhs.pub_seqno ; + else if ( lhs.pub_seqno ) + return +1 ; + else if ( rhs.pub_seqno ) + return -1 ; + else + return rhs.time_created - lhs.time_created ; // nb: we compare timestamps for back-compat + } ) ; pubs = pubs.map( p =>