Skip to content

Commit

Permalink
re introduce using Prophet..
Browse files Browse the repository at this point in the history
  • Loading branch information
epugh committed Jan 19, 2024
1 parent 1a88aa0 commit 9e591fa
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 82 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
33 changes: 33 additions & 0 deletions app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ def show
@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{ |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 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

end

@most_recent_books = []
@lookup_for_books = {}
Expand Down
82 changes: 22 additions & 60 deletions app/views/home/_case_summary.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,70 +8,32 @@
<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 9e591fa

Please sign in to comment.