示例#1
0
def reset_puzzle_pieces(puzzle):
    """
    Puzzles that are reset will reuse the same pieces as before and are
    not rerendered.
    """
    # TODO: Reset the redis puzzle token so players will be required to refresh
    # the browser if they had the puzzle open.

    cur = db.cursor()
    result = cur.execute(
        fetch_query_string("select_all_from_puzzle_for_id.sql"),
        {
            "id": puzzle
        },
    ).fetchall()
    if not result:
        cur.close()
        raise DataError("No puzzle found with that id.")

    (result, col_names) = rowify(result, cur.description)
    puzzle_data = result[0]

    # Update puzzle status to MAINTENANCE
    r = requests.patch(
        "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/details/".
        format(
            HOSTAPI=current_app.config["HOSTAPI"],
            PORTAPI=current_app.config["PORTAPI"],
            puzzle_id=puzzle_data["puzzle_id"],
        ),
        json={"status": MAINTENANCE},
    )
    if r.status_code != 200:
        raise Exception("Puzzle details api error")
    sse.publish(
        "status:{}".format(MAINTENANCE),
        channel="puzzle:{puzzle_id}".format(
            puzzle_id=puzzle_data["puzzle_id"]),
    )

    # Transfer any redis piece data out first.
    transfer(puzzle, cleanup=True)

    # timeline ui should only show when the puzzle is in 'complete' status.
    archive_and_clear(puzzle)

    (x1, y1, x2, y2) = (0, 0, puzzle_data["table_width"],
                        puzzle_data["table_height"])

    (result, col_names) = rowify(
        cur.execute(query_select_top_left_piece, {
            "puzzle": puzzle_data["id"]
        }).fetchall(),
        cur.description,
    )
    cur.close()
    topLeftPiece = result[0]
    allPiecesExceptTopLeft = list(range(0, puzzle_data["pieces"]))
    allPiecesExceptTopLeft.remove(topLeftPiece["id"])

    # Randomize piece x, y. Reset the parent
    new_piece_properties = []
    for piece in allPiecesExceptTopLeft:
        x = randint(x1, x2)
        y = randint(y1, y2)
        new_piece_properties.append({
            "x": x,
            "y": y,
            "parent": None,
            "status": None,
            "id": piece
        })

    current_app.logger.debug(new_piece_properties)
    r = requests.patch(
        "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/pieces/".
        format(
            HOSTAPI=current_app.config["HOSTAPI"],
            PORTAPI=current_app.config["PORTAPI"],
            puzzle_id=puzzle_data["puzzle_id"],
        ),
        json={"piece_properties": new_piece_properties},
    )
    if r.status_code != 200:
        raise Exception(
            "Puzzle pieces api error. Failed to patch pieces. {}".format(
                r.json()))
示例#2
0
def reset_puzzle_pieces(puzzle):
    """
    Puzzles that are reset will reuse the same pieces as before and are
    not rerendered.
    """
    # TODO: Reset the redis puzzle token so players will be required to refresh
    # the browser if they had the puzzle open.

    cur = db.cursor()
    result = cur.execute(
        fetch_query_string("select_all_from_puzzle_for_id.sql"),
        {
            "id": puzzle
        },
    ).fetchall()
    if not result:
        cur.close()
        raise DataError("No puzzle found with that id.")

    (result, col_names) = rowify(result, cur.description)
    puzzle_data = result[0]

    # Update puzzle status to MAINTENANCE
    r = requests.patch(
        "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/details/".
        format(
            HOSTAPI=current_app.config["HOSTAPI"],
            PORTAPI=current_app.config["PORTAPI"],
            puzzle_id=puzzle_data["puzzle_id"],
        ),
        json={"status": MAINTENANCE},
    )
    if r.status_code != 200:
        raise Exception("Puzzle details api error")
    sse.publish(
        "status:{}".format(MAINTENANCE),
        channel="puzzle:{puzzle_id}".format(
            puzzle_id=puzzle_data["puzzle_id"]),
    )

    # Transfer any redis piece data out first.
    transfer(puzzle, cleanup=True)

    # timeline ui should only show when the puzzle is in 'complete' status.
    archive_and_clear(puzzle)

    (table_width, table_height) = (puzzle_data["table_width"],
                                   puzzle_data["table_height"])
    width = round(table_width / 2.5)
    height = round(table_height / 2.5)
    outline_offset_x = int((table_width - width) * 0.5)
    outline_offset_y = int((table_height - height) * 0.5)

    (result, col_names) = rowify(
        cur.execute(query_select_top_left_piece, {
            "puzzle": puzzle_data["id"]
        }).fetchall(),
        cur.description,
    )
    cur.close()
    topLeftPiece = result[0]
    allPiecesExceptTopLeft = list(range(0, puzzle_data["pieces"]))
    allPiecesExceptTopLeft.remove(topLeftPiece["id"])

    # Randomize piece x, y. Reset the parent
    # Uses same method of random piece placement that piecemaker uses.
    r = requests.get(
        "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/pieces/".
        format(
            HOSTAPI=current_app.config["HOSTAPI"],
            PORTAPI=current_app.config["PORTAPI"],
            puzzle_id=puzzle_data["puzzle_id"],
        ))
    if r.status_code != 200:
        raise Exception(
            "Puzzle pieces api error. Failed to get pieces. {}".format(
                r.json()))
    piece_properties = r.json()["immutable_piece_properties"]

    # Simulate the pieces.json file from piecemaker
    piece_bboxes = {}
    for pc_id in allPiecesExceptTopLeft:
        # Just use 0 and 0 for the x, and y here since PM doesn't store that.
        piece_bboxes[pc_id] = [
            0,
            0,
            piece_properties[str(pc_id)]["w"],
            piece_properties[str(pc_id)]["h"],
        ]

    pieces_distribution = random_outside(
        table_bbox=[0, 0, table_width, table_height],
        outline_bbox=[
            outline_offset_x, outline_offset_y, outline_offset_x + width,
            outline_offset_y + height
        ],
        piece_bboxes=piece_bboxes,
        regions=("left_side", "top_middle", "bottom_middle"))
    new_piece_properties = []
    for piece in allPiecesExceptTopLeft:
        x = pieces_distribution[piece][0]
        y = pieces_distribution[piece][1]
        new_piece_properties.append({
            "x": x,
            "y": y,
            "parent": None,
            "status": None,
            "id": piece
        })

    r = requests.patch(
        "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/pieces/".
        format(
            HOSTAPI=current_app.config["HOSTAPI"],
            PORTAPI=current_app.config["PORTAPI"],
            puzzle_id=puzzle_data["puzzle_id"],
        ),
        json={"piece_properties": new_piece_properties},
    )
    if r.status_code != 200:
        # Update puzzle status to BUGGY_UNLISTED
        r = requests.patch(
            "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/details/".
            format(
                HOSTAPI=current_app.config["HOSTAPI"],
                PORTAPI=current_app.config["PORTAPI"],
                puzzle_id=puzzle_data["puzzle_id"],
            ),
            json={"status": BUGGY_UNLISTED},
        )
        if r.status_code != 200:
            raise Exception("Puzzle details api error")
        sse.publish(
            "status:{}".format(BUGGY_UNLISTED),
            channel="puzzle:{puzzle_id}".format(
                puzzle_id=puzzle_data["puzzle_id"]),
        )
        raise Exception(
            "Puzzle pieces api error. Failed to patch pieces. {}".format(
                r.json()))
示例#3
0
    def do_task(self):
        super().do_task()
        made_change = False

        cur = db.cursor()
        for (low, high) in SKILL_LEVEL_RANGES:
            in_queue_puzzle_count = cur.execute(
                read_query_file("get_in_queue_puzzle_count.sql"),
                {
                    "low": low,
                    "high": high
                },
            ).fetchone()[0]
            if in_queue_puzzle_count <= self.minimum_count:
                (result, col_names) = rowify(
                    cur.execute(
                        read_query_file("select_random_puzzle_to_rebuild.sql"),
                        {
                            "status": COMPLETED,
                            "low": low,
                            "high": high
                        },
                    ).fetchall(),
                    cur.description,
                )
                if result:
                    for completed_puzzle in result:
                        puzzle = completed_puzzle["id"]

                        current_app.logger.debug(
                            "found puzzle {id}".format(**completed_puzzle))
                        # Update puzzle status to be REBUILD and change the piece count
                        pieces = random.randint(
                            max(
                                int(current_app.config["MINIMUM_PIECE_COUNT"]),
                                max(completed_puzzle["pieces"] - 400, low),
                            ),
                            min(high - 1, completed_puzzle["pieces"] + 400),
                        )
                        r = requests.patch(
                            "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/details/"
                            .format(
                                HOSTAPI=current_app.config["HOSTAPI"],
                                PORTAPI=current_app.config["PORTAPI"],
                                puzzle_id=completed_puzzle["puzzle_id"],
                            ),
                            json={
                                "status": REBUILD,
                                "pieces": pieces,
                                "queue": QUEUE_END_OF_LINE,
                            },
                        )
                        if r.status_code != 200:
                            current_app.logger.warning(
                                "Puzzle details api error. Could not set puzzle status to rebuild. Skipping {puzzle_id}"
                                .format(**completed_puzzle))
                            continue
                        completed_puzzle["status"] = REBUILD
                        completed_puzzle["pieces"] = pieces

                        # Delete any piece data from redis since it is no longer needed.
                        query_select_all_pieces_for_puzzle = (
                            """select * from Piece where (puzzle = :puzzle)""")
                        (all_pieces, col_names) = rowify(
                            cur.execute(query_select_all_pieces_for_puzzle, {
                                "puzzle": puzzle
                            }).fetchall(),
                            cur.description,
                        )
                        deletePieceDataFromRedis(redis_connection, puzzle,
                                                 all_pieces)

                        job = self.queue.enqueue_call(
                            func="api.jobs.pieceRenderer.render",
                            args=([completed_puzzle]),
                            result_ttl=0,
                            timeout="24h",
                        )

                        archive_and_clear(puzzle)
                    made_change = True

        cur.close()
        if made_change:
            self.log_task()
    def do_task(self):
        super().do_task()
        made_change = False

        cur = db.cursor()
        in_queue_puzzles_in_piece_groups = current_app.config[
            "MINIMUM_IN_QUEUE_PUZZLES_IN_PIECE_GROUPS"].copy()
        in_queue_puzzles_in_piece_groups.reverse()
        for (low, high, minimum_count) in map(
                lambda x: (x[0], x[1], in_queue_puzzles_in_piece_groups.pop()),
                current_app.config["SKILL_LEVEL_RANGES"],
        ):
            if minimum_count == 0:
                continue
            in_queue_puzzle_count = cur.execute(
                read_query_file("get_in_queue_puzzle_count.sql"),
                {
                    "low": low,
                    "high": high
                },
            ).fetchone()[0]
            if in_queue_puzzle_count <= minimum_count:
                (result, col_names) = rowify(
                    cur.execute(
                        read_query_file("select_random_puzzle_to_rebuild.sql"),
                        {
                            "status": COMPLETED,
                            "low": max(0, low - 500),
                            "high": high + 500,
                        },
                    ).fetchall(),
                    cur.description,
                )
                if not result:
                    # try again with wider range of puzzle piece counts
                    (result, col_names) = rowify(
                        cur.execute(
                            read_query_file(
                                "select_random_puzzle_to_rebuild.sql"),
                            {
                                "status": COMPLETED,
                                "low": max(0, low - 2000),
                                "high": high + 2000,
                            },
                        ).fetchall(),
                        cur.description,
                    )
                if result:
                    for completed_puzzle in result:
                        puzzle = completed_puzzle["id"]

                        current_app.logger.debug(
                            "found puzzle {id}".format(**completed_puzzle))
                        # Update puzzle status to be REBUILD and change the piece count
                        low_range = max(
                            int(current_app.config["MINIMUM_PIECE_COUNT"]),
                            min(
                                max(low, (high - 400)),
                                max(low, (completed_puzzle["pieces"] - 400)),
                            ),
                        )
                        high_range = min(
                            high,
                            max((low + 400),
                                (completed_puzzle["pieces"] + 400)))
                        pieces = random.randint(low_range, high_range)
                        sleep(API_REQUESTS_LIMIT_RATE)
                        r = requests.patch(
                            "http://{HOSTAPI}:{PORTAPI}/internal/puzzle/{puzzle_id}/details/"
                            .format(
                                HOSTAPI=current_app.config["HOSTAPI"],
                                PORTAPI=current_app.config["PORTAPI"],
                                puzzle_id=completed_puzzle["puzzle_id"],
                            ),
                            json={
                                "status": REBUILD,
                                "pieces": pieces,
                                "queue": QUEUE_END_OF_LINE,
                            },
                        )
                        if r.status_code != 200:
                            current_app.logger.warning(
                                "Puzzle details api error. Could not set puzzle status to rebuild. Skipping {puzzle_id}"
                                .format(**completed_puzzle))
                            continue
                        completed_puzzle["status"] = REBUILD
                        completed_puzzle["pieces"] = pieces

                        # Delete any piece data from redis since it is no longer needed.
                        query_select_all_pieces_for_puzzle = (
                            """select * from Piece where (puzzle = :puzzle)""")
                        (all_pieces, col_names) = rowify(
                            cur.execute(query_select_all_pieces_for_puzzle, {
                                "puzzle": puzzle
                            }).fetchall(),
                            cur.description,
                        )
                        deletePieceDataFromRedis(redis_connection, puzzle,
                                                 all_pieces)

                        job = self.queue.enqueue(
                            "api.jobs.pieceRenderer.render",
                            [completed_puzzle],
                            result_ttl=0,
                            job_timeout="24h",
                        )

                        archive_and_clear(puzzle)
                    made_change = True

        cur.close()
        if made_change:
            self.log_task()