Skip to content

Commit

Permalink
Merge branch 'main' into track_book_view_behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
epugh committed Jan 19, 2024
2 parents 668b0d5 + 1f29c5b commit 5598d4f
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 85 deletions.
3 changes: 0 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,3 @@ GOOGLE_CLIENT_SECRET=your_google_client_secret

KEYCLOAK_REALM=quepid
KEYCLOAK_SITE=http://keycloak:9080

# Whether or not to do analytics for interesting events in Case Scores
QUEPID_PROPHET_ANALYTICS=true
46 changes: 44 additions & 2 deletions app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,50 @@
# frozen_string_literal: true

class HomeController < ApplicationController

# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def show
# @cases = @current_user.cases.not_archived.includes([ :scores ])
@cases = @current_user.cases_involved_with.not_archived.with_counts

@most_recent_cases = @current_user.cases_involved_with.not_archived.recent.limit(4).with_counts.sort_by(&:case_name)

# Run the prophet!
@prophet_case_data = {}
@most_recent_cases.each do |kase|
data = kase.scores.sampled(kase.id, 25).collect do |score|
{ ds: score.created_at.to_date.to_fs(:db), y: score.score, datetime: score.created_at.to_date }
end.uniq
# warning! blunt filter below!
data = data.uniq { |h| h[:ds] }
data = data.map { |h| h.transform_keys(&:to_s) }

do_changepoints = data.length >= 3 # need at least 3...

next unless do_changepoints

df = Rover::DataFrame.new(data)
m = Prophet.new
m.fit(df)

last_changepoint = DateTime.parse(m.changepoints.last.to_s)
initial = data.find { |h| h['datetime'].all_day.overlaps?(last_changepoint.all_day) }['y']
final = kase.scores.last_one.score
change = 100 * (final - initial) / initial

vega_data = data.map { |d| { x: d['ds'], y: d['y'] } }

@prophet_case_data[kase.id] = {
initial: initial,
final: final,
change: change,
last_changepoint: last_changepoint,
vega_data: vega_data,
}
end

@most_recent_books = []
@lookup_for_books = {}
# we really should be looking at when judgements were made, not just book updates.
Expand All @@ -26,5 +64,9 @@ def show
@cases
# @grouped_cases = candidate_cases.group_by { |kase| kase.case_name.split(':').first }
# @grouped_cases = @grouped_cases.select { |_key, value| value.count > 1 }
end
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
end
84 changes: 23 additions & 61 deletions app/views/home/_case_summary.html.erb
Original file line number Diff line number Diff line change
@@ -1,77 +1,39 @@
<div class="col-12 col-md-6 mb-4 mb-lg-0 col-lg-3">
<div class="card">
<h5 class="card-header">
<h5 class="card-header text-truncate">
<%= link_to case_title(kase), case_core_path(kase, kase.last_try_number) %>
</h5>

<div class="card-body">
<h5 class="card-title"><%= kase.last_score.score unless kase.scores.empty? %> <%= kase.scorer.name %></h5>
<p class="card-text"><%= kase.created_at.to_date.to_fs(:short) %> - <%= kase.last_score.updated_at.to_date.to_fs(:short) unless kase.scores.empty?%>
</p>
<!-- Look at https://github.com/ankane/prophet-ruby -->
<%
data = kase.scores.sampled(kase.id,100).collect{ |score| {ds: score.created_at.to_date.to_fs(:db), y: score.score, datetime: score.created_at.to_date } }.uniq
# warning! blunt filter below!
data = data.uniq { |h| h[:ds] }
data = data.map {|h| h.transform_keys(&:to_s) }

do_changepoints = data.length >= 3 ? true : false # need at least 3...

if Rails.application.config.quepid_prophet_analytics && do_changepoints
df = Rover::DataFrame.new(data)
m = Prophet.new()
m.fit(df)
%>
<!-- Data for Prophet
<%= data.to_csv %>
<%= m.changepoints.last %>
<%
last_changepoint = DateTime.parse(m.changepoints.last.to_s)
initial = data.find{ |h| h['datetime'].all_day.overlaps?(last_changepoint.all_day)}["y"]
final = kase.scores.last_one.score
%>
data:
<%= data.find{ |h| h['datetime'].all_day.overlaps?(last_changepoint.all_day)} %>
<%= initial %>
<%= final %>
<%
change = 100 * (final - initial) / initial
%>
<%= change %>
-->
<% if change > 0 %>
<% if change.positive? %>
<p class="card-text text-success"><%= number_to_percentage(change, precision:0) %> increase since <%=time_ago_in_words(last_changepoint) %> ago</p>
prophet_data = @prophet_case_data[kase.id]
if prophet_data
if prophet_data[:change] > 0 %>
<% if prophet_data[:change].positive? %>
<p class="card-text text-success"><%= number_to_percentage(prophet_data[:change] , precision:0) %> increase since <%=time_ago_in_words(prophet_data[:last_changepoint]) %> ago</p>
<% else %>
<p class="card-text text-danger"><%= number_to_percentage(change, precision:0) %> decrease since <%=time_ago_in_words(last_changepoint) %> ago</p>
<p class="card-text text-danger"><%= number_to_percentage(prophet_data[:change] , precision:0) %> decrease since <%=time_ago_in_words(prophet_data[:last_changepoint]) %> ago</p>
<% end %>
<% end %>
<% end # if do_changepoint %>
<%
# look at https://github.com/ankane/prophet-ruby to remove outliers.
data = kase.scores.sampled(kase.id, 100).collect{ |score| {x: score.created_at.to_date.to_fs(:db), y: score.score } }.uniq
# warning! blunt filter below!
data = data.uniq { |h| h[:x] }
%>
<!-- Data for Chart
<%= data %>
-->



<%= Vega.lite
.data(data)
.mark(type: "line", tooltip: true, interpolate: "cardinal", point: {size: 60})
.encoding(
x: {field: "x", type: "temporal", scale: {type: "utc"}, axis: {format: "%b %e"}},
y: {field: "y", type: "quantitative"}
)
.height(60)
.config(axis: {title: nil, labelFontSize: 12}) %>
<!-- Data for Chart
<%= prophet_data[:data] %>
-->


<%= Vega.lite
.data(prophet_data[:vega_data])
.mark(type: "line", tooltip: true, interpolate: "cardinal", point: {size: 60})
.encoding(
x: {field: "x", type: "temporal", scale: {type: "utc"}, axis: {format: "%b %e"}},
y: {field: "y", type: "quantitative"}
)
.height(60)
.config(axis: {title: nil, labelFontSize: 12}) %>
<% end # if prophet_data %>

</div>
</div>
</div>
2 changes: 0 additions & 2 deletions app/views/home/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ https://getbootstrap.com/docs/5.2/examples/navbars-offcanvas/# for example of of
</div>

<div class="row my-4">
<% if false %>
<%= render partial: "case_summary", collection: @most_recent_cases, as: :kase %>
<% end %>
</div>

<div class="row my-4">
Expand Down
5 changes: 0 additions & 5 deletions config/initializers/customize_quepid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,3 @@

# == Disable redirecting to match the TLS setting
Rails.application.config.redirect_to_match_search_engine_tls = ENV.fetch('REDIRECT_TO_MATCH_SEARCH_ENGINE_TLS', true)

# == Prophet Analytics Control
# Prophet tells you interesting things about time series curves and is used on the homepage.
# It may consume too much memory for your environment, and you may need to disable it.
Rails.application.config.quepid_prophet_analytics = bool.deserialize(ENV.fetch('QUEPID_PROPHET_ANALYTICS', true))
1 change: 0 additions & 1 deletion docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ services:
- EMAIL_PROVIDER=
- EMAIL_SENDER=
- QUERY_LIST_SORTABLE=true
- QUEPID_PROPHET_ANALYTICS=true
command: "foreman s -f Procfile"
ports:
# - 80:3000 # Map to port 80 for outside users when you are not using Nginx
Expand Down
11 changes: 0 additions & 11 deletions docs/operating_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,6 @@ the file `Procfile`

Want to monitor if Quepid is behaving? Just monitor `/healthcheck`, and you will get 200 status codes from a healthy Quepid, and 503 if not. The JSON output is `{"code":200,"status":{"database":"OK","migrations":"OK"}}`.

## Analytics Settings

We use the [Prophet.rb](https://github.com/ankane/prophet-ruby) library to decide when interesting things happen in our case scores on the homepage.
This library may use too much memory for your deploy, and can be disabled.

You can disable this behavior by setting the follow `ENV` var:

```
QUEPID_PROPHET_ANALYTICS=false
```

## Troubleshoot Your Deploy

When errors occur, Quepid logs them and shows a generic page.
Expand Down

0 comments on commit 5598d4f

Please sign in to comment.