class TagsView(Resource): decorators = [cache_filler(), cache.cached()] def get(self, filename=None): """List all tags.""" if not is_cache_filler(): logger.info("Cache miss for {}".format(request.path)) if request.args.get("include_theses_ids", False) or filename != None: results = (db.session.query(Tag).join(Tag.theses).group_by( Tag.title).order_by(Tag.title).all()) rv = { "data": [tag.to_dict(include_theses_ids=True) for tag in results] } else: results = (db.session.query(Tag, func.count(Thesis.id)).join( Tag.theses).group_by(Tag.title).all()) rv = { "data": [item[0].to_dict(thesis_count=item[1]) for item in results] } return json_response(rv, filename=filename)
class TagView(Resource): decorators = [cache_filler(), cache.cached()] def get(self, slug: str): """Tag metadata, list of all related theses and their elections.""" if not is_cache_filler(): logger.info("Cache miss for {}".format(request.path)) tag = db.session.query(Tag).filter(Tag.slug == slug.lower()).first() if tag is None: return json_response({"error": "Tag not found"}, status=404) rv = { "data": tag.to_dict(include_related_tags="full"), "theses": [thesis.to_dict() for thesis in tag.theses], "elections": { thesis.election_id: thesis.election.to_dict() for thesis in tag.theses }, } return json_response(rv) def delete(self, slug: str): admin_key = request.get_json().get("admin_key", "") if admin_key != current_app.config.get("ADMIN_KEY"): logger.warning("Invalid admin password") return json_response({"error": "Invalid admin password"}, status=401) tag = db.session.query(Tag).filter(Tag.slug == slug).first() if tag is None: return json_response({"error": "Tag not found"}, status=404) logger.warning("Removing {}".format(tag)) db.session.delete(tag) db.session.commit() rv = { "data": tag.to_dict(include_related_tags="full"), "theses": [thesis.to_dict() for thesis in tag.theses], "elections": { thesis.election_id: thesis.election.to_dict() for thesis in tag.theses }, } return json_response(rv)
class ThesisView(Resource): decorators = [cache_filler(), cache.cached()] def get(self, thesis_id: str): """Return metadata for a specific thesis.""" if not is_cache_filler(): logger.info("Cache miss for {}".format(request.path)) thesis = Thesis.query.get(thesis_id) if thesis is None: return json_response({"error": "Thesis not found"}, status=404) rv = {"data": thesis.to_dict(), "related": thesis.related()} return json_response(rv)
class ElectionView(Resource): decorators = [cache_filler(), cache.cached()] def get(self, wom_id: int): """Election data and a list of its theses.""" if not is_cache_filler(): logger.info("Cache miss for {}".format(request.path)) election = Election.query.get(wom_id) if election is None: return json_response({"error": "Election not found"}, status=404) rv = { "data": election.to_dict(), "theses": [thesis.to_dict() for thesis in election.theses], } return json_response(rv)
class BaseView(Resource): decorators = [cache_filler(), cache.cached()] def get(self): """Return base data set required by the web client.""" if not is_cache_filler(): logger.info("Cache miss for {}".format(request.path)) rv = {"data": dict()} # Elections try: elections = db.session.query(Election).all() except SQLAlchemyError as e: logger.error(e) return json_response({"error": "Server Error"}) rv["data"]["elections"] = defaultdict(list) for election in elections: rv["data"]["elections"][election.territory].append( election.to_dict(thesis_data=False)) # Tags tagItems = (db.session.query(Tag, func.count(Thesis.id)).join( Tag.theses).group_by(Tag.title).all()) rv["data"]["tags"] = [ item[0].to_dict( thesis_count=item[1], query_root_status=True, include_related_tags="simple", ) for item in tagItems ] return json_response(rv)
class Elections(Resource): decorators = [cache_filler(), cache.cached()] def get(self): """A list of all elections.""" if not is_cache_filler(): logger.info("Cache miss for {}".format(request.path)) try: elections = Election.query.all() except SQLAlchemyError as e: logger.error(e) return json_response({"error": "Server Error"}) thesis_data = request.args.get("thesis_data", False) rv = {"data": defaultdict(list)} for election in elections: rv["data"][election.territory].append( election.to_dict(thesis_data=thesis_data)) return json_response(rv)
class Quiz(Resource): method_decorators = {"get": [cache.cached(timeout=(5 * 60))]} def get(self, election_num, thesis_num=None): """Return a tally of how many users guessed yes/no for each thesis""" rv = {} error = None election = Election.query.get(election_num) if election is None: return json_response({"error": "Election not found"}, status=404) for thesis in election.theses: thesis_num = int(thesis.id[-2:]) rv[thesis_num] = thesis.quiz_tally() return json_response({"error": error, "data": rv}) def post(self, election_num, thesis_num=None): """Record an answer given by a user in a quiz.""" if thesis_num == None: return json_response( {"error": "Missing path parameter for thesis number"}, status=422) log_request_info("Quiz answer post", request) rv = None error = None status = 200 thesis_id = "WOM-{:03d}-{:02d}".format(election_num, thesis_num) data = request.get_json() if data is not None: uuid = data.get("uuid", None) answer = data.get("answer", None) if data is None or uuid is None or answer is None: logger.warning("Request missing data: {}".format(data)) error = "The request is missing data" status = 422 else: thesis = Thesis.query.get(thesis_id) if thesis is None: error = "Thesis not found" status = 404 else: qa = QuizAnswer(uuid=uuid, answer=answer, thesis=thesis) try: db.session.add(qa) db.session.commit() except IntegrityError: error = "Ignored duplicate quiz answer" logger.info("Ignored duplicate quiz answer") else: logger.info("Added {}".format(qa)) if error is None: rv = qa.to_dict() return json_response({"error": error, "data": rv}, status=status)