Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jackyvo/958a6c807208b30d5d2dbc21257a3354 to your computer and use it in GitHub Desktop.
Save jackyvo/958a6c807208b30d5d2dbc21257a3354 to your computer and use it in GitHub Desktop.
Rspec + Ruby code
class MealPreparationTimeEstimateService
def initialize(meal=nil, provider=nil)
@meal = meal
@provider = provider || meal.try(:provider)
end
# Prepare Time: Returns: Time (minutes)
def preparation_time(cache=true)
if cache
time_from_cache || time_for_meal
else
time_for_meal
end
end
def preparation_time_for_orders(orders, provider=nil)
times = orders.map { |order| order_preparation_time(order, provider) }
times = filter_error_preparation_times(times)
times = filter_by_standard_deviations_away(times, 2)
return nil if times.empty?
( (times.sum || 0) / times.size ).ceil
end
def preparation_time_of_provider
time_for_provider
end
private
def filter_error_preparation_times(array)
array.compact.select { |v| v < 50 } # more than 50, high error probab.
end
def filter_by_standard_deviations_away(array, deviations_count)
return array unless array.present?
std_dev = array.standard_deviation
mode = array.mode
lo_bound = mode - std_dev * deviations_count
hi_bound = mode + std_dev * deviations_count
array.select { |v| v > lo_bound && v < hi_bound }
end
def order_preparation_time(order, provider)
if provider
being_prepared = order.actions_logs.being_prepared_by_provider.
where(user: provider.user).first
prepared = order.actions_logs.prepared_by_provider.
where(user: provider.user).first
else
being_prepared = order.actions_logs.being_prepared_by_provider.first
prepared = order.actions_logs.prepared_by_provider.first
end
if being_prepared && prepared
(prepared.created_at - being_prepared.created_at) / 1.minute
else
nil
end
end
def orders_for_meal
orders = Order.joins(:items).where('order_items.meal_id = ?', @meal.id)
orders.select { |order| order.items.size == 1 }
end
def orders_for_provider
@orders = Order.joins(:items).
where('order_items.meal_id in (?)', @provider.meal_ids)
end
def time_for_meal
time = preparation_time_for_orders(orders_for_meal)
return time if time.present? && !time.zero?
time_for_provider
end
def time_for_provider
time = MealPreparationCache.find_by(cache_type: 'provider', cache_id: @provider.id).try(:value)
logger = Logger.new("#{Rails.root}/log/meal_cache.log")
logger.debug("Missed Cache Hit - Provider: #{@provider.id}") unless time.present?
return time if time.present? && !time.zero?
time_for_all
end
def time_for_all
time = MealPreparationCache.find_by(cache_type: 'all').try(:value)
logger = Logger.new("#{Rails.root}/log/meal_cache.log")
logger.debug("Missed Cache Hit - All") unless time.present?
time
end
def time_from_cache
time = MealPreparationCache.find_by(cache_type: 'meal', cache_id: @meal.id).try(:value)
logger = Logger.new("#{Rails.root}/log/meal_cache.log")
logger.debug("Missed Cache Hit - Meal: #{@meal.id}") unless time.present?
time
end
end
class OrderBroadcaster
def initialize(order)
@order = order
end
def broadcast_state
ActionCable.server.broadcast "order_#{@order.id}",
total_status: @order.decorate.total_status
ActionCable.server.broadcast "orders",
state: @order.decorate.state,
selector: ".order-#{@order.id}"
@order.providers.each do |provider|
ActionCable.server.broadcast "order_#{@order.id}",
provider_color: provider.decorate.status_color_for(@order),
provider_id: provider.id,
provider_status: provider.decorate.last_action_for(@order)
end
end
def broadcast_driver_info
template = ApplicationController.new.render_to_string(
partial: 'customers/orders/driver',
locals: { driver: @order.driver, order: @order }
)
ActionCable.server.broadcast("order_#{@order.id}", driver: template)
if @order.driver.present?
location = @order.driver.driver_locations.last rescue nil
if location.present?
broadcast_driver_location(location.latitude, location.longitude, @order.driver_id)
end
end
end
def broadcast_driver_location(latitude, longitude, driver_id)
if @order
ActionCable.server.broadcast(
"order_#{@order.id}", driver_location: [latitude, longitude]
)
end
ActionCable.server.broadcast("orders",
driver_id: driver_id,
driver_location: [latitude, longitude],
online: Driver.active.map(&:id)
)
driver = Driver.find(driver_id)
order = driver.current_order
driver_template = driver.decorate.location_with_template(order)
ActionCable.server.broadcast(
'orders', updated: true, driver: driver_template
)
end
def broadcast_item_info(provider, status)
ActionCable.server.broadcast("order_#{@order.id}",
provider_status: status, provider_id: provider.id,
provider_time: provider.decorate.last_time_status(@order, status.to_sym))
ActionCable.server.broadcast("order_#{@order.id}",
provider_color: provider.decorate.status_color_for(@order),
provider_id: provider.id)
end
def broadcast_reject_info(provider, cause)
ActionCable.server.broadcast(
"order_#{@order.id}", rejected: true,
rejected_all: @order.all_providers_rejected?,
provider_name: provider.name,
meals: @order.decorate.meals_from_provider(provider),
cause: cause.cause
)
end
def broadcast_close_alert
code = @order.delivery_handshake_code
msg = "El Conductor está cerca. El código para recoger es #{code}"
ActionCable.server.broadcast("order_#{@order.id}", close_to: true, msg: msg)
end
def broadcast_accept_all
ActionCable.server.broadcast("order_#{@order.id}", accepted_all: true)
end
def broadcast_admin_cancelled
ActionCable.server.broadcast("order_#{@order.id}", admin_cancelled: true)
remove_order
end
def broadcast_payment_unavailable
ActionCable.server.broadcast("order_#{@order.id}", payment_unavailable: true)
end
def broadcast_system_rejected_for_lack_of_approval
ActionCable.server.broadcast("order_#{@order.id}", system_rejected_for_lack_of_approval: true)
remove_order
end
def broadcast_completed_state
ActionCable.server.broadcast("order_#{@order.id}", completed: true, order_id: @order.id)
remove_order
end
def broadcast_created_state
driver_template = @order.driver ? @order.driver.decorate.location_with_template(@order) : ''
ActionCable.server.broadcast('orders', created: true,
customer: @order.customer.decorate.location_with_template(@order),
driver: driver_template,
providers: @order.providers.map {|provider|
provider.decorate.location_with_template(@order)
})
end
private
def remove_order
order_info = {
completed: true,
order_id: @order.id,
customer: {
id: "#{@order.customer.id}-#{@order.try(:id)}", show: false
},
providers: @order.providers.map { |provider|
{ id: provider.id, show: provider.decorate.active? }
}
}
if @order.is_a?(DeliveryOrder)
order_info[:driver] = {
id: @order.driver_id, show: @order.driver.try(:decorate).try(:active?)
}
end
ActionCable.server.broadcast('orders', order_info)
end
end
require 'rails_helper'
require 'unicode_utils/upcase'
describe 'Meal Detail', type: :feature, js: true do
let(:customer) { create(:customer) }
let(:cart) { create(:shopping_cart) }
before(:each) do
provider = create(:provider,
latitude: 9.935245, longitude: -84.108547, provider_type: 0)
create(:timetable, wday: Time.current.wday,
open_hour: 6.minutes.ago.strftime('%H:%M'),
close_hour: 6.minutes.from_now.strftime('%H:%M'),
provider: provider
)
@meal = create(:meal, provider: provider)
@user = create(:user, profile: customer)
customer.update shopping_cart: cart
address = create(:coordinates_address, latitude: 9.935245, longitude: -84.108547)
customer.delivery_addresses << address
create_cookie(:default_location_id_v1, address.id)
login_as @user
visit customers.meal_path(@meal)
end
it 'shows the order type selection' do
expect(page).to have_selector('#order-types')
end
it 'shows shopping cart' do
expect(page).to have_content('Mi Cuenta')
end
it 'shows add to cart buttons' do
expect(page).to have_content('Agregar')
expect(page).to have_content('Especificar')
end
it 'add to shopping cart' do
find('.add-btn', match: :first).click
expect(page).to have_content("Total #{@meal.decorate.base_price}")
end
it 'go to beverage page' do
click_on 'Continuar'
expect(current_path).to eq(customers.beverage_meals_path)
end
end
require 'rails_helper'
RSpec.feature 'Orders', type: :feature do
before :each do
@user = create(:customer_user)
@customer = @user.profile
login_as @user
Paperclip::Attachment.any_instance.stub(:save).and_return(true)
end
describe 'get order info' do
context 'without owner role' do
it 'redirects to home page' do
order = create(:delivery_order)
visit customers.order_path(order)
expect(current_path).to eq(root_path)
end
end
context 'with owner role' do
it 'shows order info' do
order = create(:delivery_order, customer: @customer)
visit customers.order_path(order)
expect(current_path).to eq(customers.order_path(order))
expect(page).to have_content(@customer.full_name)
end
end
end
xscenario 'the customer can reuse an order to fill the shopping cart', js: true do
item_1 = create(:meal, name: "arroz" )
item_2 = create(:meal, name: "huevos" )
cart = ShoppingCart.create
cart.add(item_1, 1000, 2)
cart.add(item_2, 3000, 3)
Customers::OrderBuilder.new(@user, cart).create()
visit customers.orders_path
click_on "Volver a Ordenar"
expect(page).to have_selector("input[value='2']")
expect(page).to have_selector("input[value='3']")
within('#edit_shopping_cart_item_3') do
fill_in 'shopping_cart_item_quantity', with: 4
click_on 'Cambiar Cantidad'
end
sleep(3)
expect(ShoppingCartItem.find(3).reload.quantity).to eq(4)
end
describe 'Current Order', js: true do
context 'with no active order' do
xit 'shows no items text' do
visit customers.current_orders_path
expect(page).to have_content('No tiene órdenes abiertas por el momento')
end
end
context 'with active order' do
before(:each){
@order = create(:delivery_order, customer: @customer, distance_covered: 3.2)
@provider = create(:provider, name: "The Restaurant")
meal = create(:meal, provider: @provider)
@order.items.create(meal_id: meal.id)
Customers::InvoiceBuilder.new(@order).build!
visit customers.current_orders_path
}
context 'with delivery order' do
it 'shows order info box' do
expect(page).to have_content('Confirmación de Pedido')
end
it 'shows CO2 information' do
expect(page).to have_content("#{(@order.distance_covered * 100).round(0)} gramos")
end
it 'doesnt show co2 after refresh' do
visit customers.current_orders_path
expect(page).not_to have_content("#{(@order.distance_covered * 100).round(0)} gramos")
end
end
context 'with other order type' do
before(:each) do
@order = create(:order, customer: @customer)
meal = create(:meal, provider: @provider)
@order.items.create(meal_id: meal.id)
visit customers.current_orders_path
end
it 'show the info box' do
expect(page).to have_content('Confirmación de Pedido')
end
it 'doesnt show the CO2 info' do
expect(page).not_to have_content("gramos")
end
end
it 'shows current order panel' do
expect(page).to have_content("Anular Orden")
expect(page).to have_content("Total: #{@order.decorate.total}")
end
describe 'cancels the order' do
context 'with valid state' do
it 'changes order state to cancelled_by_user' do
click_on 'Continuar'
click_on 'Anular Orden'
balance = @order.customer.balance
expect(@order.reload.cancelled_by_user?).to be true
end
end
context 'with invalid state' do
it 'shows errors message' do
@order.update(aasm_state: 'cancelled_by_driver')
click_on 'Continuar'
click_on 'Anular Orden'
expect(page).to have_content('La orden ya ha sido aceptada y no puede ser cancelada en este momento.')
expect(current_path).to eq(customers.root_path)
expect(@order.reload.cancelled_by_user?).to be false
end
end
end
context 'when cancelled by provider' do
before(:each){
@cause = create(:rejection_cause)
@mediator = ProviderOrderMediator.new(@order, @provider)
}
it 'updates order item to cancelled' do
@mediator.reject(@cause)
status = @order.items_for(@provider).map(&:cancelled)
expect(status).to match_array([true])
end
end
context 'when cancelled by admin' do
before(:each){
@service = Customers::OrderCancellation.new(@order)
@mediator = ProviderOrderMediator.new(@order, @provider)
@mediator.approve
}
it 'returns balance to customer' do
expect{
@service.cancel_by_administrator
}.to change { @order.customer.balance }.by(@order.total)
end
it 'destroys invoice' do
expect{
@service.cancel_by_administrator
}.to change(Invoice, :count).by(-1)
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment