Skip to content

Commit

Permalink
Merge pull request #2506 from studentinsights/feature/heggerty-processor
Browse files Browse the repository at this point in the history
Reading: Add Heggerty intervention processor, show in profile
  • Loading branch information
kevinrobinson committed Jun 12, 2019
2 parents b1adae8 + 20bd113 commit 9422f09
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 15 deletions.
35 changes: 21 additions & 14 deletions app/assets/javascripts/reader_profile/ReaderProfileJune.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,20 +134,7 @@ export default class ReaderProfileJune extends React.Component {
}
/>
}
intervention={
<Suggestion
text="SPS Heggerty"
dialog={
<div>
<Why>
<p>The SPS Heggerty intervention is short 1:1 phonological awareness program, intended for 4-8 week intervention cycles.</p>
<p>You can determine where to start with the SPS PAST, and get a sense of progress over a cycle with using the PAST as a post-test.</p>
</Why>
<External style={{display: 'block'}} href="https://www.dropbox.com/s/u6e1ek42b1gzun8/SPS%20Heggerty%20PA%20Intervention.docx?dl=0">SPS Heggerty Intervention</External>
</div>
}
/>
}
intervention={this.renderPhonologicalIntervention()}
screener={this.renderChipForDibels('blending', DIBELS_PSF)}
/>,
<Sub name="deleting" />,
Expand Down Expand Up @@ -209,6 +196,26 @@ export default class ReaderProfileJune extends React.Component {
);
}

renderPhonologicalIntervention() {
const chips = this.renderChipsForServices([601, 602, 603, 604]);
if (chips.length > 0) return <MultipleChips chips={chips} />;

return (
<Suggestion
text="SPS Heggerty"
dialog={
<div>
<Why>
<p>The SPS Heggerty intervention is short 1:1 phonological awareness program, intended for 4-8 week intervention cycles.</p>
<p>You can determine where to start with the SPS PAST, and get a sense of progress over a cycle with using the PAST as a post-test.</p>
</Why>
<External style={{display: 'block'}} href="https://www.dropbox.com/s/u6e1ek42b1gzun8/SPS%20Heggerty%20PA%20Intervention.docx?dl=0">SPS Heggerty Intervention</External>
</div>
}
/>
);
}

renderChipForIEP(words, iepLunrIndex) {
if (iepLunrIndex === null) return null;

Expand Down
4 changes: 4 additions & 0 deletions app/importers/helpers/import_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ def parse_sheets_est_timestamp(string_eastern_without_timezone, options = {})
DateTime.strptime(est_with_timezeone, '%m/%d/%Y %k:%M:%S %Z').new_offset(0)
end

def parse_human_date_text(string)
Date.strptime(string, '%m/%d/%Y') rescue nil
end

def count_valid_row
@valid_rows_count += 1
end
Expand Down
135 changes: 135 additions & 0 deletions app/importers/reading/heggerty_processor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# This is for manually importing a large spreadsheet, with historical data
# about students across a range of reading assessments.
# It outputs JSON to the console.
#
# Usage:
# file_text = <<EOD
# ...
# EOD
# educator = Educator.find_by_login_name('...')
# upload_filename = '...'
# output = HeggertyProcessor.new(educator.id, upload_filename).import(file_text);nil
class HeggertyProcessor
def initialize(uploaded_by_educator_id, upload_filename, options = {})
@uploaded_by_educator_id = uploaded_by_educator_id
@upload_filename = upload_filename

@log = options.fetch(:log, Rails.env.test? ? LogHelper::Redirect.instance.file : STDOUT)
@time_now = options.fetch(:time_now, Time.now)
@matcher = options.fetch(:matcher, ImportMatcher.new)

reset_counters!
end

def import(file_text, options = {})
reset_counters!

# create service upload record
@service_upload = ServiceUpload.create!({
uploaded_by_educator_id: @uploaded_by_educator_id,
file_name: @upload_filename,
})

# parse
rows = []
StreamingCsvTransformer.from_text(@log, file_text).each_with_index do |row, index|
flattened_rows = flat_map_rows(row, index)
next if flattened_rows.nil?

rows += flattened_rows
@matcher.count_valid_row
end
log "matcher#stats: #{@matcher.stats}"
log "HeggertyProcessor#stats: #{stats}"

[rows, stats]
end

private
def reset_counters!
@service_upload = nil

@invalid_student_name_count = 0
@invalid_student_names_list = []
@invalid_educator_count = 0
@invalid_service_type_id_count = 0
@invalid_date_started_count = 0
@processed_rows_count = 0
@valid_student_name = 0
end

def stats
{
valid_student_name: @valid_student_name,
invalid_student_name_count: @invalid_student_name_count,
invalid_educator_count: @invalid_educator_count,
invalid_service_type_id_count: @invalid_service_type_id_count,
invalid_date_started_count: @invalid_date_started_count,
processed_rows_count: @processed_rows_count
}
end

def flat_map_rows(row, index)
# match student by name
student_last_first = row['Student full name (Last names, first names)']
fuzzy_match = FuzzyStudentMatcher.new.match_from_last_first(student_last_first)
if fuzzy_match.nil?
@invalid_student_name_count += 1
@invalid_student_names_list << student_last_first
return nil
end
@valid_student_name += 1
student_id = fuzzy_match[:student_id]

# match educator by email
educator_login_name = row['Evaluator username (eg, from email address)']
educator = educator_login_name.present? ? PerDistrict.new.find_educator_by_login_text(educator_login_name) : nil
if educator.nil?
@invalid_educator_count += 1
return nil
end

# fields
service_type_id = {
1 => 601,
5 => 602,
9 => 603,
13 => 604
}[row['Heggerty start week'].to_i]
if service_type_id.nil?
@invalid_service_type_id_count += 1
return nil
end

date_started = @matcher.parse_human_date_text(row['date_started'])
if date_started.nil?
@invalid_date_started_count += 1
return nil
end

estimated_end_date = @matcher.parse_human_date_text(row['estimated_end_date'])
discontinued_at = @matcher.parse_human_date_text(row['discontinued_at'])
discontinued_by_educator_id = discontinued_at.nil? ? nil : educator.id

# describe
attrs = {
service_upload: @service_upload,
student_id: student_id,
recorded_by_educator_id: educator.id,
provided_by_educator_name: educator.full_name,
service_type_id: service_type_id,
recorded_at: @time_now,
date_started: date_started,
estimated_end_date: estimated_end_date,
discontinued_at: discontinued_at,
discontinued_by_educator_id: discontinued_by_educator_id
}
@processed_rows_count += 1
[attrs]
end

def log(msg)
text = if msg.class == String then msg else JSON.pretty_generate(msg) end
@log.puts "HeggertyProcessor: #{text}"
end
end
11 changes: 10 additions & 1 deletion app/models/service_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def self.seed_for_all_districts
{ id: 511, name: 'Afterschool Tutoring' },
{ id: 512, name: 'Freedom School' },
{ id: 513, name: 'Community Schools' },
{ id: 514, name: 'X-Block' },
{ id: 514, name: 'X-Block' }
])
end

Expand All @@ -38,4 +38,13 @@ def self.add_somerville_summer_2018_service_types
{ id: 518, name: 'Focused Math Intervention' } # An 8 week cycle, 4 days a week focused on filling in math gaps in a small group setting using a research-based mathematics intervention called Focused Math Intervention. Sessions take place during X-Block and intervention focuses on previous year’s standards that have not yet been met.
])
end

def self.add_somerville_heggerty_service_types
ServiceType.create!([
{ id: 601, name: 'SPS Heggerty, week 1' },
{ id: 602, name: 'SPS Heggerty, week 5' },
{ id: 603, name: 'SPS Heggerty, week 9' },
{ id: 604, name: 'SPS Heggerty, week 13' }
])
end
end

0 comments on commit 9422f09

Please sign in to comment.