def test_delete_order_endpoint_with_right_permission(self): user_id = BaseTestCase.user_id() meal = MealItemFactory.create() menu = MenuFactory.create() order_data = { 'user_id': user_id, 'date_booked_for': '2018-10-20', 'channel': 'web', 'meal_period': 'lunch', 'menu_id': menu.id, 'meal_items': [meal], 'location_id': 1 } order_repo = OrderRepo() order = order_repo.create_order(**order_data) response = self.client().delete(self.make_url(f'/orders/{order.id}'), headers=self.headers()) response_json = self.decode_from_json_string( response.data.decode('utf-8')) payload = response_json['payload'] self.assert200(response) self.assertEqual(payload['status'], 'success') self.assertEqual(response_json['msg'], 'Order deleted')
def test_already_deleted_order(self): user_id = BaseTestCase.user_id() meal = MealItemFactory.create() menu = MenuFactory.create() order_data = { 'user_id': user_id, 'date_booked_for': '2018-10-20', 'channel': 'web', 'meal_period': 'lunch', 'menu_id': menu.id, 'meal_items': [meal], 'location_id': 1 } order_repo = OrderRepo() order = order_repo.create_order(**order_data) self.client().delete(self.make_url(f'/orders/{order.id}'), headers=self.headers()) response = self.client().delete(self.make_url(f'/orders/{order.id}'), headers=self.headers()) response_json = self.decode_from_json_string( response.data.decode('utf-8')) self.assert400(response) self.assertEqual(response_json['msg'], 'Order has already been deleted')
def __init__(self, request): BaseController.__init__(self, request) self.vendor_rating_repo = VendorRatingRepo() self.vendor_repo = VendorRepo() self.vendor_engagement_repo = VendorEngagementRepo() self.menu_repo = MenuRepo() self.meal_repo = MealItemRepo() self.order_repo = OrderRepo()
def __init__(self, request): BaseController.__init__(self, request) self.rating_repo = VendorRatingRepo() self.order_repo = OrderRepo() self.engagement_repo = VendorEngagementRepo() self.vendor_repo = VendorRepo() self.user_repo = UserRepo() self.session_repo = MealSessionRepo()
def test_delete_order_not_yours(self): location = LocationFactory.create() user_id = BaseTestCase.user_id() meal = MealItemFactory.create() menu = MenuFactory.create() order_data = { 'user_id': '-UTG654RfggtdI', 'date_booked_for': '2018-10-20', 'channel': 'web', 'meal_period': 'lunch', 'menu_id': menu.id, 'meal_items': [meal], 'location_id': location.id } order_repo = OrderRepo() order = order_repo.create_order(**order_data) response = self.client().delete(self.make_url(f'/orders/{order.id}'), headers=self.headers()) response_json = self.decode_from_json_string(response.data.decode('utf-8')) self.assert403(response) self.assertEqual(response_json['msg'], 'You cannot delete an order that is not yours')
def __init__(self, request): BaseController.__init__(self, request) self.order_repo = OrderRepo() self.meal_item_repo = MealItemRepo() self.andela_service = AndelaService() self.rating_repo = VendorRatingRepo()
class OrderController(BaseController): default_meal_item_return_fields = ['name', 'image', 'id', 'meal_type'] def __init__(self, request): BaseController.__init__(self, request) self.order_repo = OrderRepo() self.meal_item_repo = MealItemRepo() self.andela_service = AndelaService() self.rating_repo = VendorRatingRepo() def list_orders(self): """ List all orders in the application: should rarely be should :return: """ location_id = Auth.get_location() yesterday = date.today() - timedelta(days=1) tomorrow = date.today() + timedelta(days=1) orders = self.order_repo.get_range_paginated_options_all( start_date=yesterday, end_date=tomorrow, location_id=location_id) order_list = [] if len(orders.items) > 0: for order in orders.items: meal_items = self.order_repo.get(order.id).meal_item_orders try: user = self.andela_service.get_user_by_email_or_id( order.user_id) except Exception as e: return str(e) order_item = order.serialize() order_item['mealItems'] = [ item.to_dict( only=OrderController.default_meal_item_return_fields) for item in meal_items ] order_item['user'] = '******'.format( user['first_name'], user['last_name']) if user else None rating = self.order_repo.get_rating(user['id'], 'order', order.id) if user else None order_item['user_rating'] = rating order_list.append(order_item) return self.handle_response('OK', payload={ 'orders': order_list, 'meta': self.pagination_meta(orders) }) def list_orders_date(self, start_date): """ List all orders for a particular date :param start_date: :return: """ location_id = Auth.get_location() orders = self.order_repo.get_unpaginated(is_deleted=False, date_booked_for=start_date, location_id=location_id) order_list = [] if len(orders) > 0: for order in orders: meal_items = self.order_repo.get(order.id).meal_item_orders try: user = self.andela_service.get_user_by_email_or_id( order.user_id) except Exception as e: return str(e) order_item = order.serialize() order_item['mealItems'] = [ item.to_dict( only=OrderController.default_meal_item_return_fields) for item in meal_items ] order_item['user'] = '******'.format( user['first_name'], user['last_name']) if user else None rating = self.order_repo.get_rating(user['id'], 'order', order.id) if user else None order_item['user_rating'] = rating order_list.append(order_item) return self.handle_response('OK', payload={'orders': order_list}) def list_orders_date_range(self, start_date, end_date): """ List all orders for a particular date :param start_date: :param end_date: :return: """ location_id = Auth.get_location() orders = self.order_repo.get_range_paginated_options_all( start_date=start_date, end_date=end_date, location_id=location_id) order_list = [] if len(orders.items) > 0: for order in orders.items: meal_items = self.order_repo.get(order.id).meal_item_orders try: user = self.andela_service.get_user_by_email_or_id( order.user_id) except Exception as e: return str(e) order_item = order.serialize() order_item['mealItems'] = [ item.to_dict( only=OrderController.default_meal_item_return_fields) for item in meal_items ] order_item['user'] = '******'.format( user['first_name'], user['last_name']) if user else None rating = self.order_repo.get_rating(user['id'], 'order', order.id) if user else None order_item['user_rating'] = rating order_list.append(order_item) return self.handle_response('OK', payload={'orders': order_list}) def get_order(self, order_id): """ Gets all orders for an order_id :param order_id: :return: """ order = self.order_repo.get(order_id) if order: order_serialized = order.serialize() order_serialized['mealItems'] = [ item.to_dict( only=OrderController.default_meal_item_return_fields) for item in order.meal_item_orders ] try: user = self.andela_service.get_user_by_email_or_id( order.user_id) except Exception as e: return str(e) order_serialized['user'] = '******'.format( user['first_name'], user['last_name']) if user else None rating = self.order_repo.get_rating(user['id'], 'order', order.id) if user else None order_serialized['user_rating'] = rating return self.handle_response('OK', payload={'order': order_serialized}) return self.handle_response('Order not found', status_code=400) def get_order_by_user_id(self, user_id): """ Gets all orders for a user by the user id :param user_id: :return: list of orders in json model """ orders = self.order_repo.filter_by(user_id=user_id, is_deleted=False) orders_list = [] if len(orders.items) > 0: for order in orders.items: meal_items = self.order_repo.get(order.id).meal_item_orders try: user = self.andela_service.get_user_by_email_or_id( order.user_id) except Exception as e: return str(e) order_item = order.serialize() order_item['mealItems'] = [ item.to_dict( only=OrderController.default_meal_item_return_fields) for item in meal_items ] order_item['user'] = '******'.format( user['first_name'], user['last_name']) if user else None rating = self.order_repo.get_rating(user['id'], 'order', order.id) if user else None order_item['user_rating'] = rating orders_list.append(order_item) return self.handle_response('OK', payload={'orders': orders_list}) def get_order_by_user_id_date_range(self, user_id, start_date, end_date): """ :param user_id: :param start_date: :param end_date: :return: """ orders = self.order_repo.get_range_paginated_options( user_id=user_id, start_date=start_date, end_date=end_date) order_list = [] if len(orders.items) > 0: for order in orders.items: meal_items = self.order_repo.get(order.id).meal_item_orders user = self.andela_service.get_user_by_email_or_id( order.user_id) order_item = order.serialize() order_item['mealItems'] = [ item.to_dict( only=OrderController.default_meal_item_return_fields) for item in meal_items ] order_item['user'] = '******'.format( user['first_name'], user['last_name']) if user else None rating = self.order_repo.get_rating(user['id'], 'order', order.id) if user else None order_item['user_rating'] = rating order_list.append(order_item) return self.handle_response('OK', payload={'orders': order_list}) def create_order(self): """ creates an order :return: order object """ user_id = Auth.user('id') location_id = Auth.get_location() date_booked_for, channel, meal_period, meal_items, menu_id = self.request_params( 'dateBookedFor', 'channel', 'mealPeriod', 'mealItems', 'menuId') if self.order_repo.user_has_order(user_id, date_booked_for, meal_period): return self.handle_response( 'You have already booked for this meal period.', status_code=400) location = LocationRepo().get(location_id) current_time = current_time_by_zone(location.zone) if datetime.strptime(date_booked_for, "%Y-%m-%d") < datetime.now(): return self.handle_response( 'You are not allowed to book for a date in the past', status_code=400) if int(current_time_by_zone(location.zone).strftime('%H')) > 15: if check_date_current_vs_date_for( current_time, datetime.strptime(date_booked_for, "%Y-%m-%d")): return self.handle_response( 'It is too late to book a meal for the selected date ', status_code=400) meal_object_items = self.meal_item_repo.get_meal_items_by_ids( meal_items) new_order = self.order_repo.create_order(user_id, date_booked_for, meal_object_items, location_id, menu_id, channel, meal_period).serialize() new_order['mealItems'] = [ item.to_dict(only=OrderController.default_meal_item_return_fields) for item in meal_object_items ] return self.handle_response('OK', payload={'order': new_order}, status_code=201) def update_order(self, order_id): """ updates an order based on the order Id :param order_id: :return: """ date_booked_for, channel, meal_items, menu_id = self.request_params( 'dateBookedFor', 'channel', 'mealItems', 'menuId') meal_object_items = [] for meal_item_id in meal_items: meal_item = self.meal_item_repo.get(meal_item_id) meal_object_items.append(meal_item) order = self.order_repo.get(order_id) if order: if order.is_deleted: return self.handle_response('Order has already been deleted', status_code=400) updates = {} if date_booked_for: order_date_midnight = datetime.strptime( date_booked_for, '%Y-%m-%d').replace(hour=00).replace( minute=00).replace(second=00) current_time = datetime.now() if order_date_midnight - current_time < timedelta( 'hours' == 7): return self.handle_response( 'It is too late to book meal for the selected date ', status_code=400) updates['date_booked_for'] = datetime.strptime( date_booked_for, '%Y-%m-%d') if menu_id: updates['menu_id'] = menu_id if channel: updates['channel'] = channel if meal_items: updates['meal_item_orders'] = meal_object_items updated_order = self.order_repo.update(order, **updates).serialize() updated_order['mealItems'] = [ item.to_dict( only=OrderController.default_meal_item_return_fields) for item in order.meal_item_orders ] return self.handle_response('OK', payload={'order': updated_order}) return self.handle_response('Invalid or incorrect order_id provided', status_code=400) def collect_order(self): """ Collects order and mark as collected for a user Id :param user_id: :param order_type: :param order_date: :return: """ user_id, order_type, order_date = self.request_params( 'userId', 'orderType', 'orderDate') order = self.order_repo.find_first(user_id=user_id, meal_period=order_type, date_booked_for=order_date, is_deleted=False) if not order: return self.handle_response( f'User has no {order_type} order for the date.', status_code=400) if order.order_status == OrderStatus.collected: return self.handle_response('Order already collected', status_code=400) updates = {} updates['order_status'] = OrderStatus.collected self.order_repo.update(order, **updates) return self.handle_response('Order successfully collected', payload={'order': order.serialize()}) def check_order(self): """ Checks if a user has an order for a particular date and period :return: """ user_id, order_type, order_date = self.request_params( 'userId', 'orderType', 'orderDate') # get user_id from another method and reform to db's user id order = self.order_repo.find_first(user_id=user_id, meal_period=order_type, date_booked_for=order_date, is_deleted=False) if not order: return self.handle_response( f'User has no {order_type} order for this date') return self.handle_response('OK', payload={'order': order.serialize()}) def delete_order(self, order_id): order = self.order_repo.get(order_id) if order: if order.is_deleted: return self.handle_response('Order has already been deleted', status_code=400) if Auth.user('id') != order.user_id: return self.handle_response( 'You cannot delete an order that is not yours', status_code=403) updates = {} updates['is_deleted'] = True self.order_repo.update(order, **updates) return self.handle_response('Order deleted', payload={"status": "success"}) return self.handle_response('Invalid or incorrect order_id provided', status_code=400)
class VendorRatingController(BaseController): def __init__(self, request): BaseController.__init__(self, request) self.vendor_rating_repo = VendorRatingRepo() self.vendor_repo = VendorRepo() self.vendor_engagement_repo = VendorEngagementRepo() self.menu_repo = MenuRepo() self.meal_repo = MealItemRepo() self.order_repo = OrderRepo() def list_ratings(self, date): """retrieves a list of all ratings""" query_kwargs = self.get_params_dict() date = datetime.strptime(date, '%Y-%m-%d') ratings = self.vendor_rating_repo.filter_by(service_date=date, **query_kwargs) if ratings.items: result = [] vendor_name = self.vendor_repo.get(ratings.items[0].vendor_id).name for rating in ratings.items: if not rating.main_meal_id: continue meal_name = self.meal_repo.get(rating.main_meal_id).name if meal_name not in [item.get('mainMeal') for item in result]: meal_rating = { 'mainMeal': meal_name, 'overallRating': self.vendor_rating_repo.meal_average( rating.main_meal_id, date), 'items': [ rtng.serialize() for rtng in ratings.items if rtng.main_meal_id == rating.main_meal_id ] } result.append(meal_rating) return self.handle_response('OK', payload={ 'date': date, 'vendor': vendor_name, 'result': result }) return self.handle_response('No ratings for this date', status_code=404) def get_vendor_rating(self, rating_id): """retrieves the details of a specific rating, giving the rating id""" rating = self.vendor_rating_repo.get(rating_id) if rating: rtng = rating.serialize() return self.handle_response('OK', payload={'rating': rtng}) else: return self.handle_response('Bad Request', status_code=400) def create_vendor_rating(self): '''Adds a vendor rating during a specific engagement''' (comment, rating, service_date, channel, engagement_id) = self.request_params('comment', 'rating', 'serviceDate', 'channel', 'engagementId') main_meal_id = None user_id = Auth.user('id') vendor_id = self.vendor_engagement_repo.get(engagement_id).vendor_id if self.vendor_repo.get(vendor_id): service_date = datetime.strptime(service_date, '%Y-%m-%d') rating = self.vendor_rating_repo.new_rating( vendor_id, user_id, rating, service_date, RatingType.engagement, engagement_id, engagement_id, main_meal_id, channel, comment) self.vendor_repo.update_vendor_average_rating(vendor_id) rtng = rating.serialize() return self.handle_response('Rating created', payload={'rating': rtng}, status_code=201) return self.handle_response('Invalid vendor_id provided', status_code=400) def create_order_rating(self): """Adds a order or meal rating during a specific engagement """ (order_id, main_meal_id, engagement_id, comment, rating, service_date, channel) = self.request_params('orderId', 'mainMealId', 'engagementId', 'comment', 'rating', 'serviceDate', 'channel') if not (1 <= rating <= 5): return self.handle_response( 'Rating must be between 1 and 5, inclusive.', status_code=400) user_id = Auth.user('id') if not self.meal_repo.get(main_meal_id): return self.handle_response('Meal item with this id not found', status_code=400) engagement = self.vendor_engagement_repo.get(engagement_id) if not engagement: return self.handle_response('Engagement with this id is not found', status_code=400) vendor_id = engagement.vendor_id if order_id: rating_type = RatingType.order type_id = order_id order = self.order_repo.get(order_id) if not order: return self.handle_response('Order with this id is not found', status_code=400) if order.has_rated: return self.handle_response('This order has been rated', status_code=400) else: if (datetime.now() - datetime.strptime(service_date, '%Y-%m-%d')).days < 1: return self.handle_response( 'You can only rate meals of past days.', status_code=400) rating_type = RatingType.meal type_id = main_meal_id user_meal_rating = self.vendor_rating_repo.get_unpaginated( user_id=user_id, type_id=type_id, rating_type='meal') if user_meal_rating: return self.handle_response('You have already rated this meal', status_code=400) rating = self.vendor_rating_repo.new_rating( vendor_id, user_id, rating, datetime.strptime(service_date, '%Y-%m-%d'), rating_type, type_id, engagement_id, main_meal_id, channel, comment) self.vendor_repo.update_vendor_average_rating(vendor_id) if rating.id and rating_type == RatingType.order: updates = {'has_rated': True} self.order_repo.update(order, **updates) rating_obj = rating.serialize() return self.handle_response('Rating successful', payload={'rating': rating_obj}, status_code=201) def update_vendor_rating(self, rating_id): """edits an existing rating""" rtng = self.vendor_rating_repo.get(rating_id) comment = self.get_json()['comment'] if rtng: if Auth.user( 'id' ) == rtng.user_id: #You cannot update someone else's rating updates = {} if comment: updates['comment'] = comment self.vendor_rating_repo.update(rtng, **updates) return self.handle_response( 'OK', payload={'rating': rtng.serialize()}) return self.handle_response( 'You are not allowed to update a rating that is not yours', status_code=403) return self.handle_response('Invalid or incorrect rating_id provided', status_code=404)
def interactions(self): request_payload, trigger_id = self.post_params('payload', 'trigger_id') payload = json.loads(request_payload) webhook_url = payload["response_url"] slack_id = payload['user']['id'] if payload['type'] == 'dialog_submission': slack_user_info = self.slackhelper.user_info(slack_id) slack_user_email = slack_user_info['user']['profile']['email'] if payload['callback_id'] == 'final_selection': state = payload['state'].split('_') menu_id = state[0] meal_period = state[1] date_booked_for = state[2] location_id = state[4] submitted_values = payload['submission'] meal_items = [int(v) for k, v in submitted_values.items()] meal_items.append(MenuRepo().get(menu_id).main_meal_id) meal_items = [ meal for meal in MealItemRepo().get_meal_items_by_ids( meal_items) ] channel = 'slack' # Retrieve User Object user = self.andela_service.get_user_by_email_or_id( slack_user_email) user_id = user['id'] order = OrderRepo().create_order( user_id=user_id, date_booked_for=date_booked_for, meal_items=meal_items, location_id=location_id, menu_id=menu_id, channel=channel, meal_period=meal_period) if order: slack_data = {'text': 'Booking Confirmed!'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) else: slack_data = {'text': 'Booking Failed. Please Retry'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) if payload['callback_id'] == 'submit_rating': state = payload['state'].split('_') menu_id = state[0] menu = self.menu_repo.get(menu_id) service_date = menu.date rating_type = RatingType.meal type_id = menu.main_meal_id engagement_id = menu.vendor_engagement_id vendor_id = self.engagement_repo.get(engagement_id).vendor_id rating_value = payload['submission']['rating value'] channel = 'slack' comment = payload['submission']['comment'] # Retrieve User Object user = self.andela_service.get_user_by_email_or_id( slack_user_email) user_id = user['id'] rating = self.vendor_rating_repo.new_rating( vendor_id, user_id, rating_value, service_date, rating_type, type_id, engagement_id, channel, comment, type_id) if rating: slack_data = {'text': 'Rating Successful!'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) else: slack_data = {'text': 'Rating Failed. Please Retry'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) return make_response('', 200) if payload['type'] == 'interactive_message' and payload[ 'callback_id'] == 'center_selector': location_id = payload['actions'][0]['value'] location = LocationRepo().get(location_id) menu_start_end_on = BotController.get_menu_start_end_on(location) start_on = menu_start_end_on[0] end_on = menu_start_end_on[1] date_buttons = [{ 'name': 'selected_date', 'type': 'button', 'text': '{}, {}'.format(day.strftime('%a'), day.strftime('%b %-d')), 'value': '{}_{}'.format(day.strftime('%Y-%m-%d'), location.id) } for day in daterange(start_on, end_on)] request_buttons = [{ "text": "", "callback_id": "day_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": date_buttons }] return self.handle_response(slack_response={ 'text': f'Select Date', 'attachments': request_buttons }) if payload['type'] == 'interactive_message' and payload[ 'callback_id'] == 'day_selector': payload_action_value = payload['actions'][0]['value'] selected_date = payload_action_value.split('_')[0] location_id = payload_action_value.split('_')[1] period_buttons = [ # {'name': 'meal_period', 'type': 'button', 'text': 'Breakfast', # 'value': 'breakfast_{}'.format(payload_action_value)}, { 'name': 'meal_period', 'type': 'button', 'text': 'Lunch', 'value': 'lunch_{}'.format(payload_action_value) } ] request_buttons = [{ "text": "", "callback_id": "period_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": period_buttons }] return self.handle_response(slack_response={ 'text': f'Select Meal Period', 'attachments': request_buttons }) if payload['type'] == 'interactive_message' and payload[ 'callback_id'] == 'period_selector': period = payload['actions'][0]['value'].split('_')[0] date = payload['actions'][0]['value'].split('_')[1] location_id = payload['actions'][0]['value'].split('_')[2] actions = { "attachments": [{ "text": 'What do you want to do?', "callback_id": "action_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": [{ "name": "main meal", "text": "View Menu List", "type": "button", "value": f'{period}_{date}_menu_{location_id}' }, { "name": "main meal", "text": "Place order", "type": "button", "value": f'{period}_{date}_order_{location_id}' }] }] } return self.handle_response(slack_response=actions) if payload['type'] == 'interactive_message' and payload[ 'callback_id'] == 'action_selector': payload_action_value = payload['actions'][0]['value'] if payload_action_value.split('_')[2] == 'menu': date = payload_action_value.split('_')[1] period = payload_action_value.split('_')[0] location_id = payload_action_value.split('_')[3] menus = self.menu_repo.get_unpaginated(date=date, meal_period=period, is_deleted=False) if not menus: # No Menu for provided date back_buttons = [{ 'name': 'back', 'text': 'Back', 'type': "button", 'value': location_id }] request_buttons = [{ "text": "", "callback_id": "center_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": back_buttons }] return self.handle_response( slack_response={ 'text': f'Sorry No Menu found for Date: {date}, Meal Period: {period}', 'attachments': request_buttons }) text = '' for menu in menus: side_items_list = menu.side_items.split(',') protein_items_list = menu.protein_items.split(',') main = self.meal_repo.get(menu.main_meal_id).name sides = [ side.name for side in self.meal_repo.get_meal_items_by_ids(side_items_list) ] proteins = [ protein.name for protein in self.meal_repo.get_meal_items_by_ids( protein_items_list) ] menu_info = f'Main meal: *{main}*\n Side items: {", ".join(sides)}\nProtein items: {", ".join(proteins)}\n\n\n' text += menu_info meals = { "text": f'{period.upper()}', "attachments": [{ "text": text, "callback_id": "after_menu_list", "color": "#3AA3E3", "attachment_type": "default", "actions": [{ "name": "main meal", "text": "Rate meal", "type": "button", "value": f'{period}_{date}_rate_{location_id}_{location_id}' }, { "name": "main meal", "text": "Place an order", "type": "button", "value": f'{period}_{date}_order_{location_id}_{location_id}' }] }] } return self.handle_response(slack_response=meals) if (payload['type'] == 'interactive_message' and payload['callback_id'] == 'action_selector' and payload['actions'][0]['value'].split('_')[2] == 'order' ) or (payload['callback_id'] == 'after_menu_list' and payload['actions'][0]['value'].split('_')[2] == 'order'): payload_action_value = payload['actions'][0]['value'] meal_period = payload_action_value.split('_')[0] selected_date = payload_action_value.split('_')[1] location_id = payload_action_value.split('_')[3] menus = self.menu_repo.get_unpaginated(date=selected_date, meal_period=meal_period, is_deleted=False) if not menus: # No Menu for provided date back_buttons = [{ 'name': 'back', 'text': 'Back', 'type': "button", 'value': location_id }] request_buttons = [{ "text": "", "callback_id": "center_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": back_buttons }] return self.handle_response( slack_response={ 'text': f'Sorry No Menu found for Date: {selected_date}, Meal Period: {meal_period}', 'attachments': request_buttons }) meal_buttons = [{ 'name': 'main_meal', 'type': 'button', 'text': f'{menu.main_meal.name}', 'value': f'{menu.id}_{payload_action_value}' } for menu in menus] request_buttons = [{ "text": "", "callback_id": "meal_action_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": meal_buttons }] return self.handle_response(slack_response={ 'text': 'Select Main Meal', 'attachments': request_buttons }) if payload['type'] == 'interactive_message' and payload[ 'callback_id'] == 'meal_action_selector': payload_action_value = payload['actions'][0]['value'] if payload_action_value.find('order') > -1: menu_id = payload_action_value.split('_')[0] menu = self.menu_repo.get(menu_id) slack_id = payload['user']['id'] slack_user_info = self.slackhelper.user_info(slack_id) slack_user_email = slack_user_info['user']['profile']['email'] user = self.andela_service.get_user_by_email_or_id( slack_user_email) # check if user already has an order if OrderRepo().user_has_order(user['id'], menu.date.strftime('%Y-%m-%d'), menu.meal_period): slack_data = { 'text': 'You already have an order for this meal period.' } requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) return self.handle_response(status_code=400) trigger_id = payload['trigger_id'] side_items_list = menu.side_items.split(',') protein_items_list = menu.protein_items.split(',') side_items = self.meal_repo.get_meal_items_by_ids( side_items_list) protein_items = self.meal_repo.get_meal_items_by_ids( protein_items_list) request_dialog_element = [] for i in range(1, menu.allowed_side + 1): request_dialog_element.append({ 'label': f'Select Side {i}', 'type': 'select', 'name': f'side_{i}', 'options': [{ 'label': f'{side.name}', 'value': f'{side.id}' } for side in side_items] }) for i in range(1, menu.allowed_protein + 1): request_dialog_element.append({ 'label': f'Select Protein {i}', 'type': 'select', 'name': f'protein_{i}', 'options': [{ 'label': f'{protein.name}', 'value': f'{protein.id}' } for protein in protein_items] }) state = f'{payload_action_value}' self.create_dialog(dialog_elem=request_dialog_element, trigger_id=trigger_id, title='Select Protein & Sides', callback_id='final_selection', state=state) return self.handle_response( slack_response={'text': 'Select Meal Protein and Sides'}) if payload['callback_id'] == 'after_menu_list' and payload['actions'][ 0]['value'].split('_')[2] == 'rate': payload_action_value = payload['actions'][0]['value'] meal_period = payload_action_value.split('_')[0] selected_date = payload_action_value.split('_')[1] location_id = payload_action_value.split('_')[2] menus = self.menu_repo.get_unpaginated(date=selected_date, meal_period=meal_period, is_deleted=False) if not menus: # No Menu for provided date back_buttons = [{ 'name': 'back', 'text': 'Back', 'type': "button", 'value': location_id }] request_buttons = [{ "text": "", "callback_id": "center_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": back_buttons }] return self.handle_response( slack_response={ 'text': f'Sorry No Menu found forr Date: {selected_date}, Meal Period: {meal_period}', 'attachments': request_buttons }) meal_buttons = [{ 'name': 'main_meal', 'type': 'button', 'text': f'{menu.main_meal.name}', 'value': f'{menu.id}_{payload_action_value}' } for menu in menus] request_buttons = [{ "text": "", "callback_id": "rating_selector", "color": "#3AA3E3", "attachment_type": "default", "actions": meal_buttons }] return self.handle_response(slack_response={ 'text': 'Select Main Meal', 'attachments': request_buttons }) if payload['callback_id'] == 'rating_selector': menu_id = payload['actions'][0]['value'].split('_')[0] menu = self.menu_repo.get(menu_id) trigger_id = payload['trigger_id'] main_meal = menu.main_meal_id request_dialog_element = [{ 'label': f'Rate meal: {self.meal_repo.get(main_meal).name}', 'type': 'select', 'name': 'rating value', 'options': [{ 'label': f'{value}', 'value': f'{value}' } for value in range(1, 6)] }, { 'label': 'Add a short comment', 'type': 'text', 'name': 'comment' }] state = f'{payload["actions"][0]["value"]}' self.create_dialog(dialog_elem=request_dialog_element, trigger_id=trigger_id, title='Rate a meal', callback_id='submit_rating', state=state) return self.handle_response(slack_response={'text': 'Meal rating'})
class VendorRatingController(BaseController): def __init__(self, request): BaseController.__init__(self, request) self.vendor_rating_repo = VendorRatingRepo() self.vendor_repo = VendorRepo() self.menu_repo = MenuRepo() self.order_repo = OrderRepo() def list_ratings(self, vendor_id): '''retrieves a list of ratings for a specific vendor''' ratings = self.vendor_rating_repo.get_unpaginated(vendor_id=vendor_id) if ratings: ratings_list = [rating.serialize() for rating in ratings] return self.handle_response('OK', payload={'ratings': ratings_list}) return self.handle_response('Expected vendor in request') def get_vendor_rating(self, rating_id): '''retrieves the details of a specific rating, giving the rating id''' rating = self.vendor_rating_repo.get(rating_id) if rating: rtng = rating.serialize() return self.handle_response('OK', payload={'rating': rtng}) else: return self.handle_response('Bad Request', status_code=400) def create_vendor_rating(self): '''Adds a vendor rating during a specific engagement''' (vendor_id, comment, rating, channel, engagement_id) = self.request_params('vendorId', 'comment', 'rating', 'channel', 'engagementId') user_id = Auth.user('id') if self.vendor_repo.get(vendor_id): rating = self.vendor_rating_repo.new_rating( vendor_id, user_id, rating, RatingType.engagement, 0, engagement_id, channel, comment) rtng = rating.serialize() return self.handle_response('Rating created', payload={'rating': rtng}, status_code=201) return self.handle_response('Invalid vendor_id provided', status_code=400) def create_order_rating(self): '''Adds a order rating during a specific engagement ''' (order_id, comment, rating, channel) = self.request_params('orderId', 'comment', 'rating', 'channel') user_id = Auth.user('id') order = self.order_repo.get(order_id) if order: menu = self.menu_repo.get(order.menu_id) if menu: vendor_id = menu.vendor_engagement.vendor_id engagement_id = menu.vendor_engagement.id rating = self.vendor_rating_repo.new_rating( vendor_id, user_id, rating, RatingType.order, order_id, engagement_id, channel, comment) rating_obj = rating.serialize() if rating: updates = {} updates['has_rated'] = True updated_order = self.order_repo.update( order, **updates).serialize() return self.handle_response('Rating created', payload={'rating': rating_obj}, status_code=201) return self.handle_response('Invalid vendor_id provided', status_code=400) def update_vendor_rating(self, rating_id): '''edits an existing rating''' rtng = self.vendor_rating_repo.get(rating_id) comment = self.get_json()['comment'] if Auth.user( 'id' ) == rtng.user_id: #You cannot update someone else's rating if rtng: updates = {} if comment: updates['comment'] = comment self.vendor_rating_repo.update(rtng, **updates) return self.handle_response( 'OK', payload={'rating': rtng.serialize()}) return self.handle_response( 'Invalid or incorrect rating_id provided', status_code=400) return self.handle_response( 'You are not allowed to update a rating that is not yours', status_code=403)
def __init__(self, request): BaseController.__init__(self, request) self.vendor_rating_repo = VendorRatingRepo() self.vendor_repo = VendorRepo() self.menu_repo = MenuRepo() self.order_repo = OrderRepo()
def handle_dialog_submission(self, payload, slack_id, webhook_url): slack_user_info = self.slackhelper.user_info(slack_id) slack_user_email = slack_user_info['user']['profile']['email'] if payload['callback_id'] == 'final_selection': state = payload['state'].split('_') menu_id = state[0] meal_period = state[1] date_booked_for = state[2] location_id = state[4] submitted_values = payload['submission'] meal_items = [int(v) for k, v in submitted_values.items()] meal_items.append(MenuRepo().get(menu_id).main_meal_id) meal_items = [ meal for meal in MealItemRepo().get_meal_items_by_ids(meal_items) ] channel = 'slack' # Retrieve User Object user = self.andela_service.get_user_by_email_or_id( slack_user_email) user_id = user['id'] order = OrderRepo().create_order(user_id=user_id, date_booked_for=date_booked_for, meal_items=meal_items, location_id=location_id, menu_id=menu_id, channel=channel, meal_period=meal_period) if order: slack_data = {'text': 'Booking Confirmed!'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) else: slack_data = {'text': 'Booking Failed. Please Retry'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) if payload['callback_id'] == 'submit_rating': state = payload['state'].split('_') menu_id = state[0] menu = self.menu_repo.get(menu_id) service_date = menu.date rating_type = RatingType.meal type_id = menu.main_meal_id engagement_id = menu.vendor_engagement_id vendor_id = self.engagement_repo.get(engagement_id).vendor_id rating_value = payload['submission']['rating value'] channel = 'slack' comment = payload['submission']['comment'] # Retrieve User Object user = self.andela_service.get_user_by_email_or_id( slack_user_email) user_id = user['id'] rating = self.vendor_rating_repo.new_rating( vendor_id, user_id, rating_value, service_date, rating_type, type_id, engagement_id, menu_id, channel, comment) if rating: slack_data = {'text': 'Rating Successful!'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) else: slack_data = {'text': 'Rating Failed. Please Retry'} requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) return make_response('', 200)
def handle_meal_action_selection(self, payload, webhook_url): payload_action_value = payload['actions'][0]['value'] if payload_action_value.find('order') > -1: menu_id = payload_action_value.split('_')[0] menu = self.menu_repo.get(menu_id) slack_id = payload['user']['id'] slack_user_info = self.slackhelper.user_info(slack_id) slack_user_email = slack_user_info['user']['profile']['email'] user = self.andela_service.get_user_by_email_or_id( slack_user_email) # check if user already has an order if OrderRepo().user_has_order(user['id'], menu.date.strftime('%Y-%m-%d'), menu.meal_period): slack_data = { 'text': 'You already have an order for this meal period.' } requests.post(webhook_url, data=json.dumps(slack_data), headers={'Content-Type': 'application/json'}) return self.handle_response(status_code=400) trigger_id = payload['trigger_id'] side_items_list = menu.side_items.split(',') protein_items_list = menu.protein_items.split(',') side_items = self.meal_repo.get_meal_items_by_ids(side_items_list) protein_items = self.meal_repo.get_meal_items_by_ids( protein_items_list) request_dialog_element = [] for i in range(1, menu.allowed_side + 1): request_dialog_element.append({ 'label': f'Select Side {i}', 'type': 'select', 'name': f'side_{i}', 'options': [{ 'label': f'{side.name}', 'value': f'{side.id}' } for side in side_items] }) for i in range(1, menu.allowed_protein + 1): request_dialog_element.append({ 'label': f'Select Protein {i}', 'type': 'select', 'name': f'protein_{i}', 'options': [{ 'label': f'{protein.name}', 'value': f'{protein.id}' } for protein in protein_items] }) state = f'{payload_action_value}' self.create_dialog(dialog_elem=request_dialog_element, trigger_id=trigger_id, title='Select Protein & Sides', callback_id='final_selection', state=state) return self.handle_response( slack_response={'text': 'Select Meal Protein and Sides'})
class ReportsController(BaseController): def __init__(self, request): BaseController.__init__(self, request) self.rating_repo = VendorRatingRepo() self.order_repo = OrderRepo() self.engagement_repo = VendorEngagementRepo() self.vendor_repo = VendorRepo() self.user_repo = UserRepo() self.session_repo = MealSessionRepo() def dashboard_summary(self): params = self.get_params_dict() location = Auth.get_location() vendors = self.vendor_repo.get_unpaginated(is_deleted=False) engagements = self.engagement_repo.get_unpaginated( is_deleted=False, location_id=location) if 'all_vendor_comparison' in params: orders = self.order_repo.fetch_all() vendor_orders = [] for vendor in vendors: vendor_info = {} vendor_info['id'] = vendor.id vendor_info['name'] = vendor.name vendor_info['collectedOrders'] = len([ order for order in orders.items if order.order_status == 'collected' and Menu.query.get( order.menu_id).vendor_engagement.vendor_id == vendor.id ]) vendor_info['uncollectedOrders'] = len([ order for order in orders.items if order.order_status == 'booked' and Menu.query.get( order.menu_id).vendor_engagement.vendor_id == vendor.id ]) vendor_info['cancelledOrders'] = len([ order for order in orders.items if order.order_status == 'cancelled' and Menu.query.get( order.menu_id).vendor_engagement.vendor_id == vendor.id ]) vendor_orders.append(vendor_info) return self.handle_response('ok', payload=vendor_orders) str_start_date = params.get('start_date', None) start_date = datetime.now().date( ) if str_start_date is None else datetime.strptime( str_start_date, '%Y-%m-%d').date() str_end_date = params.get('end_date', None) end_date = ( datetime.now().date() - timedelta(14)) if str_end_date is None else datetime.strptime( str_end_date, '%Y-%m-%d').date() if start_date < end_date: return self.handle_response( 'Start date must not be less than end date', status_code=400) result = [] orders = Order.query.filter( and_(Order.date_booked_for >= end_date, Order.date_booked_for <= start_date)) if orders and vendors and engagements: orders_collected = [ order for order in orders if order.order_status == 'collected' ] orders_cancelled = [ order for order in orders if order.order_status == 'cancelled' ] orders_uncollected = [ order for order in orders if order.order_status == 'booked' ] dates = [ date.date() for date in pd.bdate_range(end_date, start_date) ] for date in dates: date_info = { 'date': date, 'collectedOrders': len([ order for order in orders_collected if order.date_booked_for == date ]), 'uncollectedOrders': len([ order for order in orders_uncollected if order.date_booked_for == date ]), 'cancelledOrders': len([ order for order in orders_cancelled if order.date_booked_for == date ]), 'averageRating': self.rating_repo.daily_average_rating(date), 'vendor': self.engagement_repo.vendor_of_the_day(date) } result.append(date_info) return self.handle_response('OK', payload=result) def daily_taps(self): date_range = self.get_params_dict().get('date_range') if date_range is not None: date_range_list = date_range.split(':') start_date = datetime.strptime(date_range_list[0], '%Y-%m-%d') end_date = datetime.strptime(date_range_list[1], '%Y-%m-%d') if start_date < end_date: return self.handle_response( 'Start date must not be less than end date', status_code=400) else: start_date = datetime.today().date() end_date = start_date - timedelta(days=7) services = MealService.query.filter( and_(MealService.date >= end_date, MealService.date <= start_date)) result = [] dates = [date.date() for date in pd.bdate_range(end_date, start_date)] for date in dates: srv_list = [] current_services = [ service for service in services if service.date.date() == date ] for service in current_services: user = self.user_repo.get(service.user_id) session = self.session_repo.get(service.session_id).name service_user = { 'id': user.id, 'name': f'{user.first_name} {user.last_name}', 'userId': user.user_id, 'slackId': user.slack_id, 'session': session } srv_list.append(service_user) date_info = {'date': str(date), 'count': len(srv_list)} result.append(date_info) return self.handle_response('OK', payload=result)