Embercode
โœ•
C++ ConditionalsLesson 5 of 14

55 min read

Module assignment: Ticket Router

Why this assignment

This assignment brings the four lessons of the module together in one program. You will read a support ticket from the keyboard, decide which team should handle it, check how long it has been waiting against a response target, and print one line of status. Every decision in the program uses a tool from this module, and you will need to pick the right tool for each one. The assignment is not a test of memory. It is a test of judgement: can you look at a rule and choose the form that makes it clearest?

Work through it in order. Read the whole specification first, then write a plan on paper with one line for each rule, then write the code. The hints are hidden until you have tried each step yourself. The reference solution is hidden at the very end.

The situation

A small company runs a help desk. Customers send tickets, and each ticket is sorted into one of three queues. A billing question goes to the billing team, a fault in the product goes to the technical team, and anything else goes to the general queue. Each ticket also has a priority from 1 to 4, where 1 is the most urgent. Priority sets a response target, which is the number of hours the team has to reply.

The desk wants a program that reads one ticket and prints a single line. The line shows the queue, the target, and whether the ticket is on time. A ticket that is badly late, and is also urgent, must be marked for escalation, which means a senior person must look at it. The program must also refuse any ticket whose data makes no sense.

The input

The program reads three values, each on its own line in the Input box.

  • Line 1: one character for the category. B means billing, T means technical, and O means other.
  • Line 2: one integer for the priority, from 1 to 4.
  • Line 3: one integer for the hours the ticket has been open. It is zero or more.

For example, the input B, then 2, then 5, means a billing ticket of priority 2 that has been open for 5 hours.

Read the three values with cin. Because cin skips spaces and line breaks between values, you can type them on one line or three, and both work. Use char for the category and int for the other two values.

The rules

Apply the rules in this order. Each rule is written so that you can turn it straight into code.

Rule 1: reject bad data. If the category is not B, T or O, print REJECT and stop. If the priority is less than 1 or more than 4, print REJECT and stop. If the hours are negative, print REJECT and stop. A rejected ticket prints nothing else.

Rule 2: choose the queue. Map the category to a queue name. B becomes Billing, T becomes Technical, and O becomes General. Because this is a choice among exact characters, a switch is a natural tool here.

Rule 3: find the target. Each priority has a response target in hours. Priority 1 has a target of 1 hour. Priority 2 has 4 hours. Priority 3 has 24 hours. Priority 4 has 72 hours. Each band is a fixed value, so either a switch or an else if ladder can express it. Choose one and be ready to say why.

Rule 4: find the status. Compute the time left as the target minus the hours open. This is called remaining. If remaining is less than zero, the ticket is BREACHED, which means the target has passed. If remaining is 0 or 1, the ticket is AT RISK, because there is an hour or less before it breaches. In every other case the status is OK. Here the order of the tests matters. Test for a breach first, because a breach is also less than 1.

Rule 5: escalate. A ticket that is BREACHED and has priority 1 or 2 is marked for escalation. This is a decision inside a decision. The escalation test only makes sense for a breached ticket, so it belongs inside the breached branch. A breached ticket with priority 3 or 4 is not escalated.

Rule 6: print the line. The line has four parts separated by a vertical bar with spaces around it: the queue, the target, the status, and then the escalation marker if there is one. The format is <queue> | SLA <target>h | <status>. An escalated ticket adds | ESCALATE at the end. Note that the target is followed by the letter h, with no space before it.

Examples

Your program must produce exactly these outputs. Each row shows the input, one value per line, and the output that the program prints.

Input (category, priority, hours)Output
B, 2, 5Billing | SLA 4h | BREACHED | ESCALATE
T, 1, 0Technical | SLA 1h | AT RISK
O, 4, 10General | SLA 72h | OK
X, 1, 0REJECT
T, 5, 0REJECT
T, 3, 24Technical | SLA 24h | AT RISK
B, 1, 3Billing | SLA 1h | BREACHED | ESCALATE
O, 3, -2REJECT
B, 4, 80Billing | SLA 72h | BREACHED

The table uses \| only so that the bars do not break the table. The program prints a plain vertical bar, |, with no backslash.

Study the rows before you write any code. Find the row that tests the breach boundary, the row that tests the escalation rule, and the rows that test rejection. Each row was chosen to catch a specific mistake, and the next section names those mistakes.

Requirements checklist

Tick each item as you finish it. A solution that meets every item is the 100% solution in the rubric below.

  • The program reads the category as a char and the priority and hours as int.
  • Bad category, priority or hours print REJECT and nothing else.
  • The queue name is chosen with a switch on the category.
  • The target is chosen with an if and else if ladder, in order, from priority 1 to 4.
  • The breach test comes before the at-risk test.
  • A remaining time of exactly 0 gives AT RISK, and exactly 1 also gives AT RISK.
  • A remaining time of -1 gives BREACHED.
  • Escalation is decided by a nested if inside the breached branch.
  • The escalation suffix is chosen with a ternary operator.
  • The output uses the exact format, including the spaces around each vertical bar and the letter h after each target.
  • The program compiles with g++ -std=c++17 -Wall -Wextra and prints no warnings.
  • Every example in the table gives the exact output shown.

Some items need a word of explanation. The switch item asks for the category to pick the queue, because a switch suits a set of exact characters. The ladder item asks for the priority to pick the target in order, because the targets are ranges of priorities that you list one by one. The nested if item asks for escalation to sit inside the breached branch, because the rule is only about breached tickets. The ternary item asks for the suffix to be chosen with a ternary, because the suffix is one value chosen from two.

Grading rubric

Your work is graded in three bands. Read each band before you start, so you know what the grader will look for.

60%: a working core. The program compiles and handles the three happy-path examples in the table correctly: the ones with OK, AT RISK and BREACHED without escalation. It prints the right queue and target in each case. It may not reject invalid data, and it may not escalate. This level shows that you can read the input, map the values, and print a line.

80%: the full rules. Every example in the table gives the exact output, including both rejection cases, the boundary cases with remaining time of 0 and 1, and the escalation for breached tickets with priority 1 and 2. The program uses a switch for the queue and an else if ladder for the target. The breach test comes before the at-risk test. The code is readable, with each rule in its own part of the program.

100%: a clean, explained solution. Everything in the 80% band is present, and the escalation uses a nested if inside the breached branch, and the suffix uses a ternary. The program compiles with no warnings under -Wall -Wextra. The code has braces on every branch. You also include a short comment above each rule that names the rule in words. You can explain in one or two sentences why you chose a switch for the queue, and why you chose a ladder for the target. You have also tested the boundary of every rule, including zero hours and the hour that sits exactly on the target.

A common reason for losing marks is printing the wrong status at the boundary. A ticket with 0 remaining hours is AT RISK, not OK, and a ticket with -1 remaining hours is BREACHED, not AT RISK. The examples in the table test both of those edges.

Plan before you code

Write the plan in plain English before writing any C++. A good plan has one line per rule, and each line names the tool that will express it. Here is the start of one, to show the level of detail expected.

  1. Read category, priority and hours.
  2. Reject if the category is wrong, the priority is outside 1 to 4, or the hours are negative.
  3. Switch on the category to set the queue name.
  4. Ladder on the priority to set the target.
  5. Subtract the hours from the target to get the remaining time.
  6. Ladder on the remaining time to set the status, with a nested test for escalation under BREACHED.
  7. Ternary to pick the suffix.
  8. Print the line.

Once you have the plan, the code follows almost line by line. If you find yourself stuck while coding, go back to the plan and check which step you are on.

Hints

Try each step yourself before you open its hint.

Hint 1: reading the input

Declare one char for the category and two int variables for the priority and the hours. Read them in the order they appear on the input, with cin >> category >> priority >> hours;. Whitespace between values is skipped, so the same line works whether the values are on one line or three.

Hint 2: rejecting bad data

Put the numeric checks in one if, joined with ||: the priority is less than 1, or the priority is more than 4, or the hours are less than 0. Inside that block, print REJECT and then return 0;. The return stops the program at once, so later code never runs for a rejected ticket. For the category, you can reject it inside the switch's default case, because an unknown character is exactly what that case is for.

Hint 3: the queue switch

Declare string queue; before the switch, and assign it in each case. Remember that every case that should end needs a break. The default case prints REJECT and returns, so the queue is never empty when the program continues. You need #include <string> for string.

Hint 4: the target ladder

Test priority == 1 first, then priority == 2, then priority == 3, and make the last branch an else that covers priority 4. Because the data is already checked, you do not need a separate test for 4. The ladder is simple, but it still needs the values in the right order, so that each rung handles exactly one priority.

Hint 5: the status and the escalation

Compute int remaining = target - hours; and test it. Put the breach test first: if (remaining < 0). Inside that branch set the status to BREACHED, and then add a nested if (priority <= 2) that sets a bool named escalate to true. The next branch tests remaining <= 1 for AT RISK, and the last branch gives OK.

Hint 6: the suffix and the output

Write string suffix = escalate ? " | ESCALATE" : ""; to choose the suffix in one line. Then print the whole line with one cout: the queue, the text | SLA, the target, the letter h, the text |, the status, and the suffix. Check the spaces. The output must match the table exactly, so a missing space is a wrong answer.

Reference solution

Try the whole program yourself before you read this. When you compare, look for differences in the order of the tests, not only in the names.

Reference solution
#include <iostream>
#include <string>
using namespace std;
 
int main() {
    char category;
    int priority, hours;
    cin >> category >> priority >> hours;
 
    // Rule 1: reject bad data.
    if (priority < 1 || priority > 4 || hours < 0) {
        cout << "REJECT" << endl;
        return 0;
    }
 
    // Rule 2: choose the queue with a switch on the category.
    string queue;
    switch (category) {
        case 'B': queue = "Billing"; break;
        case 'T': queue = "Technical"; break;
        case 'O': queue = "General"; break;
        default:
            cout << "REJECT" << endl;
            return 0;
    }
 
    // Rule 3: choose the response target with a ladder on the priority.
    int target;
    if (priority == 1) {
        target = 1;
    } else if (priority == 2) {
        target = 4;
    } else if (priority == 3) {
        target = 24;
    } else {
        target = 72;
    }
 
    // Rule 4 and rule 5: status, with escalation nested under BREACHED.
    int remaining = target - hours;
    string status;
    bool escalate = false;
    if (remaining < 0) {
        status = "BREACHED";
        if (priority <= 2) {
            escalate = true;
        }
    } else if (remaining <= 1) {
        status = "AT RISK";
    } else {
        status = "OK";
    }
 
    // Rule 6: the suffix is one value chosen from two, so a ternary fits.
    string suffix = escalate ? " | ESCALATE" : "";
    cout << queue << " | SLA " << target << "h | " << status << suffix << endl;
    return 0;
}

Here is what the reference prints for the nine examples in the table, in the same order. Check your own program against this list, and then against the table, because a difference in either one is a bug.

Billing | SLA 4h | BREACHED | ESCALATE
Technical | SLA 1h | AT RISK
General | SLA 72h | OK
REJECT
REJECT
Technical | SLA 24h | AT RISK
Billing | SLA 1h | BREACHED | ESCALATE
REJECT
Billing | SLA 72h | BREACHED

The order of the two reject checks does not change the output, because both kinds of bad data print REJECT. The order of the breach test and the at-risk test does matter, and the example with remaining time of -2 shows it. If you swapped them, a ticket that is two hours late would be reported as at risk.

Stretch: a VIP flag

If you finish the assignment and want more practice, extend it. Add a fourth input line: one character, Y for a VIP customer or N for a standard one. A VIP ticket gets half of its target, rounded down, and the output line ends with | VIP after the status and before any escalation marker. A VIP ticket with priority 1 has a target of 0 after halving, so think about what that means, and decide whether to reject it or to treat its target as 1 hour. Write down your decision in a comment.

The stretch uses only what you have learned, but it asks you to combine decisions in new ways. Before you start, step through the binary search below to remind yourself how a sequence of ifs can halve a range. A VIP target could be looked up in a sorted table of targets with a binary search, and you will do that in the arrays module.

20lo5182123164235386567hi

lo, mid and hi are labelled under the boxes. An outlined box is the middle, a dashed faint box is outside the search range, a filled box is the found item.

Look for 23 in the sorted list. The search range is the whole list.

Step 1 of 4

When you have finished, reread the rubric and check each item again. A correct stretch does not replace a correct core, so make sure the nine examples still pass before you add the VIP rule.

Submitting your work

Compile your program with g++ -std=c++17 -Wall -Wextra and fix every warning before you run the examples. Run the nine examples, and record each output next to the expected line from the table. If any line differs, find the first rule that gives a different result, and fix that rule. Do not change the expected output to match your program.

When all nine match, write two sentences that explain your choice of tool for the queue and for the target. A reader should be able to tell from your explanation that you know the difference between an exact match and a range. That explanation is part of the 100% band.

What the module covered

  • if and else choose between two actions, and their conditions use comparisons joined with &&, || and !.
  • An else if ladder tests ranges in order, and a nested if asks a question only when an earlier one says yes.
  • switch picks a case by exact match, with break to end each case and default for unexpected values.
  • The ternary operator chooses one value from two and fits inside an expression.
Practice this topic: coding problems for C++ Conditionals โ†’

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.