diff options
| -rw-r--r-- | fatcat/__init__.py | 3 | ||||
| -rw-r--r-- | fatcat/api.py | 14 | ||||
| -rw-r--r-- | fatcat/models.py | 20 | ||||
| -rw-r--r-- | fatcat/sql.py | 112 | ||||
| -rw-r--r-- | tests/test_backend.py | 40 | 
5 files changed, 129 insertions, 60 deletions
| diff --git a/fatcat/__init__.py b/fatcat/__init__.py index e8c7c21a..d8494a19 100644 --- a/fatcat/__init__.py +++ b/fatcat/__init__.py @@ -11,14 +11,11 @@ examples = {      "work": {          "id": "rzga5b9cd7efgh04iljk",          "rev": "12345", -        "previous": None, -        "state": None,          "redirect_id": None,          "edit_id": None,          "extra_json": None,          "title": "Structure and Interpretation",          "work_type": "journal-article", -        "date": None,          "contributors": [              {"name": "Alyssa P. Hacker"},          ], diff --git a/fatcat/api.py b/fatcat/api.py index 1c68822a..3c407cfe 100644 --- a/fatcat/api.py +++ b/fatcat/api.py @@ -3,15 +3,19 @@ from flask import Flask, render_template, send_from_directory, request, \      url_for, abort, g, redirect, jsonify  from fatcat import app, db, examples  from fatcat.models import * +from fatcat.sql import *  ### Views ###################################################################  @app.route('/v0/work/<work_id>', methods=['GET']) -def work_get(work_id): -    if work_id == "random": -        work = examples['work'] -    else: -        work = WorkId.query.filter_by(id=work_id).first_or_404() +def api_work_get(work_id): +    if not work_id.isdigit(): +        return abort(404) +    work = hydrate_work(work_id)      return jsonify(work) +@app.route('/v0/work/random', methods=['GET']) +def api_work_random(): +    work = WorkId.query.order_by(db.func.random()).first() +    return redirect('/v0/work/{}'.format(work.id)) diff --git a/fatcat/models.py b/fatcat/models.py index 9abb286e..abac8de9 100644 --- a/fatcat/models.py +++ b/fatcat/models.py @@ -38,8 +38,8 @@ class ReleaseContrib(db.Model):  class ReleaseRef(db.Model):      __tablename__ = "release_ref"      id = db.Column(db.Integer, primary_key=True, nullable=False) -    release_rev= db.Column(db.ForeignKey('release_revision.id'), nullable=False) -    target_release_id= db.Column(db.ForeignKey('release_id.id'), nullable=True) +    release_rev = db.Column(db.ForeignKey('release_revision.id'), nullable=False) +    target_release_id = db.Column(db.ForeignKey('release_id.id'), nullable=True)      index = db.Column(db.Integer, nullable=True)      stub = db.Column(db.String, nullable=True)      doi = db.Column(db.String, nullable=True) @@ -69,6 +69,7 @@ class WorkId(db.Model):      live = db.Column(db.Boolean, nullable=False, default=False)      revision_id = db.Column(db.ForeignKey('work_revision.id'), nullable=True)      redirect_id = db.Column(db.ForeignKey('work_id.id'), nullable=True) +    revision = db.relationship("WorkRevision")  class WorkLog(db.Model):      __tablename__ = 'work_log' @@ -85,13 +86,12 @@ class WorkLog(db.Model):  class WorkRevision(db.Model):      __tablename__ = 'work_revision'      id = db.Column(db.Integer, primary_key=True) -    previous = db.Column(db.ForeignKey('work_revision.id'), nullable=True)      edit_id = db.Column(db.ForeignKey('edit.id'))      extra_json = db.Column(db.ForeignKey('extra_json.sha1'), nullable=True)      title = db.Column(db.String)      work_type = db.Column(db.String) -    date = db.Column(db.String) +    primary_release_id = db.Column(db.ForeignKey('release_id.id'), nullable=True)      creators = db.relationship('WorkContrib', lazy='subquery',          backref=db.backref('works', lazy=True)) @@ -102,11 +102,11 @@ class ReleaseId(db.Model):      live = db.Column(db.Boolean, nullable=False, default=False)      revision_id = db.Column(db.ForeignKey('release_revision.id'))      redirect_id = db.Column(db.ForeignKey('release_id.id'), nullable=True) +    revision = db.relationship("ReleaseRevision")  class ReleaseRevision(db.Model):      __tablename__ = 'release_revision'      id = db.Column(db.Integer, primary_key=True, autoincrement=True) -    previous = db.Column(db.ForeignKey('release_revision.id'), nullable=True)      edit_id = db.Column(db.ForeignKey('edit.id'))      extra_json = db.Column(db.ForeignKey('extra_json.sha1'), nullable=True) @@ -121,6 +121,8 @@ class ReleaseRevision(db.Model):      pages = db.Column(db.String, nullable=True)      issue = db.Column(db.String, nullable=True) +    #work = db.relationship("WorkId", lazy='subquery') +    container = db.relationship("ContainerId", lazy='subquery')      creators = db.relationship('ReleaseContrib', lazy='subquery')      refs = db.relationship('ReleaseRef', lazy='subquery') @@ -130,11 +132,11 @@ class CreatorId(db.Model):      live = db.Column(db.Boolean, nullable=False, default=False)      revision_id = db.Column(db.ForeignKey('creator_revision.id'))      redirect_id = db.Column(db.ForeignKey('creator_id.id'), nullable=True) +    revision = db.relationship("CreatorRevision")  class CreatorRevision(db.Model):      __tablename__ = 'creator_revision'      id = db.Column(db.Integer, primary_key=True, autoincrement=True) -    previous = db.Column(db.ForeignKey('creator_revision.id'), nullable=True)      edit_id = db.Column(db.ForeignKey('edit.id'))      extra_json = db.Column(db.ForeignKey('extra_json.sha1'), nullable=True)      #creator_ids = db.relationship("CreatorId", backref="revision", lazy=False) @@ -149,16 +151,16 @@ class ContainerId(db.Model):      live = db.Column(db.Boolean, nullable=False, default=False)      revision_id = db.Column(db.ForeignKey('container_revision.id'))      redirect_id = db.Column(db.ForeignKey('container_id.id'), nullable=True) +    revision = db.relationship("ContainerRevision")  class ContainerRevision(db.Model):      __tablename__ = 'container_revision'      id = db.Column(db.Integer, primary_key=True, autoincrement=True) -    previous = db.Column(db.ForeignKey('container_revision.id'), nullable=True)      edit_id = db.Column(db.ForeignKey('edit.id'))      extra_json = db.Column(db.ForeignKey('extra_json.sha1'), nullable=True)      name = db.Column(db.String) -    container_id = db.Column(db.ForeignKey('container_id.id')) +    #XXX: container_id = db.Column(db.ForeignKey('container_id.id'))      publisher = db.Column(db.String)        # TODO: foreign key      sortname = db.Column(db.String)      issn = db.Column(db.String)             # TODO: identifier table @@ -169,11 +171,11 @@ class FileId(db.Model):      live = db.Column(db.Boolean, nullable=False, default=False)      revision_id = db.Column('revision', db.ForeignKey('file_revision.id'))      redirect_id = db.Column(db.ForeignKey('file_id.id'), nullable=True) +    revision = db.relationship("FileRevision")  class FileRevision(db.Model):      __tablename__ = 'file_revision'      id = db.Column(db.Integer, primary_key=True, autoincrement=True) -    previous = db.Column(db.ForeignKey('file_revision.id'), nullable=True)      edit_id = db.Column(db.ForeignKey('edit.id'))      extra_json = db.Column(db.ForeignKey('extra_json.sha1'), nullable=True) diff --git a/fatcat/sql.py b/fatcat/sql.py index ad0a045f..b73ec464 100644 --- a/fatcat/sql.py +++ b/fatcat/sql.py @@ -14,23 +14,26 @@ def populate_db():          name="Noam D. Elkies",          sortname="Elkies, N",          orcid=None) -    n_elkies_id = CreatorId(revision_id=n_elkies.id) +    n_elkies_id = CreatorId(revision=n_elkies)      pi_work = WorkRevision( -        title="Why is π 2so close to 10?") -    pi_work_id = WorkId(revision_id=pi_work.id) +        title="Why is π^2 so close to 10?", +        work_type="journal-article") +    pi_work_id = WorkId(revision=pi_work)      pi_release = ReleaseRevision(          title=pi_work.title, -        work_id=pi_work.id) +        work_id=pi_work.id, +        release_type="journal-article")      pi_contrib = ReleaseContrib(creator=n_elkies_id)      pi_release.creators.append(pi_contrib) -    pi_release_id = ReleaseId(revision_id=pi_release.id) -    pi_work.primary_release = pi_release.id +    pi_release_id = ReleaseId(revision=pi_release) +    pi_work.primary_release = pi_release      # TODO:      #pi_file = File(      #    sha1="efee52e46c86691e2b892dbeb212f3b92e92e9d3",      #    url="http://www.math.harvard.edu/~elkies/Misc/pi10.pdf") -    db.session.add_all([n_elkies, pi_work, pi_work_id, pi_release, pi_release_id]) +    db.session.add_all([n_elkies, n_elkies_id, pi_work, pi_work_id, pi_release, +        pi_release_id])      # TODO:      #ligo_collab = CreatorRevision(name="LIGO Scientific Collaboration") @@ -58,19 +61,19 @@ def populate_complex_db(count=100):              sortname="{}, {}".format(last, first[0]),              orcid=None)          author_revs.append(ar) -        author_ids.append(CreatorId(revision_id=ar.id)) +        author_ids.append(CreatorId(revision=ar))      container_revs = []      container_ids = []      for _ in range(5):          cr = ContainerRevision(              name="The Fake Journal of Stuff", -            container_id=None, +            #container_id=None,              publisher="Big Paper",              sortname="Fake Journal of Stuff",              issn="1234-5678")          container_revs.append(cr) -        container_ids.append(ContainerId(revision_id=cr.id)) +        container_ids.append(ContainerId(revision=cr))      title_start = ("All about ", "When I grow up I want to be",          "The final word on", "Infinity: ", "The end of") @@ -84,22 +87,22 @@ def populate_complex_db(count=100):      for _ in range(count):          title = "{} {}".format(random.choice(title_start), random.choice(title_ends))          work = WorkRevision(title=title) -        work_id = WorkId(revision_id=work.id) +        work_id = WorkId(revision=work)          authors = set(random.sample(author_ids, 5))          release = ReleaseRevision(              title=work.title,              creators=[ReleaseContrib(creator=a) for a in list(authors)], -            work_id=work.id, -            container_id=random.choice(container_ids).id) -        release_id = ReleaseId(revision_id=release.id) -        work.primary_release = release.id +            #work=work, +            container=random.choice(container_ids)) +        release_id = ReleaseId(revision=release) +        work.primary_release = release          authors.add(random.choice(author_ids))          release2 = ReleaseRevision(              title=work.title + " (again)",              creators=[ReleaseContrib(creator=a) for a in list(authors)], -            work_id=work.id, -            container_id=random.choice(container_ids).id) -        release_id2 = ReleaseId(revision_id=release2.id) +            #work=work, +            container=random.choice(container_ids)) +        release_id2 = ReleaseId(revision=release2)          work_revs.append(work)          work_ids.append(work_id)          release_revs.append(release) @@ -115,7 +118,7 @@ def populate_complex_db(count=100):              url="http://archive.invalid/{}".format(file_sha),              releases=[FileRelease(release=release_id), FileRelease(release=release_id2)],          ) -        file_id = FileId(revision_id=file_rev.id) +        file_id = FileId(revision=file_rev)          file_revs.append(file_rev)          file_ids.append(file_id) @@ -145,25 +148,25 @@ def add_crossref(meta):              sortname="{}, {}".format(am['family'], am['given']),              orcid=None)          author_revs.append(ar) -        author_ids.append(CreatorId(revision_id=ar.id)) +        author_ids.append(CreatorId(revision=ar))      # container      container = ContainerRevision(          issn=meta['ISSN'][0],          name=meta['container-title'][0], -        container_id=None, +        #container_id=None,          publisher=meta['publisher'],          sortname=meta['short-container-title'][0]) -    container_id = ContainerId(revision_id=container.id) +    container_id = ContainerId(revision=container)      # work and release      work = WorkRevision(title=title) -    work_id = WorkId(revision_id=work.id) +    work_id = WorkId(revision=work)      release = ReleaseRevision(          title=title,          creators=[ReleaseContrib(creator=a) for a in author_ids], -        work_id=work.id, -        container_id=container_id.id, +        #work=work, +        container=container_id,          release_type=meta['type'],          doi=meta['DOI'],          date=meta['created']['date-time'], @@ -171,8 +174,8 @@ def add_crossref(meta):          issue=meta.get('issue', None),          volume=meta.get('volume', None),          pages=meta.get('page', None)) -    release_id = ReleaseId(revision_id=release.id) -    work.primary_release = release.id +    release_id = ReleaseId(revision=release) +    work.primary_release = release      extra = json.dumps({          'crossref': {              'links': meta.get('link', []), @@ -187,7 +190,7 @@ def add_crossref(meta):      # references      for i, rm in enumerate(meta.get('reference', [])):          ref = ReleaseRef( -            release_rev=release.id, +            release_rev=release,              doi=rm.get("DOI", None),              index=i+1,              # TODO: how to generate a proper stub here from k/v metadata? @@ -199,3 +202,56 @@ def add_crossref(meta):      db.session.add_all(author_revs)      db.session.add_all(author_ids)      db.session.commit() + +def hydrate_work(wid): + +    wid = int(wid) +    work = WorkId.query.filter(WorkId.id==wid).first_or_404() +    hydro = { +        "_type": "work", +        "id": wid, +        "rev": work.revision_id, +        "is_live": work.live, +        "redirect_id": work.redirect_id, +    } +    if not work.revision: +        # TODO: look up edit id here from changelog? +        hydro["edit_id"] = None +        return hydro + +    primary = None +    if work.revision.primary_release_id: +        primary = hydrate_release(work.revision.primary_release_id) +    creators = [c.creator_id for c in WorkContrib.query.filter(WorkContrib.work == work).all()] +    #releases = [r.id for r in ReleaseId.query.filter(ReleaseId.revision.work_id==work.id).all()] +    releases = [] +    hydro.update({ +        "work_type": work.revision.work_type, +        "title": work.revision.title, +        "edit_id": work.revision.edit_id, +        "primary": primary, +        "creators": creators, +        "releases": releases, +    }) +    return hydro + +def hydrate_release(rid): + +    wid = int(rid) +    release = ReleaseId.query.filter(ReleaseId.id==rid).first_or_404() + +    return { +        "_type": "release", +        "id": rid, +        "revision": release.revision_id, +        "edit_id": release.revision.edit_id, +        "is_live": release.live, + +        "work_id": release.revision.work_id, +        "release_type": release.revision.release_type, +        "title": release.revision.title, +        "creators": [], +        "releases": [], +        "files": [], +        "references": [], +    } diff --git a/tests/test_backend.py b/tests/test_backend.py index fdb29c47..bc610d25 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -12,10 +12,9 @@ import tempfile  ## Helpers ##################################################################  def check_entity_fields(e): -    for key in ('id', 'rev', 'previous', 'state', 'redirect_id', 'edit_id', -            'extra_json'): +    for key in ('id', 'rev', 'redirect_id', 'edit_id'):          assert key in e -    for key in ('id', 'rev'): +    for key in ('id',):          assert e[key] is not None  ## API Tests ################################################################ @@ -36,24 +35,28 @@ class FatcatTestCase(unittest.TestCase):          assert obj['ok']      def test_works(self): +        fatcat.sql.populate_db()          # Invalid Id          rv = self.app.get('/v0/work/_')          assert rv.status_code == 404 -        # Missing Id (TODO) -        #rv = self.app.get('/v0/work/rzga5b9cd7efgh04iljk') -        #assert rv.status == 404 - -        # Valid Id (TODO) -        #rv = self.app.get('/v0/work/r3zga5b9cd7ef8gh084714iljk') -        #assert rv.status_code == 200 - +        # Random          rv = self.app.get('/v0/work/random') -        obj = json.loads(rv.data.decode('utf-8')) -        check_entity_fields(obj) -        assert obj['title'] -        assert obj['work_type'] == "journal-article" +        rv = self.app.get(rv.location) +        work = json.loads(rv.data.decode('utf-8')) +        check_entity_fields(work) +        print(work) +        assert work['title'] +        assert work['work_type'] + +        # Valid Id (from random above) +        rv = self.app.get('/v0/work/{}'.format(work['id'])) +        assert rv.status_code == 200 + +        # Missing Id +        rv = self.app.get('/v0/work/r3zga5b9cd7ef8gh084714iljk') +        assert rv.status_code == 404      def test_populate(self):          fatcat.sql.populate_db() @@ -67,3 +70,10 @@ class FatcatTestCase(unittest.TestCase):          for obj in raw:              fatcat.sql.add_crossref(obj) +    def test_hydrate_work(self): +        fatcat.sql.populate_complex_db() +        fatcat.sql.hydrate_work(1) + +    def test_hydrate_release(self): +        fatcat.sql.populate_complex_db() +        fatcat.sql.hydrate_release(1) | 
