Skip to content

Commit

Permalink
Reports API (#7)
Browse files Browse the repository at this point in the history
* Separate contributor's guide; Document reports api endpoints

* Write test

* Re-write test

* Add jbuilder json views

* Give credit h/t SO

* Prevent data leakage by cleaning the db before this test.

* Test reports view

* Change search endpoint to regular index endpoint, clean code, refactor

* Re-write, and can now filter on one or both params, which is actually good - more robust
  • Loading branch information
s2t2 authored Oct 17, 2017
1 parent 45fbf83 commit 50e3745
Show file tree
Hide file tree
Showing 16 changed files with 212 additions and 45 deletions.
54 changes: 54 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Contributor's Guide

## Installation

Install Ruby 2.2.5, probably using rbenv.

Install PostgreSQL, probably using homebrew.

Download the source code:

```sh
git clone [email protected]:data-creative/law-schools-reporter.git
cd law-schools-reporter/
bin/setup # installs package dependencies and preps the database
```

## Setup

Seed the database with schools and employment data.

```sh
bundle exec rake db:seed
```

## Developing

Run interactive development console:

```shell
bin/rails c
```

Serve locally:

```shell
bin/rails s
```

## Testing

Run tests:

```shell
bundle exec rspec spec/
```

## Deploying

Gain access to the `law-school-reporter` heroku application. Then deploy:

```sh
git push heroku master # or ...
git push heroku mybranch:master
```
7 changes: 7 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Credits, Notes, and Reference

### Previous Projects

+ [ToneBase API](https://github.com/data-creative/tonebase-api/)

### Rails

+ [Routing via non-id attribute](https://stackoverflow.com/a/20773186)
+ [Linking to show page and passing params](https://stackoverflow.com/a/8477394)
+ [Linking to show page and passing the resource](https://stackoverflow.com/a/21708346/)
+ [Rendering JSON within a controller spec](https://stackoverflow.com/a/10038777/670433)
+ [Preventing data leakage across tests](https://stackoverflow.com/questions/9927671/rspec-database-cleaner-not-cleaning-correctly#comment15607311_9927671)
47 changes: 7 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,15 @@
# Law Schools Reporter

## Installation
## API Endpoints

Install Ruby 2.2.5, probably using rbenv.
### Schools

Install PostgreSQL, probably using homebrew.
+ GET `api/v1/schools`
+ GET `api/v1/schools/:uuid`

Download the source code:
### Employment Reports

```sh
git clone [email protected]:data-creative/law-schools-reporter.git
cd law-schools-reporter/
bin/setup # installs package dependencies and preps the database
```

## Usage

Seed the database with schools and employment data.

```sh
bundle exec rake db:seed
```

## Developing

Serve locally:

```shell
rails s
```

Run tests:

```shell
bundle exec rspec spec/
```

## Deploying

Gain access to the `law-school-reporter` heroku application. Then deploy:

```sh
git push heroku master # or ...
git push heroku mybranch:master
```
+ GET `api/v1/employment_reports`
+ GET `api/v1/employment_reports?years[]=2016&years[]=2017&schools[]=25&schools[]=36`

## [License](/LICENSE.md)
22 changes: 22 additions & 0 deletions app/controllers/api/v1/employment_reports_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Api::V1::EmploymentReportsController < ApplicationController

# GET /api/v1/employment_reports
# GET /api/v1/employment_reports.json
#
# GET /api/v1/employment_reports?years[]=2016&years[]=2017&schools[]=96&schools[]=132
def index
@reports = EmploymentReport.all
@reports = @reports.where(year: years) if params[:years]
@reports = @reports.where(school_uuid: school_uuids) if params[:schools]
end

private

def school_uuids
params.require(:schools)
end

def years
params.require(:years)
end
end
8 changes: 8 additions & 0 deletions app/models/employment_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@ class EmploymentReport < ApplicationRecord
def self.with_grads
where("total_grads <> 0")
end

def reported_school_name
school_name
end

#def real_school_name
# school.try(:name)
#end
end
9 changes: 9 additions & 0 deletions app/views/api/v1/employment_reports/_report.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
json.extract! report,
:school_uuid,
:reported_school_name,
:year,
:total_grads,
:total_employed,
:employment_types,
:employment_statuses,
:employment_locations
1 change: 1 addition & 0 deletions app/views/api/v1/employment_reports/index.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
json.array! @reports, partial: 'api/v1/employment_reports/report', as: :report
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
namespace :api do
namespace :v1, defaults: {format: :json} do
resources :schools, param: :uuid, only: [:index, :show]

resources :employment_reports, only: [:index]
end
end
end
49 changes: 49 additions & 0 deletions spec/controllers/api/v1/employment_reports_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'rails_helper'
require_relative "../../../support/api/v1/response"

RSpec.describe Api::V1::EmploymentReportsController, type: :controller do
let(:valid_session){ {} }

before(:all) do
create(:school, :with_reports, uuid: 98, report_years: [2015, 2016, 2017]) # 2 matches
create(:school, :with_reports, uuid: 76, report_years: [2011, 2012, 2016]) # 1 match
create(:school, :with_reports, uuid: 23, report_years: [2015, 2016, 2017]) # 0 matches
end

describe "GET #index", "when not given any search parameters" do
let(:response){ get(:index, params: {format: 'json'}, session: valid_session) }

render_views # avoids JSON parsing error

it "returns a success response" do
expect(response).to be_success
end

it "should exclude non-matching resources" do # ensures proper configuration of this test :-)
expect(EmploymentReport.count).to eql(3 + 3 + 3)
end

it "should include all resources" do
expect(parsed_response.count).to eql(3 + 3 + 3)
end
end

describe "GET #index", "when given both search parameters" do
let(:search_params){ {years: ["2016", "2017"], schools: ["98", "76"]} }
let(:response){ get(:index, params: search_params.merge(format: 'json'), session: valid_session) }

render_views # avoids JSON parsing error

it "returns a success response" do
expect(response).to be_success
end

it "should exclude non-matching resources" do # ensures proper configuration of this test :-)
expect(EmploymentReport.count).to eql(3 + 3 + 3)
end

it "should include matching resources" do
expect(parsed_response.count).to eql(2 + 1 + 0)
end
end
end
12 changes: 12 additions & 0 deletions spec/factories/schools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,16 @@
sequence(:url){|n| "http://law.my-school-#{n}.edu" }
reports_url "N/A - NOT FOUND"
end

trait :with_reports do
transient do
report_years [2015, 2016, 2017]
end

after(:create) do |school, evaluator|
evaluator.report_years.each do |year|
create(:employment_report, school_uuid: school.uuid, year: year)
end
end
end
end
2 changes: 2 additions & 0 deletions spec/jobs/report_seeder_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
let(:years) { mock_batches.map{|k,_| k.to_i} }

before(:all) do
DatabaseCleaner.clean_with(:truncation) # avoid dirty database errors.

RSpec::Mocks.with_temporary_scope do # use this technique to allow stubbing within a before(:all) hook
create(:school, long_name: "AKRON, UNIVERSITY OF") # to be matched with report via long_name
create(:school, name: "BOSTON COLLEGE") # to be matched with report via name
Expand Down
9 changes: 9 additions & 0 deletions spec/support/api/v1/response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Api::V1::Response
def parsed_response
JSON.parse(response.body)
end
end

RSpec.configure do |config|
config.include Api::V1::Response
end
2 changes: 1 addition & 1 deletion spec/support/api/v1/view.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Api::V1::View
def parsed_response
def parsed_view
JSON.parse(rendered)
end
end
Expand Down
25 changes: 25 additions & 0 deletions spec/views/api/v1/employment_reports/index_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require 'rails_helper'
require_relative "../../../../support/api/v1/view"

RSpec.describe "api/v1/employment_reports/index", type: :view do
before(:each) do
@reports = assign(:reports, [ create(:employment_report), create(:employment_report) ] )
render
end

it "renders a list of reports" do
expect(parsed_view.count).to eql(@reports.count)
expect(parsed_view.first.keys).to match_array(
[
"school_uuid",
"reported_school_name",
"year",
"total_grads",
"total_employed",
"employment_types",
"employment_statuses",
"employment_locations"
]
)
end
end
4 changes: 2 additions & 2 deletions spec/views/api/v1/schools/index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
end

it "renders a list of schools" do
expect(parsed_response.count).to eql(@schools.count)
expect(parsed_response.first.keys).to match_array(["uuid", "year_founded", "long_name", "name", "url", "reports_url"]) # , "report_years"
expect(parsed_view.count).to eql(@schools.count)
expect(parsed_view.first.keys).to match_array(["uuid", "year_founded", "long_name", "name", "url", "reports_url"]) # , "report_years"
end
end
4 changes: 2 additions & 2 deletions spec/views/api/v1/schools/show_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
end

it "renders the requested school" do
expect(parsed_response.keys).to match_array(["uuid", "year_founded", "long_name", "name", "url", "reports_url"]) # , "report_years"
expect(parsed_response["uuid"]).to eql(@school.uuid)
expect(parsed_view.keys).to match_array(["uuid", "year_founded", "long_name", "name", "url", "reports_url"]) # , "report_years"
expect(parsed_view["uuid"]).to eql(@school.uuid)
end
end

0 comments on commit 50e3745

Please sign in to comment.