Example #1
0
    def get(self):
        game = model.GetGame()
        team = login.GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        puzzle = MaybeGetPuzzle(game, self.request)
        if not puzzle: return self.error(404)

        feedback = model.Feedback.get_or_insert(str(team.key()), parent=puzzle)

        props = {
            "error": self.request.get_all("error"),
            "game": model.GetProperties(game),
            "team": model.GetProperties(team),
            "puzzle": model.GetProperties(puzzle),
            "feedback": model.GetProperties(feedback),
            "guesses": [],
            "solve_teams": set(),
        }

        for guess in model.Guess.all().ancestor(puzzle).order("-timestamp"):
            if guess.answer in puzzle.answers:
                props["solve_teams"].add(guess.team.key())
            if guess.team.key() == team.key():
                props["guesses"].append(model.GetProperties(guess))
                if guess.answer in puzzle.answers:
                    props["guesses"][-1]["is_correct"] = True
                    props.setdefault("solve_time", guess.timestamp)

        self.response.out.write(template.render("guess.dj.html", props))
Example #2
0
    def post(self):
        game = model.GetGame()
        team = login.GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        puzzle = MaybeGetPuzzle(game, self.request)
        if not puzzle: return self.error(404)

        self.redirect("/guess?t=%d&p=%s" %
                      (team.key().id(), self.request.get("p")))

        if self.request.get("comment", None) is not None:
            feedback = model.Feedback(parent=puzzle, key_name=str(team.key()))
            feedback.comment = self.request.get("comment")
            if game.solving_enabled or game.voting_enabled:
                for s in range(len(feedback.scores)):
                    feedback.scores[s] = NormalizeScore(
                        self.request.get("score.%d" % s))
            feedback.put()
            self.redirect("/team?t=%d" % team.key().id())

        answer = NormalizeAnswer(self.request.get("answer", ""))
        if answer and game.solving_enabled:
            start_time = datetime.datetime.now() - datetime.timedelta(
                0, SPAM_SECONDS)
            guesses = model.Guess.all().ancestor(puzzle).filter(
                "team", team.key())
            recent = guesses.filter("timestamp >=",
                                    start_time).fetch(SPAM_GUESSES)
            if answer in [g.answer for g in recent]:
                self.response.headers["location"] += "&error=dup"
            elif len(recent) >= SPAM_GUESSES:
                self.response.headers["location"] += "&error=spam"
            else:
                model.Guess(parent=puzzle, answer=answer, team=team).put()
Example #3
0
    def get(self):
        game = model.GetGame()
        puzzle = MaybeGetPuzzle(game, self.request)
        if not puzzle: return self.error(404)

        # Access control:
        #   (Solving enabled AND type is "p") OR
        #   Voting enabled OR Results enabled OR
        #   Team is puzzle author OR Team solved puzzle
        file = self.request.get("f")
        if not (game.voting_enabled or game.results_enabled or
                (game.solving_enabled and file == "p")):
            team = login.GetTeamOrRedirect(game, self.request, self.response)
            if not team: return
            if puzzle.parent_key() != team.key():
                guesses = model.Guess.all().ancestor(puzzle).filter(
                    "team", team.key())
                if not [g for g in guesses if g in puzzle.answers]:
                    return self.error(403)

        blobinfo = {
            "p": puzzle.puzzle_blob,
            "s": puzzle.solution_blob
        }.get(file)
        if not blobinfo: return self.error(404)
        self.send_blob(
            blobinfo,
            save_as="%s%s-%s" %
            (file, self.request.get("p"), blobinfo.filename or "file"))
Example #4
0
    def post(self):
        game = model.GetGame()
        team = login.GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        if game.voting_enabled:
            sr = range(len(model.Feedback().scores))
            puzzles = model.Puzzle.all().ancestor(game)
            feedback_keys = [
                db.Key.from_path("Feedback", str(team.key()), parent=p.key())
                for p in puzzles
            ]
            feedback = {}
            for p, f in zip(puzzles, model.Feedback.get(feedback_keys)):
                if p.key().parent() == team.key(): continue  # No self voting
                n = p.number
                score = [self.request.get("score.%s.%d" % (n, s)) for s in sr]
                score_orig = [
                    self.request.get("score.%s.%d.orig" % (n, s)) for s in sr
                ]
                if (score != score_orig) or not f:
                    if not f:
                        f = model.Feedback(parent=p, key_name=str(team.key()))
                    for s in sr:
                        f.scores[s] = puzzle.NormalizeScore(score[s])
                    if not self.request.get("normalize"): f.put()
                feedback.setdefault(p.key().name(), []).append(f)

            if self.request.get("normalize"):
                for flist in feedback.values():
                    for i in range(len(flist[0].scores)):
                        vector = [f.scores[i] for f in flist]
                        NormalizeVector(vector, 0, 5, 3)
                        for f, s in zip(flist, vector):
                            f.scores[i] = round(s * 10) / 10.0
                    for f in flist:
                        f.put()

        self.redirect("/team?t=%d" % team.key().id())
        team.name = self.request.get("name") or team.name
        team.email = self.request.get("email")

        set_pw = self.request.get("set_password")
        confirm_pw = self.request.get("confirm_password")
        if set_pw or confirm_pw:
            if set_pw != confirm_pw:
                self.response.headers["location"] += "&error=set_password"
            else:
                team.password = set_pw

        team.put()
Example #5
0
    def post(self):
        game = model.GetGame()
        team = GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        target = self.request.get("u") or ("/team?t=%d" % team.key().id())
        password = self.request.get("pw")
        if password == team.password:
            self.redirect(target)
            self.response.headers.add_header(
                "Set-Cookie", "password=%s; Max-Age=%d; Path=/" %
                (urllib.quote(password), 7 * 24 * 60 * 60))
        else:
            self.redirect("/login?t=%d&u=%s&error=password" %
                          (team.key().id(), urllib.quote(target)))
Example #6
0
    def get(self):
        game = model.GetGame()
        team = GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        props = {
            "error": self.request.get_all("error"),
            "game": model.GetProperties(game),
            "team": model.GetProperties(team),
            "target": self.request.get("u"),
        }

        self.response.headers.add_header("Set-Cookie",
                                         "password=; Max-Age=0; Path=/")
        self.response.out.write(template.render("login.dj.html", props))
Example #7
0
    def post(self):
        game = model.GetGame()
        team = login.GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        puzzle = MaybeGetPuzzle(game, self.request)
        if not puzzle: return self.error(404)
        if puzzle.parent_key() != team.key(): return self.error(403)
        self.redirect("/team?t=%d" % team.key().id())

        updates = {}
        for arg in self.request.arguments():
            if arg.endswith(".orig"): continue
            orig_value = urllib.unquote(self.request.get(arg + ".orig", ""))
            new_value = self.request.get(arg).replace("\r\n", "\n")
            if new_value != orig_value: updates[arg] = new_value.strip()

        for blobinfo in self.get_uploads(field_name="puzzle_file"):
            if puzzle.puzzle_blob: blobstore.delete(puzzle.puzzle_blob.key())
            puzzle.puzzle_blob = blobinfo

        for blobinfo in self.get_uploads(field_name="solution_file"):
            if puzzle.solution_blob:
                blobstore.delete(puzzle.solution_blob.key())
            puzzle.solution_blob = blobinfo

        if "title" in updates: puzzle.title = updates.get("title")

        if "answers" in updates:
            puzzle.answers = []
            for answer in updates.get("answers").split("\n"):
                answer = NormalizeAnswer(answer)
                if answer: puzzle.answers.append(answer)

        if "errata" in updates:
            puzzle.errata_timestamp = datetime.datetime.now()
            puzzle.errata = updates.get("errata")

        if "solution" in updates:
            puzzle.solution = updates.get("solution")

        puzzle.put()
Example #8
0
    def get(self):
        game = model.GetGame()
        team = login.GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        puzzle = MaybeGetPuzzle(team, self.request)
        if not puzzle: return self.error(404)
        if puzzle.parent_key() != team.key(): return self.error(403)

        props = {
            "form_url": blobstore.create_upload_url("/puzzle"),
            "game": model.GetProperties(game),
            "team": model.GetProperties(team),
            "puzzle": model.GetProperties(puzzle),
            "comments": [],
            "votes": [],
            "solves": [],
        }

        no_scores = model.Feedback().scores
        for feedback in model.Feedback.all().ancestor(puzzle):
            comment = (feedback.comment or "").strip()
            if comment: props["comments"].append(comment)
            props["votes"].append(feedback.scores)

        props["puzzle"]["answers"] = "\n".join(props["puzzle"].get(
            "answers", []))
        props["comments"].sort(key=unicode.lower)
        props["votes"].sort(reverse=True)

        solvers = {}
        for guess in model.Guess.all().ancestor(puzzle).order("timestamp"):
            if guess.answer in puzzle.answers and not solvers.get(
                    guess.team.key()):
                solvers[guess.team.key()] = 1
                props["solves"].append(guess)

        self.response.out.write(template.render("puzzle.dj.html", props))
Example #9
0
    def get(self):
        game = model.GetGame()

        props = {
            "admin_email": admin.GetAdminEmail(game),
            "admin_logout_url": users.create_logout_url(dest_url="/"),
            "game": model.GetProperties(game),
            "puzzles": [],
            "teams": [],
        }

        team_by_key = {}
        for team in model.Team.all().ancestor(game):
            team_props = team_by_key[team.key()] = model.GetProperties(team)
            team_props.update({
                "generosity": {},
                "score": team_props.get("bonus") or 0.0,
                "solve_count": 0,
                "solve_score": 0,
                "solve_time": 0,
                "works": [],
                "wrote": {},
                "wrote_score": 0,
            })
            cookie = login.CookiePassword(team, self.request)
            if cookie:
                props["cookie_team"] = team_props
                props["cookie_password"] = cookie
            props["teams"].append(team_props)

        puzzle_by_key = {}
        for p in sorted(model.Puzzle.all().ancestor(game), key=puzzle.SortKey):
            puzzle_props = puzzle_by_key[p.key()] = model.GetProperties(p)
            author_props = team_by_key.get(p.key().parent(), {})
            puzzle_props.update({
                "author": author_props.get("name"),
                "author_id": author_props.get("key_id"),
                "score": 0,
                "solve_count": 0,
                "votes": [],
            })
            author_props["wrote"][p.key().name()] = puzzle_props
            props["puzzles"].append(puzzle_props)

        for feedback in model.Feedback.all().ancestor(game):
            puzzle_props = puzzle_by_key[feedback.key().parent()]
            puzzle_props["votes"].append(feedback.scores)
            puzzle_type = puzzle_props["key_name"]
            team_props = team_by_key[db.Key(feedback.key().name())]
            generosity = team_props["generosity"].setdefault(
                puzzle_props["key_name"], [0] * len(feedback.scores))
            generosity[:] = [
                a + b for a, b in zip(generosity, feedback.scores)
            ]

        work_by_keys = {}
        for team_props in props["teams"]:
            for puzzle_props in props["puzzles"]:
                work_props = work_by_keys[(team_props["key"],
                                           puzzle_props["key"])] = {
                                               "puzzle": puzzle_props,
                                               "guess_count": 0,
                                           }
                team_props["works"].append(work_props)

        for guess in model.Guess.all().ancestor(game).order("timestamp"):
            work_props = work_by_keys[(guess.team.key(), guess.key().parent())]
            if guess.answer in work_props["puzzle"]["answers"]:
                if not work_props.get("solve_time"):
                    team_props = team_by_key[guess.team.key()]
                    work_props["solve_time"] = team_props[
                        "solve_time"] = guess.timestamp
                    work_props["puzzle"]["solve_count"] += 1
                    team_props["solve_count"] += 1

        #
        # Compute scores
        #

        no_scores = model.Feedback().scores
        for team_props in props["teams"]:
            for puzzle_props in team_props["wrote"].values():
                wrote_scores = puzzle_props["scores"] = [0 for s in no_scores]
                for scores in puzzle_props["votes"]:
                    for i in range(len(wrote_scores)):
                        if i < len(scores): wrote_scores[i] += scores[i]

                unvoted = len(team_by_key) - len(puzzle_props["votes"]) - 1
                if unvoted > 0:
                    for i in range(len(wrote_scores)):
                        wrote_scores[i] += no_scores[i] * unvoted

                points = 2 * wrote_scores[0] + sum(wrote_scores[1:])
                puzzle_props["score"] = points
                team_props["score"] += points
                team_props["wrote_score"] += points

            for work_props in team_props["works"]:
                if work_props.get("solve_time"):
                    points = 9 + len(
                        props["teams"]) - work_props["puzzle"]["solve_count"]
                    work_props["score"] = points
                    team_props["solve_score"] += points
                    team_props["score"] += points

        props["teams"].sort(
            key=lambda tp: (-tp["solve_count"], tp["solve_time"], tp["name"]))

        self.response.out.write(template.render("main.dj.html", props))
Example #10
0
    def get(self):
        game = model.GetGame()
        team = login.GetTeamOrRedirect(game, self.request, self.response)
        if not team: return

        props = {
            "error": self.request.get_all("error"),
            "game": model.GetProperties(game),
            "team": model.GetProperties(team),
            "team_puzzles": [],
            "other_puzzles": [],
            "errata_puzzles": [],
        }

        puzzle_props = {}
        for p in sorted(model.Puzzle.all().ancestor(game), key=puzzle.SortKey):
            pp = puzzle_props[p.key()] = model.GetProperties(p)
            pp.update({
                "guess_count": 0,
                "solve_teams": set(),
                "comment_count": 0,
                "vote_count": 0,
            })
            if pp.get("errata"):
                props["errata_puzzles"].append(pp)
            if p.parent_key() == team.key():
                props["team_puzzles"].append(pp)
            else:
                props["other_puzzles"].append(pp)

        feedback_keys = [
            db.Key.from_path("Feedback", str(team.key()), parent=pp["key"])
            for pp in props["other_puzzles"]
        ]
        for key, feedback in zip(feedback_keys,
                                 model.Feedback.get(feedback_keys)):
            pp = puzzle_props.get(key.parent())
            if pp:
                pp["feedback"] = model.GetProperties(
                    feedback or model.Feedback(key=key))

        no_scores = model.Feedback().scores
        for review in model.Feedback.all().ancestor(team):
            pp = puzzle_props.get(review.key().parent())
            if pp:
                if review.comment and review.comment.strip():
                    pp["comment_count"] += 1
                pp["vote_count"] += 1

        for guess in model.Guess.all().ancestor(game):
            pp = puzzle_props.get(guess.parent_key())
            if not pp: continue

            if guess.answer in pp["answers"]:
                pp["solve_teams"].add(guess.team.key())

            if guess.team.key() == team.key():
                if guess.answer in pp["answers"]:
                    pp["solve_time"] = guess.timestamp
                pp["guess_count"] += 1

        self.response.out.write(template.render("team.dj.html", props))
Example #11
0
    def post(self):
        game = model.GetGame()
        if not GetAdminEmail(game): return self.error(403)

        ingredients = [self.request.get("ingredient%d" % i) for i in range(3)]
        admin_users = re.split(r'[,\s]+', self.request.get("admin_users"))
        game.location = self.request.get("location")
        game.ingredients = [i for i in ingredients if i]
        game.ingredients_visible = bool(
            self.request.get("ingredients_visible"))
        game.admin_users = [u for u in admin_users if u]
        game.login_enabled = bool(self.request.get("login_enabled"))
        game.solving_enabled = bool(self.request.get("solving_enabled"))
        game.voting_enabled = bool(self.request.get("voting_enabled"))
        game.results_enabled = bool(self.request.get("results_enabled"))
        game.put()

        for team in model.Team.all().ancestor(game):
            try:
                bonus = float(self.request.get("bonus.%d" % team.key().id()))
            except:
                bonus = 0.0
            if bonus != team.bonus:
                team.bonus = bonus
                team.put()

        n = self.request.get("delete_team")
        if n:
            for tk in model.Team.all(keys_only=True).ancestor(game).filter(
                    "name", n):
                db.delete(
                    model.Guess.all(keys_only=True).ancestor(game).filter(
                        "team", tk))
                db.delete([
                    db.Key.from_path("Feedback", str(tk), parent=p.key())
                    for p in model.Puzzle.all().ancestor(game)
                ])
                db.delete(db.Query(keys_only=True).ancestor(tk))

        if self.request.get("new_team"):  # before assign_numbers is handled
            team = model.Team(parent=game)
            team.name = self.request.get("new_team")
            team.password = self.request.get("new_password")
            team.put()
            for pt in PUZZLE_TYPES:
                model.Puzzle(parent=team, key_name=pt).put()

        nonum = model.Puzzle().number
        puzzles = list(model.Puzzle.all().ancestor(game))
        for puzzle in puzzles:
            k = puzzle.key()
            num = self.request.get("number.%d.%s" %
                                   (k.parent().id(), k.name()))
            orig = self.request.get("number.%d.%s.orig" %
                                    (k.parent().id(), k.name()))
            if num != orig:
                puzzle.number = num or nonum
                puzzle.put()

        if self.request.get("assign_numbers"):
            try:
                n = int(self.request.get("assign_start"))
            except:
                n = 1

            taken = {}  # numbers already used
            type_puzzles = {}  # group puzzles by type before scrambling order
            for p in puzzles:
                if p.number == nonum or not p.number:
                    type_puzzles.setdefault(p.key().name(), []).append(p)
                else:
                    taken[p.number] = 1

            for ptype, plist in sorted(type_puzzles.iteritems()):
                random.shuffle(plist)
                for p in plist:
                    while taken.has_key(str(n)):
                        n += 1
                    p.number = str(n)
                    p.put()
                    n += 1

        self.redirect("/admin")
Example #12
0
    def get(self):
        game = model.GetGame()
        admin_email = GetAdminEmail(game)
        if not admin_email:
            # TODO(egnor): Without multilogin, this creates redirect loops.
            return self.redirect(users.create_login_url(dest_url="/admin"))

        props = {
            "game": model.GetProperties(game),
            "admin_email": admin_email,
            "admin_logout_url": users.create_logout_url(dest_url="/admin"),
            "puzzles": [],
            "teams": [],
        }

        team_by_key = {}
        for team in model.Team.all().ancestor(game):
            team_props = team_by_key[team.key()] = model.GetProperties(team)
            team_props.update({
                "feedback_count": 0,
                "works": [],
            })
            props["teams"].append(team_props)

        puzzle_by_key = {}
        for p in sorted(model.Puzzle.all().ancestor(game), key=puzzle.SortKey):
            puzzle_props = puzzle_by_key[p.key()] = model.GetProperties(p)
            author_props = team_by_key.get(p.key().parent(), {})
            puzzle_props.update({
                "author": author_props.get("name"),
                "author_id": author_props.get("key_id"),
                "comment_count": 0,
                "solve_count": 0,
            })
            props["puzzles"].append(puzzle_props)

        work_by_keys = {}
        for tp in props["teams"]:
            for pp in props["puzzles"]:
                work_props = work_by_keys[(tp["key"], pp["key"])] = {
                    "puzzle": pp,
                    "guess_count": 0,
                }
                tp["works"].append(work_props)

        for guess in model.Guess.all().ancestor(game).order("timestamp"):
            wp = work_by_keys[(guess.team.key(), guess.key().parent())]
            wp["guess_count"] += 1
            if guess.answer in wp["puzzle"]["answers"] and not wp.get(
                    "solve_time"):
                wp["puzzle"]["solve_count"] += 1
                wp["solve_time"] = guess.timestamp
                wp["solve_rank"] = Ordinal(wp["puzzle"]["solve_count"])

        for feedback in model.Feedback.all().ancestor(game):
            if feedback.key().parent().parent() == db.Key(
                    feedback.key().name()):
                feedback.delete(
                )  # Remove bogus self-votes from older versions.
            else:
                team_props = team_by_key[db.Key(feedback.key().name())]
                team_props["feedback_count"] += 1
                if feedback.comment and feedback.comment.strip():
                    puzzle_props = puzzle_by_key[feedback.key().parent()]
                    puzzle_props["comment_count"] += 1

        self.response.out.write(template.render("admin.dj.html", props))