diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9aa831a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: cpp +compiler: gcc +dist: trusty + +before_install: + # C++14 + - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + - sudo apt-get update -qq + +install: + # C++14 + - sudo apt-get install -qq g++-6 + - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 90 + +script: + - mkdir build + - cd build + - cmake .. + - make + - ./universityDatabase diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cdb8f91 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.1) +project(universityDatabase) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +include_directories(include) + +set(SOURCES + src/Database.cpp + src/Employee.cpp + src/Person.cpp + src/PersonalID.cpp + src/Student.cpp + src/main.cpp +) + +set(HEADER_FILES + include/Database.hpp + include/Employee.hpp + include/Person.hpp + include/PersonalID.hpp + include/Student.hpp +) + +add_compile_options(-Wall -Wextra -Werror -Wpedantic) +add_executable(${PROJECT_NAME} ${SOURCES} ${HEADER_FILES}) diff --git a/README.md b/README.md new file mode 100644 index 0000000..f7e662c --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# UniversityDatabase +[![Build Status](https://travis-ci.org/gitmajo/UniversityDatabase.svg?branch=master)](https://travis-ci.org/gitmajo/UniversityDatabase) diff --git a/include/Database.hpp b/include/Database.hpp new file mode 100644 index 0000000..2fa6b67 --- /dev/null +++ b/include/Database.hpp @@ -0,0 +1,48 @@ +#pragma once +#include +#include +#include +#include "Person.hpp" + +using personIter = std::vector>::iterator; +using personPtr = std::shared_ptr; + +class Database +{ + private: + std::vector data_ {}; + constexpr static auto dbFilename = "../database.txt"; //literal + bool parseLineByLine(std::ifstream& ifs); + bool writeLineByLine(std::ofstream& ofs); + public: + personIter searchByLastName(const std::string& lastName); + personIter searchByPersonalID(const unsigned long long& personalID); + personIter searchByStudentID(const unsigned long& studentID); + void printDatabase() const; + void sortBySalary(); + void sortByLastName(); + void sortByPersonalID(); + void sortByStudentID(); + void addPerson(personPtr person); + + bool addStudent(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const unsigned long& studentIndex); + + bool addEmployee(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const double& salary); + + bool loadFromFile(const std::string filename = dbFilename); + bool saveToFile(const std::string filename = dbFilename); + bool removeByPersonalID(const unsigned long long& personalID); + bool removeByStudentID(const unsigned long& studentID); + bool modifySalary(const unsigned long long& personalID, const double& newSalary); + bool modifyAddress(const unsigned long long& personalID, const std::string& address); +}; diff --git a/include/Employee.hpp b/include/Employee.hpp new file mode 100644 index 0000000..88f1df3 --- /dev/null +++ b/include/Employee.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "Person.hpp" + +class Employee : public Person +{ + private: + double salary_; + public: + Employee(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const double& salary); + + double getSalary() const; + void setSalary(const double& salary); + std::string getInfo() const; + +}; + diff --git a/include/Person.hpp b/include/Person.hpp new file mode 100644 index 0000000..340517c --- /dev/null +++ b/include/Person.hpp @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +enum class Gender {female, male}; + +class Person +{ + protected: + std::string firstName_; + std::string lastName_; + unsigned long long personalID_; + Gender gender_; + std::string address_; + std::map convMap_ {{Gender::female, '0'}, {Gender::male, '1'}}; + + public: + Person(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address); + + + std::string getLastName() const; + unsigned long long getPersonalID() const; + void setAddress(const std::string& newAddress); + + friend std::ostream& operator<<(std::ostream& os, const Person* person); + virtual std::string getInfo() const; + virtual double getSalary() const; + virtual unsigned long getStudentIndex() const; + virtual void setSalary(const double& salary); + virtual ~Person() {}; +}; + diff --git a/include/PersonalID.hpp b/include/PersonalID.hpp new file mode 100644 index 0000000..64e0cc5 --- /dev/null +++ b/include/PersonalID.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace ValidatePersonalID +{ + bool validatePersonalID(const unsigned long long& personalID); + void test(); +} + +namespace vpid = ValidatePersonalID; + diff --git a/include/Student.hpp b/include/Student.hpp new file mode 100644 index 0000000..3206a24 --- /dev/null +++ b/include/Student.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "Person.hpp" + +class Student : public Person +{ + private: + unsigned long studentIndex_; + + public: + Student(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const unsigned long& studentIndex); + + unsigned long getStudentIndex() const; + std::string getInfo() const; +}; diff --git a/src/Database.cpp b/src/Database.cpp new file mode 100644 index 0000000..8faeb7c --- /dev/null +++ b/src/Database.cpp @@ -0,0 +1,241 @@ +#include "Database.hpp" +#include "Employee.hpp" +#include "Student.hpp" +#include +#include +#include +#include + +using studentPtr = std::shared_ptr; +using employeePtr = std::shared_ptr; + +personIter Database::searchByLastName(const std::string& lastName) +{ + auto it = std::find_if(begin(data_), end(data_), [lastName] (personPtr person) + { + return person->getLastName() == lastName; + }); + + if (it != end(data_)) + std::cout << *it << "\n"; + else + std::cout << "Person " << lastName << " not found\n\n"; + return it; +} + +personIter Database::searchByPersonalID(const unsigned long long& personalID) +{ + auto it = std::find_if(begin(data_), end(data_), [personalID] (personPtr person) + { + return person->getPersonalID() == personalID; + }); + + if (it != end(data_)) + std::cout << *it << "\n"; + else + std::cout << "Personal ID " << personalID << " not found.\n\n"; + return it; +} + +personIter Database::searchByStudentID(const unsigned long& studentID) +{ + auto it = std::find_if(begin(data_), end(data_), [studentID] (personPtr person) + { + return person->getStudentIndex() == studentID; + }); + + if (it != end(data_)) + std::cout << *it << "\n"; + else + std::cout << "Student ID " << studentID << " not found.\n\n"; + return it; +} + +void Database::printDatabase() const +{ + if(data_.empty()) + std::cout << "Empty data_base!\n"; + + for(const auto& personPtr : data_) + std::cout << personPtr; + std::cout << "\n"; +} + +void Database::sortBySalary() +{ + std::sort(begin(data_), end(data_), [](personPtr left, personPtr right) + { //input: 10.1 Nan 2.5 Nan 3.6 //output: 2.5 3.6 10.1 Nan Nan + + //NaN 2.5 --- if NaN on the left: bad! + if (std::isnan(left->getSalary())) return false; + + //2.5 NaN -- if NaN on the right: good! + if (std::isnan(right->getSalary())) return true; + + //left and right are finite, so compare it usually + return left->getSalary() < right->getSalary(); + }); +} + +void Database::sortByLastName() +{ + std::sort(begin(data_), end(data_), [](personPtr left, personPtr right) + { + return left->getLastName() < right->getLastName(); + }); +} + +void Database::sortByPersonalID() +{ + std::sort(begin(data_), end(data_), [](personPtr left, personPtr right) + { + return left->getPersonalID() < right->getPersonalID(); + }); +} + +void Database::sortByStudentID() +{ + std::sort(begin(data_), end(data_), [](personPtr left, personPtr right) + { + return left->getStudentIndex() < right->getStudentIndex(); + }); +} + +void Database::addPerson(personPtr person) +{ + data_.push_back(person); +} + +bool Database::addStudent(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const unsigned long& studentIndex) +{ + personPtr student = std::make_shared(firstName, + lastName, personalID, gender, address, studentIndex); + addPerson(student); + return true; +} + +bool Database::addEmployee(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const double& salary) +{ + personPtr employee = std::make_shared(firstName, + lastName, personalID, gender, address, salary); + addPerson(employee); + return true; +} + +bool Database::loadFromFile(const std::string filename/*="database.txt"*/) +{ + std::ifstream ifs {filename}; //input file stream + if(!ifs) + { + std::cout << "Could not open " << filename << " for reading!\n"; + return false; + } + return parseLineByLine(ifs); +} + +bool Database::parseLineByLine(std::ifstream& ifs) +{ + std::string firstName, lastName, address, salary, studentIndex; + unsigned long long personalID; + char cGender; + std::map convMap {{'0', Gender::female}, {'1', Gender::male}}; + + while(ifs >> lastName >> firstName >> personalID >> cGender >> + address >> salary >> studentIndex) + { + if(salary == "----") + addStudent(firstName, lastName, personalID, convMap[cGender], address, std::stoul(studentIndex)); + else if(studentIndex == "----") + addEmployee(firstName, lastName, personalID, convMap[cGender], address, std::stod(salary)); + else + { + std::cout << "\nInvalid line in input file!\n"; + return false; + } + } + return true; +} + +bool Database::saveToFile(const std::string filename/*="database.txt"*/) +{ + std::ofstream ofs {filename}; //output file stream + + if(!ofs) + { + std::cout << "Could not open " << filename << " for writing!\n"; + return false; + } + return writeLineByLine(ofs); +} + +bool Database::writeLineByLine(std::ofstream& ofs) +{ + for(const auto& personPtr : data_) + ofs << personPtr; + ofs << "\n"; + return true; +} + +bool Database::removeByPersonalID(const unsigned long long& personalID) +{ + auto iter = searchByPersonalID(personalID); + + if (iter != end(data_)) + { + data_.erase(iter); + return true; + } + else + return false; +} + +bool Database::removeByStudentID(const unsigned long& studentID) +{ + auto iter = searchByStudentID(studentID); + + if (iter != end(data_)) + { + data_.erase(iter); + return true; + } + else + return false; +} + +bool Database::modifySalary(const unsigned long long& personalID, const double& newSalary) +{ + auto personIter = searchByPersonalID(personalID); + + if (personIter != data_.end()) + { + if(std::isfinite((*personIter)->getSalary())) + { + (*personIter)->setSalary(newSalary); + return true; + } + std::cout << "Personal ID " << personalID << " is not an Employee.\n\n"; + } + return false; +} + +bool Database::modifyAddress(const unsigned long long& personalID, const std::string& newAddress) +{ + auto personIter = searchByPersonalID(personalID); + + if (personIter != data_.end()) + { + (*personIter)->setAddress(newAddress); + return true; + } + return false; +} diff --git a/src/Employee.cpp b/src/Employee.cpp new file mode 100644 index 0000000..279a1e1 --- /dev/null +++ b/src/Employee.cpp @@ -0,0 +1,39 @@ +#include "Employee.hpp" +#include +#include + +Employee::Employee(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const double& salary) +: Person(firstName, lastName, personalID, gender, address), + salary_(salary) +{} + +double Employee::getSalary() const +{ + return salary_; +} + +void Employee::setSalary(const double& salary) +{ + salary_ = salary; +} + +std::string Employee::getInfo() const +{ + auto ss = std::stringstream{}; + + ss << std::left << std::setw(13) << lastName_ << " " + << std::left << std::setw(13) << firstName_ << " " + << std::left << std::setw(13) << personalID_ << " " + << std::left << std::setw(8) << convMap_.at(gender_) << " " + << std::left << std::setw(13) << address_ << " " + << std::left << std::setw(13) << salary_ << " " + << std::left << std::setw(13) << "----" << "\n"; + + return ss.str(); + +} diff --git a/src/Person.cpp b/src/Person.cpp new file mode 100644 index 0000000..77ee6ac --- /dev/null +++ b/src/Person.cpp @@ -0,0 +1,55 @@ +#include "Person.hpp" +#include +#include +#include + +Person::Person(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address) +: firstName_(firstName), + lastName_(lastName), + personalID_(personalID), + gender_(gender), + address_(address) +{} + +std::string Person::getLastName() const +{ + return lastName_; +} + +unsigned long long Person::getPersonalID() const +{ + return personalID_; +} + +std::string Person::getInfo() const +{ + auto ss = std::stringstream{}; + ss << std::left << std::setw(13) << lastName_ << " " + << std::left << std::setw(13) << firstName_ << " " + << std::left << std::setw(13) << personalID_ << " " + << std::left << std::setw(8) << convMap_.at(gender_) << " " + << std::left << std::setw(13) << address_ << "\n"; + + return ss.str(); +} + +double Person::getSalary() const +{ + return std::nan(""); +} + +unsigned long Person::getStudentIndex() const +{ + return 0; +} + +void Person::setAddress(const std::string& newAddress) +{ + address_ = newAddress; +} + +void Person::setSalary(const double& /*salary*/) { } diff --git a/src/PersonalID.cpp b/src/PersonalID.cpp new file mode 100644 index 0000000..8d90b94 --- /dev/null +++ b/src/PersonalID.cpp @@ -0,0 +1,91 @@ +#include "PersonalID.hpp" +#include +#include +#include + +namespace +{ + bool initialIdCheck(std::vector& digits, const unsigned long long& personalID); + void splitNumberIntoDigits(std::vector& digits, unsigned long long number); + bool checkFormatOfDate(std::vector& digits); + bool checkControlSum(std::vector& digits); +} + +namespace ValidatePersonalID +{ + bool validatePersonalID(const unsigned long long& personalID) + { + std::vector digits {}; + + if( !initialIdCheck(digits, personalID)) return false; + if( !checkFormatOfDate(digits)) return false; + if( !checkControlSum(digits)) return false; + + return true; + } + + void test() + { + unsigned long long a = 44051401358; + unsigned long long b = 81100216357; + unsigned long long c = 81100216353; + unsigned long long d = 65071209862; + unsigned long long e = 12076509862; + + std::cout << a << ": " << validatePersonalID(a) << "\n"; + std::cout << b << ": " << validatePersonalID(b) << "\n"; + std::cout << c << ": " << validatePersonalID(c) << "\n"; + std::cout << d << ": " << validatePersonalID(d) << "\n"; + std::cout << e << ": " << validatePersonalID(e) << "\n\n"; + } +} + +namespace +{ + bool initialIdCheck(std::vector& digits, const unsigned long long& personalID) + { + const int personalIdLength = 11; + splitNumberIntoDigits(digits, personalID); + + if(digits.size() != personalIdLength) + return false; + + return true; + } + + void splitNumberIntoDigits(std::vector& digits, unsigned long long number) + { + int digit = 0; + while(number > 0) + { + digit = number % 10; + number /= 10; + digits.insert(digits.begin(), digit); + } + } + + bool checkFormatOfDate(std::vector& digits) + { + //check if yy-mm-dd format was changed to dd-mm-yy + int day = digits[4]*10 + digits[5]; + if(day >= 1 and day <= 31) + return true; + + return false; + } + + bool checkControlSum(std::vector& digits) + { + const std::vector multipliers {1, 3, 7, 9, 1, 3, 7, 9, 1, 3, 1}; + + //sum = (dig[i]*mul[i] + dig[i+1]*mul[i+1] + ... + unsigned long int sum = std::inner_product(digits.begin(), digits.end(), + multipliers.begin(), 0); + + if(sum % 10 == 0) + return true; + + return false; + } +} + diff --git a/src/Student.cpp b/src/Student.cpp new file mode 100644 index 0000000..5a41d1b --- /dev/null +++ b/src/Student.cpp @@ -0,0 +1,34 @@ +#include "Student.hpp" +#include +#include + +Student::Student(const std::string& firstName, + const std::string& lastName, + const unsigned long long& personalID, + const Gender& gender, + const std::string& address, + const unsigned long& studentIndex) +: Person(firstName, lastName, personalID, gender, address), + studentIndex_(studentIndex) +{} + +unsigned long Student::getStudentIndex() const +{ + return studentIndex_; +} + +std::string Student::getInfo() const +{ + auto ss = std::stringstream{}; + + ss << std::left << std::setw(13) << lastName_ << " " + << std::left << std::setw(13) << firstName_ << " " + << std::left << std::setw(13) << personalID_ << " " + << std::left << std::setw(8) << convMap_.at(gender_) << " " + << std::left << std::setw(13) << address_ << " " + << std::left << std::setw(13) << "----" << " " + << std::left << std::setw(13) << studentIndex_ << "\n"; + + return ss.str(); +} + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..97228e2 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,95 @@ +#include "Database.hpp" +#include "PersonalID.hpp" + +std::ostream& operator<<(std::ostream& os, const Person* person) +{ + os << person->getInfo(); + return os; +} + + +int main() +{ + Database db; + + db.addStudent("Krzysztof", "Jarzyna", 91653426865, Gender::male, "Szczecin", 111111); + db.addStudent("Tomasz", "Kowalski", 87654237541, Gender::male, "Wroclaw", 122222); + db.addStudent("Kasia", "Nowak", 64247643211, Gender::female, "Opole", 334568); + db.addEmployee("Jan", "Szymczak", 87235681241, Gender::male, "Wroclaw", 3674); + db.addStudent("Franek", "Dabrowski", 11210754919, Gender::male, "Lodz", 123422); + db.addEmployee("Aldona", "Tomczyk", 65321543987, Gender::female, "Lublin", 2211); + db.addStudent("Stanislaw", "Olech", 90764357981, Gender::male, "Lublin", 265421); + + db.printDatabase(); + + std::cout << "sortBySalary():\n"; + db.sortBySalary(); + db.printDatabase(); + + std::cout << "sortByLastName():\n"; + db.sortByLastName(); + db.printDatabase(); + + std::cout << "sortByPersonalID():\n"; + db.sortByPersonalID(); + db.printDatabase(); + + std::cout << "Searching for Puchatek:\n"; + db.searchByLastName("Puchatek"); + + std::cout << "Searching for ID 87235681241:\n"; + db.searchByPersonalID(87235681241); + + std::cout << "Searching for index 334568:\n"; + db.searchByStudentID(334568); + std::cout << "removeByStudentID(334568):\n"; + db.removeByStudentID(334568); + db.printDatabase(); + + std::cout << "Searching for PersonalID 11210754919:\n"; + db.searchByPersonalID(11210754919); + std::cout << "removeByPersonalID(11210754919):\n"; + db.removeByPersonalID(11210754919); + db.printDatabase(); + + + std::cout << "sortByStudentID():\n"; + db.sortByStudentID(); + db.printDatabase(); + + db.saveToFile(); + + Database temp; + temp.printDatabase(); + + temp.loadFromFile(); + temp.printDatabase(); + + std::cout << "validatePersonalID():\n"; + vpid::test(); + + std::cout << "Searching for ID 87235681241:\n"; + db.searchByPersonalID(87235681241); + + std::cout << "modifyAddress(87235681241, Kalisz):\n"; + db.modifyAddress(87235681241, "Kalisz"); + db.searchByPersonalID(87235681241); + + std::cout << "modifyAddress(invalid, Kalisz):\n"; + db.modifyAddress(87235681240, "Kalisz"); + + std::cout << "modifySalary(87235681241, 3333):\n"; + db.modifySalary(87235681241, 3333); + db.searchByPersonalID(87235681241); + + std::cout << "modifySalary(invalid, 3333):\n"; + db.modifySalary(87235681240, 3333); + + std::cout << "modifySalary(student, 3333):\n"; + db.modifySalary(91653426865, 3333); + + + + + return 0; +}