student_progress 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|