<commit_before>class SchoolsController < ApplicationController
  include StudentsQueryHelper

  before_action :set_school, :authorize_for_school

  def show
    authorized_students = authorized_students_for_overview(@school)

    student_hashes = log_timing('schools#show student_hashes') do
      load_precomputed_student_hashes(Time.now, authorized_students.map(&:id))
    end

    merged_student_hashes = log_timing('schools#show merge_mutable_fields_for_slicing') do
      merge_mutable_fields_for_slicing(student_hashes)
    end

    @serialized_data = {
      students: merged_student_hashes,
      school: @school,
      current_educator: current_educator,
      constant_indexes: constant_indexes
    }
    render 'shared/serialized_data'
  end

  def csv
    authorized_students = @school.students.active
    csv_string = StudentsSpreadsheet.new.csv_string(authorized_students, @school)
    filename = "#{@school.name} - Exported #{Time.now.strftime("%Y-%m-%d")}.csv"
    send_data(csv_string, {
      type: Mime[:csv],
      disposition: "attachment; filename='#{filename}"
    })
  end

  def school_administrator_dashboard
    unless current_educator.admin == true
      redirect_to not_authorized_path and return #TodDo: determine whether there's a more appropriate action here
    end

    active_students = students_for_dashboard(@school)

    dashboard_students = active_students.map { |student| individual_student_dashboard_data(student) }

    @serialized_data = {
      absences: dashboard_students.to_json
    }
    render 'shared/serialized_data'
  end

  private

  # This should always find a record, but if it doesn't we fall back to the
  # raw query.
  # Results an array of student_hashes.
  def load_precomputed_student_hashes(time_now, authorized_student_ids)
    begin
      # the precompute job runs at 8am UTC, and takes a few minutes to run,
      # so keep a buffer that makes sure the import task and precompute job
      # can finish before cutting over to the next day.
      query_time = time_now - 9.hours
      key = PrecomputedQueryDoc.precomputed_student_hashes_key(query_time, authorized_student_ids)
      logger.warn "load_precomputed_student_hashes querying key: #{key}..."
      doc = PrecomputedQueryDoc.find_by_key(key)
      return parse_hashes_from_doc(doc) unless doc.nil?
    rescue ActiveRecord::StatementInvalid => err
      logger.error "load_precomputed_student_hashes raised error #{err.inspect}"
    end

    # Fallback to performing the full query if something went wrong reading the
    # precomputed value
    logger.error "falling back to full load_precomputed_student_hashes query for key: #{key}"
    authorized_students = Student.find(authorized_student_ids)
    authorized_students.map {|student| student_hash_for_slicing(student) }
  end

  def parse_hashes_from_doc(doc)
    JSON.parse(doc.json).deep_symbolize_keys![:student_hashes]
  end

  # Serialize what are essentially constants stored in the database down
  # to the UI so it can use them for joins.
  def constant_indexes
    {
      service_types_index: ServiceSerializer.service_types_index,
      event_note_types_index: EventNoteSerializer.event_note_types_index
    }
  end

  def authorize_for_school
    unless current_educator.is_authorized_for_school(@school)
      redirect_to(homepage_path_for_current_educator)
    end

    if current_educator.has_access_to_grade_levels?
      grade_message = " Showing students in grades #{current_educator.grade_level_access.to_sentence}."
      flash[:notice] << grade_message if flash[:notice]
    end
  end

  def set_school
    @school = School.find_by_slug(params[:id]) || School.find_by_id(params[:id])

    redirect_to root_url if @school.nil?
  end

  def authorized_students_for_overview(school)
    if current_educator.districtwide_access?
      school.students.active
    else
      current_educator.students_for_school_overview
    end
  end

  # Methods for Dashboard
  def list_student_absences(student)
    student.absences.map {|absence| absence.order(occurred_at: :desc)}
  end

  def individual_student_dashboard_data(student)
    HashWithIndifferentAccess.new({
      first_name: student.first_name,
      last_name: student.last_name,
      homeroom: student.try(:homeroom).try(:name),
      absences: student.absences.order(occurred_at: :desc)
      }
    )
  end

  def students_for_dashboard(school)
    school.students.active
  end

end<commit_msg>eager loading the student data now<commit_after>class SchoolsController < ApplicationController
  include StudentsQueryHelper

  before_action :set_school, :authorize_for_school

  def show
    authorized_students = authorized_students_for_overview(@school)

    student_hashes = log_timing('schools#show student_hashes') do
      load_precomputed_student_hashes(Time.now, authorized_students.map(&:id))
    end

    merged_student_hashes = log_timing('schools#show merge_mutable_fields_for_slicing') do
      merge_mutable_fields_for_slicing(student_hashes)
    end

    @serialized_data = {
      students: merged_student_hashes,
      school: @school,
      current_educator: current_educator,
      constant_indexes: constant_indexes
    }
    render 'shared/serialized_data'
  end

  def csv
    authorized_students = @school.students.active
    csv_string = StudentsSpreadsheet.new.csv_string(authorized_students, @school)
    filename = "#{@school.name} - Exported #{Time.now.strftime("%Y-%m-%d")}.csv"
    send_data(csv_string, {
      type: Mime[:csv],
      disposition: "attachment; filename='#{filename}"
    })
  end

  def school_administrator_dashboard
    unless current_educator.admin == true
      redirect_to not_authorized_path and return #TodDo: determine whether there's a more appropriate action here
    end

    dashboard_students = students_for_dashboard(@school).includes(:absences, :homeroom).map { |student| individual_student_dashboard_data(student) }

    @serialized_data = {
      absences: dashboard_students.to_json
    }
    render 'shared/serialized_data'
  end

  private

  # This should always find a record, but if it doesn't we fall back to the
  # raw query.
  # Results an array of student_hashes.
  def load_precomputed_student_hashes(time_now, authorized_student_ids)
    begin
      # the precompute job runs at 8am UTC, and takes a few minutes to run,
      # so keep a buffer that makes sure the import task and precompute job
      # can finish before cutting over to the next day.
      query_time = time_now - 9.hours
      key = PrecomputedQueryDoc.precomputed_student_hashes_key(query_time, authorized_student_ids)
      logger.warn "load_precomputed_student_hashes querying key: #{key}..."
      doc = PrecomputedQueryDoc.find_by_key(key)
      return parse_hashes_from_doc(doc) unless doc.nil?
    rescue ActiveRecord::StatementInvalid => err
      logger.error "load_precomputed_student_hashes raised error #{err.inspect}"
    end

    # Fallback to performing the full query if something went wrong reading the
    # precomputed value
    logger.error "falling back to full load_precomputed_student_hashes query for key: #{key}"
    authorized_students = Student.find(authorized_student_ids)
    authorized_students.map {|student| student_hash_for_slicing(student) }
  end

  def parse_hashes_from_doc(doc)
    JSON.parse(doc.json).deep_symbolize_keys![:student_hashes]
  end

  # Serialize what are essentially constants stored in the database down
  # to the UI so it can use them for joins.
  def constant_indexes
    {
      service_types_index: ServiceSerializer.service_types_index,
      event_note_types_index: EventNoteSerializer.event_note_types_index
    }
  end

  def authorize_for_school
    unless current_educator.is_authorized_for_school(@school)
      redirect_to(homepage_path_for_current_educator)
    end

    if current_educator.has_access_to_grade_levels?
      grade_message = " Showing students in grades #{current_educator.grade_level_access.to_sentence}."
      flash[:notice] << grade_message if flash[:notice]
    end
  end

  def set_school
    @school = School.find_by_slug(params[:id]) || School.find_by_id(params[:id])

    redirect_to root_url if @school.nil?
  end

  def authorized_students_for_overview(school)
    if current_educator.districtwide_access?
      school.students.active
    else
      current_educator.students_for_school_overview
    end
  end

  # Methods for Dashboard
  def list_student_absences(student)
    student.absences.map {|absence| absence.order(occurred_at: :desc)}
  end

  def individual_student_dashboard_data(student)
    HashWithIndifferentAccess.new({
      first_name: student.first_name,
      last_name: student.last_name,
      homeroom: student.try(:homeroom).try(:name),
      absences: student.absences
      }
    )
  end

  def students_for_dashboard(school)
    school.students.active
  end

end
{'fraction_non_alphanumeric': 0.07272317910615783, 'fraction_numerical': 0.00045029832263874815, 'mean_word_length': 4.12933025404157, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '805504', 'n_tokens_mistral': 2776, 'n_tokens_neox': 2711, 'n_words': 608}