Example #1
0
    def post(self):
        args = parser.parse_args()

        datamanager = api.get_datamanager()
        (id, path) = datamanager.new()

        # Writing images
        for image in args.images:
            image.save(os.path.join(path, image.filename))

        try:
            options_str = args['options']
            options = json.loads(options_str)
            options = [optionDAO(**o) for o in options]
        except KeyError:
            # Key is not present
            pass

        # Writting metadata
        metadata = Metadata(uuid=id,
                            name=args['name'],
                            images=[image.filename for image in args.images],
                            options=options,
                            creation_date=datetime.datetime.now(),
                            status=StatusDAO(StatusCode.QUEUED),
                            processingTime=-1)

        datamanager.setstatus(id, json.dumps(metadata.toDict()))
        api.get_executor().process(path,
                                   os.path.join(path, datamanager.STATUS_FILE))

        current_app.logger.info("task/new request with meta {}".format(
            json.dumps(metadata.toDict(), separators=(',', ':'))))
        return Response200DAO(id).toDict()
Example #2
0
 def setUp(self):
     self.app = create_app(environment="Testing")
     self.app.app_context().push()
     self.app.testing = True
     self.url_prefix = self.app.config['URL_PREFIX']
     self.datamanager = api.get_datamanager()
     self.executor = api.get_executor()
Example #3
0
def gen_board_async():
    """Starts the generation of a Board with a given difficulty level.
    ---
    post:
        tags:
          - Boards
        summary: Starts the generation of a Board with a given difficulty level.
        parameters:
          - name: difficultyLevel
            in: query
            description: The difficulty level. Possible values are 1 (easy), 2
                         (medium) or 3 (difficult).
            required: true
            schema:
              type: integer
              enum: [1, 2, 3]
        responses:
            202:
              description: The generation has been started. The url for querying the
                           search progress is returned in the "location" header
                           of the response.
            400:
              description: Error detail in "message".
    """
    try:
        dif_level = int(request.args.get("difficultyLevel"))
    except:
        raise (InvalidUsage(
            "Bad request: 'difficultyLevel' parameter required with value in [1, 2, 3]."
        ))
    if dif_level < 1 or dif_level > 3:
        raise InvalidUsage(
            "Bad request: invalid difficulty level, '{}'".format(dif_level),
            400)
    board_level = lsdk.PuzzleDifficulty.HARD
    if dif_level == 1:
        board_level = lsdk.PuzzleDifficulty.EASY
    elif dif_level == 2:
        board_level = lsdk.PuzzleDifficulty.MEDIUM

    executor = get_executor()

    # Invokes the generation worker giving a unique id to index the
    # corresponding future. This unique id will then be passed to the
    # generation status endpoint so it knows which future to query.
    location_value = ""
    current_job_id = str(uuid.uuid4())
    executor.submit_stored(current_job_id, gen_board_worker, board_level)

    location_value = url_for('api.get_gen_status', job_id=current_job_id)
    response = current_app.response_class(status=202)
    response.headers["Location"] = location_value
    return response
def create_all_solved_boards_async():
    """Starts a search for all the solutions for a given board.
    ---
    post:
        tags:
          - Solved Boards
        summary: Starts a search for all the solutions for a given board
                 (may take a while).
        requestBody:
            description: The board to be solved and the max. number of solutions to return.
            required: true
            content:
              application/json:
                schema: SolveAllSchema
        responses:
            202:
              description: The search has been started. The url for querying the
                           search progress is returned in the "location" header
                           of the response.
            400:
              description: Error detail in "message".
    """
    global job_info
    global job_info_lock
    board = lsdk.Board()
    try:
        body = request.get_json()
        board = lsdk.Board(body["board"])  # Board to solve
        max_solutions = body["maxSolutions"]
    except Exception as e:
        raise (InvalidUsage("Bad request: {}".format(str(e)), 400))

    executor = get_executor()

    # Invokes the solving worker giving a unique id to index the
    # corresponding future. This unique id will then be passed to the
    # solving status endpoint so it knows which future to query.
    location_value = ""
    current_job_id = str(uuid.uuid4())
    with job_info_lock:
        info = {"progress_percent": 0.0, "num_solutions": 0, "cancel": False}
        job_info[current_job_id] = info
    executor.submit_stored(current_job_id, solve_board_worker, board,
                           max_solutions, current_job_id)

    location_value = url_for('api.get_solve_status', job_id=current_job_id)
    response = current_app.response_class(status=202)
    response.headers["Location"] = location_value
    return response
 def on_solver_finished(result, solutions):
     global job_info
     global job_info_lock
     nonlocal job_id
     nonlocal asyncSolveCompleted
     asyncSolveCompleted = True
     nonlocal solver_result
     solver_result = result
     nonlocal board_solutions
     board_solutions = solutions
     if result == lsdk.SolverResult.ASYNC_SOLVED_CANCELLED:
         # Job has been cancelled externally - cleans-up.
         executor = get_executor()
         with job_info_lock:
             job_info.pop(job_id)
             executor.futures.pop(job_id)
def cancel_async_solve(job_id):
    """Cancels an ongoing search for all the solutions of a board.
    ---
    delete:
        tags:
          - Solved Boards
        summary: Cancels an ongoing search for all the solutions of a board.
        parameters:
          - name: job_id
            in: path
            description: The id of the search job to be cancelled.
            required: true
            schema:
              type: string
        responses:
            204:
              description: The search has been cancelled.
            404:
              description: The given job_id does not correspond to a known
                           ongoing search job.
    """
    global job_info
    global job_info_lock
    executor = get_executor()
    fut = None
    try:
        fut = executor.futures._futures[job_id]
    except KeyError:
        return make_response(
            jsonify({
                "message":
                "no board solving to cancel for job-id '{}'".format(job_id)
            }), 404)
    if not fut.done():
        # Marks the job to be stopped as soon as possible by the worker.
        with job_info_lock:
            job_info[job_id]["cancel"] = True
    else:
        # The job has been done but a cancel has been invoked, there's a
        # chance that no request for its result will come - do the clean-
        # up from here.
        executor.futures.pop(job_id)
    response = current_app.response_class(status=204)
    return response
Example #7
0
def get_gen_status(job_id):
    """Retrieves the status of a generate board request given its job id.
    ---
    get:
        tags:
          - Boards
        summary: Retrieves the status of a generate board request given its job id.
        parameters:
          - name: job_id
            in: path
            required: true
            description: the id of the generate board job.
            schema:
              type: string
        responses:
            200:
              description: If the generation is not finished returns a
                           json with the fields "current_step" and
                           "total_steps". If the board has been generated,
                           returns the content described below.
              content:
                application/json:
                  schema: GeneratedBoardSchema
            404:
              description: The job_id doesn't refer to a known job.
    """
    executor = get_executor()
    fut = None
    try:
        fut = executor.futures._futures[job_id]
    except KeyError:
        return make_response(
            jsonify(
                {
                    "message":
                    "no board generation for job-id '{}'".format(job_id)
                }, ), 404)
    if not fut.done():
        return jsonify({"status": "generating"})
    else:
        fut = executor.futures.pop(job_id)
        return jsonify(fut.result())
def get_solve_status(job_id):
    """Returns the status of a search for all the solutions of a given
       board.
    ---
    get:
        tags:
          - Solved Boards
        summary: Returns the status of a search for all the solutions of a given
                 board.
        parameters:
          - name: job_id
            in: path
            required: true
            description: the id of the search solutions job
            schema:
              type: string
        responses:
            200:
              description: If the search is not finished returns a json
                           with the fields "cancel_url", "progress_percent",
                           "num_unsolvables" and "num_solutions". "cancel_url"
                           is the url the client should send a "DELETE" request
                           to in order to cancel the search for all solutions.
                           "progress_percent" is the progress percentage.
                           "num_unsolvables" is the number of intermediary
                           unsolvable boards found during the search.
                           "num_solutions" is the number of solutions
                           found so far.
                           If the search is finished, a json with the structure
                           below is returned.
              content:
                application/json:
                  schema: BoardSolutionsSchema
            404:
              description: The job_id doesn't refer to a known job.
    """
    global job_info
    global job_info_lock
    executor = get_executor()
    fut = None
    try:
        fut = executor.futures._futures[job_id]
    except KeyError:
        return make_response(
            jsonify(
                {"message": "no board solving for job-id '{}'".format(job_id)
                 }, ), 404)
    if not fut.done():
        resp = {"status": "solving"}
        resp["cancel_url"] = url_for('api.cancel_async_solve',
                                     job_id=job_id,
                                     _external=True)
        with job_info_lock:
            resp["progress_percent"] = job_info[job_id]["progress_percent"]
            resp["num_unsolvables"] = job_info[job_id]["num_unsolvables"]
            resp["num_solutions"] = job_info[job_id]["num_solutions"]
        return jsonify(resp)
    else:
        fut = executor.futures.pop(job_id)
        with job_info_lock:
            job_info.pop(job_id)
        return jsonify(fut.result())