-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHW11_V3_Sarita_Hedaya.py
247 lines (206 loc) · 11.8 KB
/
HW11_V3_Sarita_Hedaya.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
from collections import defaultdict
from prettytable import PrettyTable
import os
import unittest
import sqlite3
DB_FILE = 'G:\My Drive\F18\SSW-810\Week 11\Homework11.db' #path of db file (from data grip, rightclick > properties)
db = sqlite3.connect(DB_FILE) #load the database into db
def file_reader(file_name, fields_per_line, separator=',', header=False):
"""this generator returns all the values of a line on each call to next()"""
try:
fp = open(file_name, 'r') # Do the risky action of attempting to open a file
except FileNotFoundError:
print("can't open", file_name) # If file not found, raise exception
else: # If the file is found
with fp:
line_number = 1 # Start line counter to identify line that raises ValueError
next_line = [] # Initialize next_line variable to store the line in question
for line in fp:
line = line.rstrip('\n\r').split(separator) # Strips the \n and/or \r from the end of the line and Separates the line into values using the separator
if len(line) != fields_per_line:
raise ValueError(file_name, "has", len(line), "fields in", line_number, "but expected", fields_per_line)
for value in line:
next_line.append(value)
line = next_line # Transfer the values into line, so we can empty and reuse next_line
next_line = [] # Before yielding, we must empty next_line for future use
line_number += 1 # Increase the line counter by 1
if header == True: # If there is a header, skip that line.
header = False # Set header=False so later lines don't get skipped
continue
yield tuple(line)
class University:
""" Class University imports data from .txt files, organizes such data into
dictionaries with classes, and prints them in prettytable format """
def __init__(self, dir_path):
self.dir_path = dir_path
self.students = dict() # self.students[cwid] = instance of class Student
self.instructors = dict() # self.instructors[cwid] = instance of class Instructor
self._majors = dict() # self.majors[major] = instance of class major
# Calls functions that import university data from files
self.import_majors(dir_path)
self.import_students(dir_path)
self.import_instructors(dir_path)
self.import_grades(dir_path)
# Methods that import data from .txt files, and create instances of classes as values in dicitonaries
def import_students(self, dir_path):
""" Pulls student data from .txt file and organizes it into the students dictionary """
students_file = os.path.join(dir_path, "students.txt")
try:
for cwid, name, major_name in file_reader(students_file, 3, '\t'):
self.students[cwid] = Student(cwid, name, major_name, self._majors[major_name])
except ValueError as e:
print(e)
def import_instructors(self, dir_path):
""" Pulls instructor data from .txt file and organizes it into the instructors dictionary """
instructors_file = os.path.join(dir_path, "instructors.txt")
try:
for cwid, name, department in file_reader(instructors_file, 3, '\t'):
self.instructors[cwid] = Instructor(cwid, name, department)
except ValueError as e:
print(e)
def import_grades(self, dir_path):
""" read the grades file, update the student to note the course and grade, update instructor to
note an additional student
"""
grades_file = os.path.join(dir_path, "grades.txt")
try:
for student_cwid, course, grade, instructor_cwid in file_reader(grades_file, 4, '\t'):
self.students[student_cwid].add_course(course, grade) # adds dictionary entry pair. See def in class Student
self.instructors[instructor_cwid].add_course(course) # adds a student to #students in course. See def in Instructor class.
except ValueError as e:
print(e)
def import_majors(self, dir_path):
""" reads majors from file in dir_path and adds them to a dictionary self._majors """
majors_file = os.path.join(dir_path, "majors.txt")
try:
for major, flag, course in file_reader(majors_file, 3, separator='\t', header=False):
if major not in self._majors:
self._majors[major] = Major(major)
self._majors[major].add_course(flag, course)
except ValueError as e:
print(e)
# Print summary information as tables
def student_prettytable(self):
""" create a student pretty table with info the student and courses """
student_prettytable = PrettyTable() # initialize pt
student_prettytable.field_names = Student.pt_header(self) #set headers as defined in function inside Student class
for student in self.students.values():
student_prettytable.add_row(student.pt_row()) # add rows using the output of pt_row defined in Student class
return student_prettytable
def instructor_prettytable(self):
""" create an instructor pretty table with info the instructor and courses """
instructor_prettytable = PrettyTable()
instructor_prettytable.field_names = Instructor.pt_header(self)
for row in Instructor.pt_row(self): #for each list in the set of lists returned by pt_row (each list is a row)
instructor_prettytable.add_row(row) #add it to the pt
return instructor_prettytable
def major_prettytable(self):
""" create a pretty table containing information of courses associated with majors """
major_prettytable = PrettyTable() # initialize pt
major_prettytable.field_names = Major.pt_header(self) #set headers as defined in function inside Student class
for major in self._majors.values():
major_prettytable.add_row(major.pt_row()) # add rows using the output of pt_row defined in Student class
return major_prettytable
class Student:
""" Keeps track of all information concerning students,
including what happens when a student takes a new course """
def __init__(self, cwid, name, major_name, major):
self.cwid = cwid
self.name = name
self.major_name = major_name
self.major = major
self.courses = dict() # self.courses[course] = grade
def add_course(self, course, grade):
""" note that the student took a course and earned a grade """
self.courses[course] = grade
def pt_header(self):
""" return a list of the fields in the prettytable """
return ['CWID', 'Name', 'Major', 'Completed Courses', 'Remaining Required', 'Remaining Electives']
def pt_row(self):
""" return the values for the students pretty table for self """
completed_courses, remaining_required, remaining_electives = self.major.remaining(self.courses)
return [self.cwid, self.name, self.major_name, completed_courses, remaining_required, remaining_electives]
class Instructor:
""" Keeps track of all information concerning Instructors,
including what happens to the instructor data when a student takes a new course """
def __init__(self, cwid, name, department):
self.cwid = cwid
self.department = department
self.name = name
self.courses = defaultdict(int) # self.courses[course] = number of students
def add_course(self, course):
""" tell the instructor that she taught a student in a course """
self.courses[course] += 1
def pt_header(self):
return ['CWID', 'Name', 'Department', 'Course', '#Students']
def pt_row(self): #new instructor.pt_row returns 10 lists, each list will be a row
""" a generator to return the rows with course and number of students """
return list(db.execute("""
select I.CWID, I.Name, I.Dept, G.Course, count(*) as StudentPerClass
FROM HW11_instructors I
join HW11_grades G
on I.CWID = G.Instructor_CWID
group by G.Course"""))
class Major:
""" Track all the information regarding the major, inlcuding its required and elective courses """
def __init__(self, department, passing=None):
self._department = department
self._required = set()
self._electives = set()
if passing is None:
self.passing_grades = {'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C'}
else:
self.passing_grades = passing
def add_course(self, flag, course):
""" notes another required course or elective """
if flag.upper() == 'E':
self._electives.add(course)
elif flag.upper() == 'R':
self._required.add(course)
else:
raise ValueError(f"Flag {flag} is invalid for course {course}")
def pt_header(self):
""" return a list of the fields in the prettytable """
return ['Major', 'Required Courses', 'Elective Courses']
def pt_row(self):
""" returns the list of values that populate the prettytable for a specific major """
return [self._department, self._required, self._electives]
def remaining(self, courses):
""" Calculate completed_courses, remaining_required, remaining_electives from
a dictionary of course=grade for a single student """
completed_courses = {course for course, grade in courses.items() if grade in self.passing_grades}
remaining_required = self._required - completed_courses
if self._electives.intersection(completed_courses):
remaining_electives = None
else:
remaining_electives = self._electives
return completed_courses, remaining_required, remaining_electives
def main():
stevens = University('G:\My Drive\F18\SSW-810\Week 10')
print("Student Summary")
student_summary = print(stevens.student_prettytable())
print("Instructor Summary")
instructor_summary = print(stevens.instructor_prettytable())
print("Major Summary")
major_summary = print(stevens.major_prettytable())
class UniversityTest(unittest.TestCase):
def test_student_instance(self):
"""Tests several student instances by comparing the values in the instances to the correct values"""
stevens = University('G:\My Drive\F18\SSW-810\Week 10')
self.assertEqual(stevens.students['10175'].name, "Erickson, D")
self.assertEqual(stevens.students['11461'].name, "Wright, U")
self.assertEqual(stevens.students['11461'].courses, {'SYS 800': 'A', 'SYS 750': 'A-', 'SYS 611': 'A'})
def test_instructor_instance(self):
"""Tests several instructor instances by comparing the values in the instances to the correct values"""
stevens = University('G:\My Drive\F18\SSW-810\Week 10')
self.assertEqual(stevens.instructors['98764'].name, "Feynman, R")
self.assertEqual(stevens.instructors['98765'].name, "Einstein, A")
self.assertEqual(stevens.instructors['98760'].courses, {'SYS 800': 1, 'SYS 750': 1, 'SYS 611': 2, 'SYS 645': 1})
def test_major_instance(self):
""" Tests Major instances to compare to the correct values """
stevens = University('G:\My Drive\F18\SSW-810\Week 10')
self.assertEqual(stevens._majors['SFEN']._required, {'SSW 540', 'SSW 555', 'SSW 564', 'SSW 567'})
self.assertEqual(stevens._majors['SFEN']._electives, {'CS 501', 'CS 545', 'CS 513'})
if __name__ == '__main__':
unittest.main(exit = False, verbosity = 2)
main()