FAQ

How do we handle file uploads?

Actually uploading files is a bit complicated. It’s okay to allow users to copy/paste the entire contents of a file into a <textarea>. (I recommend this approach.) The point is to allow batch creation of multiple entries.

You may find that copying and pasting a CSV from Excel pastes as tabs, not commas. It’s okay to handle tabs instead of commas; the point is that the user should have a simple build upload process, and not have to enter rows one at a time. Understand your process, and make sure your batch creation page has instructions for the user.

Does the data have duplicate rows?

Your data are correct. There is at least one employee ID number that is duplicated across the two files. Look at the data, and try to understand what it’s telling you…. How might that data represent real life? (Imagine the two cohorts were created six months apart. Why might some data values associated with an ID have changed?)

Just as importantly, what should you do about the duplicate rows? There are really only a few options:

  1. Ignore the changes,
  2. Halt and catch fire (error on the changes, and stop processing),
  3. Make updates based on the new data (overwrite the old data),
  4. Notify the user of the changes and provide a way for them to edit it.

Some of these are harder than others. It’s okay to do one thing now, and plan on a different thing for a future sprint. (Think about what you need for a Minimum Viable Product and consider the Triple Constraint of projects.) It doesn’t matter which option you choose; the important part is your rationale in choosing an option. (This is an untruth; don’t do #1.)

Try to apply the data to real life. Can students be in more than one cohort? (Your data says yes. What are the implications of that? Don’t assume all cohorts happen at the same time.)

Can we use the EmployeeId as StudentId?

EmployeeId may uniquely identify a student for a given company, but it is not necessarily unique across all companies. Thus, it should not (by itself) be the primary key of Students. You can use it as part of a composite key, but in practice that’s cumbersome, especially as Students may have a many:many relationship with another entity.

A common solution to this is to create a pseudo key, an internally generated integer primary key. (This is one way theory and practice often differ: in practice, pseudo keys are very common.) Sushi does this, for example. That internal key largely stays hidden from the users, but is useful in several practical ways. For example, the key might appear in the URL. The user should not be required to know or remember the pseudo key.

Is it okay to “hard code” file names or data?

No.

Don’t hard code numbers or file names. These are called “magic numbers”, and you should be avoid using them.

For example, "students1-program1.csv" should not appear in your code. The maximum number of students in a section (35) might , but it should first appear as a variable. Declare it once, and from then on use the variable. This will make the code easy to change if the number ever changes.

Do we have to do the models and controllers the way it’s done in the sample code?”

The two most important criteria are (1) Does it work?, and (2) Does everyone in your group understand it? So long as you meet those standards, do it how you like.

However, this MVC pattern, which follows the computing principle “separation of concerns”, is done for a reason, and represents years of industry knowledge of the best (fastest, easiest) way to write software. It may seem more complex than is needed for such a small application (it is, a bit, but is still greatly simplified from what a full app might do), but there is a lesson in understanding the structure that I hope you can apply to future projects.

How do I handle bulk creation of courses/students?

Take a look at the edit examples. I suggest having the user paste the data into a <textarea>, which you can then read in your form handler controller.

When you read the data in from your form handler controller, the entire file will be a single string. You’ll need to split the string into multiple rows. (See the Python docs for split())

Then, for each row, do the following:

  1. (for student data) Split each row into multiple columns.
  2. Create a new model object.
  3. Call the new model object’s create() method.

The provided example code gives you some examples of how to do this.

How how can I create the proper number of sections and enroll multiple students in multiple sections?

There are a significant problem to solve before we get to creating these relationships. How do you identify which students and which courses need to be combined? Any of these would work:

  1. The simplest may be to avoid need to pass data across pages entirely by creating bulk section and bulk course on the same page, so that both lists are already available in variables for you to use. (This is probably the easiest for you to implement.)
  2. If you look at your data model, you’ll see and students would all be part of the same cohort. It should be trivial to modify the model classes to return a list of students and a list of courses by cohort id. (Just write new “find” methods in the respective model classes, patterned after find_all()). This means the user would have to pick a cohort, but that’s doable.
  3. You could store the data in cookies. This is beyond the scope of this assignment, but it’s a possible solution.

This following pseudo code is loosely based on Python. It is not necessarily Python, or any other programming language (but it’s pretty close to Python). It’s just pseudo code. It hasn’t been tested. You have been warned.

Before looking at the following code, think through the best process for creating sections then adding a balanced number of students to each section of a course. This is just one way of doing this. It’s not even necessarily the best way.

# You should define MAX_STUDENTS_PER_SECTION as a global.

def enrollStudentsInCourse(sList: List[Student], whichCourse: Course):
    studentCount = sList.count()
    numSections = math.ceil( studentCount / MAX_STUDENTS_PER_SECTION )
    studentsPerSection = math.ceil(studentCount / numSections)

    for i in range(numSections):
        sec = Section(Course) # Write the constructor so that
                              # it populates good default attributes when passed a Course
        sec.create()          # Save the new section to the database via the model

        for j in range(studentsPerSection): # See how this loop is nested?
            # TODO:  check there are students left in sList, else break,
            #        so we don't error on an uneven count
            temp_stu = sList.pop() # remove a Student from sList
            temp_stu.enroll(sec) # Enroll the student in the newly created section.

That’s it. All of the database magic should be happening on the model side. (In this case, in Student#enroll(Section)) For each course, create the needed number of sections. For each created section, enroll the appropriate number of students. Each of the create() methods is an almost exact duplicate of the one provided for you in Sushi.

It’s hopefully easy to imagine that Student#enroll() is easily built, much like any of the model’s other database methods. It simply calls an INSERT statement on your Enrollment table.