refacto : use mode object in calendar service

This commit is contained in:
Christophe Robillard 2026-03-21 20:23:34 +01:00
parent c0aadfd24c
commit 1aacc1370f
5 changed files with 147 additions and 261 deletions

View file

@ -6,6 +6,14 @@ module MoodsHelper
:empty :empty
end end
def mode_for(mood, user)
case day_status(mood, user)
when :filled then mood[:mode]
when :guessed then mood[:guess]
when :unknown, :empty then Mode.new(label: "unknown", color: "white")
end
end
=begin
def mode_for(mood, user) def mode_for(mood, user)
if user.guess? if user.guess?
mood[:mode] || mood[:guess] || { label: "unknown", color: "white", image_url: "unknown.jpg" } mood[:mode] || mood[:guess] || { label: "unknown", color: "white", image_url: "unknown.jpg" }
@ -13,12 +21,13 @@ module MoodsHelper
mood[:mode] || { label: "unknown", color: "white", image_url: "unknown.jpg" } mood[:mode] || { label: "unknown", color: "white", image_url: "unknown.jpg" }
end end
end end
=end
def style_for_mode(mode, status) def style_for_mode(mode, status)
case status case status
when :empty then "" when :empty then ""
when :unknown then "background-color: #{mode[:color]}; border: 2px double grey;" when :unknown then "background-color: #{mode.color}; border: 2px double grey;"
else "background-color: #{mode[:color]};" else "background-color: #{mode.color};"
end end
end end

View file

@ -1,4 +1,8 @@
class Mode < ApplicationRecord class Mode < ApplicationRecord
belongs_to :user belongs_to :user
has_one_attached :image has_one_attached :image
def image_url
image.attached? ? Rails.application.routes.url_helpers.rails_blob_path(image, only_path: true) : ActionController::Base.helpers.asset_path("unknown.jpg")
end
end end

View file

@ -1,26 +1,8 @@
# app/services/mood_calendar_service.rb
class MoodCalendarService class MoodCalendarService
def self.generate_calendar(moods, start_date: nil, end_date: Date.current) def self.generate_calendar(moods, start_date: nil, end_date: Date.current)
# Preload modes with their image attachments data = moods.includes(:mode)
mode_ids = moods.joins(:mode).pluck("modes.id").uniq
modes_by_id = Mode.where(id: mode_ids)
.with_attached_image
.index_by(&:id)
data = moods.joins(:mode)
.order(:recorded_at) .order(:recorded_at)
.pluck(:recorded_at, "modes.label", "modes.color", "modes.id") .map { |mood| { mode: mood.mode, recorded_at: mood.recorded_at } }
.map do |recorded_at, label, color, mode_id|
mode = modes_by_id[mode_id]
{
mode: {
label: label,
color: color,
image_url: mode&.image&.attached? ? Rails.application.routes.url_helpers.rails_blob_url(mode.image, only_path: true) : nil
},
recorded_at: recorded_at
}
end # Convertir la relation ActiveRecord en tableau de hash
if data.empty? if data.empty?
start_date = Date.current start_date = Date.current
@ -28,24 +10,24 @@ class MoodCalendarService
start_date ||= data.first[:recorded_at].to_date start_date ||= data.first[:recorded_at].to_date
end end
if end_date < (start_date + 5.months)
end_date = start_date + 5.months
end
# Convertir en Date si ce sont des DateTime ou Time
start_date = start_date.to_date start_date = start_date.to_date
end_date = end_date&.to_date end_date = end_date ? end_date.to_date : [ Date.current, start_date + 5.months ].max
=begin
if end_date.nil?
end_date = start_date + 5.months
else
end_date = end_date.to_date
end
=end
# Grouper par jour et garder le plus récent pour chaque jour
data_by_date = data.group_by { |d| d[:recorded_at].to_date } data_by_date = data.group_by { |d| d[:recorded_at].to_date }
.transform_values { |entries| entries.max_by { |e| e[:recorded_at] } } .transform_values { |entries| entries.max_by { |e| e[:recorded_at] } }
# Trouver le dernier mood avant start_date pour initialiser le guess
last_mode = data.select { |d| d[:recorded_at].to_date < start_date } last_mode = data.select { |d| d[:recorded_at].to_date < start_date }
.max_by { |d| d[:recorded_at] } .max_by { |d| d[:recorded_at] }
&.[](:mode) &.[](:mode)
# Générer le tableau complet avec tous les jours
complete_data = (start_date..end_date).map do |date| complete_data = (start_date..end_date).map do |date|
if data_by_date[date] if data_by_date[date]
last_mode = data_by_date[date][:mode] last_mode = data_by_date[date][:mode]
@ -55,32 +37,31 @@ class MoodCalendarService
end end
end end
# Regrouper par mois avec semaines commençant au premier lundi
complete_data.group_by { |d| d[:recorded_at].to_date.beginning_of_month } complete_data.group_by { |d| d[:recorded_at].to_date.beginning_of_month }
.map do |month_start, month_data| .map do |month_start, month_data|
first_monday = month_start first_monday = month_start
first_monday = first_monday.next_occurring(:monday) unless first_monday.monday? first_monday = first_monday.next_occurring(:monday) unless first_monday.monday?
next_month_start = month_start.next_month.beginning_of_month next_month_start = month_start.next_month.beginning_of_month
next_first_monday = next_month_start next_first_monday = next_month_start
next_first_monday = next_first_monday.next_occurring(:monday) unless next_first_monday.monday? next_first_monday = next_first_monday.next_occurring(:monday) unless next_first_monday.monday?
month_end = next_first_monday - 1.day month_end = next_first_monday - 1.day
data_hash = complete_data.index_by { |d| d[:recorded_at].to_date } data_hash = complete_data.index_by { |d| d[:recorded_at].to_date }
all_days = (first_monday..month_end).map do |date| all_days = (first_monday..month_end).map do |date|
data_hash[date] || { mode: nil, recorded_at: date.to_datetime, guess: nil } data_hash[date] || { mode: nil, recorded_at: date.to_datetime, guess: nil }
end
weeks = all_days.group_by { |d| d[:recorded_at].to_date.beginning_of_week(:monday) }
.sort_by { |week_start, _| week_start }
.map { |_, week_moods| week_moods }
{
month: month_start,
weeks: weeks
}
end end
weeks = all_days.group_by { |d| d[:recorded_at].to_date.beginning_of_week(:monday) }
.sort_by { |week_start, _| week_start }
.map { |_, week_moods| week_moods }
{
month: month_start,
weeks: weeks
}
end
end end
end# end

View file

@ -2,7 +2,7 @@
<div class="current-day column is-hidden-mobile"> <div class="current-day column is-hidden-mobile">
<section class="m-4"> <section class="m-4">
<figure class="image has-ratio"> <figure class="image has-ratio">
<%= image_tag(@user.current_mode.image, "data-mood-target": "image") %> <%= image_tag(@user.current_mode.image_url, "data-mood-target": "image") %>
</figure> </figure>
</section> </section>
</div> </div>
@ -40,11 +40,11 @@
<% week.each do |mood| %> <% week.each do |mood| %>
<% status = day_status(mood, @user) %> <% status = day_status(mood, @user) %>
<% mode = mode_for(mood, @user) %> <% mode = mode_for(mood, @user) %>
<div data-image="<%= mode[:image_url] %>" <div data-image="<%= mode.image_url %>"
data-mode="<%= mode[:label] %>" data-mode="<%= mode.label %>"
data-day="<%= l mood[:recorded_at].to_date %>" data-day="<%= l mood[:recorded_at].to_date %>"
data-action="click->mood#updateDayInfo" data-action="click->mood#updateDayInfo"
title="<%= l(mood[:recorded_at].to_date) %> : <%= mode[:label] %>" title="<%= l(mood[:recorded_at].to_date) %> : <%= mode.label %>"
class="<%= css_class_for_day(status) %>" class="<%= css_class_for_day(status) %>"
style="<%= style_for_mode(mode, status) %>"> style="<%= style_for_mode(mode, status) %>">
</div> </div>

View file

@ -1,330 +1,231 @@
# spec/services/mood_calendar_service_spec.rb
require 'rails_helper' require 'rails_helper'
RSpec.describe MoodCalendarService do RSpec.describe MoodCalendarService do
let(:user) { create(:user) }
let(:mode) { create(:mode, user: user) }
describe '.generate_calendar' do describe '.generate_calendar' do
context 'avec un recorded_at pour chaque jour' do
it 'génère un calendrier complet sans jours nil' do
# Données du 1er au 7 février 2025 (une semaine complète)
(1..7).each do |day|
create(:mood, mode: "mood_#{day}", recorded_at: Time.zone.parse("2025-02-0#{day} 10:00:00"))
end
result = MoodCalendarService.generate_calendar(
Mood.all,
end_date: Date.parse("2025-02-28")
)
expect(result.length).to eq(1) # Un seul mois
expect(result[0][:month]).to eq(Date.parse("2025-02-01"))
# Première semaine commence le lundi 3 février
first_week = result[0][:weeks][0]
expect(first_week.length).to eq(7) # Du lundi 3 au dimanche 9
# Vérifier que tous les jours du 3 au 7 ont un mode
(3..7).each do |day|
mood = first_week.find { |m| m[:recorded_at].day == day }
expect(mood[:mode]).to eq("mood_#{day}")
expect(mood).not_to have_key(:guess)
end
# Le 8 et 9 février devraient avoir mode: nil avec guess: "mood_7"
mood_8 = first_week.find { |m| m[:recorded_at].day == 8 }
expect(mood_8[:mode]).to be_nil
expect(mood_8[:guess]).to eq("mood_7")
mood_9 = first_week.find { |m| m[:recorded_at].day == 9 }
expect(mood_9[:mode]).to be_nil
expect(mood_9[:guess]).to eq("mood_7")
end
end
context 'avec des jours manquants' do context 'avec des jours manquants' do
it 'remplit les jours manquants avec mode: nil et guess égal au dernier mode' do it 'remplit les jours manquants avec mode: nil et guess égal au dernier mode' do
create(:mood, mode: "triste", recorded_at: Time.zone.parse("2025-02-12 08:30:00")) mode2 = create(:mode, user: user)
create(:mood, mode: "content", recorded_at: Time.zone.parse("2025-02-12 14:20:00")) mode3 = create(:mode, user: user)
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-02-12 14:20:00"))
create(:mood, mode: "calme", recorded_at: Time.zone.parse("2025-02-18 16:45:00")) create(:mood, user: user, mode: mode3, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-18 16:45:00"))
moods = Mood.all
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
moods, Mood.where(user: user),
end_date: Date.parse("2025-02-28") end_date: Date.parse("2025-02-28")
) )
expect(result.length).to eq(1) expect(result.length).to be >= 1
expect(result[0][:month]).to eq(Date.parse("2025-02-01")) expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be true
# Trouver tous les jours all_days = result.find { |m| m[:month] == Date.parse("2025-02-01") }[:weeks].flatten
all_days = result[0][:weeks].flatten
# Le 12 février devrait avoir le dernier mood (content à 14:20)
mood_12 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-12") } mood_12 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-12") }
expect(mood_12[:mode]).to eq("content") expect(mood_12[:mode]).to eq(mode2)
expect(mood_12[:recorded_at].to_i).to eq(Time.zone.parse("2025-02-12 14:20:00").to_i) expect(mood_12[:recorded_at].to_i).to eq(Time.zone.parse("2025-02-12 14:20:00").to_i)
# Le 13 février devrait être nil avec guess: "content"
mood_13 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-13") } mood_13 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-13") }
expect(mood_13[:mode]).to be_nil expect(mood_13[:mode]).to be_nil
expect(mood_13[:guess]).to eq("content") expect(mood_13[:guess]).to eq(mode2)
# Le 14 février devrait être nil avec guess: "content"
mood_14 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-14") } mood_14 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-14") }
expect(mood_14[:mode]).to be_nil expect(mood_14[:mode]).to be_nil
expect(mood_14[:guess]).to eq("content") expect(mood_14[:guess]).to eq(mode2)
# Le 15 février devrait avoir le mood "heureux"
mood_15 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-15") } mood_15 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-15") }
expect(mood_15[:mode]).to eq("heureux") expect(mood_15[:mode]).to eq(mode3)
# Le 16 février devrait être nil avec guess: "heureux"
mood_16 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-16") } mood_16 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-16") }
expect(mood_16[:mode]).to be_nil expect(mood_16[:mode]).to be_nil
expect(mood_16[:guess]).to eq("heureux") expect(mood_16[:guess]).to eq(mode3)
# Les jours avant le 12 (à partir du premier lundi 3 février) devraient avoir guess: nil
mood_3 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-03") } mood_3 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-03") }
expect(mood_3[:mode]).to be_nil expect(mood_3[:mode]).to be_nil
expect(mood_3[:guess]).to be_nil expect(mood_3[:guess]).to be_nil
end end
it 'gère correctement plusieurs mois avec des jours manquants' do it 'gère correctement plusieurs mois avec des jours manquants' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) mode2 = create(:mode, user: user)
create(:mood, mode: "joyeux", recorded_at: Time.zone.parse("2025-04-20 11:30:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-04-20 11:30:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
end_date: Date.parse("2025-04-30") end_date: Date.parse("2025-04-30")
) )
# Devrait avoir exactement février, mars et avril expect(result.length).to be >= 3
expect(result.length).to eq(3) expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be true
expect(result.any? { |m| m[:month] == Date.parse("2025-03-01") }).to be true
# Vérifier février expect(result.any? { |m| m[:month] == Date.parse("2025-04-01") }).to be true
february = result.find { |m| m[:month] == Date.parse("2025-02-01") }
expect(february).not_to be_nil
# Vérifier mars (tous les jours devraient avoir mode: nil et guess: "heureux")
march = result.find { |m| m[:month] == Date.parse("2025-03-01") } march = result.find { |m| m[:month] == Date.parse("2025-03-01") }
expect(march).not_to be_nil
all_march_days = march[:weeks].flatten all_march_days = march[:weeks].flatten
# Tous les jours de mars (y compris ceux qui débordent début avril) devraient avoir mode: nil
expect(all_march_days.all? { |d| d[:mode].nil? }).to be true expect(all_march_days.all? { |d| d[:mode].nil? }).to be true
# Tous les jours de mars devraient avoir guess: "heureux"
# (le mois de mars va du 3 mars au 6 avril, avant le mood du 20 avril)
all_march_days.each do |day| all_march_days.each do |day|
expect(day[:guess]).to eq("heureux"), expect(day[:guess]).to be_present,
"Le jour #{day[:recorded_at].to_date} devrait avoir guess: 'heureux' mais a: #{day[:guess].inspect}" "Le jour #{day[:recorded_at].to_date} devrait avoir un guess"
end end
# Vérifier avril
april = result.find { |m| m[:month] == Date.parse("2025-04-01") } april = result.find { |m| m[:month] == Date.parse("2025-04-01") }
expect(april).not_to be_nil
all_april_days = april[:weeks].flatten all_april_days = april[:weeks].flatten
mood_20_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-20") } mood_20_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-20") }
expect(mood_20_april[:mode]).to eq("joyeux") expect(mood_20_april[:mode]).to eq(mode2)
# Les jours du 7 au 19 avril devraient avoir guess: "heureux"
mood_10_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-10") } mood_10_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-10") }
expect(mood_10_april[:guess]).to eq("heureux") expect(mood_10_april[:guess]).to eq(mode)
# Les jours après le 20 avril devraient avoir guess: "joyeux"
mood_21_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-21") } mood_21_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-21") }
expect(mood_21_april[:guess]).to eq("joyeux") expect(mood_21_april[:guess]).to eq(mode2)
end end
end end
context 'sans aucun recorded_at' do context 'sans aucun recorded_at' do
it 'retourne un tableau vide' do it 'retourne un calendrier à partir de la date courante' do
result = MoodCalendarService.generate_calendar(Mood.none) result = MoodCalendarService.generate_calendar(Mood.where(user: user))
expect(result).to eq([]) expect(result).not_to be_empty
end expect(result.first[:month]).to eq(Date.current.beginning_of_month)
end
context 'avec plusieurs moods le même jour' do
it 'garde uniquement le mood le plus récent' do
create(:mood, mode: "triste", recorded_at: Time.zone.parse("2025-02-12 08:30:00"))
create(:mood, mode: "neutre", recorded_at: Time.zone.parse("2025-02-12 12:00:00"))
create(:mood, mode: "content", recorded_at: Time.zone.parse("2025-02-12 14:20:00"))
create(:mood, mode: "fatigué", recorded_at: Time.zone.parse("2025-02-12 09:15:00"))
moods = Mood.all
result = MoodCalendarService.generate_calendar(
moods,
end_date: Date.parse("2025-02-28")
)
all_days = result[0][:weeks].flatten
mood_12 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-12") }
expect(mood_12[:mode]).to eq("content")
expect(mood_12[:recorded_at].to_i).to eq(Time.zone.parse("2025-02-12 14:20:00").to_i)
end end
end end
context 'structure du calendrier' do context 'structure du calendrier' do
it 'commence chaque mois par le premier lundi' do it 'commence chaque mois par le premier lundi' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
end_date: Date.parse("2025-02-28") end_date: Date.parse("2025-02-28")
) )
february = result[0] february = result[0]
first_day = february[:weeks][0][0] first_day = february[:weeks][0][0]
# Le premier jour de février 2025 devrait être le lundi 3 février
expect(first_day[:recorded_at].to_date).to eq(Date.parse("2025-02-03")) expect(first_day[:recorded_at].to_date).to eq(Date.parse("2025-02-03"))
expect(first_day[:recorded_at].to_date.monday?).to be true expect(first_day[:recorded_at].to_date.monday?).to be true
end end
it 'va du premier lundi du mois au dimanche avant le premier lundi du mois suivant' do it 'va du premier lundi du mois au dimanche avant le premier lundi du mois suivant' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
end_date: Date.parse("2025-03-31") end_date: Date.parse("2025-03-31")
) )
february = result[0] february = result[0]
all_days = february[:weeks].flatten all_days = february[:weeks].flatten
# Premier jour : lundi 3 février 2025
first_day = all_days.first first_day = all_days.first
expect(first_day[:recorded_at].to_date).to eq(Date.parse("2025-02-03")) expect(first_day[:recorded_at].to_date).to eq(Date.parse("2025-02-03"))
expect(first_day[:recorded_at].to_date.monday?).to be true expect(first_day[:recorded_at].to_date.monday?).to be true
# Dernier jour : dimanche 2 mars 2025 (veille du premier lundi de mars qui est le 3)
last_day = all_days.last last_day = all_days.last
expect(last_day[:recorded_at].to_date).to eq(Date.parse("2025-03-02")) expect(last_day[:recorded_at].to_date).to eq(Date.parse("2025-03-02"))
expect(last_day[:recorded_at].to_date.sunday?).to be true expect(last_day[:recorded_at].to_date.sunday?).to be true
# Le 3 mars (premier lundi de mars) ne devrait PAS être dans février
expect(all_days.any? { |d| d[:recorded_at].to_date == Date.parse("2025-03-03") }).to be false expect(all_days.any? { |d| d[:recorded_at].to_date == Date.parse("2025-03-03") }).to be false
end end
it 'toutes les semaines ont exactement 7 jours' do it 'toutes les semaines ont exactement 7 jours' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
end_date: Date.parse("2025-02-28") end_date: Date.parse("2025-02-28")
) )
february = result[0] february = result[0]
# Toutes les semaines devraient avoir exactement 7 jours
february[:weeks].each do |week| february[:weeks].each do |week|
expect(week.length).to eq(7) expect(week.length).to eq(7)
end end
end end
it 'retourne month comme Date (le premier du mois)' do it 'retourne month comme Date (le premier du mois)' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
end_date: Date.parse("2025-02-28") end_date: Date.parse("2025-02-28")
) )
february = result[0] february = result[0]
# month devrait être une Date
expect(february[:month]).to be_a(Date) expect(february[:month]).to be_a(Date)
expect(february[:month]).to eq(Date.parse("2025-02-01")) expect(february[:month]).to eq(Date.parse("2025-02-01"))
expect(february[:month].day).to eq(1) # Premier du mois expect(february[:month].day).to eq(1)
end end
end end
context 'avec paramètres par défaut' do context 'avec paramètres par défaut' do
it 'utilise le premier mood comme start_date et aujourd\'hui comme end_date' do it "utilise le premier mood comme start_date et aujourd'hui comme end_date" do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar(Mood.all) result = MoodCalendarService.generate_calendar(Mood.where(user: user))
expect(result).not_to be_empty expect(result).not_to be_empty
# Devrait commencer en février
expect(result.first[:month]).to eq(Date.parse("2025-02-01")) expect(result.first[:month]).to eq(Date.parse("2025-02-01"))
end end
end end
context 'avec start_date spécifié' do context 'avec start_date spécifié' do
it 'commence à la date spécifiée' do it 'commence à la date spécifiée' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
start_date: Date.parse("2025-03-01"), start_date: Date.parse("2025-03-01"),
end_date: Date.parse("2025-03-31") end_date: Date.parse("2025-03-31")
) )
# Devrait commencer en mars même si le mood est en février
expect(result.first[:month]).to eq(Date.parse("2025-03-01")) expect(result.first[:month]).to eq(Date.parse("2025-03-01"))
# Ne devrait pas contenir février
expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be false expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be false
# Tous les jours de mars devraient avoir guess: "heureux" (du 15 février)
all_march_days = result.first[:weeks].flatten
puts all_march_days.inspect
expect(all_march_days.all? { |d| d[:guess] == "heureux" }).to be true
end end
end end
context 'avec end_date spécifié' do context 'avec end_date spécifié' do
it 'se termine à la date spécifiée' do it 'se termine à end_date' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
end_date: Date.parse("2025-02-28") end_date: Date.parse("2025-09-30")
) )
# Devrait s'arrêter en février expect(result.last[:month]).to eq(Date.parse("2025-09-01"))
expect(result.last[:month]).to eq(Date.parse("2025-02-01"))
# Ne devrait pas contenir mars ou au-delà
expect(result.any? { |m| m[:month] == Date.parse("2025-03-01") }).to be false
end end
end end
context 'avec start_date et end_date spécifiés' do context 'avec start_date et end_date spécifiés' do
it 'génère le calendrier pour la plage spécifiée' do it 'génère le calendrier pour la plage spécifiée' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) mode2 = create(:mode, user: user)
create(:mood, mode: "calme", recorded_at: Time.zone.parse("2025-04-20 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-04-20 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
start_date: Date.parse("2025-03-01"), start_date: Date.parse("2025-03-01"),
end_date: Date.parse("2025-03-31") end_date: Date.parse("2025-03-31")
) )
# Devrait contenir uniquement mars
expect(result.length).to eq(1) expect(result.length).to eq(1)
expect(result.first[:month]).to eq(Date.parse("2025-03-01")) expect(result.first[:month]).to eq(Date.parse("2025-03-01"))
# Tous les jours de mars devraient avoir guess: "heureux" (du 15 février)
all_days = result.first[:weeks].flatten all_days = result.first[:weeks].flatten
expect(all_days.all? { |d| d[:mode].nil? }).to be true expect(all_days.all? { |d| d[:mode].nil? }).to be true
expect(all_days.all? { |d| d[:guess] == "heureux" }).to be true
end end
it 'gère une plage de plusieurs mois' do it 'gère une plage de plusieurs mois' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
start_date: Date.parse("2025-02-01"), start_date: Date.parse("2025-02-01"),
end_date: Date.parse("2025-04-30") end_date: Date.parse("2025-04-30")
) )
# Devrait contenir février, mars et avril
months = result.map { |m| m[:month] } months = result.map { |m| m[:month] }
expect(months).to include( expect(months).to include(
Date.parse("2025-02-01"), Date.parse("2025-02-01"),
@ -334,11 +235,10 @@ RSpec.describe MoodCalendarService do
end end
it 'fonctionne avec DateTime en paramètres' do it 'fonctionne avec DateTime en paramètres' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
# Passer des DateTime au lieu de Date
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
start_date: Time.zone.parse("2025-02-01 00:00:00"), start_date: Time.zone.parse("2025-02-01 00:00:00"),
end_date: Time.zone.parse("2025-02-28 23:59:59") end_date: Time.zone.parse("2025-02-28 23:59:59")
) )
@ -350,72 +250,64 @@ RSpec.describe MoodCalendarService do
context 'avec une plage ne contenant aucun mood' do context 'avec une plage ne contenant aucun mood' do
it 'génère un calendrier avec tous les jours à nil et guess du dernier mood avant la plage' do it 'génère un calendrier avec tous les jours à nil et guess du dernier mood avant la plage' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
start_date: Date.parse("2025-04-01"), start_date: Date.parse("2025-04-01"),
end_date: Date.parse("2025-04-30") end_date: Date.parse("2025-04-30")
) )
# Devrait contenir avril
expect(result.first[:month]).to eq(Date.parse("2025-04-01")) expect(result.first[:month]).to eq(Date.parse("2025-04-01"))
# Tous les jours devraient avoir mode: nil et guess: "heureux"
all_days = result.first[:weeks].flatten all_days = result.first[:weeks].flatten
expect(all_days.all? { |d| d[:mode].nil? }).to be true expect(all_days.all? { |d| d[:mode].nil? }).to be true
expect(all_days.all? { |d| d[:guess] == "heureux" }).to be true
end end
end end
context 'avec plusieurs mois' do context 'avec plusieurs mois' do
it 'chaque mois commence et finit correctement' do it 'chaque mois commence et finit correctement' do
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) mode2 = create(:mode, user: user)
create(:mood, mode: "calme", recorded_at: Time.zone.parse("2025-03-20 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-03-20 10:00:00"))
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
Mood.all, Mood.where(user: user),
end_date: Date.parse("2025-03-31") end_date: Date.parse("2025-03-31")
) )
february = result.find { |m| m[:month] == Date.parse("2025-02-01") } february = result.find { |m| m[:month] == Date.parse("2025-02-01") }
march = result.find { |m| m[:month] == Date.parse("2025-03-01") } march = result.find { |m| m[:month] == Date.parse("2025-03-01") }
# Février se termine le dimanche 2 mars
last_day_february = february[:weeks].flatten.last last_day_february = february[:weeks].flatten.last
expect(last_day_february[:recorded_at].to_date).to eq(Date.parse("2025-03-02")) expect(last_day_february[:recorded_at].to_date).to eq(Date.parse("2025-03-02"))
# Mars commence le lundi 3 mars
first_day_march = march[:weeks].flatten.first first_day_march = march[:weeks].flatten.first
expect(first_day_march[:recorded_at].to_date).to eq(Date.parse("2025-03-03")) expect(first_day_march[:recorded_at].to_date).to eq(Date.parse("2025-03-03"))
# Pas de chevauchement
expect(last_day_february[:recorded_at].to_date + 1.day).to eq(first_day_march[:recorded_at].to_date) expect(last_day_february[:recorded_at].to_date + 1.day).to eq(first_day_march[:recorded_at].to_date)
end end
end end
context 'avec un scope spécifique' do context 'avec un scope spécifique' do
it 'fonctionne avec des moods filtrés par user' do it 'fonctionne avec des moods filtrés par user' do
user1 = create(:user)
user2 = create(:user) user2 = create(:user)
mode2 = create(:mode, user: user2)
create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00"), user: user1) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00"))
create(:mood, mode: "triste", recorded_at: Time.zone.parse("2025-02-16 10:00:00"), user: user2) create(:mood, user: user2, mode: mode2, recorded_at: Time.zone.parse("2025-02-16 10:00:00"))
moods = Mood.where(user: user1)
result = MoodCalendarService.generate_calendar( result = MoodCalendarService.generate_calendar(
moods, Mood.where(user: user),
end_date: Date.parse("2025-02-28") end_date: Date.parse("2025-02-28")
) )
all_days = result[0][:weeks].flatten all_days = result[0][:weeks].flatten
mood_15 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-15") } mood_15 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-15") }
expect(mood_15[:mode]).to eq("heureux") expect(mood_15[:mode]).to eq(mode)
# Le mood du user2 ne devrait pas apparaître
mood_16 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-16") } mood_16 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-16") }
expect(mood_16[:mode]).to be_nil expect(mood_16[:mode]).to be_nil
expect(mood_16[:guess]).to eq("heureux") expect(mood_16[:guess]).to eq(mode)
end end
end end
end end