Skip to content

Commit

Permalink
Merge pull request #7 from adrianrbp/6-shift-management-api
Browse files Browse the repository at this point in the history
6-shift management api
  • Loading branch information
adrianrbp authored Aug 11, 2024
2 parents 024cb24 + e7a8bee commit 1a22746
Show file tree
Hide file tree
Showing 54 changed files with 1,205 additions and 165 deletions.
34 changes: 32 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ jobs:
backend:
name: 'Rails API Tests'
runs-on: ubuntu-latest

services:
postgres:
image: postgres:latest
env:
POSTGRES_USER: postgres_user
POSTGRES_PASSWORD: testpassword
POSTGRES_DB: monitoring_sys_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -18,12 +34,26 @@ jobs:
- name: Install dependencies
run: |
cd backend
gem install bundler
bundle install
- name: Run Rails tests
- name: Set up environment variables
run: |
echo "DATABASE_HOST=localhost" >> $GITHUB_ENV
echo "DATABASE_USER=postgres_user" >> $GITHUB_ENV
echo "DATABASE_PASSWORD=testpassword" >> $GITHUB_ENV
- name: Set up database
run: |
cd backend
RAILS_ENV=test rails db:create
RAILS_ENV=test rails db:migrate
RAILS_ENV=test rails db:seed
- name: Run RSpec tests
run: |
cd backend
bin/rails test
RAILS_ENV=test bundle exec rspec
frontend:
name: 'Vue Frontend Tests'
Expand Down
Binary file added 2-availability_management.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 76 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,22 @@
### Componentes
#### Endpoints - Gestion de Turnos (Shifts)
- 1st Dropdown (Services)
- GET /api/company_services
- GET /api/company_services
- [example response](frontend/src/mock/company_services.json)
- 2nd Dropdown (Weeks)
- GET /api/company_services/:id/weeks
- [example response](frontend/src/mock/weeks_service_a.json)
- Engineers Table
- GET /api/company_services/:id/engineers?week=YYYY-WW
- [example response](frontend/src/mock/engineers_a_w1.json)
- Shifts Table
- GET /api/company_services/:id/shifts?week=YYYY-WW
- [example response](frontend/src/mock/shifts_a_w1.json)
#### Endpoints - Gestion de Disponibilidad (Availability)
- Dropdowns anteriores (gestion de turnos) para el filtrado y llenado de semana
- Boton Editar Disponibilidad: Consultar Disponibilidad de ingenieros
- GET /api/company_services/:id/engineers/availability?week=YYYY-WW
- [example response](frontend/src/mock/eng_availability_a_w1.json.json)
- Updates Engineer Availability
- POST /api/company_services/:id/engineers/availability
- week
Expand All @@ -51,12 +56,66 @@

#### Modelos
1. Servicios monitoreados
- Bloques de 1h
- Horario establecido (grupo de bloques)
2. Semana
3. Engineer
4. Turno (bloques de 1 hora)
5. Asignacion (Relacion Ingeniero - Hora)
- Contrato: Fechas Establecidas
2. Engineer
3. CompanyServiceEngineer
- Asignar 3 ingenieros encargados del servicio durante el contrato.
4. Shift (Turno) - bloques de 1 hora
- Contrato: Horas por dia de semana establecidas (grupo de bloques)
5. EngineerShift
- Bloque asignado a ingeniero
6. Availability (Disponibilidad) - Must: engineer

#### Modelos - Instancias Ejemplo
1. CompanyService
id: 1
name: "Service A"
contract_start_date: "2024-08-01"
contract_end_date: "2024-08-31"

2. Engineer
id: 1
name:"Alice Smith"
color:"Bob Johnson"

3. Shift
company_service:1
engineer:(sin asignar)
week:"2024-32"
day:"Monday"
start_time:"2024-08-07 09:00:00"
end_time:"2024-08-07 10:00:00"

4. Availability
engineer:1
week:"2024-32"
day:"Monday"
start_time:"2024-08-07 09:00:00"
end_time:"2024-08-07 10:00:00"
available:true

#### Modelos - Instancias Factory Bot
```ruby
# 1. CompanyService
FactoryBot.attributes_for :company_service
=> {:name=>"Farrell, Mohr and Haley", :contract_start_date=>Thu, 18 Jul 2024, :contract_end_date=>Tue, 20 Aug 2024}

# 2. Engineer
FactoryBot.attributes_for :engineer
=> {:name=>"Russell Hermann", :color=>"#0c0d0d"}

# 3. CompanyServiceEngineer
#FactoryBot.attributes_for :company_service_engineer

# 4. Shift
FactoryBot.attributes_for :shift
=> {:week=>"2024-32", :day=>"Tuesday", :start_time=>"13:00", :end_time=>"18:00"}
# 5. EngineerShift

# 6. Availability
FactoryBot.attributes_for :availability

```

#### Arquitectura Frontend (Grafica Figma)
- View
Expand All @@ -65,6 +124,7 @@
- CompanyServiceApi.ts
#### Arquitectura Backend (Grafica Figma)


### Ejecución
#### Ambiente de desarrollo
- Se ha usado Devcontainer y docker-compose para facilitar el desarrollo usando contenedores y vscode
Expand All @@ -77,12 +137,17 @@
- abrir command palette: ctrl + shift + p
- Seleccionar: Reopen in container
- Seleccionar: "Rails API Container"
- Dentro ejecutar: `rails s`
- Dentro ejecutar:
```bash
rails db:setup
rails s -b 0.0.0.0
```
- Ejecutar el contenedor Vue Container:
- abrir command palette: ctrl + shift + p
- Seleccionar: Reopen in container
- Seleccionar: "Vue Container"
- Dentro ejecutar: `yarn dev`
- Dentro ejecutar mocked: `yarn dev`
- (alternativa) Dentro ejecutar api: `yarn serve:api`
- navegar a 0.0.0.0:8080 para empezar a usar la app
- Ejecutar tests e2e:
```bash
Expand All @@ -106,6 +171,8 @@ docker-compose -f .devcontainer/docker-compose.yml up
### Screenshots
#### Ejecución
![figma-1](./1-shift_management.png)
![figma-2](./2-availability_management.png)


#### Figma
![figma-1](./shift-availability-management-figma.jpg)
Expand Down
1 change: 1 addition & 0 deletions backend/app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
class ApplicationController < ActionController::API
include JsonResponse
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module CompanyServices
class EngineersController < ApplicationController
def index
company_service = CompanyService.find(params[:company_service_id])
week = params[:week]
@engineers = company_service.engineers
end
end
end
7 changes: 7 additions & 0 deletions backend/app/controllers/company_services/shifts_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module CompanyServices
class ShiftsController < ApplicationController
def index
@shifts = FetchShiftsService.new(params[:company_service_id],params[:week]).call
end
end
end
Empty file.
26 changes: 26 additions & 0 deletions backend/app/controllers/concerns/json_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module JsonResponse
extend ActiveSupport::Concern

included do
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
rescue_from ActionController::RoutingError, with: :render_not_found

before_action :ensure_json_request
before_action :set_default_format

def render_not_found
render json: { error: 'Not Found' }, status: :not_found
end

end

private
def set_default_format
request.format = :json
end

def ensure_json_request
return if request.format.json?
render json: { error: 'Not Acceptable' }, status: :not_acceptable
end
end
5 changes: 5 additions & 0 deletions backend/app/controllers/weeks_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class WeeksController < ApplicationController
def index
@weeks = WeekService.new(params[:company_service_id]).call
end
end
3 changes: 3 additions & 0 deletions backend/app/models/company_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@
# updated_at :datetime not null
#
class CompanyService < ApplicationRecord
has_many :company_service_engineers, dependent: :destroy
has_many :engineers, through: :company_service_engineers
has_many :shifts, dependent: :destroy
end
24 changes: 24 additions & 0 deletions backend/app/models/company_service_engineer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# == Schema Information
#
# Table name: company_service_engineers
#
# id :bigint not null, primary key
# company_service_id :bigint not null
# engineer_id :bigint not null
# created_at :datetime not null
# updated_at :datetime not null
#
class CompanyServiceEngineer < ApplicationRecord
belongs_to :company_service
belongs_to :engineer

validate :limited_engineers

private

def limited_engineers
if company_service.present? && company_service.company_service_engineers.count >= 3
errors.add(:base, "Cannot assign more than 3 engineers to a company service")
end
end
end
14 changes: 14 additions & 0 deletions backend/app/models/engineer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# == Schema Information
#
# Table name: engineers
#
# id :bigint not null, primary key
# name :string
# color :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Engineer < ApplicationRecord
has_many :company_service_engineers, dependent: :destroy
has_many :company_services, through: :company_service_engineers
end
16 changes: 16 additions & 0 deletions backend/app/models/shift.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# == Schema Information
#
# Table name: shifts
#
# id :bigint not null, primary key
# company_service_id :bigint not null
# week :string
# day :string
# start_time :time
# end_time :time
# created_at :datetime not null
# updated_at :datetime not null
#
class Shift < ApplicationRecord
belongs_to :company_service
end
60 changes: 60 additions & 0 deletions backend/app/services/fetch_shifts_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class FetchShiftsService
def initialize(company_service_id, week)
@company_service = CompanyService.find(company_service_id)
@week = week
end

def call
shifts_by_day.map do |day, shifts|
{
day: day,
dayLabel: formatted_day_label(day),
time_blocks: format_time_blocks(shifts)
}
end
end

private

def shifts_by_day
# "Monday" => [shifts]
@company_service.shifts
.where(week: @company_service.contract_start_week)
.group_by(&:day)
end

def formatted_day_label(day)
current_year = Date.today.year
week_number = @week.split('-').last.to_i
day_index = Date::DAYNAMES.index(day.capitalize)
day_index = day_index == 0 ? 7 : day_index
date = Date.commercial(
current_year,
week_number,
day_index
)
# I18n.l(date, format: "%A %d de %B", locale: I18n.locale)
I18n.l(date, format: :long, locale: :es)
end

def format_time_blocks(shifts)
shifts.map do |shift|
{
start_time: shift.start_time.strftime("%H:%W"),
end_time: shift.end_time.strftime("%H:%W"),
amount_of_hours: ((shift.end_time - shift.start_time) / 1.hour).to_i,
engineer: nil #format_engineer(shift.engineer)
}
end
end

def format_engineer(engineer)
return nil unless engineer.present?

{
id: engineer.id,
name: engineer.name,
color: engineer.color
}
end
end
Loading

0 comments on commit 1a22746

Please sign in to comment.