Skip to content

Commit

Permalink
Merge pull request #320 from cs169/main
Browse files Browse the repository at this point in the history
CS169 Spring 2024 Final Changes
  • Loading branch information
cycomachead authored May 15, 2024
2 parents e0df660 + 878d3d4 commit c2df482
Show file tree
Hide file tree
Showing 38 changed files with 992 additions and 92 deletions.
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ gem "activerecord-import", require: false

gem "httparty", "~> 0.21.0"


gem "country_select", "~> 8.0"

group :development do
Expand Down
50 changes: 45 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,54 @@ Sp23 Badges:
The Beauty and Joy of Computing (BJC) is an introductory computer science curriculum developed at UC Berkeley meant for high school freshmen up to college freshmen. The program has a teacher guide and a newly generated password that is to be given to any teacher who signs up for the program as a volunteer educator in the Bay Area - there are plans to expand the program to other states in late 2020. This pilot application is designed as a dashboard to track the workflow of teachers who run the program and provide high-level, descriptive statistics on the courses taught and participating schools. The BJC Teacher Tracker is a NEW project started in Fall 2019 by a group of 5 student developers in UC Berkeley's CS 169: Software Engineering.

## Features
```mermaid
C4Context
title BJC Curriculum Management Dashboard
Boundary(b1, "Dashboard System") {
Person(teacher, "Teacher", "Signs up and manages own information")
System(curriculumSystem, "Curriculum Dashboard", "Central platform for curriculum management")
Person(admin, "Admin", "Administers systems and oversees communications")
System(teacherInfoSystem, "Teacher Management System", "Manages teacher profiles")
System(emailSystem, "Email System", "Handles all email communications")
System(workshopSystem, "Workshop System", "Coordinates workshop registrations")
Rel(admin, curriculumSystem, "Administers", "HTTPS", "left")
Rel(admin, teacherInfoSystem, "Administers", "HTTPS", "left")
Rel(admin, emailSystem, "Sends Emails", "SMTP", "down")
Rel(admin, workshopSystem, "Organizes Workshops", "HTTPS", "up")
Rel(teacher, curriculumSystem, "Accesses if approved", "HTTPS", "right")
Rel(teacher, teacherInfoSystem, "Manages own profile", "HTTPS", "left")
UpdateElementStyle(admin, $bgColor="#F9D648FF", $fontColor="#000000", $borderColor="#C4972F")
UpdateElementStyle(teacher, $bgColor="#8ABAD3FF", $fontColor="#000000", $borderColor="#045D75")
UpdateElementStyle(curriculumSystem, $bgColor="#F0F0F0", $fontColor="#000000", $borderColor="#707070")
UpdateElementStyle(teacherInfoSystem, $bgColor="#B8DEE6", $fontColor="#000000", $borderColor="#76A5AF")
UpdateElementStyle(emailSystem, $bgColor="#F7E0B5", $fontColor="#000000", $borderColor="#DDBE7E")
UpdateElementStyle(workshopSystem, $bgColor="#D1F4C9", $fontColor="#000000", $borderColor="#95C896")
UpdateRelStyle(teacher, curriculumSystem, $textColor="#046307", $lineColor="#0D4006")
UpdateRelStyle(teacher, teacherInfoSystem, $textColor="#046307", $lineColor="#0D4006")
UpdateRelStyle(admin, curriculumSystem, $textColor="#860136", $lineColor="#540229")
UpdateRelStyle(admin, teacherInfoSystem, $textColor="#860136", $lineColor="#540229")
UpdateRelStyle(admin, emailSystem, $textColor="#860136", $lineColor="#540229")
UpdateRelStyle(admin, workshopSystem, $textColor="#860136", $lineColor="#540229")
}
**TODO: This section needs to be re-written.**
```

* Teachers can sign up at https://teachers.bjc.berkeley.edu
* Admins can login and approve/deny requests
* Admins can see a dashboard of teacher stats
* Teachers can log in to view protected solutions content
* Admins can create/modify email templates.
* For homeschoolers, they need to upload additional support documents.
* Admins can log in to approve, deny, or request more information for each request.
* Admins can merge duplicate teacher records.
* Admins can create and modify workshops and relevant registrations.
* Admins can see a dashboard of teacher stats.
* Teachers can log in to view protected solutions content.
* Admins can create and modify email templates.


## Installation:

Expand Down
55 changes: 55 additions & 0 deletions app/controllers/merge_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

class MergeController < ApplicationController
before_action :require_admin

def preview
@from_teacher = Teacher.find(params[:from])
@into_teacher = Teacher.find(params[:into])
@result_teacher = merge_teachers(@from_teacher, @into_teacher)
end

def execute
@from_teacher = Teacher.find(params[:from])
@into_teacher = Teacher.find(params[:into])
@merged_teacher = merge_teachers(@from_teacher, @into_teacher)

merged_attributes = @merged_teacher.attributes.except("id")
@into_teacher.update!(merged_attributes)
@from_teacher.destroy
redirect_to teachers_path, notice: "Teachers merged successfully."
end

private
# Returns a merged teacher without saving it to the database.
# Rendered in the preview, and only saved to the DB in a call to merge.
def merge_teachers(from_teacher, into_teacher)
merged_attributes = {}
into_teacher.attributes.each do |attr_name, attr_value|
from_teacher_attr_value = from_teacher.attributes[attr_name]
if attr_value.blank?
merged_attributes[attr_name] = from_teacher_attr_value
elsif from_teacher_attr_value.blank?
merged_attributes[attr_name] = attr_value
else
case attr_name
when "session_count"
merged_attributes[attr_name] = attr_value + from_teacher_attr_value
when "ip_history"
merged_attributes[attr_name] = (attr_value + from_teacher_attr_value).uniq
when "last_session_at"
# The resulting last session time is the most recent one
merged_attributes[attr_name] = attr_value > from_teacher_attr_value ? attr_value : from_teacher_attr_value
when "created_at"
# The resulting record creation time is the least recent one
merged_attributes[attr_name] = attr_value < from_teacher_attr_value ? attr_value : from_teacher_attr_value
else
merged_attributes[attr_name] = attr_value
end
end
end

merged_teacher = Teacher.new(merged_attributes)
merged_teacher
end
end
4 changes: 2 additions & 2 deletions app/controllers/schools_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def create
flash[:success] = "Created #{@school.name} successfully."
redirect_to schools_path
else
flash[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
flash.now[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
render "new"
end
end
Expand All @@ -49,7 +49,7 @@ def update
flash[:success] = "Updated #{@school.name} successfully."
redirect_to school_path(@school)
else
flash[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
flash.now[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
render "edit"
end
end
Expand Down
35 changes: 30 additions & 5 deletions app/controllers/teachers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class TeachersController < ApplicationController
include CsvProcess

before_action :load_pages, only: [:new, :create, :edit, :update]
before_action :load_teacher, except: [:new, :index, :create, :import]
before_action :load_teacher, except: [:new, :index, :create, :import, :search]
before_action :sanitize_params, only: [:new, :create, :edit, :update]
before_action :require_login, except: [:new, :create]
before_action :require_admin, only: [:validate, :deny, :destroy, :index, :show]
before_action :require_admin, only: [:validate, :deny, :destroy, :index, :show, :search]
before_action :require_edit_permission, only: [:edit, :update, :resend_welcome_email]

rescue_from ActiveRecord::RecordNotUnique, with: :deny_access
Expand All @@ -32,6 +32,7 @@ def index
end

def show
@all_teachers_except_current = Teacher.where.not(id: @teacher.id)
@school = @teacher.school
@status = is_admin? ? "Admin" : "Teacher"
end
Expand Down Expand Up @@ -79,7 +80,8 @@ def create
TeacherMailer.teacher_form_submission(@teacher).deliver_now
redirect_to root_path
else
redirect_to new_teacher_path, alert: "An error occurred: #{@teacher.errors.full_messages.join(', ')}"
flash.now[:alert] = "An error occurred: #{@teacher.errors.full_messages.join(', ')}"
render "new"
end
end

Expand All @@ -90,6 +92,18 @@ def edit
@readonly = !is_admin?
end

def remove_file
file_attachment = @teacher.files.find(params[:file_id])
file_attachment.purge
flash[:notice] = "File was successfully removed"
redirect_back fallback_location: teacher_path(@teacher)
end

def upload_file
@teacher.files.attach(params[:file])
redirect_back fallback_location: teacher_path(@teacher), notice: "File was successfully uploaded"
end

def update
load_school
ordered_schools
Expand All @@ -104,7 +118,7 @@ def update
else
@school.update(school_params) if school_params
unless @school.save
flash[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
flash.now[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
render "edit" && return
end
@teacher.school = @school
Expand All @@ -115,6 +129,7 @@ def update
return
end

attach_new_files_if_any
send_email_if_application_status_changed_and_email_resend_enabled

if fail_to_update
Expand Down Expand Up @@ -195,6 +210,7 @@ def import

private
def load_teacher
@teachers = Teacher.all
@teacher ||= Teacher.find(params[:id])
end

Expand Down Expand Up @@ -252,9 +268,18 @@ def load_school
@school ||= School.find_or_create_by(**unique_school_params)
end

def attach_new_files_if_any
if params.dig(:teacher, :more_files).present?
params[:teacher][:more_files].each do |file|
@teacher.files.attach(file)
end
end
end

def teacher_params
teacher_attributes = [:first_name, :last_name, :school, :status, :snap,
:more_info, :personal_website, :education_level, :school_id, languages: []]
:more_info, :personal_website, :education_level, :school_id, languages: [], files: [],
more_files: []]
admin_attributes = [:application_status, :request_reason, :skip_email]
teacher_attributes.push(*admin_attributes) if is_admin?

Expand Down
15 changes: 15 additions & 0 deletions app/javascript/styles/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ $fa-font-path: '~@fortawesome/fontawesome-free/webfonts';
@import 'layout';
@import 'forms';
@import 'edit_teachers';
@import 'merge_modal';
@import './sidebar.scss';
@import "./selectize.scss";

Expand Down Expand Up @@ -55,6 +56,20 @@ body {
}
}

.btn-purple {
background-color: #8A2BE2; /* Light purple */
}

/* Style for the box around the file upload link */
.box-link {
display: inline-block;
padding: 5px 10px;
height: 40px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}

.btn-blue {
background-color: #9dc0ee;
}
Expand Down
41 changes: 41 additions & 0 deletions app/javascript/styles/merge_modal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* Modal styling */
.merge_modal {
display: none; /* Hide the modal by default */
position: fixed;
z-index: 1000; /* Ensure modal appears on top of other elements */
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto; /* Enable scrolling if content exceeds the modal size */
background-color: rgba(0, 0, 0, 0.4); /* Semi-transparent black background */
}

/* Modal content */
.merge_modal-content {
background-color: #fefefe;
margin: 15% auto; /* Center modal vertically and horizontally */
padding: 20px; /* You can increase the padding to add more hspace */
border: 1px solid #888;
border-radius: 5px;
width: 95%; /* You can adjust the width of the modal content as needed */
position: relative; /* Enable positioning of the close button */
}

/* Close button */
.close {
position: absolute; /* Position the close button absolutely */
top: 10px; /* Adjust this value to move the close button up or down */
right: 10px; /* Position the close button on the right side of the modal content */
color: #aaa;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}

.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
5 changes: 5 additions & 0 deletions app/models/teacher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class Teacher < ApplicationRecord
WORLD_LANGUAGES = [ "Afrikaans", "Albanian", "Arabic", "Armenian", "Basque", "Bengali", "Bulgarian", "Catalan", "Cambodian", "Chinese (Mandarin)", "Croatian", "Czech", "Danish", "Dutch", "English", "Estonian", "Fiji", "Finnish", "French", "Georgian", "German", "Greek", "Gujarati", "Hebrew", "Hindi", "Hungarian", "Icelandic", "Indonesian", "Irish", "Italian", "Japanese", "Javanese", "Korean", "Latin", "Latvian", "Lithuanian", "Macedonian", "Malay", "Malayalam", "Maltese", "Maori", "Marathi", "Mongolian", "Nepali", "Norwegian", "Persian", "Polish", "Portuguese", "Punjabi", "Quechua", "Romanian", "Russian", "Samoan", "Serbian", "Slovak", "Slovenian", "Spanish", "Swahili", "Swedish ", "Tamil", "Tatar", "Telugu", "Thai", "Tibetan", "Tonga", "Turkish", "Ukrainian", "Urdu", "Uzbek", "Vietnamese", "Welsh", "Xhosa" ].freeze

has_many :email_addresses, dependent: :destroy
has_many_attached :files
has_many_attached :more_files
accepts_nested_attributes_for :email_addresses, allow_destroy: true

validates :first_name, :last_name, :status, presence: true
Expand Down Expand Up @@ -83,6 +85,7 @@ class Teacher < ApplicationRecord
developer: 6,
excite: 7,
middle_school_bjc: 8,
home_school_bjc: 9
}

# Always add to the bottom of the list!
Expand All @@ -96,6 +99,7 @@ class Teacher < ApplicationRecord
"I am a BJC curriculum or tool developer.",
"I am teaching with the ExCITE project",
"I am teaching Middle School BJC.",
"I am teaching homeschool with the BJC curriculum."
].freeze

# From an admin perspective, we want to know if a teacher has any **meaningful** change
Expand Down Expand Up @@ -150,6 +154,7 @@ def self.status_options
:excite,
:teals_teacher,
:teals_volunteer,
:home_school_bjc,
:other,
:developer,
]
Expand Down
4 changes: 2 additions & 2 deletions app/views/main/dashboard.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
<table class="table js-dataTable">
<thead class="thead-dark">
<tr>
<%= render 'teachers/table_headers' %>
<%= render 'teachers/table_headers', include_id: false %>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<% @unreviewed_teachers.each do |teacher| %>
<tr>
<%= render 'teachers/teacher', teacher: teacher %>
<%= render 'teachers/teacher', teacher: teacher, merge_table: false %>
<td>
<div class="btn-group" role="group" aria-label="Validate or Remove Teacher">
<%= button_to("✔️", validate_teacher_path(teacher.id),
Expand Down
Loading

0 comments on commit c2df482

Please sign in to comment.