Files
BasicsOfComputerSoftwareEng…/FinalProject/ListDisplay.cpp
2023-06-24 17:31:16 +08:00

535 lines
22 KiB
C++

#include "ListDisplay.hpp"
#include "Exceptions.hpp"
#include "Tools.hpp"
#include <iomanip>
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<BaseRecord *> &_allRecordsPtrList)
: allRecordsPtrList(_allRecordsPtrList), fromDate(), toDate(),
searchForStu(), searchForCourseName(""),
searchForRecordType(StuRecord::Any), recordNumPerPage(20),
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<BaseRecord *> 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<int>("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;
#ifdef __APPLE__
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<int>("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<int>("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<int>("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<int>("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<int>("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<int>("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;
#ifdef __APPLE__
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<int>("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<int>("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<int>("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<int>("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<int>("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<int>("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<int>("Please input a positive integer.\n",
[](const int &num) { return num > 0; });
this->searchForStu.setNumber(tempStuID == 0 ? this->searchForStu.getNumber()
: 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);
this->searchForStu.setName(tempStuName == "0" ? this->searchForStu.getName()
: 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<int>("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();
}