# Copyright (c) 2018, George Tokmaji # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from ..helpers import * from .media import * from .media import _upload_file, _delete_file from bottle import put from itertools import chain import string def _add_upload(entry : Upload, session : DBSession): e = entry.json() e["comments"] = [] for comment in entry.comments: c = comment.json() c["voting"] = { "sum" : 0, "count" : 0, "votes" : [] } for vote in comment.votes: c["voting"]["sum"] += vote.impact c["voting"]["count"] += 1 c["voting"]["votes"].append(vote.json()) e["comments"].append(c) e["files"] = [file.json() for file in entry.files] e["dependencies"] = [] #TODO votes = entry.votes e["voting"] = { "sum" : sum(i.impact for i in votes), "count" : len(votes) } return e @get("/api/uploads") def get_uploads(): ret = { "pagination" : { "total" : 0, "limit" : 50, "page" : 1, "pages" : 1 }, "uploads" : [] } session = DBSession() for entry in session.query(Upload).order_by(Upload.updated_at.desc()): ret["uploads"].append(_add_upload(entry, session)) ret["pagination"]["total"] = ret["pagination"]["limit"] = len(ret["uploads"]) return ret @post("/api/uploads") @put("/api/uploads/") @jwt_auth_required def post_upload(id=None): try: session = DBSession() if id is not None: entry = session.query(Upload).get(id) if not entry: raise HTTPResponse(status=404) else: if session.query(Upload).filter_by(title=request_data()["title"]).count(): raise HTTPResponse("An entry with the specified title already exists", 410) entry = Upload() entry.title = request_data()["title"] entry.version = request_data()["version"] entry.description=request_data()["description"] entry.slug = "".join(i for i in request_data()["title"].lower() if i in string.ascii_letters) entry.tags = request_data().get("tags", []) entry.author = get_user(session) session.add(entry) try: os.mkdir(os.path.join(os.getcwd(), "media")) except FileExistsError: pass file_ids = [] if "files" in request_data(): for f in request_data()["files"]: try: f = f["id"] except TypeError: pass if ObjectId.is_valid(f): file_ids.append(ObjectId(f)) file = session.query(File).get(ObjectId(f)) file.upload = entry session.add(file) else: for file in request.files.values(): f = _upload_file(file, entry) session.add(f) file_ids.append(f.id) for file in session.query(File).filter(File.upload_id == entry.id, ~File.id.in_(file_ids)): # we change the upload above, so we can't use Filter.upload session.delete(file) except KeyError as e: session.rollback() raise HTTPResponse(f"Missing form value: {e.args[0]}", 400) session.commit() return _add_upload(entry, session) @get("/api/uploads/") def get_upload(id): session = DBSession() entry = session.query(Upload).get(id) if entry is not None: return _add_upload(entry, session) raise HTTPResponse(status=404) @delete("/api/uploads/") @jwt_auth_required def delete_upload(id): session = DBSession() author = get_user(session) try: entry = session.query(Upload).filter_by(id=id, author=author).one() except db.orm.exc.NoResultFound: raise HTTPResponse(status=404) if entry.readonly: raise HTTPResponse("Resource is read-only", 403) session.delete(entry.votes) session.delete(entry.comments.votes) session.delete(entry.comments) session.delete(entry) for f in session.query(File).filter_by(upload=entry): session.delete(f) _delete_file(f) #TODO: Dependencies session.commit() session.flush() return HTTPResponse(status=204) @get("/api/uploads//comments") def get_comments(id): session = DBSession() upload = session.query(Upload).get(id) if upload is None: raise HTTPResponse("Invalid upload id", 404) return { "comments" : [{**(comment.json()), **_vote_dummy} for comment in session.query(Comment).filter_by(upload=upload)] } @post("/api/uploads//comments") @jwt_auth_required def post_comments(id): session = DBSession() upload = session.query(Upload).get(id) if upload is None: raise HTTPResponse("Invalid upload id", 404) try: comment = Comment( body=request_data()["body"], author=get_user(session), upload=upload ) except KeyError as e: raise HTTPResponse(f"Missing json value: {e.args[0]}", 400) session.add(comment) session.commit() return comment.json() @delete("/api/uploads//comments/") @jwt_auth_required def delete_comments(id, comment_id): session = DBSession() try: comment = session.query(Comment).filter_by(id=comment_id, author=get_user(session), upload=session.query(Upload).get(id)).one() except db.orm.exc.NoResultFound: raise HTTPResponse(status=404) session.delete(comment) session.commit() return HTTPResponse(status=204) @get("/api/uploads//comments//vote") @jwt_auth_required def get_comment_vote(id, comment_id): session = DBSession() upload = session.query(Upload).get(id) if upload is None: raise HTTPResponse("Invalid upload id", 404) try: comment = session.query(Comment).filter_by(id=comment_id, author=get_user(session), upload=upload).one() except db.orm.exc.NoResultFound: raise HTTPResponse("Invalid comment id", status=404) try: return session.query(CommentVote).filter_by(comment=comment, author=get_user(session)).one().json() except db.orm.exc.NoResultFound: raise HTTPResponse(status=404) @post("/api/uploads//comments//vote") @jwt_auth_required def post_comment_vote(id, comment_id): session = DBSession() upload = session.query(Upload).get(id) if upload is None: raise HTTPResponse("Invalid upload id", 404) try: comment = session.query(Comment).filter_by(id=comment_id, author=get_user(session), upload=session.query(Upload).get(id)).one() except db.orm.exc.NoResultFound: raise HTTPResponse("Invalid comment id", status=404) author = get_user(session) try: vote = session.query(CommentVote).filter_by(comment=comment, author=author).one() except db.orm.exc.NoResultFound: vote = CommentVote(author=author, comment=comment) vote.impact = request_data()["impact"] session.add(vote) session.commit() return vote.json() @get("/api/uploads//vote") @jwt_auth_required def get_vote(id): session = DBSession() upload = session.query(Upload).get(id) if upload is None: raise HTTPResponse("Invalid upload id", 404) try: return session.query(Vote).filter_by(upload=upload, author=get_user(session)).one().json() except db.orm.exc.NoResultFound: raise HTTPResponse(status=404) @post("/api/uploads//vote") @jwt_auth_required def post_vote(id): session = DBSession() upload = session.query(Upload).get(id) if upload is None: raise HTTPResponse("Invalid upload id", 404) author = get_user(session) try: vote = session.query(Vote).filter_by(upload=upload, author=author).one() except db.orm.exc.NoResultFound: vote = Vote(author=author, upload=upload) vote.impact = request_data()["impact"] session.add(vote) session.commit() return vote.json()