Classroom Grades

Let’s consider the hypothetical scenario of a group of students who have taken a programming exam. The professor responsible of that exam is interested in knowing the average grade of the grades of the students seating in the same row. The information available is the following:

  • A grades dictionary: A dict where:

    • the key is the Student Id (int)

    • the value is a list with the name (str) and the grade (float) of that student.

    For example:

    
    >>> stgD = {
    ... 10: ['JBB', 0.0],
    ... 15: ['GWV', 5.5],
    ... 22: ['JJJ', 6.5],
    ... 27: ['AWV', 3.5],
    ... 31: ['MMP', 5.5],
    ... 35: ['BWV', 4.5],
    ... 42: ['SCC', 7.5],
    ... 49: ['BWV', 9.0],
    ... 55: ['FCC', 5.0],
    ... 72: ['ABC', 7.0],
    ... 81: ['GMM', 8.0],
    ... 92: ['MRP', 10.0],
    ... 99: ['ARG', 10.0],
    ... }
    
    
  • A classroom list: This is a 2-dimension list representing the locations where the students were seating during the exam. More precisely it is a list of rows. Each row in turn is a list of int which is either a Student Id or -1 is nobody was seating in that place. Rows are counted top-down starting at 1 and locations are counted from left to right starting at 1. For example:

    
    >>> cl1 = [
    ... [22, -1, 42, 81,  -1, 10,  -1],
    ... [15, -1, 25,  -1,  -1, 36, 49],
    ... [92, -1, 72,  -1, 31, 55, 99],
    ... ]
    
    

Please implement the following Python function in the classroom_grades module (classroom_grades.py file):

list_avg_by_row(classroomL, stgradesD)

such that

given

  • classroomL, a classroom list as described above.

  • stgradesD, a grades dict as described above.

  • It is not garanteed that all Student Id in the classroom appear as a key in gradesD. This is considered an error.

returns 2 list, one corresponding to rows in classroomL that are free of errors and one with the rows that are not:

  1. “OK list”: For each row in classroomL that has no errors, there is a tuple with 3 values:

    • The row number (int): Remember that the row indexes starts a 1.

    • The average grade (float) of the grades of the students in that row rounded to 1 decimal.

    • The number of students in that row (ie. the ones used to computer the average grade).

  2. “Not OK list”: For each row in classroomL that that has one or more errors there is tuple) with 3 int values:

    • The row number (int): Remember that the row indexes starts a 1.

    • The seat number (int) of the first “error” found (considering the ordering of seat indicated in the classroom list description).

    • The erroneous student’s id (int).

Note

Notice that a row might have no students at all. In this case the average grade would be -1.0.

For example, in case of the classroom above, the function should return the following:


>>> sol = list_avg_by_row(cl1, stgD)
>>> sol
([(1, 5.5, 4), (3, 7.5, 5)], [(2, 3, 25)])

Doctests are available at the list_avg_by_row.test file.

Note

To implement this function is recommended (but not mandatory) to call the following auxiliar function. Therefore it is recommended to first implement and test this function:

compute_avg(row, gradesD)

such that

given

  • row a list of int representing a row as described above.

  • gradesD, a dict of students grades as described above.

returns a tuple with3-value tuple that is different depending on whether the row is OK all student’s ids occur as a key in gradesD) or is not.

  • It the row is OK it returns a 3-component tuple with:

    1. True

    2. The average grade of the grades of the students in that row (float rounded to 1 decimal).

    3. The number of students (int) in that row (ie. the ones used to computer the average grade).

  • It the row is not OK, it returns:

    1. False

    2. The seat number (int) of the first “error” found.

    3. The erroneous student’s id (int).

For example:


>>> stgD = {
... 10: ['JBB', 0.0],
... 15: ['GWV', 5.5],
... 22: ['JJJ', 6.5],
... 27: ['AWV', 3.5],
... 31: ['MMP', 5.5],
... 35: ['BWV', 4.5],
... 42: ['SCC', 7.5],
... 49: ['BWV', 9.0],
... 55: ['FCC', 5.0],
... 72: ['ABC', 7.0],
... 81: ['GMM', 8.0],
... 92: ['MRP', 10.0],
... 99: ['ARG', 10.0],
... }

>>> cl1 = [
... [22, -1, 42, 81,  -1, 10,  -1],
... [15, -1, 25,  -1,  -1, 36, 49],
... [92, -1, 72,  -1, 31, 55, 99],
... ]

>>> sol = compute_avg(cl1[0], stgD)
>>> sol
(True, 5.5, 4)

>>> sol = compute_avg(cl1[1], stgD)
>>> sol
(False, 3, 25)

>>> sol = compute_avg(cl1[2], stgD)
>>> sol
(True, 7.5, 5)

Doctests are available at the compute_avg.test file.