Skip to content

Commit

Permalink
Merge pull request #5 from ByrdOfAFeather/ALTML-DEV
Browse files Browse the repository at this point in the history
[MERGE] Alt-ML Dev Merge
  • Loading branch information
ByrdOfAFeather committed Mar 17, 2018
2 parents 14be276 + 6328c3f commit 466c490
Show file tree
Hide file tree
Showing 8 changed files with 502 additions and 381 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ DataProcessing/logs
DataProcessing/source.py
GUI/config
temp
TODO.txt

*.pyc
*.idea
Expand Down
49 changes: 29 additions & 20 deletions DataProcessing/collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@


class UserCollector:
def __init__(self, url, header, verify):
def __init__(self, url, header):
self.url = url
self.header = header
self.verify = verify

def get_associated_courses(self):
api_target = r"{}/api/v1/courses?enrollment_state=active&per_page=50"
courses = requests.put(api_target.format(self.url), headers=self.header, verify=self.verify)
courses = requests.put(api_target.format(self.url), headers=self.header, verify=True)
course_dict = {}

for courses in courses.json():
Expand All @@ -41,7 +40,7 @@ def get_associated_courses(self):
class Collector:
"""Collects data from the Canvas API
"""
def __init__(self, url, header, class_id, verify=True):
def __init__(self, url, header, class_id):
"""
:param url: The URL used for Canvas ex. http://stanford.instructure.com
:param header: The authorization information for the canvas API
Expand All @@ -50,7 +49,6 @@ def __init__(self, url, header, class_id, verify=True):
self.header = header
self.url = url
self.class_id = class_id
self.verify = verify

def get_class_users(self, output_folder, output_file_name):
"""Gets all users in a specific class
Expand All @@ -59,7 +57,7 @@ def get_class_users(self, output_folder, output_file_name):
:return: A dictionary containing user ids linked to user names
"""
api_target = r'{}/api/v1/courses/{}/enrollments?per_page=50'
enrollment = requests.put(api_target.format(self.url, self.class_id), headers=self.header, verify=self.verify)
enrollment = requests.put(api_target.format(self.url, self.class_id), headers=self.header, verify=True)

with open('{}/{}.json'.format(output_folder, output_file_name), 'w') as f:
json.dump(enrollment.json(), f)
Expand All @@ -74,7 +72,7 @@ def get_class_users(self, output_folder, output_file_name):

def get_course_modules(self):
api_target = "{}/api/v1/courses/{}/modules?per_page=50"
module = requests.put(api_target.format(self.url, self.class_id), headers=self.header, verify=self.verify)
module = requests.put(api_target.format(self.url, self.class_id), headers=self.header, verify=True)
module_dict = {}
for modules in module.json():
if modules['published']:
Expand All @@ -88,9 +86,9 @@ def get_course_modules(self):
class Module(Collector):
"""Represents and collects data from a single module
"""
def __init__(self, module_id, url, header, class_id, verify=True):
def __init__(self, module_id, url, header, class_id):
self.module_id = module_id
super(Module, self).__init__(url=url, header=header, class_id=class_id, verify=verify)
super(Module, self).__init__(url=url, header=header, class_id=class_id)

@staticmethod
def _get_module_quizzes(module_items):
Expand Down Expand Up @@ -128,7 +126,7 @@ def get_module_items(self):
"""
api_target = r"{}/api/v1/courses/{}/modules/{}/items"
module = requests.put(api_target.format(self.url, self.class_id, self.module_id),
headers=self.header, verify=self.verify)
headers=self.header, verify=True)
notes = self._get_module_notes(module.json())
quizzes = self._get_module_quizzes(module.json())
module_dict = {'Subsections': {'Notes': notes, 'Quizzes': quizzes}, 'Overall': module.json()}
Expand All @@ -141,16 +139,16 @@ def module_times(self):
for students in self.get_class_users('temp', 'temp'):
# url = r'{}/api/v1/courses/{}/analytics/users/{}/activity'
api_target = r'{}/api/v1/users/{}/page_views'
response = requests.put(api_target.format(self.url, students), headers=self.header, verify=self.verify)
response = requests.put(api_target.format(self.url, students), headers=self.header, verify=True)
# print(response.json())


class Quiz(Collector):
"""Represents a single quiz
"""
def __init__(self, quiz_id, class_id, header, url, verify=True):
def __init__(self, quiz_id, class_id, header, url):
self.quiz_id = quiz_id
super(Quiz, self).__init__(class_id=class_id, header=header, url=url, verify=verify)
super(Quiz, self).__init__(class_id=class_id, header=header, url=url)
self.submissions = self._get_quiz_submissions()

def get_quiz_question_ids(self):
Expand All @@ -159,7 +157,7 @@ def get_quiz_question_ids(self):
"""
api_target = r'{}/api/v1/courses/{}/quizzes/{}/questions?per_page=100'
quiz_response = requests.put(api_target.format(self.url, self.class_id, self.quiz_id),
headers=self.header, verify=self.verify)
headers=self.header, verify=True)

quiz_question_id_dict = {}
for questions in quiz_response.json():
Expand All @@ -178,7 +176,7 @@ def _get_quiz_submissions(self):
"""
api_target = r'{}/api/v1/courses/{}/quizzes/{}/submissions?per_page=100'
quiz = requests.put(url=api_target.format(self.url, self.class_id, self.quiz_id),
headers=self.header, verify=self.verify)
headers=self.header, verify=True)

submission_list = []
submission_dict = []
Expand Down Expand Up @@ -255,15 +253,26 @@ def get_correct_answers(self):
"""
event_dict = self.get_quiz_events()
user_specific_correct_answer_dict = {}
cur_correct_answer_dict = {}
questions_answered = self.get_questions_answered()

for students, event_dict in event_dict.items():
cur_correct_answer_dict = {}
student_dict = {}

cur_quiz_data = list(event_dict.values())[0][0]['event_data']['quiz_data']

for questions in cur_quiz_data:
cur_correct_answer_dict[questions["id"]] = [i["id"] for i in questions["answers"] if i["weight"] > 0]

user_specific_correct_answer_dict[students] = cur_correct_answer_dict
cur_questions_answered = questions_answered[students]
correct_questions = cur_correct_answer_dict.keys()
for questions in cur_questions_answered:
for ids in correct_questions:
if ids == int(questions['event_data'][0]['quiz_question_id']):
if cur_correct_answer_dict[ids][0] == int(questions['event_data'][0]['answer']):
student_dict[ids] = cur_correct_answer_dict[ids]

user_specific_correct_answer_dict[students] = student_dict

return user_specific_correct_answer_dict

Expand All @@ -288,7 +297,7 @@ def get_page_leaves(self):

current_list = []
for items in event_dict['quiz_submission_events']:
if items['event_type'] == 'page_blurred' or items['event_type'] == 'page_focused':
if items['event_type'] == 'page_blurred':
current_list.append(items)

page_leaves_dict[user_id] = current_list
Expand Down Expand Up @@ -316,7 +325,7 @@ def get_quiz_events(self):
quiz_events = {}
for submission_id, user_id in self.submissions[0]:
events = requests.put(api_target.format(self.url, self.class_id, self.quiz_id, submission_id),
headers=self.header, verify=self.verify)
headers=self.header, verify=True)
quiz_events[user_id] = events.json()

with open('{}/{}.json'.format(temp_dir, 'events_{}'.format(self.quiz_id)), 'w') as f:
Expand All @@ -342,7 +351,7 @@ def get_discussion(self, output_folder='Discussions', output_file_name='discussi
"""
api_target = '{}/api/v1/courses/{}/discussion_topics/{}/view'
r = requests.put(api_target.format(self.url, self.class_id, self.discussion_id),
headers=self.header, verify=self.verify)
headers=self.header, verify=True)

with open('{}/{}.json'.format(output_folder, output_file_name), 'w') as f:
json.dump(r.json(), f)
Expand Down
64 changes: 45 additions & 19 deletions DataProcessing/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,21 @@

class QuizEvents:
"""A builder of data sets for quiz event information"""
def __init__(self, quiz, questions_answered=None, anon=True, pre_flags=False):
print("Initializing Quiz")
def __init__(self, quiz, data_options, anon=True, pre_flags=False, controller=None):
self.anon = anon # anon = anonymous (false: index = user id & true: index = user name)
self.data_set = {}
self.quiz = quiz

if not questions_answered: self._get_questions_answered()
else: self.questions_answered = questions_answered
self._get_questions_answered()

self.pre_flags = pre_flags
self.controller = controller
self.data_options = data_options
self._init_data_set()

def _get_questions_answered(self):
"""Gets submission events and questions answered events
"""
print("Building Questions Answered")
self.submissions = self.quiz.submissions[1]
self.questions_answered = self.quiz.get_questions_answered()

Expand All @@ -50,12 +49,45 @@ def _init_data_set(self):
self.data_set[user_id] = {}
self.data_set['Overall'] = {}

# self._build_changed_questions(correct_only=True)
self._build_average_question_time()
# self._build_user_scores()
self._build_time_taken()
self._build_user_page_leaves()
if not self.anon: self._non_anon_data_set()
options = {
"Changed Questions": self._build_changed_questions,
"Average Question Time": self._build_average_question_time,
"User Scores": self._build_user_scores,
"Time Taken": self._build_time_taken,
"Page Leaves": self._build_user_page_leaves,
'Difficulty Index': self._build_difficulty_index
}

for keys in self.data_options:
if self.controller is not None:
self.controller.labelvar.set("Building {}".format(keys))
self.controller.update()
options[keys]()

if not self.anon:
if self.controller is not None:
self.controller.labelvar.set("Formatting With Names")
self.controller.update()
self._non_anon_data_set()

def _build_difficulty_index(self):

api_target = '{}/api/v1/courses/{}/quizzes/{}/statistics?per_page=10000'
question_stats = requests.put(api_target.format(self.quiz.url, self.quiz.class_id, self.quiz.quiz_id),
headers=self.quiz.header)

correct_answers = self.quiz.get_correct_answers()

for students, answers in self.questions_answered.items():
self.data_set[str(students)]['difficulty_index'] = 0

current_correct_answers = correct_answers[students]
for correct_questions in current_correct_answers:
for question in question_stats.json()['quiz_statistics'][0]['question_statistics']:
if int(question['id']) == correct_questions:
self.data_set[str(students)]['difficulty_index'] += question['difficulty_index']

self.data_set['Overall']['difficulty_index'] = 0

def _build_changed_questions(self, correct_only=False):
correct_answers = self.quiz.get_correct_answers()
Expand Down Expand Up @@ -153,8 +185,8 @@ def _build_user_page_leaves(self):
# Converts the dictionary keys into a list, takes the length to get the total number of questions
# the multiplies by .5 to get 50 percent of the total number of questions
if self.pre_flags:
questions_no_subdivision = len(list(self.quiz.get_quiz_question_ids().keys())) * .75
if (cur_length / 2) >= questions_no_subdivision:
questions_no_subdivision = len(list(self.quiz.get_quiz_question_ids().keys())) * .70
if cur_length >= questions_no_subdivision:
self.data_set[user_id]['page_leaves'] = 'CA'
else:
self.data_set[user_id]['page_leaves'] = cur_length
Expand Down Expand Up @@ -192,7 +224,6 @@ def _non_anon_data_set(self):
if len(quiz_users['Overall']) != len(self.data_set['Overall']) or len(quiz_users) != len(self.data_set):
rebuild = True
else:
print("Getting Already Made User Name List")
# Sets pandas options for displaying a much larger data set
pd.set_option('display.max_columns', 100)
pd.set_option('display.width', 100000)
Expand All @@ -202,7 +233,6 @@ def _non_anon_data_set(self):
else: rebuild = True

if rebuild:
print("Rebuilding Name List")
name_set = {}
for ids, values in self.data_set.items():
profile = requests.put(r'{}/api/v1/users/{}/profile'.format(self.quiz.url, ids), headers=self.quiz.header)
Expand Down Expand Up @@ -278,7 +308,3 @@ def build_dataframe(self):
return pre_flags, data_frame
else:
return data_frame


# if risk > everything:
# program.work()
Loading

0 comments on commit 466c490

Please sign in to comment.