Beispiel #1
0
def patch_courier(courier_id, new_data: dict, couriers_db: Couriers,
                  orders_db: Orders):
    """
    Change data for courier with courier_id and remove orders from assigned orders,
    if they don't fit by region, delivery hours or weight anymore
    """
    courier_id = int(courier_id)
    courier = couriers_db.get_item(courier_id)

    # Get assigned orders' data and create copy of its list
    assigned_orders_ids = get_ids(courier['assigned_orders'])
    assigned_orders = orders_db.get_items_by_ids(assigned_orders_ids)
    new_assigned_orders = deepcopy(assigned_orders)

    # Remove orders, that don't fit anymore, from assigned_orders
    if 'regions' in new_data:
        new_assigned_orders = list(
            filter(lambda o: o['region'] in new_data['regions'],
                   new_assigned_orders))
    if 'working_hours' in new_data:
        new_assigned_orders = list(
            filter(
                lambda o: _is_intervals_fitted(new_data['working_hours'], o[
                    'intervals']), new_assigned_orders))
    if 'courier_type' in new_data and \
            COURIERS_CAPACITY[new_data['courier_type']] < COURIERS_CAPACITY[courier['courier_type']]:
        # if capacity of courier get down
        new_capacity = _get_courier_capacity(new_data['courier_type'],
                                             new_assigned_orders)
        new_assigned_orders = _replace_orders(new_assigned_orders,
                                              new_capacity)

    # Add edited assigned list to new data and edit courier data in DB
    new_data['assigned_orders'] = [{
        'id': order['id']
    } for order in new_assigned_orders]
    couriers_db.edit_item(courier_id, new_data)

    # Update status to 0 (not assigned) for orders that are not in new assigned orders
    dropped_orders = [{
        'id': order['id']
    } for order in assigned_orders if order not in new_assigned_orders]
    orders_db.update_status(dropped_orders, 0, courier_type='')

    # Get edited courier's data from DB
    edited_courier = couriers_db.get_item(courier_id)
    return {
        'courier_id': edited_courier['_id'],
        'courier_type': edited_courier['courier_type'],
        'regions': edited_courier['regions'],
        'working_hours': edited_courier['working_hours']
    }
def assign_orders(courier_id_data: dict, couriers_db: Couriers,
                  orders_db: Orders):
    """
    Find orders that fit by region, delivery hours and put max number of them to assigned_orders.
    """
    courier_id = courier_id_data['courier_id']
    courier = couriers_db.get_item(courier_id)

    assigned_orders_ids = get_ids(
        courier['assigned_orders']
    )  # ids of orders that already in courier's bag

    # if courier has assigned orders, return them
    if len(assigned_orders_ids) != 0:
        courier = couriers_db.get_item(courier_id)
        return {
            'orders': [{
                'id': o['id']
            } for o in courier['assigned_orders']],
            'assign_time': courier['assign_time'].isoformat()
        }

    # Find orders that fit time intervals
    intervals = parse_intervals(
        courier['working_hours'])  # get intervals in seconds
    orders_to_place = []
    for interval in intervals:
        fitted_orders = orders_db.get_fitted_orders(interval[0], interval[1],
                                                    courier['regions'])
        orders_to_place = update_orders(orders_to_place, fitted_orders)

    # Put all possible new orders to courier's bag
    courier_type = courier['courier_type']
    capacity = COURIERS_CAPACITY[courier['courier_type']]
    assigned_orders = _place_orders(orders_to_place, capacity)

    # Write assigned orders to DB (if it is empty list, it is already in DB) and update their statuses
    if len(assigned_orders) != 0:
        couriers_db.write_assigned_orders(courier_id,
                                          assigned_orders,
                                          assign_time=datetime.now())
        orders_db.update_status(assigned_orders, 1, courier_type=courier_type)

    # Get assigned orders from DB
    courier = couriers_db.get_item(courier_id)
    return {
        'orders': [{
            'id': o['id']
        } for o in courier['assigned_orders']],
        'assign_time': courier['assign_time'].isoformat()
    }
def get_courier(courier_id, couriers_db: Couriers, orders_db: Orders):
    """
    Return 'courier_id', 'courier_type', 'regions', 'working_hours'.
    If courier has completed orders, return 'rating' and 'earning' too
    """
    courier_id = int(courier_id)
    courier = couriers_db.get_item(courier_id)

    completed_orders_ids = get_ids(courier['completed_orders'])
    # If no completed orders, no rating and earning returns
    if len(completed_orders_ids) == 0:
        return {
            'courier_id': courier_id,
            'courier_type': courier['courier_type'],
            'regions': courier['regions'],
            'working_hours': courier['working_hours']
        }

    completed_orders = orders_db.get_items_by_ids(completed_orders_ids)
    rating, earning = _calculate_rating_and_earning(completed_orders)
    return {
        'courier_id': courier_id,
        'courier_type': courier['courier_type'],
        'regions': courier['regions'],
        'working_hours': courier['working_hours'],
        'rating': rating,
        'earning': earning
    }
Beispiel #4
0
def complete_order(complete_data: dict, couriers_db: Couriers,
                   orders_db: Orders):
    """
    Move order with id=complete_data['order_id'] from assigned_orders to completed_orders and change its status
    """
    courier_id = complete_data['courier_id']
    order_id = complete_data['order_id']
    complete_time = str_to_datetime(complete_data['complete_time'])

    courier_type = couriers_db.get_item(courier_id)['courier_type']
    delivery_time = _calculate_delivery_time(courier_id, complete_time,
                                             couriers_db, orders_db)
    # Update data of completed order in DB
    orders_db.update_status([{
        'id': order_id
    }],
                            2,
                            complete_time=complete_time,
                            delivery_time=delivery_time)
    # Change status of order in couriers' DB
    couriers_db.move_order_to_completed(courier_id, order_id)
    return {'order_id': order_id}
Beispiel #5
0
def validate_complete_orders(complete_data: dict, couriers_db: Couriers, orders_db: Orders) -> (dict, int):
    # Check if data matches the schema
    with open('application/schemas/OrdersCompletePostRequest.json') as f:
        complete_order_schema = ValidatorWithDatetime(json.load(f))
    for err in complete_order_schema.iter_errors(complete_data):
        return {'validation_error': err.message}, 400

    # Check if courier_id and order_id exist
    courier_id = complete_data['courier_id']
    order_id = complete_data['order_id']
    if not couriers_db.get_item(courier_id):
        return {'validation_error': f'There are no courier with id {courier_id}'}, 400
    elif not orders_db.get_item(order_id):
        return {'validation_error': f'There are no order with id {order_id}'}, 400

    # Check status of the order
    order_status = orders_db.get_item(order_id)['status']
    if order_status == 0:
        return {'validation_error': f'order with id {order_id} is not assigned'}, 400
    elif order_status == 2:
        return {'validation_error': f'order with id {order_id} is already completed'}, 400

    # Check that complete time is later than assign time
    courier = couriers_db.get_item(courier_id)
    assign_time = courier['assign_time']
    complete_time = str_to_datetime(complete_data['complete_time'])
    if complete_time < assign_time:
        return {'validation_error':
                    f'\'complete_time\' must be later than \'assign_time\': {assign_time.isoformat()}'}, 400

    # Check if the order is assigned to this courier
    assigned_orders = courier['assigned_orders']
    if {'id': order_id} not in assigned_orders:
        return {
                   'validation_error': f'order with id {order_id} is assigned to another courier'}, 400

    return complete_data, 200
Beispiel #6
0
def _calculate_delivery_time(courier_id: int, complete_time: datetime,
                             couriers_db: Couriers,
                             orders_db: Orders) -> float:
    """
    Return delta between complete_time of order with order_id and complete_time of previous order or assign time
    (if new assigning occurs after the last completed order)
    """
    courier = couriers_db.get_item(courier_id)

    completed_orders_ids = get_ids(courier['completed_orders'])
    assign_time: datetime = courier['assign_time']

    # If there are no completed orders, return delta with assign time
    if len(completed_orders_ids) == 0:
        return (complete_time - assign_time).total_seconds()

    completed_orders = orders_db.get_items_by_ids(completed_orders_ids)
    previous_time = _find_previous_time(assign_time, completed_orders)

    return (complete_time - previous_time).total_seconds()
Beispiel #7
0
def make_app(db: Database) -> Flask:
    """
    Create service 'Slasti'
    """
    app = Flask(__name__)

    couriers_db = Couriers(db['couriers'])
    orders_db = Orders(db['orders'])

    @app.route('/couriers', methods=['POST'])
    def add_couriers() -> Response:
        """
        Get couriers data as request, save it to couriers_db and return response with added ids
        """
        if not request.is_json:
            return validator.bad_header

        couriers_data = request.get_json()
        data, status = validator.validate_couriers(couriers_data)
        if status == 201:
            data = post_couriers(data, couriers_db)
        return Response(json.dumps(data),
                        status,
                        headers={'Content-Type': 'application/json'})

    @app.route('/couriers/<courier_id>', methods=['PATCH'])
    def update_courier(courier_id) -> Response:
        """
        Get new data for courier with courier_id, save it to couriers_db and return response with new data
        """
        if not request.is_json:
            return validator.bad_header

        new_data = request.get_json()
        data, status = validator.validate_update_courier(
            new_data, courier_id, couriers_db)
        if status == 200:
            data = patch_courier(courier_id, new_data, couriers_db, orders_db)
        return Response(json.dumps(data),
                        status,
                        headers={'Content-Type': 'application/json'})

    @app.route('/orders', methods=['POST'])
    def add_orders() -> Response:
        """
        Get orders data as request, save it to orders_db and return response with added ids
        """
        if not request.is_json:
            return validator.bad_header

        orders_data = request.get_json()
        data, status = validator.validate_orders(orders_data)
        if status == 201:
            data = post_orders(data, orders_db)
        return Response(json.dumps(data),
                        status,
                        headers={'Content-Type': 'application/json'})

    @app.route('/orders/assign', methods=['POST'])
    def post_orders_assign() -> Response:
        """
        Get json with courier_id, find optimal orders and assign it to him
        """
        if not request.is_json:
            return validator.bad_header

        courier_id_data = request.get_json()
        data, status = validator.validate_assign_orders(
            courier_id_data, couriers_db)
        if status == 200:
            data = assign_orders(courier_id_data, couriers_db, orders_db)
        return Response(json.dumps(data),
                        status,
                        headers={'Content-Type': 'application/json'})

    @app.route('/orders/complete', methods=['POST'])
    def post_order_complete() -> Response:
        """
        Get json with courier_id, order_id and complete time, and complete this orders
        """
        if not request.is_json:
            return validator.bad_header

        complete_data = request.get_json()
        data, status = validator.validate_complete_orders(
            complete_data, couriers_db, orders_db)
        if status == 200:
            data = complete_order(data, couriers_db, orders_db)
        return Response(json.dumps(data),
                        status,
                        headers={'Content-Type': 'application/json'})

    @app.route('/couriers/<courier_id>', methods=['GET'])
    def get_courier_info(courier_id) -> Response:
        """
        Return info about courier with courier_id
        """
        data, status = validator.validate_courier_id(courier_id, couriers_db)
        if status == 200:
            data = get_courier(courier_id, couriers_db, orders_db)
        return Response(json.dumps(data),
                        status=200,
                        headers={'Content-Type': 'application/json'})

    return app
def post_orders(orders_data: dict, orders_db: Orders):
    """
    Add orders to DB
    """
    posted_ids = orders_db.add_items(orders_data['data'])
    return {'orders': [{'id': i} for i in posted_ids]}