15
Assignment 5: Repeat Student Finder-V1.00 Page 1/15 Assignment 5: Repeat Student Finder Due Wednesday April 11 th by 11:59 pm. Submit deliverables via CourSys: https://courses.cs.sfu.ca/ Late penalty is 10% per calendar day (each 0 to 24 hour period past due). April 13 th is the last possible day to submit (11:59 pm). This assignment may be done individually or in pairs (2). Do not show other students your code, do not copy code found online, and do not post questions about the assignment online. Direct all questions to the instructor or TA: [email protected] See the marking guide for details on how each part will be marked. 0. Overview Imagine you are an instructor about to start a new semester teaching. One of your first questions is, “Will I be teaching any students I have taught before?” You could read through the list of students enrolled to see how many you recognize; however, since your memory is not that good you want a computer program to do the work. Your mission, should you choose to implement it, is to create such a program using C++ and Qt. 0.1 Basic Features The basic UI (with data loaded from simpleData.csv) is shown below. The upper left table lists all courses that were found in the data file. The bottom left table shows students; the bottom right also shows students, but is restricted to showing only students who have taken more than one course. The upper right displays some statistics about data. Figure 1: Screenshot showing all data after loading the sample data file.

Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 1/15

Assignment 5: Repeat Student FinderDue Wednesday April 11th by 11:59 pm.

◦ Submit deliverables via CourSys: https://courses.cs.sfu.ca/◦ Late penalty is 10% per calendar day (each 0 to 24 hour period past due).

▪ April 13th is the last possible day to submit (11:59 pm). ◦ This assignment may be done individually or in pairs (2). Do not show other students your

code, do not copy code found online, and do not post questions about the assignment online.◦ Direct all questions to the instructor or TA: [email protected]◦ See the marking guide for details on how each part will be marked.

0. OverviewImagine you are an instructor about to start a new semester teaching. One of your first questions

is, “Will I be teaching any students I have taught before?” You could read through the list of students enrolled to see how many you recognize; however, since your memory is not that good you want a computer program to do the work.

Your mission, should you choose to implement it, is to create such a program using C++ and Qt.

0.1 Basic Features The basic UI (with data loaded from simpleData.csv) is shown below. The upper left table lists

all courses that were found in the data file. The bottom left table shows students; the bottom right also shows students, but is restricted to showing only students who have taken more than one course. The upper right displays some statistics about data.

Figure 1: Screenshot showing all data after loading the sample data file.

Page 2: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 2/15

One critical feature is that the system allows the user to clicks on a course, such as CMPT212, and the bottom tables only show students in this course. The statistics are unaffected by the selection.

The figure shows there are three students in CMPT 212, and that both Dr. Evil and Andzo have taken other courses. Clicking on a different course (PHIL 042) updates the UI to show the appropriate students:

Figure 3: PHIL 042 selected, and so it shows only students in that course.

When the user clicks on a student from either of the bottom two tables, it displays a new dialog

Figure 2: Application restricted to showing only CMPT 212 students.

Page 3: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 3/15

box appears which displays the student's information in more detail:

0.2 Design & Provided Files This section presents an overview of the system's OOD. Your system need not match this exactly,

but it should be quite close in structure, especially if you want to make good use of the provided materials and be able to get meaningful help from the instructor and TA on the assignment.

This document guides you through implementing the system in stages. It is highly recommended you complete a stage before moving on to the next as later stages depend heavily on earlier stages.

Below is the high-level UML diagram for the system, together with some annotations showing the flow of signals (dotted lines). Connection labels are descriptive, rather than the names in C++.

The application is built on the data model, shown at the bottom of the diagram. The ItemMagager is instantiated twice: one to hold courses, one to hold students. Data from the ItemManagers is used by the ItemListModel class (and two derived classes) to prepare the data to be shown in the QTableViews in the UI (MainWindow). The DataFileProcessor reads a data file and populates the ItemManager

Figure 4: Detailed information on one student.

Figure 5: High-level UML diagram with signal annotations (dotted-lines) showing general system structure.

Page 4: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 4/15

with Courses and Students. Finally, the UiStudentDetails class shows the information about a single student (Figure 4).

A number of files are provided for you, as well as the application's implementation is broken down into steps or phases. The following figure shows which components are provided (green), and a rough outline of the steps for building the application.

0.3 Implementation Tips • Use Q_ASSERT early and often.

If you are handed or retrieve a pointer that cannot be null, Q_ASSERT(pBlah != 0);• For each member variable which is a pointer, make sure the constructor initializes it to null.• Code slowly, and test well. It's easy to find a bug if you have changed a little; it's impossible if

you have changed a lot.• If the UML diagrams show something named pCourse but show its type as Course (not

Course*), it's likeyl a typo in the diagram. Please let me know.•

Figure 6: UML diagram showing provided resources (in green), and the steps for implementing each class.

Page 5: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 5/15

1. Item, Course, StudentItem is the ABC for the basic Course and Student classes. These classes store and provide

access to data for a single course, or a single student. The Item ABC allows other classes to treat Courses and Students generically as just Items. Its structure is shown in Figure 7.

Item's member functions are a generic interface for clients to get information about its fields. This is used (later) to put data into the UI's tables. The functions in Item's interface return the number of fields (getFieldCount()), header information (getFieldHeading()) and value information (getFieldValue()).

For example, Course has two (2) fields. Its headers are “Course” and “Semester”, as seen in QTableView in the upper left corner of Figure 1. The getFieldValue() function returns the value stored in m_name (for fieldIndex == 0), or m_semester (for fieldIndex == 1).

matchesPrimaryField() allows the client to pass in a value (the course name for a Course, or the student number for a Student), and have the Item (its derived class) answer if it is a match to the object. For example, Student checks if the argument equals its m_studentNumber (all students must have a unique student number). To simplify your task, Course just checks the course name (m_name) and ignores the semester. This ignores the fact that the same course (say CMPT 125) is offered in multiple semesters. You may assume that the data set has only one offering of each course.

matchesPrimaryField(), as well as getFieldValue() work with the QVariant data type. You can set a QVariant to many value types, including int and QString. Also, it works well with comparisons, such as if (myVariant == 42), or if (myVariant == “Hello”).

Figure 7: UML class diagram for Item, Course and Student.

Page 6: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 6/15

The Course and Student classes, in addition to the Item functions, implement simple data access routines; most data will not change, so setters are not required. Plus, Student allows adding Course pointers (one at a time) to the student's list of courses taken. getCoursesTakenNames() returns QString of course names which are comma separated. For example, “CMPT 125, CMPT212, MATH 151”, or just “CMPT 212”. Note there is no trailing comma! The hasTankenCourse() function simply compares the pointer argument to each pointers in m_courses. If it matches one of them (i.e., both point to the same address) it returns true, otherwise returns false.

Sample test code is provided in unittest.cpp to exercise these classes.

Page 7: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 7/15

2. Item Manager & GUIThe ItemManager class stores a collection of pointers to Items. Your application will instantiate two ItemManagers: one for Courses, one for Students. Its structure is shown in Figure 8.

Figure 8: ItemManager's structure in UML. Signals indicated with <<sig>>.

Items can be added to an ItemManager using addItem(), which accepts a pointer to an Item which was dynamically allocated on the heap. When an Item is added, ItemManager emits the itemChanged() signal to notify any other objects which are observing that the set of items has changed. Client code may use getIterator() to get a Java-style iterator to access the elements of the collection; see the sample test code in main.cpp for an example of its use. Hint: Add a qDebug() message to addItem() to say that it is firing the itemsChanged() signal; this will be useful later!

findMatchingItem()'s one argument is a “primary key value” (a QVariant), and uses the Item::matchesOnPrimaryKey() to find a matching element. For example, passing in “CMPT 310” will check each Item if it matches, and find the course of name “CMPT 310”. Or, passing in 1234 will find any matching Items, such as a Student with student number 1234. If no Item matches, it returns a null pointer (0). See Item::matchesOnPrimaryKey() for more information.

dumpDebugData() iterates through the list and, for each Item, uses the getFieldCount(), getFieldHeader() and getFieldValue() functions to dump information to the qDebug() console. Note that for Student, not all information is accessible via these field commands so it will not show the email or preferred name, for instance.

ItemManager's destructor deletes all pointers which it stores, thus freeing the stored space.

After running the ItemManager unit tests in unittests.cpp, add the provided MainWindow and DataFileProcessor files to the project, and uncomment the code that instantiates one of each in main.cpp.

2.1 Testing Run the application; and “Load Data From File” (click button and select the simpleData.csv

file). All three tables should still be empty, but “Dump Models” will show the contents of the models to the qDebug() console. See the sample output file modelDump.txt.

Page 8: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 8/15

3. Data onto the GUI: ItemListModelThe next step is to populate the QTableViews in the GUI with data. This is done by providing

each of the QTableViews with a QAbstractTableModel object from which it can extract data. The MainWindow::setListModels() function accepts three pointers to ItemListModel objects (one for each QTextView) and connects them to the QTableViews.

The heart of ItemListModel is “QList<Item*> m_items”. This list contains an Item pointers for each Item that will be displayed in the QTableView. In this step you will implement the ItemListModel class; the next step implements the rest of the hierarchy shown in Figure 9.

The QAbstractTableModel defines four pure virtual methods which ItemListModel must override. The signatures (for the .h file) for these methods are:

int rowCount(const QModelIndex &parent = QModelIndex()) const;int columnCount(const QModelIndex &parent = QModelIndex()) const;

QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

rowCount() returns the number of rows, which is the number of Items in m_items.

columnCount() returns the number of fields which the Items in m_items are exposing with the getFieldCount() method. Note that there are two challenges with this. First, you don't know at compile time what type of Item you are holding, so you have to ask one of the Items in m_items how many elements it has. Second, you should handle the case where m_items is empty (just return 0).

headerData() returns a QVariant with the text of the header, as found by call getFieldHeading() on an Item in m_items. However, there are a few conditions under which it should just “return QVariant();” to indicate no value. See the QAbstractTableModel documentation for more; these include:

Figure 9: Inheritance hierarchy for ItemListModel and derived classes.

Page 9: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 9/15

1. m_items is empty.

2. orientation != Qt::Horizontal

3. role != Qt::DisplayRole

data() returns elements (as QVariant) that should be displayed in the table. First, it should “return QVariant();” if role != Qt::DisplayRoll. To find out which element to access, it must decode the index parameter into the row and column number (0 indexed) for the table:

int row = index.row();int col = index.column();

Finally, use getFieldValue() on the appropriate object in m_items() to return the data.

The constructor does two things. First, it stores a pointer to the associated ItemManager (the model) for later use when updating the ItemListModel's stored data. Second, it registers itself as an observer with the ItemManager for a notification each time the model changes. For this, connect the ItemManager's itemsChanged() signal to the ItemListModel's updateItems() slot. This can be done in the constructor.

When the updateItems() slot is called, it must update its data. The process is:

void ItemListModel::updateItems(){

// Notify attached QTableView that the data is changing.emit layoutAboutToBeChanged();

// Repopulate the items present in the table by iterating // through items in the connected ItemManager.... your code here ...

// Notify attached QTableView that the update is finished.emit layoutChanged();

}

The two layout... signals emitted are automatically directed to the QTableView. To repopulate m_items, you must add code (in the “.... your code here...” part) to do the following:

1. Clear all existing items stored in the list. (Don't delete them! They are in the model!)

2. Get an iterator from the ItemManager via getIterator() and iterate through all items in the model.

3. For each Item, ask the ItemListModel::shouldIncludeInList() function if the Item should be added to the list: if so then add it to the list; if not then ignore it.

1. This implements the Template Method design pattern. updateItems() is the template method; shouldIncludeInList() is the primitive operation overridden by the subclasses.

2. The ItemListModel class should implement shouldIncludeInList() to always return true; it will let all items into the display. Other derived classes will be more selective.

4. Hint: At the end of the algorithm, use qDebug() to print out the number of elements that are in m_items. This will help you debug the signals and slots later because updateItems() is a slot.

Finally, implement the setSelectedItem() slot. The argument is the index of the Item in m_items which has been selected on the UI. Extract a pointer to this item and then emit it in the

Page 10: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 10/15

selectedItemChanged() signal. Note: when the index is invalid (like -1) you should emit a null pointer (0). Again, it would be a reasonable idea to add a qDebug() statement to help track signals.

3.1 Testing ItemListModel In main.cpp, uncomment the couple lines which instantiate an ItemListModel and call MainWindow's setListModels(). Run the application and load the data file. If all went well, you should see the top-left QTableView populate with data.

If your application does not work, here are some things to check:1. Make it compile! Try re-running qmake and rebuild all.2. Loading a file should add items to the model; use the Dump Models button to ensure that the

Courses have been loaded correctly.3. Look at the qDebug() messages to see if it your updateItems() slot was called, and how many

items were added.The sequence of calls is shown in the following UML sequence diagrams. Closed-head arrows indicate function calls; open arrows (→) are signals.

4. Load a different file; the display in the QTableView should update.

Figure 10: Expected result of loading the data file with a working ItemListModel.

Figure 11: Sequence of signals emitted (and one function call) when loading a file.

Page 11: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 11/15

4. All Data onto the GUI: {Selected, RepeatSelected}ListModelsThe ItemListModel stores a set of Item*'s for display in a QTableView on the UI. The other two tables will be populated by the two derived classes from ItemListModel, as shown below:

The SelectedStudentListModel shows only students who are in the selected course (the course that the user has clicked on in the All Courses table). Implement the SelectedStudentListModel:

1. Implement the setSelectedCourse() slot. ◦ The argument is an Item*, not a Course*, so you'll have to cast the pointer in order to store

it in m_pSelectedCourse.◦ When the value selected course changes, the list of students in that course must also update,

so call the base class's updateItems() method to refresh the data.2. Complete the Template Method design pattern by overrinding the protected

shouldIncludeInList() function.◦ Only include students who have taken the course m_pSelectedCourse. Use the

Course::hasTakenCourse() function to perform this check.◦ If no course is selected (m_pSelectedCourse is null), then include all students (return true).

Implement the RepeatStudentListModel class:1. Override the shouldIncludeInList() function. The easiest way is to include only those

students who:◦ Are included in the list by SelectedStudentsListModel::shouldIncludeInList()◦ and who have taken more than one course (i.e., getCoursesTakenCount() > 1).

Figure 12: Hierarchy of classes derived from ItemListModel. (ItemListModel is abridged in this diagram).

Page 12: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 12/15

4.1 Testing In main, uncomment the code for step 4. When it executes, you should now see all the courses, all the students, and all the repeat students. Note that we have not yet wired up the course selection logic, so clicking on courses or students will not do anything. Your application should look like:

Figure 13: Application with all the ItemListModel classes implemented.

Page 13: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 13/15

5. Handling Course SelectionIn main.cpp, connect the item-selection signal from the course ItemListModel, to the set-selected-course slot of each of the student ItemListModels (or derived classes).

5.1 Testing Run the application. Clicking on a course should change each table of students. Clicking the Clear Selection button for the courses should return to showing all students. If this does not work, add qDebug() messages to where the appropriate signals are emitted, or in the slots which receive them.

Figure 14: Selecting MACM 210 restricts the set of students shown in the student tables.

Page 14: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 14/15

6. Handling Student SelectionFinally, implement a brand-new Qt Designer Form Class (under New File, Qt).

1. Choose the Dialog without Buttons template, and name it StudentDialog.2. Create the UI to look like the following screenshot. (Tip: A Form Layout helps).

Figure 15: Empty student details dialog box.

3. Implement the following interface for the class:

4. In the constructor, connect the UI's close button to the dialog's close() slot (via this).5. Have the setSelectedStudent() slot populate the UI with information.

◦ If the parameter to the slot is non-null, fill in all the information from the Student pointer.◦ If the parameter is null (0), clear all fields (as shown in screenshot above).◦ Show the dialog using the following code inside the setSelectedStudent() slot:

// If the dialog is not shown, show it and run its event loop.// If shown, then just change its fields and leave it up.if (isHidden()) {

show();exec();

}

Figure 16: Class diagram of StudentDetails dialog.

Page 15: Assignment 5: Repeat Student Finder · 2012-03-30 · Assignment 5: Repeat Student Finder-V1.00 Page 3/15 box appears which displays the student's information in more detail: 0.2

Assignment 5: Repeat Student Finder-V1.00 Page 15/15

6. In main.cpp, add the following:◦ Instantiate the StudentDetails dialog box. Set its parent (via its constructor) to be the

MainWindow. This will make the StudentDetails dialog disappear when you close the MainWindow.

◦ Connect the selected-item-change signals from both student models up to the new StudentDialog box instance.

6.1 Testing Run the application, load data, and click on a student. The dialog box should display the details of the student.

7. DeliverablesSubmit a zip file to CourSys ( https://courses.cs.sfu.ca/ ). You will have to create a group when you submit; if you worked alone, then create a group of one. If you worked with a partner, add your partner. You'll find the create group option in CourSys under Manage Groups when you are viewing the Assignment 5 activity.Submission file:

• as5.zip: containing all of your project's .cpp, .h, .ui, and .pro files.

Note that the case of the ZIP file name matters! Each of your .cpp and .h files must begin with a comment stating your name, your SFU user ID, and your SFU student number.

Please remember that all submissions will be compared for unexplainable similarities.

8. Updates

• None Yet!

Figure 17: Populated student details dialog.