student_progress 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/lib/student_progress.rb +25 -0
- data/lib/student_progress/cli.rb +87 -8
- data/lib/student_progress/cohort.rb +26 -2
- data/lib/student_progress/completed_lesson.rb +3 -0
- data/lib/student_progress/lesson.rb +6 -0
- data/lib/student_progress/progress_importer.rb +42 -0
- data/lib/student_progress/progress_report.rb +7 -0
- data/lib/student_progress/scraper.rb +11 -2
- data/lib/student_progress/student.rb +1 -0
- data/lib/student_progress/student_report.rb +10 -0
- data/lib/student_progress/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 429b3f920a6cba30aa9acc9f355dc9d9c68e1e2f1dc429b356645ccc0fcf4da3
|
4
|
+
data.tar.gz: 30097be3a7e0c598b58a52a04074499df3eeb49d243e099f8f04fc54de4d127c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46dd020fb09416d02df9a3d5ad11b92a8677da10a60a0d72d8f8173eb86f7b75268cbf8116344ae62977b12310d535b567136bbf467ced87d4161690a527b020
|
7
|
+
data.tar.gz: 65f7b13ade40d3a4ef3d4b159e34ed3efda6790162c8bea7a20c78f3859201e96a919bac16a04f9b6046dd784d2e063946d72d56141c74c4f91a1d18ab4733e6
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
student_progress (0.
|
4
|
+
student_progress (0.2.0)
|
5
5
|
json
|
6
6
|
nokogiri
|
7
7
|
poltergeist
|
@@ -40,7 +40,7 @@ GEM
|
|
40
40
|
method_source (~> 0.9.0)
|
41
41
|
public_suffix (3.0.2)
|
42
42
|
rack (2.0.5)
|
43
|
-
rack-test (1.
|
43
|
+
rack-test (1.1.0)
|
44
44
|
rack (>= 1.0, < 3)
|
45
45
|
rake (10.5.0)
|
46
46
|
rspec (3.7.0)
|
data/lib/student_progress.rb
CHANGED
@@ -64,6 +64,29 @@ unless DB.table_exists? :students
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
unless DB.table_exists? :completed_lessons
|
68
|
+
DB.create_table :completed_lessons do
|
69
|
+
primary_key :id
|
70
|
+
Integer :lesson_id
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
unless DB[:completed_lessons].columns.include?(:date)
|
75
|
+
DB.add_column :completed_lessons, :date, :date
|
76
|
+
end
|
77
|
+
|
78
|
+
unless DB[:cohorts].columns.include?(:periscope_url)
|
79
|
+
DB.add_column :cohorts, :periscope_url, :string
|
80
|
+
end
|
81
|
+
|
82
|
+
unless DB[:completed_lessons].columns.include?(:student_id)
|
83
|
+
DB.add_column :completed_lessons, :student_id, :integer
|
84
|
+
end
|
85
|
+
|
86
|
+
unless DB[:student_reports].columns.include?(:current_topic_status)
|
87
|
+
DB.add_column :student_reports, :current_topic_status, :string
|
88
|
+
end
|
89
|
+
|
67
90
|
require_relative "./student_progress/version"
|
68
91
|
require_relative "./student_progress/cli"
|
69
92
|
require_relative "./student_progress/student"
|
@@ -74,6 +97,8 @@ require_relative "./student_progress/topic"
|
|
74
97
|
require_relative "./student_progress/unit"
|
75
98
|
require_relative "./student_progress/student_report"
|
76
99
|
require_relative "./student_progress/progress_report"
|
100
|
+
require_relative "./student_progress/progress_importer"
|
101
|
+
require_relative "./student_progress/completed_lesson"
|
77
102
|
|
78
103
|
|
79
104
|
# module StudentProgress
|
data/lib/student_progress/cli.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class StudentProgress::CLI
|
2
2
|
def call
|
3
3
|
puts "Hello from CLI"
|
4
|
-
prompt_for_login
|
4
|
+
prompt_for_login
|
5
5
|
StudentProgress::Scraper.scrape_lessons
|
6
6
|
main_menu
|
7
7
|
goodbye
|
@@ -14,7 +14,10 @@ class StudentProgress::CLI
|
|
14
14
|
puts "\n"
|
15
15
|
print "Password: "
|
16
16
|
password = STDIN.noecho(&:gets).chomp
|
17
|
-
StudentProgress::Scraper.login_user(email, password)
|
17
|
+
unless StudentProgress::Scraper.login_user(email, password)
|
18
|
+
prompt_for_login
|
19
|
+
end
|
20
|
+
true
|
18
21
|
end
|
19
22
|
|
20
23
|
def main_menu
|
@@ -48,7 +51,7 @@ class StudentProgress::CLI
|
|
48
51
|
|
49
52
|
def list_choices
|
50
53
|
puts "Choose an option below by number"
|
51
|
-
puts "1.
|
54
|
+
puts "1. Manage Cohorts"
|
52
55
|
puts "2. Add Cohort"
|
53
56
|
puts "3. Set goal for the week"
|
54
57
|
puts "4. To view a list of past reports"
|
@@ -72,11 +75,86 @@ class StudentProgress::CLI
|
|
72
75
|
puts "Whoops! Didn't understand that input"
|
73
76
|
end
|
74
77
|
end
|
75
|
-
|
78
|
+
show_options_for_cohort
|
79
|
+
end
|
80
|
+
|
81
|
+
def show_options_for_cohort
|
82
|
+
puts "What would you like to do with the cohort?"
|
83
|
+
puts "1. Create Progress Report"
|
84
|
+
puts "2. Add a member(s) to the cohort"
|
85
|
+
puts "3. Remove a member from the cohort"
|
86
|
+
puts "4. Add a periscope URL to scrape progress from"
|
87
|
+
puts "5. Delete this cohort"
|
88
|
+
puts "type 'back' to return to the main menu or 'exit' to exit the CLI"
|
89
|
+
@input = gets.strip.downcase
|
90
|
+
return if ["back", "exit"].include?(@input)
|
91
|
+
case @input.to_i
|
92
|
+
when 1
|
93
|
+
display_students
|
94
|
+
when 2
|
95
|
+
add_students_to_cohort_view
|
96
|
+
when 3
|
97
|
+
remove_students_from_cohort_view
|
98
|
+
when 4
|
99
|
+
add_periscope_link_for_cohort_data
|
100
|
+
when 5
|
101
|
+
delete_this_cohort_view
|
102
|
+
else
|
103
|
+
puts "Whoops! didn't understand that input"
|
104
|
+
show_options_for_cohort
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def add_students_to_cohort_view
|
109
|
+
puts "Enter the github usernames of the students you'd like to add as a comma separated list"
|
110
|
+
@input = gets.strip
|
111
|
+
return if ["back", "exit"].include?(@input.downcase)
|
112
|
+
usernames = @input.chomp.split(',').map(&:strip)
|
113
|
+
@selected_cohort.add_students(usernames)
|
114
|
+
puts "Just added #{usernames.count} students to your cohort"
|
115
|
+
puts "#{@selected_cohort.name} now has #{@selected_cohort.students.count} students"
|
116
|
+
show_options_for_cohort
|
76
117
|
end
|
77
118
|
|
78
|
-
def
|
119
|
+
def remove_students_from_cohort_view
|
120
|
+
puts "Enter the github usernames of the students you'd like to remove as a comma separated list"
|
121
|
+
puts "Current usernames:"
|
122
|
+
puts @selected_cohort.students.collect{|s| s.github_username}.join(', ')
|
123
|
+
@input = gets.strip
|
124
|
+
return if ["back", "exit"].include?(@input.downcase)
|
125
|
+
usernames = @input.chomp.split(',').map(&:strip)
|
126
|
+
removed = @selected_cohort.remove_students(usernames)
|
127
|
+
puts "Just removed #{removed} students from the cohort"
|
128
|
+
puts "#{@selected_cohort.name} now has #{@selected_cohort.students.count} students"
|
129
|
+
show_options_for_cohort
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_periscope_link_for_cohort_data
|
133
|
+
puts "Paste in the link to the periscope doc you'd like to use to import Cohort progress"
|
134
|
+
@input = gets.strip
|
135
|
+
@selected_cohort.periscope_url = @input
|
136
|
+
@selected_cohort.save
|
137
|
+
puts "Successfully added the periscope url to #{@selected_cohort.name}"
|
138
|
+
end
|
79
139
|
|
140
|
+
def delete_this_cohort_view
|
141
|
+
puts "Are you sure you want to delete the #{@selected_cohort.name} cohort?"
|
142
|
+
puts "type 'yes' to confirm or 'no' to go back to the cohort menu"
|
143
|
+
puts "You can also type 'back' to return to the main menu, or 'exit' to exit the CLI"
|
144
|
+
@input = gets.strip
|
145
|
+
return if ["back", "exit"].include?(@input.downcase)
|
146
|
+
case @input.downcase
|
147
|
+
when 'yes'
|
148
|
+
@selected_cohort.delete_and_unassign_students
|
149
|
+
@selected_cohort = nil
|
150
|
+
puts "cohort successfully deleted"
|
151
|
+
list_choices
|
152
|
+
when 'no'
|
153
|
+
show_options_for_cohort
|
154
|
+
else
|
155
|
+
puts "Whoops! Didn't understand that."
|
156
|
+
delete_this_cohort_view
|
157
|
+
end
|
80
158
|
end
|
81
159
|
|
82
160
|
def prompt_for_cohort_name
|
@@ -88,7 +166,7 @@ class StudentProgress::CLI
|
|
88
166
|
|
89
167
|
def prompt_for_usernames
|
90
168
|
puts "Please enter the GitHub usernames for the students you'd like info about (separated by commas)"
|
91
|
-
usernames = gets.chomp.split(',').map(&:strip)
|
169
|
+
usernames = gets.chomp.split(',').map(&:strip).map(&:downcase)
|
92
170
|
puts "Hang on a sec, adding #{usernames.count} students"
|
93
171
|
@new_cohort.add_students(usernames)
|
94
172
|
puts "All done!"
|
@@ -139,10 +217,11 @@ class StudentProgress::CLI
|
|
139
217
|
output << "Students should #{@goal[:goal]} by the end of the week"
|
140
218
|
output << "To do that, they'll have completed #{@goal[:lessons]} lessons and #{@goal[:labs]} labs"
|
141
219
|
end
|
142
|
-
headers = ['Student Name', 'Current Lab', 'Lessons', 'Labs']
|
220
|
+
headers = ['Student Name', 'Current Lab', 'Current Topic', 'Lessons', 'Labs']
|
143
221
|
report = @selected_cohort.progress_reports.last
|
144
222
|
rows = report.student_reports.collect.with_index(1) do |student_report|
|
145
|
-
|
223
|
+
student_report.generate_current_topic_status
|
224
|
+
[student_report.student_name, student_report.current_lab, student_report.current_topic_status, student_report.lessons_complete, student_report.labs_complete]
|
146
225
|
end
|
147
226
|
table = Terminal::Table.new headings: headers, rows: rows
|
148
227
|
output << table
|
@@ -3,10 +3,34 @@ class StudentProgress::Cohort < Sequel::Model
|
|
3
3
|
one_to_many :progress_reports
|
4
4
|
def add_students(usernames)
|
5
5
|
usernames.each do |username|
|
6
|
-
student = StudentProgress::Student.find_or_create(github_username: username)
|
7
|
-
|
6
|
+
student = StudentProgress::Student.find_or_create(github_username: username.downcase)
|
7
|
+
self.add_student(student)
|
8
8
|
student.save
|
9
9
|
end
|
10
|
+
self.save
|
11
|
+
end
|
12
|
+
|
13
|
+
def remove_students(usernames)
|
14
|
+
removed = 0
|
15
|
+
usernames.each do |username|
|
16
|
+
student = self.students_dataset.first(github_username: username.downcase)
|
17
|
+
if student
|
18
|
+
self.remove_student(student)
|
19
|
+
removed += 1
|
20
|
+
else
|
21
|
+
puts "Student with github username: #{username} not found."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
self.save
|
25
|
+
removed
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_and_unassign_students
|
29
|
+
students.each do |student|
|
30
|
+
student.cohort_id = nil
|
31
|
+
student.save
|
32
|
+
end
|
33
|
+
self.delete
|
10
34
|
end
|
11
35
|
|
12
36
|
def run_progress_report
|
@@ -53,6 +53,12 @@ class StudentProgress::Lesson
|
|
53
53
|
lesson ? lesson.unit.title : "couldn't find that lesson"
|
54
54
|
end
|
55
55
|
|
56
|
+
def self.topic_from_lesson_title(lesson_title)
|
57
|
+
lesson = @@all.find{ |l| l.title.strip == lesson_title.strip}
|
58
|
+
binding.pry unless lesson
|
59
|
+
lesson ? lesson.unit.topic : "couldn't find that lesson"
|
60
|
+
end
|
61
|
+
|
56
62
|
def self.progress_when_unit_complete(unit_title)
|
57
63
|
lessons = 0
|
58
64
|
labs = 0
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class StudentProgress::ProgressImporter
|
2
|
+
require 'csv'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
def self.import(url)
|
6
|
+
options = { headers: true, header_converters: :symbol }
|
7
|
+
|
8
|
+
|
9
|
+
csv_file = open(url)
|
10
|
+
content = File.new(csv_file)
|
11
|
+
CSV.foreach(content, options, &method(:create_and_associate_progress))
|
12
|
+
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.create_and_associate_progress(row)
|
17
|
+
# Example Row:
|
18
|
+
#<CSV::Row
|
19
|
+
# first_name:"Brennan"
|
20
|
+
# last_name:"Fulmer"
|
21
|
+
# deprecated_github_username:"BrennanFulmer"
|
22
|
+
# curriculum_id:"31648"
|
23
|
+
# completed_at:"2018-07-26 19:12:46.353279"
|
24
|
+
# track_id:"31303"
|
25
|
+
# title:"Rails Static Request"
|
26
|
+
# >
|
27
|
+
student = StudentProgress::Student.first(github_username: row[:deprecated_github_username].downcase)
|
28
|
+
lesson = StudentProgress::Lesson.find_by_id(row[:curriculum_id].to_i)
|
29
|
+
if student && lesson
|
30
|
+
if completed = StudentProgress::CompletedLesson.first(student_id: student.id, lesson_id: lesson.id)
|
31
|
+
return
|
32
|
+
else
|
33
|
+
completed = StudentProgress::CompletedLesson.create(student_id: student.id, lesson_id: lesson.id)
|
34
|
+
completed.date = row[:completed_at]
|
35
|
+
completed.save
|
36
|
+
end
|
37
|
+
else
|
38
|
+
binding.pry
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
class StudentProgress::ProgressReport < Sequel::Model
|
2
2
|
one_to_many :student_reports
|
3
|
+
many_to_one :cohort
|
3
4
|
|
4
5
|
def on_track(labs_goal, cohort=nil)
|
5
6
|
result = {}
|
@@ -14,4 +15,10 @@ class StudentProgress::ProgressReport < Sequel::Model
|
|
14
15
|
end
|
15
16
|
result
|
16
17
|
end
|
18
|
+
|
19
|
+
def import_progress_from_periscope
|
20
|
+
if self.cohort.periscope_url
|
21
|
+
StudentProgress::ProgressImporter.import(self.cohort.periscope_url)
|
22
|
+
end
|
23
|
+
end
|
17
24
|
end
|
@@ -9,7 +9,7 @@ class StudentProgress::Scraper
|
|
9
9
|
end.each.with_index(1) do |username, index|
|
10
10
|
@@session.visit("https://learn.co/#{username}")
|
11
11
|
student = StudentProgress::Student.find_or_create(
|
12
|
-
github_username: username
|
12
|
+
github_username: username.downcase
|
13
13
|
)
|
14
14
|
student.first_name = @@session.first('.media-block__content--fill').text.split("\n").first.split(' ').first
|
15
15
|
student.last_name = @@session.first('.media-block__content--fill').text.split("\n").first.split(' ').slice(1)
|
@@ -22,9 +22,11 @@ class StudentProgress::Scraper
|
|
22
22
|
student_id: student.id,
|
23
23
|
progress_report_id: report.id
|
24
24
|
)
|
25
|
-
|
26
25
|
print "#{index}.."
|
27
26
|
end
|
27
|
+
if cohort.periscope_url
|
28
|
+
StudentProgress::ProgressImporter.import(cohort.periscope_url)
|
29
|
+
end
|
28
30
|
puts "Done!"
|
29
31
|
end
|
30
32
|
|
@@ -37,6 +39,13 @@ class StudentProgress::Scraper
|
|
37
39
|
puts "Signing in now..."
|
38
40
|
sleep 0.5
|
39
41
|
@@session.click_button "Sign in"
|
42
|
+
if @@session.current_path == "/account_check/failure" || @@session.current_path == "/"
|
43
|
+
puts "Invalid email or password"
|
44
|
+
puts "\n"
|
45
|
+
false
|
46
|
+
else
|
47
|
+
true
|
48
|
+
end
|
40
49
|
end
|
41
50
|
|
42
51
|
def self.scrape_lessons
|
@@ -5,4 +5,14 @@ class StudentProgress::StudentReport < Sequel::Model
|
|
5
5
|
def student_name
|
6
6
|
self.student && self.student.full_name
|
7
7
|
end
|
8
|
+
|
9
|
+
def generate_current_topic_status
|
10
|
+
topic = StudentProgress::Lesson.topic_from_lesson_title(student.current_lab)
|
11
|
+
ratio = (topic.lessons.map(&:id) & student.completed_lessons.map(&:lesson_id)).length/topic.lessons.count.to_f
|
12
|
+
percent = (ratio*100).round(2)
|
13
|
+
status = student.cohort.periscope_url ? "#{topic.title}: #{percent}% complete" : "to track this, add perisocpe url to cohort"
|
14
|
+
self.current_topic_status = status
|
15
|
+
self.save
|
16
|
+
status
|
17
|
+
end
|
8
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: student_progress
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dakota Martinez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -187,7 +187,9 @@ files:
|
|
187
187
|
- lib/student_progress.rb
|
188
188
|
- lib/student_progress/cli.rb
|
189
189
|
- lib/student_progress/cohort.rb
|
190
|
+
- lib/student_progress/completed_lesson.rb
|
190
191
|
- lib/student_progress/lesson.rb
|
192
|
+
- lib/student_progress/progress_importer.rb
|
191
193
|
- lib/student_progress/progress_report.rb
|
192
194
|
- lib/student_progress/scraper.rb
|
193
195
|
- lib/student_progress/student.rb
|