Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
baa307a
チェック後の日報と提出物に確認ボタンが出てしまう不具合を修正
hirokiej Aug 30, 2025
7fd807c
CorrectAnswerNotifierをnewspaperからActiveSupport::Notificationsへ移行
ryufuta Jul 21, 2025
ab4ac58
GraduationNotifierをnewspaperからActiveSupport::Notificationsへ移行
ryufuta Aug 5, 2025
e206393
ComebackNotifierをnewspaperからActiveSupport::Notificationsへ移行
ryufuta Aug 5, 2025
40eaac4
Events.jsxを非React化した
sjabcdefin Sep 4, 2025
6ae0bd2
Events.jsxの非React化に伴い不要となったファイルを削除
sjabcdefin Sep 5, 2025
51fcc76
Events.jsxの非React化に伴いテストを修正
sjabcdefin Sep 6, 2025
edad3cf
CodeRabbitの指摘事項を修正
sjabcdefin Sep 7, 2025
d1aa1f2
修正箇所以外のCodeRabbitの指摘事項を修正
sjabcdefin Sep 7, 2025
ce41a59
カテゴリの位置を一番上に移動した
yokomaru Sep 14, 2025
d29aa3a
エラーの表示位置を他のページに合わせて移動した
yokomaru Sep 14, 2025
5d20af4
Queryオブジェクトにリファクタリング
smallmonkeykey Sep 3, 2025
94bd530
self_assigned_no_replied_product_idsをテストが通るように書き換え
smallmonkeykey Sep 8, 2025
9dae3a8
テストで不要な箇所を削除とWIPを明示
smallmonkeykey Sep 8, 2025
0c6cff0
published_atが同一のケースでも期待順になることを検証
smallmonkeykey Sep 8, 2025
9221f70
ORDER BY部分を.reorder(nil)で明示的にクリアにする
smallmonkeykey Sep 8, 2025
d32587b
最後のコメントがチェック担当者本人でないテストケースを追加
smallmonkeykey Sep 16, 2025
33ab4e1
本文を他のテストの内容と合わす
smallmonkeykey Sep 16, 2025
2f4507d
テスト名を修正した
smallmonkeykey Sep 16, 2025
4ef74fd
テスト名が日本語のため英語のテスト名に変更
karlley Jul 2, 2025
9928b0b
初投稿のみというニュアンスのテスト名に変更
karlley Aug 13, 2025
95f31c3
初日報というニュアンスをテスト名に追加
karlley Aug 14, 2025
c1c4f95
Merge pull request #9175 from fjordllc/chore/move-category-on-faq-page
komagata Sep 19, 2025
b1d5c93
お試し期間作成ボタンの位置を移動した
yokomaru Sep 15, 2025
ca87ad9
新規作成ページ上のお試し延長一覧ボタンに適用するCSSを変更した
yokomaru Sep 20, 2025
dee3984
Merge pull request #9109 from fjordllc/bug/check-button-disappear-aft…
komagata Sep 21, 2025
1894d14
Refactor: Patterns::Queryに合わせたクエリ実装のリファクタリング
sharoa119 Jul 31, 2025
55a7555
GrassLearningTimeQueryで明示的なstart_date指定に対応
sharoa119 Aug 4, 2025
685001f
GrassLearningTimeQueryの年またぎ・月またぎ・velocity計算に関するテストを追加
sharoa119 Aug 4, 2025
8d66c89
Rubocopの指摘に従ってスタイル修正(ハッシュ記法、クオートなど)
sharoa119 Aug 4, 2025
0dd9261
学習時間集計SQLを最適化(期間フィルタ追加・暗黙キャスト解消・ORDER BY追加)
sharoa119 Aug 22, 2025
82d3bcd
GrassLearningTimeQuery の initialize を簡単にしてテストを整理
sharoa119 Aug 27, 2025
900813c
テスト名を修正: series が end_date.prev_year.sunday から始まることを明示
sharoa119 Sep 5, 2025
d4e30b9
reports(user_id, reported_on) にインデックスを追加
sharoa119 Sep 5, 2025
a570196
不要なインデックス idx_reports_user_date を削除
sharoa119 Sep 5, 2025
4dfd669
GrassLearningTimeQueryのvelocity計算で2h/4h/6hの境界値をテスト(タイポ修正含む)
sharoa119 Sep 8, 2025
49543e3
不要な reports インデックス関連マイグレーションを削除
sharoa119 Sep 22, 2025
ec8456d
お知らせにブックマーク機能を追加した
smallmonkeykey Sep 22, 2025
d29393c
fixtureに依存してたテストを、テスト内でデータを作成する形に変更した
smallmonkeykey Sep 22, 2025
26e3c56
Bump rack from 2.2.17 to 2.2.18
dependabot[bot] Sep 25, 2025
0359184
未確認の日報ページのURLを修正
komagata Sep 25, 2025
a069234
Merge pull request #9210 from fjordllc/bug/unchecked-reports
komagata Sep 25, 2025
69f15f3
markdown-it-purifierを外した
komagata Sep 27, 2025
f60a1bf
uncheckedページのcontrollerを整理した
komagata Sep 27, 2025
1478511
Merge pull request #9147 from fjordllc/chore/migrate-event-list-to-ra…
komagata Oct 1, 2025
5e20ef6
Merge pull request #9150 from fjordllc/chore/refactor-product-self-as…
komagata Oct 1, 2025
0d44aed
Merge pull request #9015 from fjordllc/chore/replace-newspaper-in-cor…
komagata Oct 1, 2025
27b7a23
Merge pull request #9012 from fjordllc/chore/refactor-grass_times-method
komagata Oct 1, 2025
2c7400d
Merge pull request #9207 from fjordllc/dependabot/bundler/rack-2.2.18
komagata Oct 1, 2025
5f1522f
Merge pull request #8878 from fjordllc/chore/fix-japanese-test-title
komagata Oct 1, 2025
a969789
Merge pull request #9196 from fjordllc/feature/add-bookmark-to-announ…
komagata Oct 1, 2025
5216319
Merge pull request #9179 from fjordllc/chore/move-campaign-create-button
komagata Oct 1, 2025
3bcbeb1
Merge pull request #9213 from fjordllc/bug/html-in-quotes
komagata Oct 1, 2025
f21bb0b
不要なテストを削除
komagata Oct 1, 2025
241422d
Merge pull request #9215 from fjordllc/chore/tidy-unchecked-controller
komagata Oct 1, 2025
cc1493c
textareaのsanitizeのテストをスキップした
komagata Oct 2, 2025
0dd119a
Merge pull request #9227 from fjordllc/chore/skip-sanitize-test
komagata Oct 2, 2025
d9e852b
未チェックの日報を一括で開くボタンを復活させた
komagata Oct 2, 2025
96f3cd0
新料金表示に変更
machida Oct 2, 2025
a0438e7
Merge pull request #9226 from fjordllc/changed-price
machida Oct 2, 2025
2ee11c9
Merge pull request #9228 from fjordllc/bug/restore-open-all-reports-b…
komagata Oct 2, 2025
dcb4bf7
not_solved_question_countメソッドを修正
Miya096jp Sep 28, 2025
fb0e6a1
unsolved_badgeメソッドを修正
Miya096jp Sep 28, 2025
ff7f6b8
グローバルナビゲーションのQ&Aに表示する未解決質問数の参照先をCacheに置換
Miya096jp Sep 29, 2025
2c6617f
デバッグ用にRails.loggerを追加
Miya096jp Sep 30, 2025
528fcef
回答の作成・更新時の不要なキャッシュクリアを除去
Miya096jp Oct 2, 2025
a470dcb
キャッシュクリアのデバッグのため、Rails.loggerを追加
Miya096jp Oct 2, 2025
2b8ccfa
削除する回答がベストアンサーの場合にのみキャッシュクリアを実行するように修正
Miya096jp Oct 2, 2025
c7420e4
回答削除のイベントリスナーにおいて、不要にcancelBestAnswerメソッドを呼び出していた部分を削除し、回答削除時に2重にキャッシ…
Miya096jp Oct 2, 2025
14e1244
Rails.loggerにAnswerCacheControllerの呼び出し元情報を出力するように修正
Miya096jp Oct 2, 2025
742f571
呼び出し情報をRails.loggerに渡すためのpayloadを追加
Miya096jp Oct 2, 2025
d388980
updateで定義されるローカル変数answerをNewspaperのpayloadに渡すように修正
Miya096jp Oct 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ GEM
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.8.1)
rack (2.2.17)
rack (2.2.18)
rack-cors (2.0.2)
rack (>= 2.0.0)
rack-dev-mark (0.8.0)
Expand Down
9 changes: 6 additions & 3 deletions app/controllers/api/answers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ def create
@answer.user = current_user
if @answer.save
ActiveSupport::Notifications.instrument('answer.create', answer: @answer)
Newspaper.publish(:answer_save, { answer: @answer })
render partial: 'questions/answer', locals: { question:, answer: @answer, user: current_user }, status: :created
else
head :bad_request
Expand All @@ -39,16 +38,20 @@ def create

def update
if @answer.update(answer_params)
Newspaper.publish(:answer_save, { answer: @answer })
head :ok
else
head :bad_request
end
end

def destroy
if @answer.is_a?(CorrectAnswer)
Newspaper.publish(:answer_destroy, {
answer: @answer,
action: "#{self.class.name}##{action_name}"
})
end
@answer.destroy
Newspaper.publish(:answer_destroy, { answer: @answer })
end

private
Expand Down
12 changes: 9 additions & 3 deletions app/controllers/api/correct_answers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ def create
@answer = @question.answers.find(params[:answer_id])
@answer.type = 'CorrectAnswer'
if @answer.save
Newspaper.publish(:answer_save, { answer: @answer })
Newspaper.publish(:correct_answer_save, { answer: @answer })
Newspaper.publish(:answer_save, {
answer: @answer,
action: "#{self.class.name}##{action_name}"
})
ActiveSupport::Notifications.instrument('correct_answer.save', answer: @answer)
head :ok
else
head :bad_request
Expand All @@ -18,7 +21,10 @@ def create
def update
answer = @question.answers.find(params[:answer_id])
answer.update!(type: '')
Newspaper.publish(:answer_save, { answer: @answer })
Newspaper.publish(:answer_save, {
answer:,
action: "#{self.class.name}##{action_name}"
})
end
Comment on lines 21 to 28
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

変数の不一致によるバグ

Line 22でanswerをローカル変数として定義していますが、Line 25のイベント発行では@answerを使用しています。updateアクションでは@answerが設定されていないため、nilがペイロードに含まれることになります。

以下のdiffを適用して、正しい変数を使用するように修正してください:

   def update
     answer = @question.answers.find(params[:answer_id])
     answer.update!(type: '')
     Newspaper.publish(:answer_save, {
-                        answer: @answer,
+                        answer: answer,
                         action: "#{self.class.name}##{action_name}"
                       })
   end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def update
answer = @question.answers.find(params[:answer_id])
answer.update!(type: '')
Newspaper.publish(:answer_save, { answer: @answer })
Newspaper.publish(:answer_save, {
answer: @answer,
action: "#{self.class.name}##{action_name}"
})
end
def update
answer = @question.answers.find(params[:answer_id])
answer.update!(type: '')
Newspaper.publish(:answer_save, {
answer: answer,
action: "#{self.class.name}##{action_name}"
})
end
🤖 Prompt for AI Agents
In app/controllers/api/correct_answers_controller.rb around lines 21 to 28, the
update action defines a local variable answer but publishes @answer (which is
nil); change the publish call to use the local answer (or assign @answer =
answer before publishing) so the payload contains the updated record; ensure
Newspaper.publish passes answer: answer and keep the action metadata unchanged.


private
Expand Down
12 changes: 0 additions & 12 deletions app/controllers/api/events_controller.rb

This file was deleted.

2 changes: 1 addition & 1 deletion app/controllers/comeback_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def create
if @user
if @user&.hibernated?
@user.comeback!
Newspaper.publish(:comeback_update, { user: @user })
ActiveSupport::Notifications.instrument('comeback.update', user: @user)
@user.create_comebacked_comment
redirect_to root_url, notice: '休会から復帰しました。'
else
Expand Down
3 changes: 3 additions & 0 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# frozen_string_literal: true

class EventsController < ApplicationController
PAGER_NUMBER = 20

before_action :set_event, only: %i[edit update destroy]

def index
@events = Event.with_avatar.includes(:comments, :users).order(start_at: :desc).page(params[:page]).per(PAGER_NUMBER)
@upcoming_events_groups = UpcomingEvent.upcoming_events_groups
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/graduation_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class GraduationController < ApplicationController
def update
if @user.update(graduated_on: Date.current)
Subscription.new.destroy(@user.subscription_id) if @user.subscription_id
Newspaper.publish(:graduation_update, { user: @user })
ActiveSupport::Notifications.instrument('graduation.update', user: @user)
redirect_to @redirect_url, notice: 'ユーザー情報を更新しました。'
else
redirect_to @redirect_url, alert: 'ユーザー情報の更新に失敗しました'
Expand Down
18 changes: 18 additions & 0 deletions app/controllers/reports/unchecked_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

class Reports::UncheckedController < ApplicationController
PAGER_NUMBER = 25
before_action :require_admin_or_mentor!

def index
@reports = Report.list.page(params[:page]).per(PAGER_NUMBER)
@reports = @reports.unchecked.not_wip
render 'reports/index'
end

private

def require_admin_or_mentor!
redirect_to reports_path unless admin_or_mentor_login?
end
end
1 change: 0 additions & 1 deletion app/controllers/reports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class ReportsController < ApplicationController # rubocop:todo Metrics/ClassLeng
def index
@reports = Report.list.page(params[:page]).per(PAGER_NUMBER)
@reports = @reports.joins(:practices).where(practices: { id: params[:practice_id] }) if params[:practice_id].present?
@reports = @reports.unchecked.not_wip if params[:unchecked].present? && admin_or_mentor_login?
end

def show
Expand Down
4 changes: 2 additions & 2 deletions app/helpers/meta_tags_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def default_meta_tags
site: 'FBC',
reverse: true,
charset: 'utf-8',
description: '月額29,800円、全機能が使えるお試し期間付き。FBCは現場の即戦力になるためのスキルとプログラミングの楽しさを伝える、現役ソフトウェアエンジニアが考える理想のプログラミングスクールの実現に励んでいます。',
description: '月額32,780円(税込)、全機能が使えるお試し期間付き。FBCは現場の即戦力になるためのスキルとプログラミングの楽しさを伝える、現役ソフトウェアエンジニアが考える理想のプログラミングスクールの実現に励んでいます。',
viewport: 'width=device-width, initial-scale=1.0',
og: {
title: :title,
Expand All @@ -31,7 +31,7 @@ def default_meta_tags
def welcome_meta_tags
default_meta_tags.deep_merge({
title:,
description: '月額29,800円、全機能が使えるお試し期間付き。FBCは現場の即戦力になるためのスキルとプログラミングの楽しさを伝える、現役ソフトウェアエンジニアが考える理想のプログラミングスクールの実現に励んでいます。',
description: '月額32,780円(税込)、全機能が使えるお試し期間付き。FBCは現場の即戦力になるためのスキルとプログラミングの楽しさを伝える、現役ソフトウェアエンジニアが考える理想のプログラミングスクールの実現に励んでいます。',
og: {
title: title || 'FJORD BOOT CAMP(フィヨルドブートキャンプ)',
description: :description
Expand Down
1 change: 0 additions & 1 deletion app/javascript/initializeAnswer.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ export default function initializeAnswer(answer) {
if (window.confirm('本当に宜しいですか?')) {
deleteAnswer(answerId)
if (answerBadgeElement.classList.contains('correct-answer')) {
cancelBestAnswer(answerId, questionId)
const otherCancelBestAnswerButtons = document.querySelectorAll(
'.make-best-answer-button'
)
Expand Down
2 changes: 0 additions & 2 deletions app/javascript/markdown-initializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import MarkDownItContainerMessage from 'markdown-it-container-message'
import MarkDownItContainerDetails from 'markdown-it-container-details'
import MarkDownItLinkAttributes from 'markdown-it-link-attributes'
import MarkDownItContainerSpeak from 'markdown-it-container-speak'
import MarkdownItPurifier from 'markdown-it-purifier'
import ReplaceLinkToCard from 'replace-link-to-card'
import MarkDownItContainerFigure from 'markdown-it-container-figure'
import MarkdownItVimeo from 'markdown-it-vimeo'
Expand Down Expand Up @@ -62,7 +61,6 @@ export default class {
md.use(MarkDownItContainerFigure)
md.use(MarkdownItVimeo)
md.use(MarkdownItYoutube)
md.use(MarkdownItPurifier)
return md.render(text)
}
}
29 changes: 23 additions & 6 deletions app/javascript/stylesheets/lp/blocks/lp/_lp-price.sass
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@
font-size: .875rem
+media-breakpoint-down(sm)
font-size: .5625rem
&.is-total
gap: .25em
&.is-sm
justify-content: flex-start
+media-breakpoint-up(lg)
font-size: .5625em
.lp-price__tax
font-size: 1rem
font-size: 1.5rem
+media-breakpoint-only(md)
font-size: .5em
.lp-price__tax
font-size: .875rem
font-size: 1rem
+media-breakpoint-down(sm)
font-size: .375em
.lp-price__tax
font-size: .75rem
font-size: .875rem

.lp-price__label
+text-block(1.25em 1, 700)
Expand All @@ -40,6 +42,7 @@
display: flex
align-self: baseline
gap: .5em
flex-wrap: wrap

.lp-price__amount
+text-block(6.25em 1, 900)
Expand All @@ -51,14 +54,28 @@
align-self: baseline

.lp-price__details
+text-block(1.25em 1, 800)
+text-block(2.25em 1, 800)
display: flex
align-self: baseline
gap: .125em

.lp-price__separator
font-weight: 100
align-self: baseline

.lp-price__per-person
align-self: baseline

.lp-price__tax
font-size: 1.25em
align-self: baseline

.lp-price__total-value
display: flex
align-self: baseline
gap: .25em
font-size: 1.5em
font-weight: 600

.lp-price-note
+media-breakpoint-down(sm)
font-size: .75rem
line-height: 1.4
2 changes: 1 addition & 1 deletion app/javascript/stylesheets/lp/layouts/_l-cards.sass
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@

.l-cards__item-inner
flex: 0 0 40rem
max-width: 100%
max-width: calc(100vw - 2rem)
.a-card
margin-inline: 0
4 changes: 1 addition & 3 deletions app/javascript/textarea-initializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import CSRF from 'csrf'
import TextareaMarkdownLinkify from 'textarea-markdown-linkify'
import ReplaceLinkToCard from 'replace-link-to-card'
import MarkDownItContainerFigure from 'markdown-it-container-figure'
import MarkdownItPurifier from 'markdown-it-purifier'
import MarkdownItVimeo from 'markdown-it-vimeo'
import MarkdownItYoutube from 'markdown-it-youtube'

Expand Down Expand Up @@ -90,8 +89,7 @@ export default class {
MarkDownItContainerSpeak,
MarkDownItContainerFigure,
MarkdownItVimeo,
MarkdownItYoutube,
MarkdownItPurifier
MarkdownItYoutube
],
markdownOptions: MarkdownOption
})
Expand Down
1 change: 1 addition & 0 deletions app/models/announcement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Announcement < ApplicationRecord
include Reactionable
include WithAvatar
include Watchable
include Bookmarkable

enum target: {
all: 0,
Expand Down
2 changes: 2 additions & 0 deletions app/models/answer_cache_destroyer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
class AnswerCacheDestroyer
def call(payload)
_answer = payload[:answer]
action = payload[:action]
Cache.delete_not_solved_question_count
Rails.logger.info "[AnswerCacheDestroyer] #{action} Cache destroyed for unsolved question count."
end
end
3 changes: 2 additions & 1 deletion app/models/cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def delete_self_assigned_no_replied_product_count(user_id)

def not_solved_question_count
Rails.cache.fetch 'not_solved_question_count' do
Question.not_solved.count
Rails.logger.info '[CACHE MISS] Executing DB query for not_solved_question_count'
Question.not_solved.not_wip.count
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/comeback_notifier.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class ComebackNotifier
def call(payload)
def call(_name, _started, _finished, _unique_id, payload)
user = payload[:user]
User.admins_and_mentors.each do |admin_or_mentor|
ActivityDelivery.with(sender: user, receiver: admin_or_mentor).notify(:comebacked)
Expand Down
2 changes: 1 addition & 1 deletion app/models/correct_answer_notifier.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class CorrectAnswerNotifier
def call(payload)
def call(_name, _started, _finished, _unique_id, payload)
answer = payload[:answer]
notify_correct_answer(answer)
notify_to_chat(answer)
Expand Down
2 changes: 1 addition & 1 deletion app/models/graduation_notifier.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class GraduationNotifier
def call(payload)
def call(_name, _started, _finished, _unique_id, payload)
user = payload[:user]
User.mentor.each do |mentor|
ActivityDelivery.with(sender: user, receiver: mentor).notify(:graduated)
Expand Down
40 changes: 1 addition & 39 deletions app/models/grass.rb
Original file line number Diff line number Diff line change
@@ -1,45 +1,7 @@
# frozen_string_literal: true

class Grass
# rubocop:disable Metrics/MethodLength
def self.times(user, end_date)
start_date = end_date.prev_year.sunday
sql = <<~SQL
WITH series AS (
SELECT
date
FROM
generate_series(:start_date::DATE, :end_date, '1 day') AS series(date)
),
summary AS (
SELECT
reported_on AS date,
EXTRACT(epoch FROM SUM(finished_at - started_at)) / 60 / 60 AS total_hour
FROM
learning_times JOIN reports ON learning_times.report_id = reports.id
WHERE
reports.user_id = :user_id
GROUP BY
reported_on
ORDER BY
reported_on
)
SELECT
series.date AS date,
CASE
WHEN summary.total_hour > 6 THEN 4
WHEN 6 >= summary.total_hour AND summary.total_hour > 4 THEN 3
WHEN 4 >= summary.total_hour AND summary.total_hour > 2 THEN 2
WHEN 2 >= summary.total_hour AND summary.total_hour > 0 THEN 1
ELSE 0
END AS velocity
FROM
series
LEFT JOIN
summary ON series.date = summary.date
SQL

LearningTime.find_by_sql([sql, { start_date:, end_date:, user_id: user.id }])
GrassLearningTimeQuery.call(user, end_date)
end
# rubocop:enable Metrics/MethodLength
end
Loading