summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fatcat/__init__.py3
-rw-r--r--fatcat/api.py14
-rw-r--r--fatcat/models.py20
-rw-r--r--fatcat/sql.py112
-rw-r--r--tests/test_backend.py40
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)