From e7332e3b9152809e32259b36c7979e1c93e6ae03 Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Fri, 25 Dec 2015 21:38:59 +0100 Subject: [PATCH] Add routes to fetch relationships * Add routes to fetch relationships for a given paper. * Add backrefs in relationships to be able to get the reversed relationship. * Update some doc. --- README.md | 2 +- database.py | 29 +++++++++++++--- main.py | 4 +-- routes/get.py | 91 ++++++++++++++++++++++++++++++++++++++++---------- routes/post.py | 2 +- 5 files changed, 103 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index d20aae2..8e4c9ec 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Metadata for arXiv The goal of this repository is to provide a minimal API to put metadata on arXiv papers. -TODO: Better description. +TODO: Better description + API description. ## Installation diff --git a/database.py b/database.py index 1fc34d7..510fb39 100644 --- a/database.py +++ b/database.py @@ -30,17 +30,29 @@ class Association(Base): relationship_id = Column(Integer, ForeignKey("relationships.id", ondelete="CASCADE")) - right_paper = sqlalchemy_relationship("Paper", foreign_keys=right_id) + right_paper = sqlalchemy_relationship("Paper", + foreign_keys=right_id, + back_populates="related_by") relationship = sqlalchemy_relationship("Relationship") + left_paper = sqlalchemy_relationship("Paper", + foreign_keys=left_id, + back_populates="related_to") + class Paper(Base): __tablename__ = "papers" id = Column(Integer, primary_key=True) doi = Column(String(), nullable=True, unique=True) arxiv_id = Column(String(25), nullable=True, unique=True) - related = sqlalchemy_relationship("Association", - foreign_keys="Association.left_id") + # related_to are papers related to this paper (this_paper R …) + related_to = sqlalchemy_relationship("Association", + foreign_keys="Association.left_id", + back_populates="left_paper") + # related_by are papers referenced by this paper (… R this_paper) + related_by = sqlalchemy_relationship("Association", + foreign_keys="Association.right_id", + back_populates="right_paper") def __repr__(self): return "" % ( @@ -53,6 +65,7 @@ class Paper(Base): """ Dict to dump for the JSON API. """ + relationships = [a.relationship.name for a in self.related_to] return { "types": self.__tablename__, "id": self.id, @@ -64,7 +77,15 @@ class Paper(Base): "self": "/papers/%d" % (self.id,) }, "relationships": { - # TODO + k: { + "links": { + "related": ( + "/papers/%d/relationships/%s?reverse={reverse}" % + (self.id, k) + ) + } + } + for k in relationships } } diff --git a/main.py b/main.py index 4bbab3c..f61a689 100755 --- a/main.py +++ b/main.py @@ -45,8 +45,8 @@ app.get("/papers", callback=routes.get.fetch_papers) app.get("/papers/", callback=routes.get.fetch_by_id) app.get("/papers//relationships/", callback=routes.get.fetch_relationship) - -# TODO: Fetch relationships +app.get("/papers//", + callback=routes.get.fetch_relationship) app.post("/papers", callback=routes.post.create_paper) diff --git a/routes/get.py b/routes/get.py index b74d560..7a38ef6 100644 --- a/routes/get.py +++ b/routes/get.py @@ -36,7 +36,12 @@ def fetch_papers(db): "self": "/papers/1" }, "relationships": { - TODO + "cite": { + "links": { + "related": "/papers/1/relationships/cite" + } + }, + … } } ] @@ -62,7 +67,7 @@ def fetch_by_id(id, db): .. code-block:: bash - GET /id/ + GET /papers/1 Accept: application/vnd.api+json @@ -70,24 +75,27 @@ def fetch_by_id(id, db): { "data": { - { - "type": "papers", - "id": 1, - "attributes": { - "doi": "10.1126/science.1252319", - "arxiv_id": "1401.2910" + "type": "papers", + "id": 1, + "attributes": { + "doi": "10.1126/science.1252319", + "arxiv_id": "1401.2910" + }, + "links": { + "self": "/papers/1" + }, + "relationships": { + "cite": { + "links": { + "related": "/papers/1/relationships/cite" + } }, - "links": { - "self": "/papers/1" - }, - "relationships": { - TODO - } + … } } } - :param id: The id of the requested article. + :param id: The id of the requested paper. :param db: A database session, injected by the ``Bottle`` plugin. :returns: An ``HTTPResponse``. """ @@ -101,6 +109,55 @@ def fetch_by_id(id, db): def fetch_relationship(id, name, db): """ - TODO + Fetch relationships of the given type associated with the given paper. + + .. code-block:: bash + + GET /papers/1/relationships/cite + Accept: application/vnd.api+json + + + .. code-block:: json + + { + "links": { + "self": "/papers/1/relationships/cite", + "related": "/papers/1/cite" + }, + "data": [ + { + "type": "papers", + "id": 2, + }, + … + ] + } + + :param id: The id of the requested paper. + :param name: The name of the requested relationship for this paper. + :param db: A database session, injected by the ``Bottle`` plugin. + :returns: An ``HTTPResponse``. """ - pass + reversed = ( + "reverse" in bottle.request.params and + bottle.request.params["reverse"] != 0 + ) + resource = db.query(database.Paper).filter_by(id=id).first() + if resource: + response = { + "links": { + "self": "/papers/%d/relationships/%s" % (id, name), + "related": "/papers/%d/%s" % (id, name), + }, + "data": [ + ] + } + if reversed: + relationships = resource.related_by + else: + relationships = resource.related_to + for r in relationships: + if r.relationship.name == name: + response["data"].append({"type": name, "id": r.right_id}) + return tools.APIResponse(tools.pretty_json(response)) + return bottle.HTTPError(404, "Not found") diff --git a/routes/post.py b/routes/post.py index da2179c..2aa2c70 100644 --- a/routes/post.py +++ b/routes/post.py @@ -193,7 +193,7 @@ def update_relationship_backend(left_id, right_id, name, db): # Update the relationship a = database.Association(relationship_id=relationship.id) a.right_paper = right_paper - left_paper.related.append(a) + left_paper.related_to.append(a) try: db.add(a) db.add(left_paper)