#include "ListDisplay.hpp" #include "Exceptions.hpp" #include "Tools.hpp" #include const std::string ListDisplay::searchForCourseNameToString() const { return this->searchForCourseName == "" ? "[Any]" : this->searchForCourseName; } const std::string ListDisplay::searchForRecordTypeToString() const { switch (this->searchForRecordType) { case StuRecord::Late: return "Late"; case StuRecord::Absent: return "Absent"; case StuRecord::PersonalLeave: return "Personal"; default: return "[Any]"; } } ListDisplay::ListDisplay(const List &_allRecordsPtrList) : allRecordsPtrList(_allRecordsPtrList), fromDate(), toDate(), searchForStu(), searchForCourseName(""), searchForRecordType(StuRecord::Any), recordNumPerPage(10), currentPageIndex(0), maxPageIndex((allRecordsPtrList.length() - 1) / recordNumPerPage), sortOrder(ASCENT), sortByProp(RecordID) { this->filteredRecordsPtrList = allRecordsPtrList.filtered([](BaseRecord *const &) { return true; }); }; // -1 / 20 = 0, so don't worry about the maxPageIndex being -1. bool ListDisplay::hasNext() { return currentPageIndex < maxPageIndex; } bool ListDisplay::hasPrev() { return currentPageIndex > 0; } void ListDisplay::next() { if (!this->hasNext()) { throw(PageIndexError("No next page!")); } this->currentPageIndex++; } void ListDisplay::prev() { if (!this->hasPrev()) { throw(PageIndexError("No prev page!")); } this->currentPageIndex--; } void ListDisplay::display() { std::cout << setoutputcolor(ConsoleColorTool::blue) << "Search date: from " << resetOutputColor << this->fromDate.toString() << setoutputcolor(ConsoleColorTool::blue) << " to " << resetOutputColor << this->toDate.toString(); std::cout << " "; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Student: ID: " << resetOutputColor << this->searchForStu.getNumberString() << setoutputcolor(ConsoleColorTool::blue) << " Name: " << resetOutputColor << this->searchForStu.getName() << "\n"; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Course name: " << resetOutputColor << this->searchForCourseNameToString(); std::cout << " "; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Record type: " << resetOutputColor << this->searchForRecordTypeToString() << "\n"; // std::cout << setbgcolor(ConsoleColorTool::cyan); // std::cout << this->tableTitleRow << std::endl; std::cout << "│ "; std::string directionChar = (this->sortOrder == ASCENT ? "^ " : "v "); std::cout << setMiddle( (this->sortByProp == SortBy::RecordID ? directionChar : "") + "Record ID", recordDisplayRowSize.recordID); std::cout << " │ "; std::cout << setMiddle( (this->sortByProp == SortBy::RecordDate ? directionChar : "") + "Date", recordDisplayRowSize.date); std::cout << " │ "; std::cout << setMiddle( (this->sortByProp == SortBy::RecordCourseName ? directionChar : "") + "Course Name", recordDisplayRowSize.courseName); std::cout << " │ "; std::cout << setMiddle( (this->sortByProp == SortBy::RecordStudentID ? directionChar : "") + "Stud. ID", recordDisplayRowSize.studentID); std::cout << " │ "; std::cout << setMiddle( (this->sortByProp == SortBy::RecordStudentName ? directionChar : "") + "Stud. Name", recordDisplayRowSize.studentName); std::cout << " │ "; std::cout << setMiddle( (this->sortByProp == SortBy::RecordType ? directionChar : "") + "Type", recordDisplayRowSize.type); std::cout << " │\n"; if (this->filteredRecordsPtrList.length() == 0) { std::cout << "(No record)\n"; } else { Iterator iter = this->filteredRecordsPtrList.iterate( this->currentPageIndex * this->recordNumPerPage); int i = recordNumPerPage; bool brightBG = true; while (i > 0 && iter) { if (brightBG) { std::cout << setbgcolor(ConsoleColorTool::lightGray); } iter.next()->display(); if (brightBG) { std::cout << resetOutputColor; } brightBG = !brightBG; i--; } } std::cout << setoutputcolor(ConsoleColorTool::green) << "Total " << resetOutputColor << this->allRecordsPtrList.length() << setoutputcolor(ConsoleColorTool::green) << " record(s), " << resetOutputColor << this->filteredRecordsPtrList.length() << setoutputcolor(ConsoleColorTool::green) << " matched Page " << resetOutputColor << this->currentPageIndex + 1 << setoutputcolor(ConsoleColorTool::green) << " of " << resetOutputColor << this->maxPageIndex + 1 << std::endl; } void ListDisplay::setSortBy(const SortBy _propName) { this->sortByProp = _propName; this->sortOrder = ASCENT; this->reapplyFilter(); this->currentPageIndex = 0; } void ListDisplay::setSortOrder(const bool newOrder) { this->sortOrder = newOrder; this->reapplyFilter(); this->currentPageIndex = 0; } void ListDisplay::flipSortOrder() { this->sortOrder = !this->sortOrder; this->reapplyFilter(); this->currentPageIndex = 0; } void ListDisplay::reapplyFilter() { this->filteredRecordsPtrList = this->allRecordsPtrList.filtered( [&](BaseRecord *const &recordPtr) -> bool { return (recordPtr->getDate() >= this->fromDate && recordPtr->getDate() <= this->toDate && recordPtr->getStudent() == this->searchForStu && (this->searchForCourseName.empty() || recordPtr->getCourseName() == this->searchForCourseName) && (this->searchForRecordType == StuRecord::Any || recordPtr->getRecordType() == this->searchForRecordType)); }); switch (this->sortByProp) { case SortBy::RecordID: this->filteredRecordsPtrList.sort( [&](BaseRecord *const &recordAPtr, BaseRecord *const &recordBPtr) -> bool { return (recordAPtr->getRecordID() < recordBPtr->getRecordID() == this->sortOrder || recordAPtr->getRecordID() == recordBPtr->getRecordID()); }); break; case SortBy::RecordDate: this->filteredRecordsPtrList.sort( [&](BaseRecord *const &recordAPtr, BaseRecord *const &recordBPtr) -> bool { return (recordAPtr->getDate() > recordBPtr->getDate() == this->sortOrder || recordAPtr->getDate() == recordBPtr->getDate()); }); break; case SortBy::RecordCourseName: this->filteredRecordsPtrList.sort( [&](BaseRecord *const &recordAPtr, BaseRecord *const &recordBPtr) -> bool { return ( recordAPtr->getCourseName() < recordBPtr->getCourseName() == this->sortOrder || recordAPtr->getCourseName() == recordBPtr->getCourseName()); }); break; case SortBy::RecordStudentID: this->filteredRecordsPtrList.sort( [&](BaseRecord *const &recordAPtr, BaseRecord *const &recordBPtr) -> bool { return (recordAPtr->getStudentNumber() < recordBPtr->getStudentNumber() == this->sortOrder || recordAPtr->getStudentNumber() == recordBPtr->getStudentNumber()); }); break; case SortBy::RecordStudentName: this->filteredRecordsPtrList.sort( [&](BaseRecord *const &recordAPtr, BaseRecord *const &recordBPtr) -> bool { return (recordAPtr->getStudentName() < recordBPtr->getStudentName() == this->sortOrder || recordAPtr->getStudentName() == recordBPtr->getStudentName()); }); break; case SortBy::RecordType: this->filteredRecordsPtrList.sort( [&](BaseRecord *const &recordAPtr, BaseRecord *const &recordBPtr) -> bool { return ( recordAPtr->getRecordType() < recordBPtr->getRecordType() == this->sortOrder || recordAPtr->getRecordType() == recordBPtr->getRecordType()); }); break; default: { // Does Nothing ; } } this->currentPageIndex = 0; this->maxPageIndex = (this->filteredRecordsPtrList.length() - 1) / this->recordNumPerPage; } void ListDisplay::promptForRecordID() { int targetID; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Record ID to search for: " << resetOutputColor << std::flush; // std::cin >> targetID; targetID = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); try { clearScreen(); this->allRecordsPtrList .search([&](BaseRecord *const &rescordPtr) -> bool { return rescordPtr->getRecordID() == targetID; }) ->displayComplete(); } catch (const ValueError &e) { std::cout << setoutputcolor(ConsoleColorTool::red) << "Such record does not exist!" << resetOutputColor << std::endl; } } void ListDisplay::promptForFromDate() { time_t originTime = this->fromDate.getTime(); tm curDate; #if defined __APPLE__ || defined __linux__ curDate = *(localtime(&originTime)); #endif #if defined _WIN64 || defined _WIN32 localtime_s(&curDate, &originTime); #endif int tempInput; if (!this->fromDate.isAny()) { char buffer[20]; strftime(buffer, 20, "%F", &curDate); std::cout << setoutputcolor(ConsoleColorTool::green) << "Current date: " << buffer << ". Enter 0 to retain original value." << resetOutputColor << std::endl; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Year: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num >= 0; }); curDate.tm_year = (tempInput == 0 ? curDate.tm_year : tempInput - 1900); std::cout << setoutputcolor(ConsoleColorTool::blue) << "Month: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num >= 0; }); curDate.tm_mon = (tempInput == 0 ? curDate.tm_mon : tempInput - 1); std::cout << setoutputcolor(ConsoleColorTool::blue) << "Day: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num >= 0; }); curDate.tm_mday = (tempInput == 0 ? curDate.tm_mday : tempInput); } else { std::cout << setoutputcolor(ConsoleColorTool::blue) << "Year: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); curDate.tm_year = tempInput - 1900; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Month: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); curDate.tm_mon = tempInput - 1; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Day: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); curDate.tm_mday = tempInput; } this->fromDate = mktime(&curDate); this->reapplyFilter(); } void ListDisplay::promptForToDate() { time_t originTime = this->fromDate.getTime(); tm curDate; #if defined __APPLE__ || defined __linux__ curDate = *(localtime(&originTime)); #endif #if defined _WIN64 || defined _WIN32 localtime_s(&curDate, &originTime); #endif int tempInput; if (!this->toDate.isAny()) { char buffer[20]; strftime(buffer, 20, "%F", &curDate); std::cout << setoutputcolor(ConsoleColorTool::green) << "Current date: " << buffer << ". Enter 0 to retain original value." << resetOutputColor << std::endl; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Year: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num >= 0; }); curDate.tm_year = (tempInput == 0 ? curDate.tm_year : tempInput - 1900); std::cout << setoutputcolor(ConsoleColorTool::blue) << "Month: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num >= 0; }); curDate.tm_mon = (tempInput == 0 ? curDate.tm_mon : tempInput - 1); std::cout << setoutputcolor(ConsoleColorTool::blue) << "Day: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num >= 0; }); curDate.tm_mday = (tempInput == 0 ? curDate.tm_mday : tempInput); } else { std::cout << setoutputcolor(ConsoleColorTool::blue) << "Year: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); curDate.tm_year = tempInput - 1900; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Month: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); curDate.tm_mon = tempInput - 1; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Day: " << resetOutputColor << std::flush; // std::cin >> tempInput; // std::cin.ignore(); tempInput = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); curDate.tm_mday = tempInput; } curDate.tm_hour = 0; curDate.tm_min = 0; curDate.tm_sec = 0; curDate.tm_isdst = 0; this->toDate = mktime(&curDate); this->reapplyFilter(); } void ListDisplay::promptForSearchStuID() { int tempStuID; std::cout << setoutputcolor(ConsoleColorTool::green) << "Current student info: \n" << setoutputcolor(ConsoleColorTool::blue) << "Student ID: " << resetOutputColor << this->searchForStu.getNumber() << "\n" << setoutputcolor(ConsoleColorTool::blue) << "Student Name: " << resetOutputColor << this->searchForStu.getName() << "\n" << setoutputcolor(ConsoleColorTool::green) << "Enter 0 to retain original value." << std::endl; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Student ID: " << resetOutputColor << std::flush; // std::cin >> tempStuID; // std::cin.ignore(); tempStuID = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num >= 0; }); if (tempStuID != 0) { this->searchForStu.setNumber(tempStuID); } this->reapplyFilter(); } void ListDisplay::promptForSearchStuName() { std::string tempStuName; std::cout << setoutputcolor(ConsoleColorTool::green) << "Current student info: \n" << setoutputcolor(ConsoleColorTool::blue) << "Student ID: " << resetOutputColor << this->searchForStu.getNumber() << "\n" << setoutputcolor(ConsoleColorTool::blue) << "Student Name: " << resetOutputColor << this->searchForStu.getName() << "\n" << setoutputcolor(ConsoleColorTool::green) << "Enter 0 to retain original value." << std::endl; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Student Name: " << resetOutputColor << std::flush; // std::cin >> tempStuName; // std::cin.ignore(); std::getline(std::cin, tempStuName); if (tempStuName != "0") { this->searchForStu.setName(tempStuName); } this->reapplyFilter(); } void ListDisplay::promptForCourseName() { std::string tempCourseName; std::cout << setoutputcolor(ConsoleColorTool::green) << "Original course name: " << resetOutputColor << this->searchForCourseNameToString() << "\n" << setoutputcolor(ConsoleColorTool::green) << "Enter 0 to retain original value." << resetOutputColor << std::endl; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Course name: " << resetOutputColor << std::flush; // std::cin >> tempCourseName; // std::cin.ignore(); std::getline(std::cin, tempCourseName); this->searchForCourseName = (tempCourseName == "0" ? this->searchForCourseName : tempCourseName); this->reapplyFilter(); } void ListDisplay::promptForRecordType() { std::string tempIn; std::cout << setoutputcolor(ConsoleColorTool::green) << "Original target record type: " << resetOutputColor << this->searchForRecordTypeToString() << "\n" << setoutputcolor(ConsoleColorTool::green) << "Enter 0 to retain original value." << resetOutputColor << std::endl; std::cout << "Choices are: 1. (L)ate, 2. (A)bsent, 3. (P)ersonal\n"; std::cout << setoutputcolor(ConsoleColorTool::blue) << "Search for record type: " << resetOutputColor << std::flush; // std::cin >> tempIn; // std::cin.ignore(); std::getline(std::cin, tempIn); if (tempIn == "0") { return; } else if (tempIn == "1" || tempIn == "l" || tempIn == "late" || tempIn == "L" || tempIn == "Late") { this->searchForRecordType = StuRecord::Late; } else if (tempIn == "2" || tempIn == "a" || tempIn == "absent" || tempIn == "A" || tempIn == "Absent") { this->searchForRecordType = StuRecord::Absent; } else if (tempIn == "3" || tempIn == "p" || tempIn == "personal" || tempIn == "P" || tempIn == "Personal") { this->searchForRecordType = StuRecord::PersonalLeave; } else { std::cout << setoutputcolor(ConsoleColorTool::red) << "Unknown record type. No change made." << resetOutputColor << std::endl; } this->reapplyFilter(); } void ListDisplay::promptRecordNumPerPage() { int tempPerPageNum; std::cout << setoutputcolor(ConsoleColorTool::green) << "Current record number per page: " << resetOutputColor << this->recordNumPerPage << std::endl; std::cout << setoutputcolor(ConsoleColorTool::blue) << "New record number per page: " << resetOutputColor << std::flush; // std::cin >> tempPerPageNum; // std::cin.ignore(); tempPerPageNum = safeInputNum("Please input a positive integer.\n", [](const int &num) { return num > 0; }); if (tempPerPageNum <= 0) { std::cout << setoutputcolor(ConsoleColorTool::red) << "Not a valid number! Record number per page not changed." << resetOutputColor << std::endl; } else { this->recordNumPerPage = tempPerPageNum; } } void ListDisplay::resetSearch() { this->fromDate.setAny(); this->toDate.setAny(); this->searchForStu.setAnyNumber(); this->searchForStu.setAnyName(); this->searchForCourseName = ""; this->searchForRecordType = StuRecord::Any; this->reapplyFilter(); }