-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hxl stats view 145 147 #180
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great so far @leungant, and local tests of results are exciting! 🕺
Output is missing a single but quite key row, i.e. the HXL hashtags, but that should be a quick fix!
Also some notes on how we can refactor this a bit and make you fight with Rubocop less. Please bug me if you'd like to go through anything together, pair or delegate some tasks to me. Thanks again for jumping on this so quick! :D
@@ -0,0 +1,3 @@ | |||
# Place all the behaviors and hooks related to the matching controller here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure these were scaffolded so we can get rid of any unused files. Will mark any below we can remove with an amazing hashtag like #ScaffoldScrap! ✌️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed!
app/assets/stylesheets/hxlstats.scss
Outdated
@@ -0,0 +1,3 @@ | |||
// Place all the styles related to the hxlstats controller here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#ScaffoldScrap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed!
@@ -0,0 +1,69 @@ | |||
class HxlstatsController < ApplicationController | |||
# Class to collect and aggregate data to parse to the HXL proxy/endpoint | |||
def new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#ScaffoldScrap the new
, create
and index
methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed!
app/decorators/hxlstats_decorator.rb
Outdated
@@ -0,0 +1,13 @@ | |||
class HxlstatsDecorator < Draper::Decorator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#ScaffoldScrap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok!
app/helpers/hxlstats_helper.rb
Outdated
@@ -0,0 +1,2 @@ | |||
module HxlstatsHelper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#ScaffoldScrap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok!
app/views/hxlstats/show.html.erb
Outdated
@@ -0,0 +1,11 @@ | |||
<%# country moodrating stage_of_journey count gender1 gender2 gender3 age1 age2 age3 age4 %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can remove comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
app/models/hxl_stats_view.rb
Outdated
true | ||
end | ||
|
||
def self.to_csv # Not actually used! But could be convenient for data analysts. was %w{ } but rubocop! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this one. Ideally for any of our HDX integrations, this app will only act as an input source for the HXL Proxy, and any exports / filtering / analysis will occur in the HXL Proxy itself. Better to keep things minimal rather than keep methods in that most likely won't be used but would still need maintenance with view changes etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed!
app/views/hxlstats/index.html.erb
Outdated
@@ -0,0 +1,23 @@ | |||
<h1> hello </h1> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is used for anything? If not, we can remove too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed!
|
||
# rubocop:disable MethodLength | ||
def show | ||
# Prepare output results array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working all this grouping logic out! Tested locally and so exciting to finally have an endpoint with data.
👏
A couple of suggestions for cleaning it up a little bit, keeping Rubocop happy and also making testing a bit easier:
-
Move everything from line 12 to 59, i.e. creating the
results
array, out of the controller. One suggestion is making it a public class method in theHxlStatsView
model, likeresults_by_emotional_state
. Or another option is to create a new service object / Plain Old Ruby Object (PORO) likedrawings_by_emotional_state_query
in a new directory likeapp/models/queries
, to save from bloating the model also. -
Once the logic is extracted, you can extract each block below into its own private method, which will be easier to read / maintain / identify the purpose of each block by explicit method naming - this last bit also makes it less necessary to feel like you need to prefix comments on each block, as the methods should tell the tale. AND this will make you fight with Rubocop less and remove the need for
# rubocop:disable MethodLength
I see stuck up there :) -
Once the logic is extracted outside of the controller, testing becomes a bit clearer/easier. For controller specs (not detrimental if these are missing btw), you can set up just a couple of tests that focus on asserting the response of the
show
method (i.e. returns html if normal request, returns data if json specified) and just stub the value of the results generation method. Then you can focus on testing the actual results generation in the model / service object, using FactoryGirl for HxlStatsView.
As always, buzz or let me know if you'd like to go through this or pair!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly done, in the most minimal way possible ;) thanks for the comments!
def show | ||
# Prepare output results array | ||
@results = [] | ||
@results.append(["Emotional State", "Stage of journey", "Country drawn in", "Total children affected", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As well as these headings, we need another row with the HXL tagged headings (e.g. #impact +indicator +code
) as per the spreadsheet. Both the headings and HXL headings can probably live as constants in the HxlStatsView model, or service object as suggested above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When to have spaces in the tags and when not?
Deferring named constants to when we start using in multiple places.
Appreciate any further thoughts! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Have added a few more comments. Let me know if you'd like to go over anything. 🍍
app/views/hxlstats/new.html.haml
Outdated
@@ -0,0 +1,2 @@ | |||
%h1 Hxlstats#new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove this too as we don't have a new
route set up anymore.
app/models/hxl_stats_view.rb
Outdated
@@ -0,0 +1,109 @@ | |||
HXLSTATS_COLUMN_HEADERS = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason line 1 - 66 are outside of the class? Let's pop them back in. The constants (in this case HXLSTATS_COLUMN_HEADERS
and HXL_STATS_TAGS
) usually would live right at the top of the class (but inside it), and the methods get_counts_by_gender
and get_counts_by_age
can be private methods within the class. I.e. stick them under the keyword private
.
app/models/hxl_stats_view.rb
Outdated
def get_counts_by_gender(hxlstatsmajorgroup) | ||
# Group by gender and aggregate | ||
gender_totals = Hash.new(0) | ||
@hxlstatsgroupsgender = hxlstatsmajorgroup.group_by(&:gender) # shorthand for { |hxlstat| hxlstat.gender } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for an instance variable here as it will only be used locally within the method. So @hxlstatsgroupsgender
-> hxlstatsgroupsgender
app/models/hxl_stats_view.rb
Outdated
# rubocop:disable MethodLength | ||
def get_counts_by_age(hxlstatsmajorgroup) | ||
# Processing Age aggregations | ||
@hxlstatsgroupsage = hxlstatsmajorgroup.group_by(&:age) # shorthand for { |hxlstat| hxlstat.age } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for an instance variable here as it will only be used locally within the method. So @hxlstatsgroupsage
-> hxlstatsgroupsage
app/models/hxl_stats_view.rb
Outdated
|
||
def self.hxl_stats_counts | ||
results = [] | ||
@hxlstats = HxlStatsView.all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same re replacing instance variable with local variable - @hxlstats -> hxlstats
app/models/hxl_stats_view.rb
Outdated
@hxlstats = HxlStatsView.all | ||
|
||
# First group by major categories, before aggregating independently for gender and age and recombining | ||
@hxlstatsgroups = @hxlstats.group_by { |hxlstat| [hxlstat.mood_rating, hxlstat.stage_of_journey, hxlstat.country] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same re replacing instance variable with local variable - @hxlstatsgroups -> hxlstatsgroups
app/models/hxl_stats_view.rb
Outdated
end | ||
|
||
def self.results_by_emotional_state | ||
@results = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same re replacing instance variable with local variable - @results -> results.
app/models/hxl_stats_view.rb
Outdated
true | ||
end | ||
|
||
@gender_lookup = { 'male' => 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already conveniently have the gender mapping available as an enum in the Drawing model. We can reference that in the hxl_stats_counts
method rather than create a new hash here to avoid repetition.
I.e.
Drawing.genders
# Response => {"not_specified"=>0, "female"=>1, "male"=>2, "other"=>3}
Drawing.genders["female"]
# Response => 1
What value does hxl_stats_counts
return for drawings with 'other' i.e. 3? We might need to update the logic so anything with 0 or 3 falls into 'other' for now.
app/models/hxl_stats_view.rb
Outdated
"#affected+children+age_under5", | ||
"#affected+children+age_18plus"].freeze | ||
|
||
private_class_method :get_counts_by_gender |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah the 'private class method' debacle! Yeah, wish this was a bit less painful in Rails.
We can use the private_class_method
option, however this needs to be declared after the methods themselves or Rails complains. A second, think less PITA option, is to use: class << self
around all of the methods. It's usually clearer to use the def self.method_name
style to define class methods, however when all of the methods within a class are public, wrapping everything in class << self
is fine and more readable. Also, this way you can use the private
keyword as normal.
I.e.
class << self
def results_by_emotional_state
...
end
private
def hxl_stats_counts
...
end
def get_counts_by_gender(hxlstatsmajorgroup)
...
end
def get_counts_by_age(hxlstatsmajorgroup)
...
end
end
app/models/hxl_stats_view.rb
Outdated
gender_totals # hash of key gender to v counts | ||
end | ||
|
||
# rubocop:disable MethodLength |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rubocop doesn't need this methodlength exception for this method.
Also, you can run rubocop locally without having to wait for CI to build. Just cd into the root of the project and type rubocop
. Or you can do it for a specific file by typing the path after it.
@@ -0,0 +1,24 @@ | |||
class CreateHxlStatsView < ActiveRecord::Migration | |||
def up | |||
execute <<-SQL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Run locally, some rubocop bits:
db/migrate/20170601162837_create_hxl_stats_view.rb:2:9: C: Trailing whitespace detected.
def up
^
db/migrate/20170601162837_create_hxl_stats_view.rb:5:33: C: Trailing whitespace detected.
SELECT d.id AS drawing_id,
^
db/migrate/20170601162837_create_hxl_stats_view.rb:8:29: C: Trailing whitespace detected.
o.id AS org_id,
^^
db/migrate/20170601162837_create_hxl_stats_view.rb:11:29: C: Trailing whitespace detected.
users u,
^
db/migrate/20170601162837_create_hxl_stats_view.rb:24:1: C: 1 trailing blank lines detected.
…unknown age groups.
@leungant I reviewed the last couple of changes and QA'ed them locally. Had to add an extra 'unknown ages' column to the spreadsheet and HXL logic as age isn't compulsory in the form. All else looking fine so will get this merged! |
Change description:
Addresses #145 and #147:
http://.../hxlstats and http://.../hxlstats.json now show array of arrays conforming to Google spreadsheet
Any pointers/assistance much appreciated as I am a bit unfamiliar with the testing requirements.
Instructions:
or
will bring VERSION up to 20170601162837
The view is available at http://.../hxlstats.json
Sample output
.json: