···44 before_action :ensure_not_authenticated, only: [:new, :create]
5566 def new
77- @user = User.new
87 StatsD.increment("login_page_viewed")
981010- if session[:user_id]
1111- StatsD.increment("already_logged_in")
1212- redirect_to overview_path
1313- else
1414- StatsD.increment("login_page_viewed")
1515- render "new"
99+ # Measure the time taken to process the rendering of the new page
1010+ StatsD.measure('view.render_login_page') do
1111+ if session[:user_id]
1212+ StatsD.increment("already_logged_in")
1313+ redirect_to overview_path
1414+ else
1515+ render "new"
1616+ end
1617 end
1718 end
18191920 def create
2020- @user = User.find_by(email: auth_params[:email])
2121 StatsD.increment("login_attempt")
22222323- if @user&.authenticate(auth_params[:password])
2424- StatsD.increment("login_successful")
2525- session[:user_id] = @user.id
2626- redirect_to overview_path
2727- else
2828- StatsD.increment("login_failed")
2929- flash[:danger] = "Login failed. Please try again."
3030- render "new"
2323+ # Measure the time taken for the authentication process
2424+ StatsD.measure('auth.authenticate_user') do
2525+ @user = User.find_by(email: auth_params[:email])
2626+ if @user&.authenticate(auth_params[:password])
2727+ StatsD.increment("login_successful")
2828+ session[:user_id] = @user.id
2929+ StatsD.set('users.unique_logged_in', @user.id) # Track unique users logging in
3030+ redirect_to overview_path
3131+ else
3232+ StatsD.increment("login_failed")
3333+ flash[:danger] = "Login failed. Please try again."
3434+ render "new"
3535+ end
3136 end
3237 end
33383439 def destroy
3535- session[:user_id] = nil
3636- StatsD.increment("logout")
3737- redirect_to login_path
4040+ # Measure the time taken to process the logout action
4141+ StatsD.measure('auth.process_logout') do
4242+ StatsD.increment("logout")
4343+ session[:user_id] = nil
4444+ StatsD.event('User Logged Out', "User with ID #{session[:user_id]} logged out") # Log an event for logout
4545+ redirect_to login_path
4646+ end
3847 end
39484049 private
+21-8
app/controllers/borrowers_controller.rb
···3344 before_action :ensure_authenticated
5566-def index
77- @borrowers = Borrower.all
88- StatsD.increment("borrowers_index_viewed")
99-end
66+ def index
77+ # Measure the time taken to retrieve and display all borrowers
88+ StatsD.measure('borrowers.index_request') do
99+ @borrowers = Borrower.all
1010+1111+ # Example gauge for tracking the number of borrowers
1212+ StatsD.gauge('borrowers.count', @borrowers.count)
1313+1414+ StatsD.increment("borrowers_index_viewed")
1515+ end
1616+ end
1717+1818+ def show
1919+ # Measure the time taken to find and display a specific borrower
2020+ StatsD.measure('borrowers.show_request') do
2121+ @borrower = Borrower.find(params[:id])
10221111-def show
1212- @borrower = Borrower.find(params[:id])
1313- StatsD.increment("borrower_page_viewed")
1414-end
2323+ # Log an event when a specific borrower page is viewed
2424+ StatsD.event('Borrower Page Viewed', "User viewed borrower page with ID #{params[:id]}")
15252626+ StatsD.increment("borrower_page_viewed")
2727+ end
2828+ end
1629end
+24-15
app/controllers/concerns/authenticatable.rb
···2020 end
21212222 def ensure_authenticated
2323- unless is_authenticated?
2424- flash[:warning] = "You need to login to view that page."
2525- redirect_to main_app.login_path
2323+ StatsD.measure('auth.ensure_authenticated') do
2424+ unless is_authenticated?
2525+ flash[:warning] = "You need to login to view that page."
2626+ StatsD.event('Authentication Failure', 'User not authenticated, redirecting to login')
2727+ redirect_to main_app.login_path
2828+ end
2629 end
2730 end
28312932 def ensure_not_authenticated
3030- if is_authenticated?
3131- flash[:info] = "You are already logged in."
3232- redirect_to root_path
3333+ StatsD.measure('auth.ensure_not_authenticated') do
3434+ if is_authenticated?
3535+ flash[:info] = "You are already logged in."
3636+ StatsD.event('Already Authenticated', 'User already logged in, redirecting to root')
3737+ redirect_to root_path
3838+ end
3339 end
3440 end
35413636-3742 def ensure_admin
3838- unless current_user&.admin?
3939- flash[:danger] = "You do not have permission to view that page."
4040- redirect_to root_path
4343+ StatsD.measure('auth.ensure_admin') do
4444+ unless current_user&.admin?
4545+ flash[:danger] = "You do not have permission to view that page."
4646+ StatsD.event('Admin Access Denied', 'Non-admin user attempted to access admin page')
4747+ redirect_to root_path
4848+ end
4149 end
4250 end
43514452 def ensure_super_admin
4545- unless current_user&.super_admin?
4646- flash[:danger] = "You do not have permission to view that page."
4747- redirect_to root_path
5353+ StatsD.measure('auth.ensure_super_admin') do
5454+ unless current_user&.super_admin?
5555+ flash[:danger] = "You do not have permission to view that page."
5656+ StatsD.event('Super Admin Access Denied', 'Non-super-admin user attempted to access super admin page')
5757+ redirect_to root_path
5858+ end
4859 end
4960 end
5050-5151-5261end
+91-73
app/controllers/loaners_controller.rb
···3344 before_action :set_loaner, only: [:show, :return, :enable, :disable, :repair, :broken]
55 before_action :ensure_authenticated, only: [:show, :list, :return, :enable, :disable, :repair, :broken, :new, :create]
66-before_action :ensure_super_admin, only: [:new, :create]
66+ before_action :ensure_super_admin, only: [:new, :create]
7788-def new
99- @loaner = Loaner.new
1010- StatsD.increment("loaner_new_viewed")
1111-end
88+ def new
99+ StatsD.increment("loaner_new_viewed")
12101313-def create
1414- @loaner = Loaner.new(loaner_params)
1515- StatsD.increment("loaner_create_attempt")
1616-1717- if @loaner.save
1818- redirect_to @loaner, notice: 'Loaner was successfully created.'
1919- StatsD.increment("loaner_created")
2020- else
2121- StatsD.increment("loaner_create_failed")
2222- render :new
1111+ # Measure the time taken to render the 'new' loaner form
1212+ StatsD.measure('view.render_new_loaner') do
1313+ @loaner = Loaner.new
1414+ end
2315 end
2424-end
25161717+ def create
1818+ StatsD.increment("loaner_create_attempt")
26192727-def list
2828- @loaners = Loaner.all
2929- StatsD.increment("loaners_list_viewed")
3030-end
2020+ # Measure the time taken to create a new loaner
2121+ StatsD.measure('loaner.create') do
2222+ @loaner = Loaner.new(loaner_params)
2323+ if @loaner.save
2424+ StatsD.increment("loaner_created")
2525+ redirect_to @loaner, notice: 'Loaner was successfully created.'
2626+ else
2727+ StatsD.increment("loaner_create_failed")
2828+ render :new
2929+ end
3030+ end
3131+ end
31323232-def show
3333- @loaner
3434- StatsD.increment("loaner_page_viewed")
3535-end
3333+ def list
3434+ # Measure the time taken to retrieve and display all loaners
3535+ StatsD.measure('loaners.list_request') do
3636+ @loaners = Loaner.all
3737+ StatsD.increment("loaners_list_viewed")
3838+ end
3939+ end
4040+4141+ def show
4242+ StatsD.increment("loaner_page_viewed")
4343+ # Measure the time taken to find and display the loaner
4444+ StatsD.measure('loaner.show_request') do
4545+ @loaner
4646+ end
4747+ end
36483737- # GET /loaners/:id/return
3849 def return
3950 StatsD.increment("loaner_return_attempt")
4040- if @loaner.present?
4141- @loaner.mark_as_returned
4242- StatsD.increment("loaner_returned")
4343- redirect_to loaners_path, notice: "Asset checked in successfully."
4444- else
4545- StatsD.increment("loaner_return_failed")
4646- redirect_to loaners_path, alert: "Loaner not found."
5151+ StatsD.measure('loaner.return_action') do
5252+ if @loaner.present?
5353+ @loaner.mark_as_returned
5454+ StatsD.increment("loaner_returned")
5555+ redirect_to loaners_path, notice: "Asset checked in successfully."
5656+ else
5757+ StatsD.increment("loaner_return_failed")
5858+ redirect_to loaners_path, alert: "Loaner not found."
5959+ end
4760 end
4861 end
49625050- # GET /loaners/:id/enable
5163 def enable
5264 StatsD.increment("loaner_enable_attempt")
5353- if @loaner.present?
5454- @loaner.mark_as_enabled
5555- StatsD.increment("loaner_enabled")
5656- redirect_to loaners_path, notice: "Asset marked as enabled successfully."
5757- else
5858- StatsD.increment("loaner_enable_failed")
5959- redirect_to loaners_path, alert: "Loaner not found."
6565+ StatsD.measure('loaner.enable_action') do
6666+ if @loaner.present?
6767+ @loaner.mark_as_enabled
6868+ StatsD.increment("loaner_enabled")
6969+ redirect_to loaners_path, notice: "Asset marked as enabled successfully."
7070+ else
7171+ StatsD.increment("loaner_enable_failed")
7272+ redirect_to loaners_path, alert: "Loaner not found."
7373+ end
6074 end
6175 end
62766363- # GET /loaners/:id/disable
6477 def disable
6578 StatsD.increment("loaner_disable_attempt")
6666- if @loaner.present?
6767- @loaner.mark_as_disabled
6868- StatsD.increment("loaner_disabled")
6969- redirect_to loaners_path, notice: "Asset marked as disabled successfully."
7070- else
7171- StatsD.increment("loaner_disable_failed")
7272- redirect_to loaners_path, alert: "Loaner not found."
7979+ StatsD.measure('loaner.disable_action') do
8080+ if @loaner.present?
8181+ @loaner.mark_as_disabled
8282+ StatsD.increment("loaner_disabled")
8383+ redirect_to loaners_path, notice: "Asset marked as disabled successfully."
8484+ else
8585+ StatsD.increment("loaner_disable_failed")
8686+ redirect_to loaners_path, alert: "Loaner not found."
8787+ end
7388 end
7489 end
75907691 def broken
7792 StatsD.increment("loaner_broken_attempt")
7878- if @loaner.present?
7979- @loaner.mark_as_broken
8080- StatsD.increment("loaner_broken")
8181- redirect_to loaners_path, notice: "Asset marked as broken successfully."
8282- else
8383- StatsD.increment("loaner_broken_failed")
8484- redirect_to loaners_path, alert: "Loaner not found."
9393+ StatsD.measure('loaner.broken_action') do
9494+ if @loaner.present?
9595+ @loaner.mark_as_broken
9696+ StatsD.increment("loaner_broken")
9797+ redirect_to loaners_path, notice: "Asset marked as broken successfully."
9898+ else
9999+ StatsD.increment("loaner_broken_failed")
100100+ redirect_to loaners_path, alert: "Loaner not found."
101101+ end
85102 end
86103 end
871048888- # GET /loaners/:id/repair
89105 def repair
90106 StatsD.increment("loaner_repair_attempt")
9191- if @loaner.present?
9292- @loaner.mark_as_repaired
9393- StatsD.increment("loaner_repaired")
9494- redirect_to loaners_path, notice: "Asset marked as repaired successfully."
9595- else
9696- StatsD.increment("loaner_repair_failed")
9797- redirect_to loaners_path, alert: "Loaner not found."
107107+ StatsD.measure('loaner.repair_action') do
108108+ if @loaner.present?
109109+ @loaner.mark_as_repaired
110110+ StatsD.increment("loaner_repaired")
111111+ redirect_to loaners_path, notice: "Asset marked as repaired successfully."
112112+ else
113113+ StatsD.increment("loaner_repair_failed")
114114+ redirect_to loaners_path, alert: "Loaner not found."
115115+ end
98116 end
99117 end
100118101101-102102-103103-private
104104-# Use callbacks to share common setup or constraints between actions.
105105-def set_loaner
106106- @loaner = Loaner.find(params[:id])
107107-end
119119+ private
108120109109-def loaner_params
110110- params.require(:loaner).permit(:asset_tag, :serial_number, :status)
111111-end
121121+ # Use callbacks to share common setup or constraints between actions.
122122+ def set_loaner
123123+ StatsD.measure('loaner.find') do
124124+ @loaner = Loaner.find(params[:id])
125125+ end
126126+ end
112127128128+ def loaner_params
129129+ params.require(:loaner).permit(:asset_tag, :serial_number, :status)
130130+ end
113131end
+83-70
app/controllers/loans_controller.rb
···44 before_action :ensure_authenticated, only: [:create, :list, :pending, :out, :checkout, :checkin]
5566 def new
77- @loan = Loan.new
87 StatsD.increment("loan_new_viewed")
88+99+ # Measure the time taken to render the 'new' loan form
1010+ StatsD.measure('view.render_new_loan') do
1111+ @loan = Loan.new
1212+ end
913 end
10141115 def create
1216 borrower_email = loan_params[:borrower_email]
1317 StatsD.increment("loan_create_attempt")
14181515- # Find or create a Borrower based on the email
1616- @borrower = Borrower.find_or_create_by(email: borrower_email)
1919+ # Measure the time taken to find or create a borrower and save a loan
2020+ StatsD.measure('loan.create_process') do
2121+ @borrower = Borrower.find_or_create_by(email: borrower_email)
2222+ @loan = @borrower.loans.build(loan_params.except(:borrower_email))
17231818- # Build a new Loan associated with the found or created Borrower
1919- @loan = @borrower.loans.build(loan_params.except(:borrower_email))
2020-2121- if @loan.save
2222- StatsD.increment("loan_created")
2323- flash[:success] = "Submitted Successfully!"
2424- redirect_to loans_path
2525- else
2626- StatsD.increment("loan_create_failed")
2727- flash.now[:danger] = "FAILED TO SUBMIT! Please check with a tech."
2828- render :new
2424+ if @loan.save
2525+ StatsD.increment("loan_created")
2626+ flash[:success] = "Submitted Successfully!"
2727+ redirect_to loans_path
2828+ else
2929+ StatsD.increment("loan_create_failed")
3030+ flash.now[:danger] = "FAILED TO SUBMIT! Please check with a tech."
3131+ render :new
3232+ end
2933 end
3034 end
31353236 def list
3333- @loans = Loan.all.order(created_at: :desc)
3434- StatsD.increment("loans_list_viewed")
3737+ StatsD.measure('loan.list_request') do
3838+ @loans = Loan.all.order(created_at: :desc)
3939+ StatsD.increment("loans_list_viewed")
4040+ end
3541 end
36423743 def pending
3838- @loans = Loan.where(status: "pending").order(created_at: :desc)
3939- StatsD.increment("loans_pending_viewed")
4444+ StatsD.measure('loan.pending_request') do
4545+ @loans = Loan.where(status: "pending").order(created_at: :desc)
4646+ StatsD.increment("loans_pending_viewed")
4747+ end
4048 end
41494250 def out
4343- @loans = Loan.where(status: "out").order(created_at: :desc)
4444- StatsD.increment("loans_out_viewed")
5151+ StatsD.measure('loan.out_request') do
5252+ @loans = Loan.where(status: "out").order(created_at: :desc)
5353+ StatsD.increment("loans_out_viewed")
5454+ end
4555 end
46564757 def checkout
4848- @loan = Loan.find(params[:id])
4949- loaner = Loaner.find_by(asset_tag: params[:asset_tag])
5050-5158 StatsD.increment("loan_checkout_attempt")
52595353- if loaner.nil?
5454- StatsD.increment("loan_checkout_failed")
5555- flash[:danger] = "Loaner not found."
5656- redirect_to overview_path and return
5757- end
6060+ # Measure the time taken to process the checkout action
6161+ StatsD.measure('loan.checkout_process') do
6262+ @loan = Loan.find(params[:id])
6363+ loaner = Loaner.find_by(asset_tag: params[:asset_tag])
58645959- unless loaner.available?
6060- StatsD.increment("loan_checkout_failed")
6161- flash[:danger] = "Loaner is not available."
6262- redirect_to overview_path and return
6363- end
6565+ if loaner.nil?
6666+ StatsD.increment("loan_checkout_failed")
6767+ flash[:danger] = "Loaner not found."
6868+ redirect_to overview_path and return
6969+ end
7070+7171+ unless loaner.available?
7272+ StatsD.increment("loan_checkout_failed")
7373+ flash[:danger] = "Loaner is not available."
7474+ redirect_to overview_path and return
7575+ end
7676+7777+ begin
7878+ loaner.loan!
7979+ @loan.update!(loaner_id: loaner.id)
8080+ @loan.loan!
8181+ StatsD.increment("loan_checked_out")
8282+ flash[:success] = "Loan successful."
8383+ rescue AASM::InvalidTransition => e
8484+ StatsD.increment("loan_checkout_failed")
8585+ flash[:danger] = "Loan transition failed: #{e.message}"
8686+ end
64876565- begin
6666- loaner.loan!
6767- @loan.update!(loaner_id: loaner.id)
6868- @loan.loan!
6969- StatsD.increment("loan_checked_out")
7070- flash[:success] = "Loan successful."
7171- rescue AASM::InvalidTransition => e
7272- StatsD.increment("loan_checkout_failed")
7373- flash[:danger] = "Loan transition failed: #{e.message}"
8888+ redirect_to overview_path
7489 end
7575-7676- redirect_to overview_path
7790 end
78917992 def checkin
8080- @loan = Loan.find(params[:id])
8181- loaner = @loan.loaner
8282-8393 StatsD.increment("loan_checkin_attempt")
84948585- if loaner.nil?
8686- flash[:danger] = "Loaner not found."
8787- StatsD.increment("loan_checkin_failed")
8888- redirect_to overview_path
8989- return
9090- end
9595+ # Measure the time taken to process the check-in action
9696+ StatsD.measure('loan.checkin_process') do
9797+ @loan = Loan.find(params[:id])
9898+ loaner = @loan.loaner
91999292- if loaner.available?
9393- flash[:danger] = "Loaner is already available."
9494- StatsD.increment("loan_checkin_failed")
9595- redirect_to overview_path
9696- return
9797- else
9898- loaner.return!
9999- @loan.return!
100100+ if loaner.nil?
101101+ StatsD.increment("loan_checkin_failed")
102102+ flash[:danger] = "Loaner not found."
103103+ redirect_to overview_path
104104+ return
105105+ end
100106101101- StatsD.increment("loan_checked_in")
102102- flash[:success] = "Check-in successful."
103103- redirect_to overview_path
107107+ if loaner.available?
108108+ StatsD.increment("loan_checkin_failed")
109109+ flash[:danger] = "Loaner is already available."
110110+ redirect_to overview_path
111111+ return
112112+ else
113113+ loaner.return!
114114+ @loan.return!
115115+ StatsD.increment("loan_checked_in")
116116+ flash[:success] = "Check-in successful."
117117+ redirect_to overview_path
118118+ end
104119 end
105120 end
106121122122+ private
107123108108-private
109109-110110-def loan_params
111111- params.require(:loan).permit(:reason, :borrower_email)
112112-end
113113-124124+ def loan_params
125125+ params.require(:loan).permit(:reason, :borrower_email)
126126+ end
114127end
+29-27
app/controllers/main_controller.rb
···1010 end
11111212 def overview
1313- @loans = Loan.all
1414- @loaners = Loaner.all
1515- @borrowers = Borrower.all
1616- @staff = User.all
1717- @pending_loans = Loan.where(status: 'pending')
1818- @loaners_out = Loaner.includes(:current_loan).where(status: 'loaned')
1313+ StatsD.measure('overview.load_time') do
1414+ @loans = Loan.all
1515+ @loaners = Loaner.all
1616+ @borrowers = Borrower.all
1717+ @staff = User.all
1818+ @pending_loans = Loan.where(status: 'pending')
1919+ @loaners_out = Loaner.includes(:current_loan).where(status: 'loaned')
19202020- StatsD.increment("overview_viewed")
2121+ StatsD.increment("overview_viewed")
2222+ end
2123 end
22242325 def temp
2426 @borrower = Borrower.find(1)
2525- BorrowerMailer.notify_repair_ready(@borrower).deliver_now
2626- BorrowerMailer.notify_loaner_disabled(@borrower).deliver_now
2727- BorrowerMailer.return_reminder(@borrower).deliver_now
2828-2929- # StatsD.increment("temp")
2727+ StatsD.increment("temp_action_triggered")
2828+ StatsD.measure('temp.mail_sending') do
2929+ BorrowerMailer.notify_repair_ready(@borrower).deliver_now
3030+ BorrowerMailer.notify_loaner_disabled(@borrower).deliver_now
3131+ BorrowerMailer.return_reminder(@borrower).deliver_now
3232+ end
3333+ # Consider adding an event to log this action in a more descriptive way if needed.
3034 end
31353236 def import
3333- # This action will render the import form
3437 StatsD.increment("import_viewed")
3538 end
3639···3841 StatsD.increment("import_attempt")
3942 if params[:csv_file].present?
4043 csv_file = params[:csv_file].path
4141- process_csv(csv_file)
4444+ StatsD.measure('csv_processing_time') do
4545+ process_csv(csv_file)
4646+ end
4247 StatsD.increment("import_successful")
4348 flash[:notice] = 'CSV file imported successfully.'
4449 redirect_to overview_path
···53585459 def process_csv(file_path)
5560 StatsD.increment("csv_processing_attempt")
5656- CSV.foreach(file_path, headers: true) do |row|
5757- puts "\n\n"
5858- puts "Processing row: #{row}"
5959- puts row.to_hash
6060- puts "\n\n"
6161-6262- # Loaner.find_or_create_by!(asset_tag: row['asset_tag'], ) do |loaner|
6363- # loaner.serial_number = row['serial_number']
6464- # loaner.status = row['status']
6565- # end
6161+ row_count = 0
6262+ StatsD.measure('csv_processing_time') do
6363+ CSV.foreach(file_path, headers: true) do |row|
6464+ row_count += 1
66656767- Loaner.new(asset_tag: row['asset_tag'], serial_number: row['serial_number'], loaner_id: 100).save!
6868- StatsD.increment("csv_row_processed")
6666+ # Create a new loaner (this needs to be adjusted based on your actual model attributes)
6767+ Loaner.create!(asset_tag: row['asset_tag'], serial_number: row['serial_number'], loaner_id: 100)
6868+ StatsD.increment("csv_row_processed")
6969+ end
6970 end
7171+ # Optionally set a gauge to reflect the number of rows processed
7272+ StatsD.gauge('csv_row_count', row_count)
7073 end
7171-7274end
+40-33
app/mailers/borrower_mailer.rb
···11class BorrowerMailer < ApplicationMailer
22+ default from: 'ithelper@jaspermayone.com'
2333-default from: 'ithelper@jaspermayone.com'
44+ def notify_repair_ready(borrower)
55+ StatsD.increment("email.notify_repair_ready_sent")
66+ StatsD.measure('email.notify_repair_ready_delivery_time') do
77+ @borrower = borrower
88+ mail(
99+ to: @borrower.email,
1010+ subject: 'Your device has been repaired.',
1111+ track_opens: "true",
1212+ track_clicks: "true",
1313+ message_stream: "outbound"
1414+ )
1515+ end
1616+ end
41755-def notify_repair_ready(borrower)
66- @borrower = borrower
77- mail(
88- to: @borrower.email,
99- subject: 'Your device has been repaired.',
1010- track_opens: "true",
1111- track_clicks: "true",
1212- message_stream: "outbound"
1313- )
1414-end
1818+ def notify_loaner_disabled(borrower)
1919+ StatsD.increment("email.notify_loaner_disabled_sent")
2020+ StatsD.measure('email.notify_loaner_disabled_delivery_time') do
2121+ @borrower = borrower
2222+ mail(
2323+ to: @borrower.email,
2424+ subject: 'Your device has been disabled.',
2525+ track_opens: "true",
2626+ track_clicks: "true",
2727+ message_stream: "outbound"
2828+ )
2929+ end
3030+ end
15311616-def notify_loaner_disabled(borrower)
1717- @borrower = borrower
1818- mail(
1919- to: @borrower.email,
2020- subject: 'Your device has been disabled.',
2121- track_opens: "true",
2222- track_clicks: "true",
2323- message_stream: "outbound"
2424- )
2525-end
2626-2727-def return_reminder(borrower)
2828- @borrower = borrower
2929- mail(
3030- to: @borrower.email,
3131- subject: 'Please return your device.',
3232- track_opens: "true",
3333- track_clicks: "true",
3434- message_stream: "outbound"
3535- )
3636-end
3737-3232+ def return_reminder(borrower)
3333+ StatsD.increment("email.return_reminder_sent")
3434+ StatsD.measure('email.return_reminder_delivery_time') do
3535+ @borrower = borrower
3636+ mail(
3737+ to: @borrower.email,
3838+ subject: 'Please return your device.',
3939+ track_opens: "true",
4040+ track_clicks: "true",
4141+ message_stream: "outbound"
4242+ )
4343+ end
4444+ end
3845end
+26-22
app/mailers/user_mailer.rb
···11class UserMailer < ApplicationMailer
22-33-default from: 'ithelper@jaspermayone.com'
44-55-def notify_unreturned_after_seven_days(loan)
66- @loan = loan
77- @borrower = Borrower.find(@loan.borrower_id)
88- mail(
99- subject: "Student has not returned Loaner after seven days.",
1010- to: "jmayone2025@huusd.org",
1111- track_opens: "true",
1212- message_stream: "outbound"
1313- )
1414-end
22+ default from: 'ithelper@jaspermayone.com'
1531616-def hello()
1717- mail(
1818- subject: "Hello from Postmark",
1919- to: "me@jaspermayone.com",
2020- html_body: "<strong>Hello</strong> dear Postmark user.",
2121- track_opens: "true",
2222- message_stream: "outbound"
2323- )
2424-end
44+ def notify_unreturned_after_seven_days(loan)
55+ StatsD.increment("email.notify_unreturned_after_seven_days_sent")
66+ StatsD.measure('email.notify_unreturned_after_seven_days_delivery_time') do
77+ @loan = loan
88+ @borrower = Borrower.find(@loan.borrower_id)
99+ mail(
1010+ subject: "Student has not returned Loaner after seven days.",
1111+ to: "jmayone2025@huusd.org",
1212+ track_opens: "true",
1313+ message_stream: "outbound"
1414+ )
1515+ end
1616+ end
25171818+ def hello
1919+ StatsD.increment("email.hello_sent")
2020+ StatsD.measure('email.hello_delivery_time') do
2121+ mail(
2222+ subject: "Hello from Postmark",
2323+ to: "me@jaspermayone.com",
2424+ html_body: "<strong>Hello</strong> dear Postmark user.",
2525+ track_opens: "true",
2626+ message_stream: "outbound"
2727+ )
2828+ end
2929+ end
2630end
+18
app/models/authentication.rb
···99class Authentication < ApplicationRecord
1010 # frozen_string_literal: true
11111212+ # Track creation of an Authentication instance
1313+ after_create :track_creation
1414+1515+ # Track destruction of an Authentication instance
1616+ after_destroy :track_destruction
1717+1218 def initialize(session, user)
1919+ StatsD.increment("authentication.initialized")
1320 session[:current_authentication] = self
1421 @user_id = user.id
1522 end
16231724 def destroy(session)
2525+ StatsD.increment("authentication.destroyed")
1826 session[:current_authentication] = nil
1927 @user_id = nil
2028 end
21292230 def user
3131+ StatsD.increment("authentication.user_lookup")
2332 User.find_by(id: @user_id)
2433 end
25342635 def user=(user)
3636+ StatsD.increment("authentication.user_set")
2737 @user_id = user.id
2838 end
29394040+ private
30414242+ def track_creation
4343+ StatsD.increment("authentication.created")
4444+ end
4545+4646+ def track_destruction
4747+ StatsD.increment("authentication.deleted")
4848+ end
3149end
+45-21
app/models/borrower.rb
···1212#
1313class StudentEmailValidator < ActiveModel::Validator
1414 def validate(record)
1515- unless record.email =~ /^[a-zA-Z][a-zA-Z]+[0-9]{4}@huusd\.org$/
1515+ # Increment a counter for validation attempts
1616+ StatsD.increment("email_validation_attempts")
1717+1818+ if record.email.blank?
1919+ # Log and increment counter for blank email errors
2020+ StatsD.increment("email_validation_blank_errors")
2121+ record.errors.add(:email, "can't be blank")
2222+ elsif record.email =~ /^[a-zA-Z][a-zA-Z]+[0-9]{4}@huusd\.org$/
2323+ # Log successful validation
2424+ StatsD.increment("email_validation_success")
2525+ else
2626+ # Log and increment counter for format errors
2727+ StatsD.increment("email_validation_format_errors")
1628 record.errors.add(:email, 'must be in the format: first initial, last name, and 4-digit graduation year (e.g., jdoe2024@huusd.org)')
1729 end
1830 end
···2335 validates :email, presence: true, student_email: true
24362537 before_create :parse_email
3838+ after_create :track_creation
3939+ after_update :track_update
4040+ after_destroy :track_destruction
26412742 def full_name
4343+ StatsD.increment("borrower.full_name_accessed")
2844 "#{first_name} #{last_name}"
2945 end
30463147 def name
4848+ StatsD.increment("borrower.name_accessed")
3249 "#{full_name}"
3350 end
34513552 def grade_level
5353+ StatsD.increment("borrower.grade_level_calculated")
3654 current_month = Time.now.month
3737- current_year = Time.now.year
3838- graduation_year = self.graduation_year
5555+ current_year = Time.now.year
5656+ graduation_year = self.graduation_year
39574058 if current_month <= 5
4159 academic_year = current_year - 1
···4462 end
45634664 tmp_grade_level = academic_year - graduation_year
4747- # if tmp_grade_level.abs is = 1, then the student is in 12th grade
4848- # if tmp_grade_level.abs is = 2, then the student is in 11th grade
4949- # etc
5050-5151- if tmp_grade_level.abs == 1
6565+ case tmp_grade_level.abs
6666+ when 1
5267 grade_level = 12
5353- elsif tmp_grade_level.abs == 2
6868+ when 2
5469 grade_level = 11
5555- elsif tmp_grade_level.abs == 3
7070+ when 3
5671 grade_level = 10
5757- elsif tmp_grade_level.abs == 4
7272+ when 4
5873 grade_level = 9
5959- elsif tmp_grade_level.abs == 5
7474+ when 5
6075 grade_level = 8
6161- # add grade 7
6276 else
6377 grade_level = 0 # staff (should not error)
6478 end
···6680 grade_level
6781 end
68826969-7083 private
71847285 def parse_email
7373-7474- # email comes in as "first initial, last name, and 4-digit graduation year @huusd.org", split out the parts
7575- # and assign them to the appropriate attributes
7676-8686+ StatsD.increment("borrower.parse_email_started")
7787 # Split the email address before the "@" symbol
7888 local_part = email.split('@').first
7989···8191 name_part, graduation_year = local_part.scan(/([a-zA-Z]+)(\d{4})/).flatten
82928393 # Assign the appropriate attributes
8484- self.first_name = name_part[0].capitalize
8585- self.last_name = name_part[1..-1].capitalize
8686- self.graduation_year = graduation_year
9494+ self.first_name = name_part[0].capitalize
9595+ self.last_name = name_part[1..-1].capitalize
9696+ self.graduation_year = graduation_year
9797+9898+ StatsD.increment("borrower.parse_email_completed")
9999+ end
100100+101101+ def track_creation
102102+ StatsD.increment("borrower.created")
103103+ end
104104+105105+ def track_update
106106+ StatsD.increment("borrower.updated")
107107+ end
108108+109109+ def track_destruction
110110+ StatsD.increment("borrower.deleted")
87111 end
88112end
+10-11
app/models/loan.rb
···3232 validates :reason, presence: true
33333434 aasm :column => 'status' do
3535-3635 state :pending, initial: true, display: "Pending"
3736 state :out, display: "Out"
3837 state :returned, display: "Returned"
···4342 transitions from: :pending, to: :out
44434544 after do
4545+ StatsD.increment("loan.loaned")
4646 self.update(loaned_at: Time.now)
4747- # SHOWME: This is the time for the due date, confirm this with justin
4847 self.update(due_date: Date.today + 1.day)
49485049 due_date_time = self.due_date.to_time
51505252- RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + 1.day).perform_later(self.id)
5353- RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + 2.days).perform_later(self.id)
5454- RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + 3.days).perform_later(self.id)
5555- RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + 4.days).perform_later(self.id)
5656- RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + 5.days).perform_later(self.id)
5757- RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + 6.days).perform_later(self.id)
5858- RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + 7.days).perform_later(self.id)
5151+ # Schedule reminder jobs
5252+ (1..7).each do |day|
5353+ StatsD.increment("loan.reminder_job_scheduled", tags: ["day:#{day}"])
5454+ RemindBorrowerToReturnLoanerJob.set(wait_until: due_date_time + day.days).perform_later(self.id)
5555+ end
5656+ StatsD.increment("loan.borrower_unreturned_after_seven_days_job_scheduled")
5957 BorrowerUnreturnedAfterSevenDaysJob.set(wait_until: due_date_time + 8.days).perform_later(self.id)
6058 end
6159 end
···6462 transitions from: :out, to: :returned
65636664 after do
6565+ StatsD.increment("loan.returned")
6766 self.update(returned_at: Time.now)
6867 end
6968 end
7070-7169 end
72707371 def log_status_change
7474- puts "changing from #{aasm.from_state} to #{aasm.to_state} (event: #{aasm.current_event})"
7272+ StatsD.increment("loan.status_change", tags: ["from:#{aasm.from_state}", "to:#{aasm.to_state}", "event:#{aasm.current_event}"])
7373+ Rails.logger.info "Changing from #{aasm.from_state} to #{aasm.to_state} (event: #{aasm.current_event})"
7574 end
76757776 enum reason: { charging: 1, device_repair: 2, forgot_at_home: 3 }
+48-18
app/models/loaner.rb
···2424#
2525class AssetTagValidator < ActiveModel::Validator
2626 def validate(record)
2727- unless record.asset_tag =~ /^[0-9]{6}$/
2828- record.errors.add(:asset_tag, 'must be a 6-digit number')
2727+ # Increment a counter for validation attempts
2828+ StatsD.increment("asset_tag_validation_attempts")
2929+3030+ if record.asset_tag.blank?
3131+ # Log and increment counter for blank asset tag errors
3232+ StatsD.increment("asset_tag_validation_blank_errors")
3333+ record.errors.add(:asset_tag, "can't be blank")
3434+ elsif record.asset_tag =~ /^[0-9]{6}$/
3535+ # Log successful validation
3636+ StatsD.increment("asset_tag_validation_success")
3737+ else
3838+ # Log and increment counter for format errors
3939+ StatsD.increment("asset_tag_validation_format_errors")
4040+ record.errors.add(:asset_tag, 'must be a 6-digit number')
4141+ end
2942 end
3043end
3131-end
32443345class Loaner < ApplicationRecord
3446 include AASM
···4456 after_all_transitions :log_status_change
45574658 event :loan do
4747- # transitions from: :available, to: :loaned, guard: :pending_loan_present?
4859 transitions from: :available, to: :loaned
49605061 after do
6262+ StatsD.increment("loaner.loaned")
5163 assign_current_loan
5264 end
5365 end
54665567 event :return do
5668 transitions from: :loaned, to: :available
6969+7070+ after do
7171+ StatsD.increment("loaner.returned")
7272+ end
5773 end
58745975 event :disable do
6076 transitions from: [:available, :loaned], to: :disabled
7777+7878+ after do
7979+ StatsD.increment("loaner.disabled")
8080+ end
6181 end
62826383 event :enable do
6484 transitions from: :disabled, to: :available
8585+8686+ after do
8787+ StatsD.increment("loaner.enabled")
8888+ end
6589 end
66906791 event :broken do
6892 transitions from: [:available, :loaned], to: :maintenance
9393+9494+ after do
9595+ StatsD.increment("loaner.broken")
9696+ end
6997 end
70987199 event :repair do
72100 transitions from: :maintenance, to: :available
101101+102102+ after do
103103+ StatsD.increment("loaner.repaired")
104104+ end
73105 end
74106 end
7510776108 def log_status_change
7777- puts "changing from #{aasm.from_state} to #{aasm.to_state} (event: #{aasm.current_event})"
7878- end
7979-8080- # def pending_loan_present?
8181- # loans.pending.exists?
8282- # end
8383-8484- def assign_current_loan
8585- current_loan = loans.pending.first
8686- if current_loan
8787- self.current_loan_id = current_loan.id
8888- save!
8989- current_loan.loan!
9090- end
109109+ StatsD.increment("loaner.status_change", tags: ["from:#{aasm.from_state}", "to:#{aasm.to_state}", "event:#{aasm.current_event}"])
110110+ Rails.logger.info "Changing from #{aasm.from_state} to #{aasm.to_state} (event: #{aasm.current_event})"
91111 end
9211293113 validates :asset_tag, presence: true, uniqueness: true
···105125 # Find the maximum current loaner_id
106126 max_id = Loaner.maximum(:loaner_id) || 0
107127 self.loaner_id = max_id + 1
128128+ StatsD.increment("loaner.id_set")
108129 end
109130131131+ def assign_current_loan
132132+ current_loan = loans.pending.first
133133+ if current_loan
134134+ self.current_loan_id = current_loan.id
135135+ save!
136136+ StatsD.increment("loaner.current_loan_assigned")
137137+ current_loan.loan!
138138+ end
139139+ end
110140end
···11+# == Route Map
22+#
33+# D, [2024-06-29T22:50:42.483604 #96446] DEBUG -- : using default configuration
44+15Rails.application.routes.draw do
26 # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
37