Add basic routes to update relationships
This commit is contained in:
parent
dd1af6a8f0
commit
a8f8590c5a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
__pycache__
|
||||
docs/
|
||||
dev.db
|
||||
|
30
database.py
30
database.py
@ -1,9 +1,11 @@
|
||||
"""
|
||||
This file contains the database schema in SQLAlchemy format.
|
||||
"""
|
||||
from sqlalchemy import event, Column, Integer, String
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import relationship as sqlalchemy_relationship
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
@ -18,11 +20,27 @@ def set_sqlite_pragma(dbapi_connection, connection_record):
|
||||
cursor.close()
|
||||
|
||||
|
||||
# TODO: Backref
|
||||
class Association(Base):
|
||||
# Relationships are to be read "left RELATION right"
|
||||
__tablename__ = "association"
|
||||
id = Column(Integer, primary_key=True)
|
||||
left_id = Column(Integer, ForeignKey("papers.id", ondelete="CASCADE"))
|
||||
right_id = Column(Integer, ForeignKey("papers.id", ondelete="CASCADE"))
|
||||
relationship_id = Column(Integer,
|
||||
ForeignKey("relationships.id",
|
||||
ondelete="CASCADE"))
|
||||
right_paper = sqlalchemy_relationship("Paper", foreign_keys=right_id)
|
||||
relationship = sqlalchemy_relationship("Relationship")
|
||||
|
||||
|
||||
class Paper(Base):
|
||||
__tablename__ = 'papers'
|
||||
__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")
|
||||
|
||||
def __repr__(self):
|
||||
return "<Paper(id='%d', doi='%s', arxiv_id='%s')>" % (
|
||||
@ -49,3 +67,11 @@ class Paper(Base):
|
||||
# TODO
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Relationship(Base):
|
||||
__tablename__ = "relationships"
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(), unique=True)
|
||||
associations = sqlalchemy_relationship("Association",
|
||||
back_populates="relationship")
|
||||
|
12
main.py
12
main.py
@ -8,7 +8,8 @@ import routes
|
||||
import tools
|
||||
|
||||
# Initialize db and include the SQLAlchemy plugin in bottle
|
||||
engine = create_engine('sqlite:///:memory:', echo=True)
|
||||
# engine = create_engine('sqlite:///:memory:', echo=True)
|
||||
engine = create_engine('sqlite:///dev.db', echo=True)
|
||||
|
||||
app = bottle.Bottle()
|
||||
plugin = sqlalchemy.Plugin(
|
||||
@ -33,6 +34,7 @@ app.install(plugin)
|
||||
|
||||
|
||||
# Routes
|
||||
# TODO: Routes for deletion
|
||||
@app.get("/")
|
||||
def index():
|
||||
return tools.APIResponse(tools.pretty_json({
|
||||
@ -41,13 +43,19 @@ def index():
|
||||
|
||||
app.get("/papers", callback=routes.get.fetch_papers)
|
||||
app.get("/papers/<id:int>", callback=routes.get.fetch_by_id)
|
||||
app.get("/papers/<id:int>/relationships/<name>",
|
||||
callback=routes.get.fetch_relationship)
|
||||
|
||||
# TODO: Fetch relationships
|
||||
|
||||
|
||||
app.post("/papers", callback=routes.post.create_paper)
|
||||
|
||||
# TODO: Update relationships
|
||||
app.post("/papers/<id:int>/relationships/<name>",
|
||||
callback=routes.post.update_relationships)
|
||||
# Complete replacement of relationships is forbidden
|
||||
app.route("/papers/<id:int>/relationships/<name>", method="PATCH",
|
||||
callback=lambda id, name: bottle.HTTPError(403, "Forbidden"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -97,3 +97,10 @@ def fetch_by_id(id, db):
|
||||
"data": resource.json_api_repr()
|
||||
}))
|
||||
return bottle.HTTPError(404, "Not found")
|
||||
|
||||
|
||||
def fetch_relationship(id, name, db):
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
pass
|
||||
|
@ -138,3 +138,67 @@ def create_by_arxiv(arxiv, db):
|
||||
|
||||
# Return the paper
|
||||
return paper
|
||||
|
||||
|
||||
def update_relationships(id, name, db):
|
||||
"""
|
||||
Update the relationships associated to a given paper.
|
||||
|
||||
:param id: The id of the paper to update relationships.
|
||||
:param name: The name of the relationship to update.
|
||||
:param db: A database session, passed by Bottle plugin.
|
||||
:returns: No content. 204 on success, 403 on error.
|
||||
"""
|
||||
data = json.loads(bottle.request.body.read().decode("utf-8"))
|
||||
# Validate the request
|
||||
if "data" not in data:
|
||||
return bottle.HTTPError(403, "Forbidden")
|
||||
# Filter data, invalid entries are ignored
|
||||
data = [i for i in data["data"]
|
||||
if "type" in i and i["type"] == name and "id" in i]
|
||||
# Complete replacement (data == []) is forbidden
|
||||
if len(data) == 0:
|
||||
return bottle.HTTPError(403, "Forbidden")
|
||||
# Update all the relationships
|
||||
for i in data:
|
||||
updated = update_relationship_backend(id, i["id"], name, db)
|
||||
if updated is None:
|
||||
# An error occurred => 403
|
||||
return bottle.HTTPError(403, "Forbidden")
|
||||
# Return an empty 204 on success
|
||||
return tools.APIResponse(status=204, body="")
|
||||
|
||||
|
||||
def update_relationship_backend(left_id, right_id, name, db):
|
||||
"""
|
||||
Backend method to update a single relationship between two papers.
|
||||
|
||||
:param left_id: ID of the paper on the left of the relationship.
|
||||
:param right_id: ID of the paper on the right of the relationship.
|
||||
:param name: Name of the relationship between the two papers.
|
||||
:param db: A database session.
|
||||
:returns: The updated left paper on success, ``None`` otherwise.
|
||||
"""
|
||||
# Load necessary resources
|
||||
left_paper = db.query(database.Paper).filter_by(id=left_id).first()
|
||||
right_paper = db.query(database.Paper).filter_by(id=right_id).first()
|
||||
if left_paper is None or right_paper is None:
|
||||
# Abort
|
||||
return None
|
||||
relationship = db.query(database.Relationship).filter_by(name=name).first()
|
||||
if relationship is None:
|
||||
relationship = database.Relationship(name=name)
|
||||
db.add(relationship)
|
||||
db.flush()
|
||||
# Update the relationship
|
||||
a = database.Association(relationship_id=relationship.id)
|
||||
a.right_paper = right_paper
|
||||
left_paper.related.append(a)
|
||||
try:
|
||||
db.add(a)
|
||||
db.add(left_paper)
|
||||
except IntegrityError:
|
||||
# Unique constraint violation, relationship already exists
|
||||
db.rollback()
|
||||
return None
|
||||
return left_paper
|
||||
|
Loading…
Reference in New Issue
Block a user