Hi,

I am trying to extend my "Student with the best GPA" program so that it allows the user to sort a file of students based on gpa, name, or credits. The program needs to prompt for the input and output files and also the field to sort on (gpa, name or credits).

The input file consists of students name, credits(hours) and quality points (gpa = quality points / hours)

I keep getting an error that global name "gpa" is not defined and I don't understand why. Can anyone steer me in the right direction as to where I am going wrong? Thank you!


This is what I have so far on my program:

from p2gpa import Student, makeStudent
def readStudents(filename):
    infile = open(filename, 'r')
    students = []
    for line in infile:
        students.append(makeStudent(line))
    infile.close()
    return students
 
def writeStudents(students, filename):
    outfile = open(filename, 'w')
    for s in students:
        outfile.write("%s\t%f\t%f\n" %
                      (s.getName(), s.getHours(), s.getQPoints()))
    outfile.close()
def cmpgpa(s1, s2):
    return cmp(s1.gpa(), s2.gpa())
def cmpname(s1, s2):
    return cmp("s1.name()", "s2.name()")
def cmpcredits(s1, s2):
    return cmp(s1.credits(), s2.credits())
 
def main():
    print "This program sorts student grade information"
    filename = raw_input("Enter the name of the data file: ")
    dfield = raw_input("Enter gpa, name, or credits to sort: ")
    filename = raw_input("Enter a name for the output file: ")
    data = readStudents(filename)
    if dfield == gpa:
        data.sort(cmpgpa)
    elif dfield == name:
        data.sort(cmpname)
    else:
        data.sort(cmpcredits)
    filename = raw_input("Enter a name for the output file: ")    
    writeStudents(data, filename)
    print "The data has been written to", filename
if __name__ == '__main__':
    main()

This is the p2gpa program that I imported:

import string
import math
class Student:
    def __init__(self, name, hours, qpoints):
        self.name = name
        self.hours = float(hours)
        self.qpoints = float(qpoints)
    def getName(self):
        return self.name
    def getHours(self):
        return self.hours
    def getQPoints(self):
        return self.qpoints
    def gpa(self):
        return self.qpoints/self.hours
    def name(self):
        return self.name
def makeStudent(infoStr):
    name, hours, qpoints = string.split(infoStr,"\t")
    return Student(name, hours, qpoints)
def main():
    filename = raw_input("Enter name the grade file: ")
    infile = open(filename, 'r')
    best = makeStudent(infile.readline())
    for line in infile:
        s = makeStudent(line)
        if s.gpa() > best.gpa():
            best = s
    infile.close()
    print "The best student is:", best.getName()
    print "hours:", best.getHours()
    print "GPA:", best.gpa()
if __name__ == '__main__':
    main()

This is the input file I want to sort (p2sortin.py):

Computewell, Susan 100 400
DibbleBit, Denny 18 41.5
Jones, Jim 48.5 155
Smith, Frank 37 125.33
Adams, Henry 127 228

This is the error that I keep getting:

This program sorts student grade information
Enter the name of the data file: p2sortin.py
Enter gpa, name, or credits to sort: gpa
Enter a name for the output file: p2sortout.py
Traceback (most recent call last):
  File "C:\Python23\p2sort.py", line 50, in ?
    main()
  File "C:\Python23\p2sort.py", line 40, in main
    if dfield == gpa:
NameError: global name 'gpa' is not defined
>>>

Thanks!

Your initial problem is right here:

def main():
    print "This program sorts student grade information"
    filename = raw_input("Enter the name of the data file: ")
    dfield = raw_input("Enter gpa, name, or credits to sort: ")
    filename = raw_input("Enter a name for the output file: ")
    data = readStudents(filename)
    ...

You are assigning the same variable 'filename' to both the input and output file.

Your next problem is here:

if dfield == gpa:
        data.sort(cmpgpa)

The input function raw_input() returns a string, so this ought to be:

if dfield == 'gpa':
        data.sort(cmpgpa)

Give your data file a name like in.dat, don't use .py (it is not Python code!).
Your next problem is here ( I added a test print):

def makeStudent(infoStr):
    print string.split(infoStr,"\t")  # test --> ['Computewell, Susan 100 400\n']
    name, hours, qpoints = string.split(infoStr,"\t")
    return Student(name, hours, qpoints)

The result of splitting a string is a list, not a tuple! Also the data file needs tabs at the right places to make this work!

File "C:\Python23\p2sort.py", line 40, in main
if dfield == gpa:
NameError: global name 'gpa' is not defined

I think you want "gpa" in quotes.
if dfield == "gpa":
A simple menu would be easier for the user, with 1=gpa, etc. In any case, you should have a list of correct responses, or be able to add something like 0 < number < 4, to check for a valid input.

I played with the code for a while and made these observations and corrections ...

# save as p2sort.py
from p2gpa import Student, makeStudent
def readStudents(filename):
    infile = open(filename, 'r')
    students = []
    for line in infile:
        students.append(makeStudent(line))
    infile.close()
    return students
 
def writeStudents(students, filename):
    outfile = open(filename, 'w')
    for s in students:
        print "%-20s %0.2f %0.2f" % (s.getName(), s.gpa(), s.getHours())  # test
        outfile.write("%s\t%0.2f\t%0.2f\n" %
                      (s.getName(), s.getHours(), s.getQPoints()))
    outfile.close()
 
# for these compares s1 and s2 are class instances ...
def cmpgpa(s1, s2):
    # low to high
    #return cmp(s1.gpa(), s2.gpa())
    # high to low
    return cmp(s2.gpa(), s1.gpa())
def cmpname(s1, s2):
    #return cmp(s1.name(), s2.name())  # problems!!!?
    return cmp(s1.getName(), s2.getName())
def cmpcredits(s1, s2):
    # low to high
    #return cmp(s1.getHours(), s2.getHours())
    # high to low
    return cmp(s2.getHours(), s1.getHours())
 
def main():
    print "This program sorts student grade information"
    # file has line format -->  name tab hours tab qpoints
    file_in = raw_input("Enter the name of the data file: ")
    dfield = raw_input("Enter gpa, name, or credits to sort: ")
    # data is a list of Student class instances
    data = readStudents(file_in)
    if dfield == 'gpa':
        data.sort(cmpgpa)
    elif dfield == 'name':
        data.sort(cmpname)
    else:
        data.sort(cmpcredits)
    file_out = raw_input("Enter a name for the output file: ")
    writeStudents(data, file_out)
    print "The data has been written to", file_out
 
if __name__ == '__main__':
    main()

Here is the module file ...

# save this module file as p2gpa.py
import string
#import math
class Student:
    def __init__(self, name, hours, qpoints):
        self.name = name
        self.hours = float(hours)
        self.qpoints = float(qpoints)
    def getName(self):
        return self.name
    def getHours(self):
        return self.hours
    def getQPoints(self):
        return self.qpoints
    def gpa(self):
        return self.qpoints/self.hours
    def name(self):
        return self.name
 
def makeStudent(infoStr):
    if '\t' in infoStr:
        #print string.split(infoStr,"\t")  # test --> ['Computewell, Susan ', '100 ', '400\n']
        name, hours, qpoints = string.split(infoStr,"\t")
    return Student(name, hours, qpoints)
def main():
    filename = raw_input("Enter name the grade file: ")
    infile = open(filename, 'r')
    best = makeStudent(infile.readline())
    for line in infile:
        s = makeStudent(line)
        if s.gpa() > best.gpa():
            best = s
    infile.close()
    print "The best student is:", best.getName()
    print "hours:", best.getHours()
    print "GPA:", best.gpa()
 
# test the module ...
if __name__ == '__main__':
    main()
    #raw_input("Press Enter to go on ... ")

I also would recommend to use extension .dat or .txt for your data files, and make sure the text editor you use leaves the tab separator intact. Some editors replace tabs with spaces!

This little code will produce a correct sample data file called 'in.txt' ...

# this will create a sample data file with tab separators
data = [['Computewell, Susan', 100, 400],
['DibbleBit, Denny', 18, 41.5],
['Jones, Jim', 48.5, 155],
['Smith, Frank', 37, 125.33],
['Adams, Henry', 127, 228]]
fout = open("in.txt", "w")
for line in data:
    text = "%s\t%s\t%s\n" % (line[0], line[1], line[2])
    print text,
    fout.write(text)
fout.close()

Thank you all very much for your input!! I will have to study this and see if I can figure out exactly what is going on (so I know what I am doing).

Now I see that one of my major problems was that I was trying to return:

return cmp(s1.name, etc..) instead of return cmp(s1.getName, etc...)
(same thing for Hours)

I have also fixed the other problem where I did not have my quotation marks in the

proper places.

Now I just have to fix a few more things in my files and I should be able to finish!!

Thank You For Helping!!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.