Era37

Locker Tracker

Feb 1st, 2024 (edited)
1,139
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 18.11 KB | None | 0 0
  1. /*
  2. Program Name: Locker Tracker
  3. Programmer Name: Osa George-Mannella
  4. Programming Language: C++
  5. Date Project started: Jan 22nd 20 2024
  6. Date Last edited: Jan 22nd 20 2024
  7. Program Description: The Locker Tracker tracks the location and owner of each locker within TAS.
  8.  
  9. Data Dictionary:
  10. command_list: A list of objects that represent the commands which the user can run.
  11. command_line: Represents the text entered by the user and is then parsed and passed into the individual handlers.
  12. command: Represents the command when iterating through the command_list
  13. found: Represents whether or not the entered command line matches an existing command.
  14. selected_function: Represents the ActionCall function of the command that the user entered.
  15. */
  16. #include <iostream>
  17. #include <string>
  18. #include <unordered_map>
  19. #include <vector>
  20. #include <fstream>
  21. #include <regex>
  22. #include <sstring>
  23. #include <functional>
  24. #include "json.hpp"
  25. #define RESET "\033[0m" //Resets the colour to default
  26. #define RED "\033[31m" //Defines the colour of red
  27. #define GREEN "\033[32m" //Defines the colour of green
  28. #define YELLOW "\033[33m" //Defines the colour of yellow
  29. #define BLUE "\033[34m" //Defines the colour of blue
  30. #define SOFTTAB "   " //Defines the softtab to three spaces
  31. #define OPTIONAL_PREFIX "--" // Defined optional prefix for optional args
  32. #ifdef __linux__ // Defines the clear command based on what operating system project was compiled with
  33. #define CLEAR "clear"
  34. #elif _WIN32
  35. #define CLEAR "cls"
  36. #elif __APPLE__
  37. #define CLEAR "clear"
  38. #else
  39. #define CLEAR "clear"
  40. #endif
  41. void PrintColor(std::string color, std::string text) // Takes in the colour and string that should be formatted
  42. {
  43.     std::cout << color << text << RESET << std::endl; // Pipe colour than text than RESET macro into output strea
  44. }
  45. int stringToInt(std::string string) // Variable to convert input string to integer
  46. {
  47.     try
  48.     {
  49.         int myInteger = std::stoi(string); // Casts string to an int
  50.         return myInteger;                  // Returns the int
  51.     }
  52.     catch (const std::invalid_argument &e) // Catched an error
  53.     {
  54.         std::cerr << "Invalid argument: " << e.what() << std::endl; // Outputs the error
  55.         exit(1);                                                    // Exists program
  56.     }
  57.     catch (const std::out_of_range &e) // Catches an error
  58.     {
  59.         std::cerr << "Out of range: " << e.what() << std::endl; // Outputs the error
  60.         exit(1);                                                // Exists the program
  61.     }
  62. }
  63.  
  64. void Error(std::string message, bool exitProgram) // Prints a red error line unless the second argument is true thee it exits the program
  65. {
  66.     PrintColor(RED, message); // Print the message in red
  67.     if (exitProgram)          // If the exit program is true
  68.         exit(1);              // Exit the program
  69. }
  70.  
  71. std::vector<std::string> stringSplit(std::string string, char split_char) // creates a vector of strings
  72. {
  73.     std::istringstream iss(string);              // Create a stringstream of the entered string
  74.     std::string token;                           // Defines the token as a storage media (passed by reference)
  75.     std::vector<std::string> tokens;             // This will be returned and is all of the tokens held by string split by split_char
  76.     while (std::getline(iss, token, split_char)) // While this function returns true that pipes in iss, stores in token seperated by split_char
  77.     {
  78.         tokens.push_back(token); // Pushes the token into the token vec
  79.     }
  80.     return tokens; // Returns tokens
  81. }
  82.  
  83. using json = nlohmann::json;                           // Bringing blohamnn/joson to top scope for ease of reference
  84. using FunctionType = std::function<void(std::string)>; // Defining the type of function that each command must abide by
  85.  
  86. class Command // Defining a command class that will store the attributes of each command when defined for ease of interaction and to ensure a basic template is followed
  87. {
  88. public:
  89.     std::string name;        // The name of the command
  90.     std::string description; // The description of what the command does
  91.     std::string usage;       // How the command should be used i.e what arguments are required/optional
  92.     FunctionType ActionCall; // The function that is called if the command is entered
  93.  
  94.     Command(std::string newName, std::string newDescription, std::string newUsage, FunctionType func)
  95.         : name(newName), description(newDescription), usage(newUsage), ActionCall(func) {} // Describes the class constructor, nothing fancy, this is boilerplate
  96. };
  97.  
  98. std::vector<Command> command_list; // Defines the command list that will store the vector of commands. This has been defined here to prevent type errors with the help command
  99.  
  100. void help(std::string helpArgs) // The help command handler is defined, follows the schema outlined by the class and iterates through the commands in the command_list
  101. {
  102.     for (const Command cmd : command_list) // Iterates through each command in the vector of commmands
  103.     {
  104.         std::cout << cmd.name << "\t" << GREEN << cmd.description << std::endl // Formats and outputs all of the attributes of the command to display the required data to the user by giving the command name then a tab then changes the colour to green then pipes the description into the output stream followed by a newline where the colour is changed to blue then a tab is inserted followed by "Usage: " then the command usage and lastly the colour is reset to the default grey
  105.                   << BLUE << "\tUsage: " << cmd.usage << RESET << std::endl;
  106.     }
  107. }
  108.  
  109. void printOwner(json owner_json) // Outputs the owner of the locker, this is a helper function, NOT A HANDLER!!!
  110. {
  111.     PrintColor(BLUE, "Owner:"); // Outputs the words "Owner: " (without quotes) in blue
  112.     if (owner_json.is_null())
  113.     {
  114.         PrintColor(RED, std::string(SOFTTAB) + "This Locker has no Owner"); // If the locker selected has no owner assigned to it, the code Displays "Owner: " in blue text followed by "This locker has no owner" which is 3 spaces away and in red text 3
  115.         return;
  116.     }
  117.     std::string student_name(owner_json["name"].get<std::string>());                  // Retrieves the students name from the json passed into the function
  118.     int student_grade(owner_json["grade"].get<int>());                                // Gets the students grade from the json
  119.     std::cout << SOFTTAB << BLUE << "Name: " << RESET << student_name << std::endl    // leaves 3 spaces before "Name:", reseting the text colour to blue and then displays the Name assigned
  120.               << SOFTTAB << BLUE << "Grade: " << RESET << student_grade << std::endl; // leaves 3 spaces before "Grade" and restest the text back to blue, displays the assigned grade followed by a % sign
  121. }
  122.  
  123. void info(std::string infoArgs) // Provides info on a locker number
  124. {
  125.     std::vector<std::string> tokens = stringSplit(infoArgs, ' '); // Splits input by space in order to isolate the entered locker number
  126.     if (tokens.size() != 2)                                       // If the split returns more than two tokens error
  127.     {
  128.         Error("Incorrect number of arguments", false); // Return incorrect number of args error
  129.         return;                                        // Return and void function
  130.     }
  131.     int lockerNumber = stringToInt(tokens[1]);               // Parse the second token into the locker number
  132.     json locker_value;                                       // Define an empty json of name locker_value
  133.     json data = json::parse(std::ifstream("students.json")); // Parses the data from students.json into a json object
  134.     for (auto &el : data)                                    // For every entry in the students json
  135.     {
  136.         if (el["lockerNumber"].get<int>() == lockerNumber) // Checks if the locker number of the item being iterated over is equal to the locker number passed in
  137.         {
  138.             locker_value = el; // If it is set the locker value variable equal to the item being iterated on
  139.             break;             // Break from the for loop since the locker has been found
  140.         }
  141.     }
  142.     std::cout << BLUE << "Number #: " << YELLOW << locker_value["lockerNumber"].get<int>() << std::endl   // Outputs the locker number of the locker with the "Number #:" part in blue and the number in yellow
  143.               << BLUE << "Hallway: " << RESET << locker_value["Hallway"].get<std::string>() << std::endl; // Outputs the Hallway text in blue then the hallway in which the locker resides in default grey
  144.     printOwner(locker_value["Owner"]);                                                                    // Calls the printOwner function which will print the owner or the respective error
  145. }
  146.  
  147. void clear(std::string clearArgs)
  148. {
  149.     std::system(CLEAR);                                            // Executes the clear command which was defined based on which machine compiled the code NOT BASED ON THE MACHINE WHICH THE CODE IS BEING RUN!
  150.     std::cout << GREEN << "Console Cleared" << RESET << std::endl; // Output success message of "Console Cleared" then reset colour back to default
  151. }
  152.  
  153. void list(std::string listArgs) // Defines the list function that will list commands with the respective filter
  154. {
  155.     std::regex range_regex(R"(--range(\s|=)[0-9]+-[0-9]+)");      // Defines regex that outlines how the range of lockers should be inputted
  156.     std::regex floor_regex(R"(--floor(\s|=)[0-9])");              // Defines regex that outlines how the floor of lockers should be inputted
  157.     std::regex hallway_regex(R"(--hallway(\s|=)[a-zA-z\-]+)");    // Defines regex that outlines how the hallway of lockers should be inputted
  158.     json data = json::parse(std::ifstream("students.json"));      // Parses the students.json into a data json
  159.     json data_copy = json::parse(std::ifstream("students.json")); // Parses students.json into a copy of the data variable
  160.     std::smatch match{};                                          // Defines the match variable that will store the regex match
  161.     if (std::regex_search(listArgs, match, range_regex))          // Checks if the listArgs matches the range regex
  162.     {
  163.         std::string matched_string = match.str().substr(8, 10);             // Substrings the match to solely extract the number range inputted
  164.         std::vector<std::string> tokens = stringSplit(matched_string, '-'); // Splits on the - so they can be each assigned their own variable later
  165.         if (tokens.size() != 2)                                             // Checks if the token array is not equal to two which would result in an error
  166.         {
  167.             Error("Incorrect number of arguments", false); // Returns an error
  168.             return;                                        // Exists program
  169.         }
  170.         int offset = 0;                           // Defines the offset that will later be releavent
  171.         int bottomRange = stringToInt(tokens[0]); // Parses the first token into the bottom range
  172.         int topRange = stringToInt(tokens[1]);    // Parses the second token into the top range
  173.         if (bottomRange >= topRange)              // If the bottom range is greater than or equal to the top range error
  174.         {
  175.             Error("Top range must be greater than bottom range", false); // Print error
  176.             return;                                                      // Return from request
  177.         }
  178.         for (auto &el : data) // Iterate through the entries in data
  179.         {
  180.             int locker_number = el["lockerNumber"].get<int>();           // Get the locker number from the entry
  181.             if (locker_number > topRange || locker_number < bottomRange) // If the locker number is more than the top range and less than the bottom range delete it from the data copy
  182.             {
  183.                 data_copy.erase(offset); // Erase from the data copy with index offset
  184.             }
  185.             else
  186.                 offset++; // Otherwise increase the offset, this is because we want to ensure we're overshooting the data we want to keep and not deleting solely the head of the vector (its not a vector but it basically is so just use ur imagination) so each time we hit a data that hits our criteria we want to increase the offset to ensure on the following iterations we will be deleting the data AFTER it and not deleting the wanted data.
  187.         }
  188.     }
  189.     if (std::regex_search(listArgs, match, floor_regex)) // Checks if the inputted listArgs meets the floor_regex criteria
  190.     {
  191.         json local_revision_copy = json::parse(data_copy.dump()); // creates a local copy of the data_copy to edit because we want to make sure we iterating through a clone of our editable data when we're editing it and `data` is NOT (or may not be) a clone of `data_clone` since it may have been edited above so for safety we need to make a copy here
  192.         int floor = stringToInt(match.str().substr(8, 11));       // Grabs the substring from 8 to 11 and converts it to an int so we have an int representing the floor
  193.         int offset = 0;                                           // Define the offset like we did above
  194.         for (auto &el : local_revision_copy)                      // Iterate through the clone we made
  195.         {
  196.             if (el["Floor"].get<int>() != floor) // Check if the floor of the object we're on is not equal to the floor the user entered
  197.             {
  198.                 data_copy.erase(offset); // If it's not equal we just erase it on the data_copy
  199.             }
  200.             else
  201.                 offset++; // Increase our offset like we did above
  202.         }
  203.     }
  204.     if (std::regex_search(listArgs, match, hallway_regex)) // Checks if the listArgs matches the hallway_regex
  205.     {
  206.         json local_revision_copy = json::parse(data_copy.dump()); // Creates the copy of the data_copy again
  207.         std::string hallway_string(match.str().substr(10, 25));   // Defines substring but no convert to int, just takes it as a string
  208.         int offset = 0;                                           // Defines offset
  209.         for (auto &el : local_revision_copy)                      // Iterates through our local copy
  210.         {
  211.             if (el["Hallway"].get<std::string>() != hallway_string) // If the hallway is not equal to the hallway entered
  212.             {
  213.                 data_copy.erase(offset); // Delete it from the data_copy
  214.             }
  215.             else
  216.                 offset++; // Increase offset blah blah blah
  217.         }
  218.     }
  219.     if (!data_copy.size()) // If the data size is equal to zero
  220.     {
  221.         Error("No lockers meet this criteria", false); // Throw this error
  222.         return;                                        // And return
  223.     }
  224.     for (auto &el : data_copy) // Iterate through the data copy we created
  225.     {
  226.         std::cout << YELLOW << el["lockerNumber"].get<int>() << GREEN << " - " << BLUE << el["Hallway"].get<std::string>() << RESET << std::endl; // Outputs locker number in yellow then green dash and the hallway that the locker is in followed by a endline statement
  227.     }
  228. }
  229.  
  230. void exitHandler(std::string exitArgs)
  231. {
  232.     Error("Bye!", true); // Prints "Bye!" in red and closes program
  233. }
  234.  
  235.  
  236.  
  237. int main()
  238. {
  239.     std::cout << "Type \"help\" for a list of commands" << std::endl;                                                                                                                                                                                                                                                                                                                                                                                    // Outputs the help command for new users
  240.     command_list = {{"exit", "Exists the program", "exit", exitHandler}, {"help", "Displays all of the commands", "help", help}, {"list", "Lists all the lockers which follow the inputted parameters as shown below", "list --range=(int-int) --floor=(int) --hallway=(string)", list}, {"info", "Lists all the info on the locker owner, requires user to input locker number", "info (int)", info}, {"clear", "Clears the console", "clear", clear}}; // Defines all of the commands that the user can call with their name, description, usage and ActionCall function that will handle the command.
  241.     while (true)                                                                                                                                                                                                                                                                                                                                                                                                                                         // Defines a true look to ask the user unlimited times
  242.     {
  243.         std::string command_line;                  // Defines the command_line that will store the text entered by the user.
  244.         std::cout << "> ";                         // Outputs line prefix
  245.         std::getline(std::cin, command_line);      // Uses getline to get the input from the user to ensure the entire line is captured.
  246.         bool found = false;                        // Set found to false as a flag so it can later be set to true if a command matching the input is found.
  247.         FunctionType selected_function;            // Defines the selected function that will store the handler function.
  248.         for (const Command command : command_list) // Iterates through the command_list
  249.         {
  250.             if (command.name == command_line.substr(0, command.name.length())) // Checks if the command name is equal to the first x letters of the comnmand line x being the length of the command.
  251.             {
  252.                 found = true;                           // Sets found to true to prevent later error
  253.                 selected_function = command.ActionCall; // Sets the selected_function to the actioncall of the command.
  254.                 break;                                  // Breaks from the for-loop. This does not terminate the upper while loop.
  255.             }
  256.         }
  257.         if (!found) // If no command was found ouput an error.
  258.         {
  259.             PrintColor(RED, "Command Not Found");
  260.         }
  261.         else // Otherwise run the function that was found.
  262.         {
  263.             selected_function(command_line);
  264.         }
  265.     }
  266. }
  267.  
Advertisement
Add Comment
Please, Sign In to add comment