From 4af76e7e660de2b39e2cbf91899c18d8c3617fc3 Mon Sep 17 00:00:00 2001 From: s_arme Date: Fri, 23 Nov 2018 17:34:10 +0330 Subject: [PATCH 1/5] update cmake version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fd9156..1b6ce60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.8) project(stackoverflow_in_cpp) set(CMAKE_CXX_STANDARD 14) From 6474765f0b1250f9bf8effe2b5206de61612417b Mon Sep 17 00:00:00 2001 From: realsarm <37932944+realsarm@users.noreply.github.com> Date: Fri, 23 Nov 2018 21:17:09 +0330 Subject: [PATCH 2/5] Set theme jekyll-theme-merlot --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c50ff38 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-merlot \ No newline at end of file From c90a243d62eec62a6159d9eff0e2fd96d40b91fd Mon Sep 17 00:00:00 2001 From: Mirana Date: Tue, 11 Dec 2018 22:20:45 +0330 Subject: [PATCH 3/5] CRUD --- AbstractUser.h | 168 ++++++++++- Exceptions.h | 75 +++-- User.cpp | 756 +++++++++++++++++++++++++++++++++++++++++++++---- User.h | 72 +++-- main.cpp | 527 +++++++++++++++++++++++++++------- 5 files changed, 1394 insertions(+), 204 deletions(-) diff --git a/AbstractUser.h b/AbstractUser.h index 67def7a..c4a641b 100644 --- a/AbstractUser.h +++ b/AbstractUser.h @@ -3,22 +3,170 @@ #include #include #include +#include + #define lower(str) transform(str.begin(), str.end(), str.begin(), ::tolower) +#define questionTagsSize 20 +#define questionAnswersSize 20 +#define usersSeenQuestionSize 20 +#define questionEditorsSize 20 + using namespace std; +class AbstractUser; +class Answer; +class Edit; +class EditRequest; + enum UserType { - ADMIN, - MEMBER + ADMIN, + MEMBER +}; + +class Duplicate { +public: + string author; + string title; +}; + +class Answer { +public: + string author; + string content; + string creationDate; + vector editors; + int numReputation; + + Answer(string au, string co, string cd, int nr) + :author(au), content(co), creationDate(cd), numReputation(nr) { + editors.reserve(questionEditorsSize); + } +}; + +class Edit { +public: + string editorName; + string editDate; + string description; + + Edit(string en, string ed, string dc) :editorName(en), editDate(ed), description(dc) {} +}; + +class questionEditRequest { +public: + string questionAuthor; + string questionTitle; + string editorName; + string editedTitle; + string editedContent; + vector editedTags; + string editDate; + string description; + + questionEditRequest(string qa, string qt, string en, string et, string ec, vector ta, string ed, string ds) + :questionAuthor(qa), questionTitle(qt), editorName(en), editedTitle(et), editedContent(ec), editDate(ed), description(ds) { + editedTags.reserve(questionTagsSize); + editedTags.insert(editedTags.end(), ta.begin(), ta.end()); + } +}; + +class answerEditRequest { +public: + string questionAuthor; + string questionTitle; + string answerAuthor; + string editorName; + string editedContent; + string editDate; + string description; + + answerEditRequest(string qa, string qt, string aa, string en, string ec, string ed, string ds) + :questionAuthor(qa), questionTitle(qt), answerAuthor(aa), editorName(en), editedContent(ec), editDate(ed), description(ds) {} }; class AbstractUser { public: - hash pass_hash; - virtual bool authenticate(string username, string password) = 0; - virtual void deleteAccount() = 0; - string username; + // Login related + hash pass_hash; + string username; + + // Login related + virtual bool authenticate(string username, string password) = 0; + virtual void deleteAccount() = 0; + + // Question related + virtual void createQuestion(string title, string content, vector tags) = 0; + virtual void editQuestion(string title, string author, + string newTitle, string newContent, vector newTags, string description) = 0; + virtual void handleQuestionEditRequests() = 0; // for admin + virtual void deleteQuestion(string title, string author) = 0; + virtual void upvoteQuestion(string title, string author) = 0; + virtual void downvoteQuestion(string title, string author) = 0; + virtual void addQuestionToFavs(string title, string author) = 0; + virtual void deleteQuestionFromFavs(string title, string author) = 0; + virtual void viewFavQuestions() = 0; + virtual void viewQuestions(string title, string author) = 0; + virtual void changeStatus(string copyTitle, string copyAuthor, string originalTitle, string originalAuthor) = 0; // for admin + + // Answer related + virtual void addAnswer(string questionTitle, string questionAuthor, string answerContent) = 0; + virtual void editAnswer(string questionTitle, string questionAuthor, + string answerAuthor, string answerNewContent, string description) = 0; + virtual void handleAnswerEditRequest() = 0; // for admin + virtual void deleteAnswer(string questionTitle, string questionAuthor, string answerAuthor) = 0; + virtual void upvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor) = 0; + virtual void downvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor) = 0; + protected: - string password; - string email; - UserType type; -}; + class Question { + public: + string author; + string title; + string content; + vector tags; + string creationDate; + Duplicate duplicateTo; + vector seenUsers; + vector editors; + vector answers; + int numReputation; + int numFavorite; + int numViews; + bool duplicate; + + Question(string au, string ti, string co, string cd, vector tg, int re, int nv, int nf, int du) + :author(au), title(ti), content(co), creationDate(cd), numReputation(re), + numViews(nv), numFavorite(nf), duplicate(du) { + seenUsers.reserve(usersSeenQuestionSize); + editors.reserve(questionEditorsSize); + tags.reserve(questionTagsSize); + answers.reserve(questionAnswersSize); + tags.insert(tags.end(), tg.begin(), tg.end()); + } + + Question() {} + bool operator ==(Question q) { + return this->title == q.title; + } + }; + + // Login related + string password; + string email; + UserType type; + + // Question related + vector upvoted; + vector downvoted; + vector thumbedUp; + vector thumbedDown; + vector questionEditRequests; + + // Answer related + vector upvotedAnswers; + vector downvotedAnswers; + vector answerEditRequests; + + // common between Answer and Question + map< string, vector > contents; +}; \ No newline at end of file diff --git a/Exceptions.h b/Exceptions.h index f304c0b..3439ded 100644 --- a/Exceptions.h +++ b/Exceptions.h @@ -5,42 +5,85 @@ class UsernameAlreadyExistsException : public std::exception { public: - const char *what() const throw() { - return message.c_str(); - } + const char *what() const throw() { + return message.c_str(); + } private: - const std::string message = "Error: username already exists"; + const std::string message = "Error: username already exists"; }; class EmailAlreadyExistsException : public std::exception { public: - const char *what() const throw() { - return message.c_str(); - } + const char *what() const throw() { + return message.c_str(); + } private: - const std::string message = "Error: email already exists"; + const std::string message = "Error: email already exists"; }; class WrongUsernameOrPasswordException : public std::exception { private: - const std::string message = "Error: wrong username or password!"; + const std::string message = "Error: wrong username or password!"; public: - const char *what() const throw() { - return message.c_str(); - } + const char *what() const throw() { + return message.c_str(); + } }; class DeleteAdminException : public std::exception { public: - const char *what() throw() { - return message.c_str(); - } + const char *what() throw() { + return message.c_str(); + } private: - const std::string message = "Error: can't delete admin account!"; + const std::string message = "Error: can't delete admin account!"; }; + +class RepetitiveQuestionException : public std::exception { +public: + const char *what() const throw() { + return message.c_str(); + } + +private: + const std::string message = "Error: there is a question with same title"; + +}; + +class NoSuchQuestionException : public std::exception { +public: + const char *what() const throw() { + return message.c_str(); + } + +private: + const std::string message = "Error: there isn't question with specified title"; + +}; + +class NoSuchAnswerException : public std::exception { +public: + const char *what() const throw() { + return message.c_str(); + } + +private: + const std::string message = "Error: there isn't answer with specified author in this question"; + +}; + +class NotUserPropertyException : public std::exception { +public: + const char *what() const throw() { + return message.c_str(); + } + +private: + const std::string message = "Error: only admin can use this property"; +}; diff --git a/User.cpp b/User.cpp index 7aa9f46..6abd20f 100644 --- a/User.cpp +++ b/User.cpp @@ -1,83 +1,723 @@ +#include #include - -// -// Created by spsina on 11/8/18. -// - #include +#include #include "User.h" #include "Exceptions.h" -#include +#pragma warning(disable:4996) + +#define questionsSize 20 +#define upvotedQuestionSize 20 +#define downvotedQuestionSize 20 +#define thumbedUpSize 20 +#define thumbedDownSize 20 +#define RequestsSize 20 + +string currentDateTime() { + time_t now = time(0); + struct tm tstruct; + char buf[80]; + tstruct = *localtime(&now); + strftime(buf, sizeof(buf), "%b %d'%y at %H:%M", &tstruct); + return buf; +} vector User::users; string User::salt; - -User::User(string username, string password, string email, UserType type){ - lower(username); - this->username = username; - set_password(std::move(password)); - this->email = email; - this->type = type; +User::User(string username, string password, string email, UserType type) { + lower(username); + this->username = username; + set_password(std::move(password)); + this->email = email; + this->type = type; + this->contents["Question"].reserve(questionsSize); + this->thumbedUp.reserve(thumbedUpSize); + this->thumbedDown.reserve(thumbedDownSize); + this->upvoted.reserve(upvotedQuestionSize); + this->downvoted.reserve(downvotedQuestionSize); + this->upvotedAnswers.reserve(thumbedUpSize); + this->downvotedAnswers.reserve(thumbedDownSize); + if (type == UserType::ADMIN) { + this->questionEditRequests.reserve(RequestsSize); + this->answerEditRequests.reserve(RequestsSize); + } } -void User::set_password(string password){ - size_t ps = pass_hash(password + salt); - stringstream out; - out << ps; - this->password = out.str(); +void User::set_password(string password) { + size_t ps = pass_hash(password + salt); + stringstream out; + out << ps; + this->password = out.str(); } -bool User::check_password(string password){ - size_t check = pass_hash(password + salt); - stringstream out; - out << check; - return (this->password == out.str()); +bool User::check_password(string password) { + size_t check = pass_hash(password + salt); + stringstream out; + out << check; + return (this->password == out.str()); } -bool User::authenticate(string username, string password){ - lower(username); - return this->username == username and check_password(password); +bool User::authenticate(string username, string password) { + lower(username); + return this->username == username and check_password(password); } -void User::deleteAccount(){ - if (this->type == UserType::ADMIN) { - throw DeleteAdminException(); - } +void User::deleteAccount() { + if (this->type == UserType::ADMIN) { + throw DeleteAdminException(); + } - for (auto user = users.begin(); user != users.end();user++){ - if ( user->username == this->username ) { - users.erase(user); - break; - } - } + for (auto user = users.begin(); user != users.end(); user++) { + if (user->username == this->username) { + users.erase(user); + break; + } + } } -User& User::login(string username, string password){ - for (auto &user : users) { - if(user.authenticate(username, password)) { - return user; - } - } - throw WrongUsernameOrPasswordException(); +User& User::login(string username, string password) { + for (auto &user : users) { + if (user.authenticate(username, password)) { + return user; + } + } + throw WrongUsernameOrPasswordException(); } -User& User::signup(string username, string password, string email){ - for (auto &user : users) { - if (user.username == username) { - throw UsernameAlreadyExistsException(); - } - else if (user.email == email) { - throw EmailAlreadyExistsException(); - } - } - //Create user - users.emplace_back(username, password, email, UserType::MEMBER); - return users[users.size() - 1]; +User& User::signup(string username, string password, string email) { + for (auto &user : users) { + if (user.username == username) { + throw UsernameAlreadyExistsException(); + } + else if (user.email == email) { + throw EmailAlreadyExistsException(); + } + } + //Create user + users.emplace_back(username, password, email, UserType::MEMBER); + return users[users.size() - 1]; } void User::init(const string &salt) { - User::salt = salt; - users.reserve(20); - users.emplace_back("admin", "admin", "admin@stackoverflow.com", UserType::ADMIN); + User::salt = salt; + users.reserve(20); + users.emplace_back("admin", "admin", "admin@stackoverflow.com", UserType::ADMIN); +} + +// point userIt to end of users which means there is no question with specified title +// or +// point userIt to related user and point questionIt to specified question +void User::searchAllQuestions( + string title, + string author, + vector::iterator& userIt, + vector::iterator& questionIt) +{ + vector::iterator userSourceIt = users.begin(); + vector::iterator userDestIt = users.end(); + for (; userSourceIt != userDestIt; userSourceIt++) { + map >::iterator contentsIt = userSourceIt->contents.find("Question"); + if (contentsIt != userSourceIt->contents.end()) + for (vector::iterator + questionSourceIt = contentsIt->second.begin(), questionDestIt = contentsIt->second.end(); + questionSourceIt != questionDestIt; + questionSourceIt++) + if (questionSourceIt->title == title && questionSourceIt->author==author) { + questionIt = questionSourceIt; + userIt = userSourceIt; + return; + } + } + userIt = userSourceIt; +} + +// point answerIt to end of answers which means there is no answer with specified question, author, and title +// or +// point answerIt to related answer +void User::searchQuestionAnswers( + string author, + vector::iterator& answerIt, + vector::iterator& questionIt) +{ + vector::iterator answerSourceIt = questionIt->answers.begin(); + vector::iterator answersDestIt = questionIt->answers.end(); + for (; answerSourceIt != answersDestIt; answerSourceIt++) + if (answerSourceIt->author == author) { + answerIt = answerSourceIt; + return; + } + answerIt = answerSourceIt; +} + +void User::createQuestion(string title, string content, vector tags) { + + vector::iterator userIt; + vector::iterator questionIt; + searchAllQuestions(title, this->username, userIt, questionIt); + if (userIt != users.end()) + throw RepetitiveQuestionException(); + + this->contents["Question"].emplace_back(this->username, title, content, currentDateTime(), tags, 0, 0, 0, 0); +} + +void User::editQuestion(string title, string author, + string newTitle, string newContent, vector newTags, string description) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(title, author, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + else if (usersIt->username != this->username) { // need to be accept by admin + for (vector::iterator + usersSourceIt = users.begin(), usersDestIt = users.end(); + usersSourceIt != usersDestIt; + usersSourceIt++) + if (usersSourceIt->type == UserType::ADMIN) { + cout << "your request is sended to ADMIN " << usersSourceIt->username << endl; + usersSourceIt->questionEditRequests.emplace_back(questionIt->author, questionIt->title, this->username, + newTitle, newContent, newTags, currentDateTime(), description); + } + } + + else if (usersIt->username == this->username) { // don't need admin acception + questionIt->title = newTitle; + questionIt->content = newContent; + questionIt->tags.clear(); + questionIt->tags.insert(questionIt->tags.end(), newTags.begin(), newTags.end()); + questionIt->editors.emplace_back(this->username, currentDateTime(), description); + cout << "done :)" << endl; + } +} + +void User::handleQuestionEditRequests() { + if (this->type != UserType::ADMIN) + throw NotUserPropertyException(); + + vector::iterator editRequestSourceIt = this->questionEditRequests.begin(); + while (editRequestSourceIt != this->questionEditRequests.end()) { + + vector::iterator userIt; + vector::iterator questionIt; + searchAllQuestions(editRequestSourceIt->questionTitle, editRequestSourceIt->questionAuthor, userIt, questionIt); + + cout << "main question was:" << endl; + if (questionIt->duplicate == true) + cout << "duplicat to: " << questionIt->duplicateTo.title << + " from " << questionIt->duplicateTo.author << endl; + cout << "title: " << questionIt->title << endl; + cout << "content: " << questionIt->content << endl; + cout << "tags: "; + vector::iterator previousTagsSourceIt = questionIt->tags.begin(); + vector::iterator previousTagsDestIt = questionIt->tags.end(); + if (previousTagsSourceIt != previousTagsDestIt) { // prevent last excess comma such as in (a, b, ) + cout << *previousTagsSourceIt; + previousTagsSourceIt++; + } + for (; previousTagsSourceIt != previousTagsDestIt; previousTagsSourceIt++) + cout << ", " << *previousTagsSourceIt; + cout << endl; + cout << "asked " << questionIt->creationDate << endl; + cout << "user: " << userIt->username << endl; + for (vector::iterator + previousEditsSourceIt = questionIt->editors.begin(), previousEditsDestIt = questionIt->editors.end(); + previousEditsSourceIt != previousEditsDestIt; + previousEditsSourceIt++) + cout << "edited " << previousEditsSourceIt->editDate << endl << previousEditsSourceIt->editorName << endl; + cout << "viewed " << questionIt->numViews << " times" << endl; + cout << "reputation: " << questionIt->numReputation << endl; + cout << "favorites: " << questionIt->numFavorite << endl; + cout << endl; + + cout << "edited question is:" << endl; + cout << "Edited: " << editRequestSourceIt->editDate << endl; + cout << "By: " << editRequestSourceIt->editorName << endl; + cout << "Title: " << editRequestSourceIt->editedTitle << endl; + cout << "Body: " << editRequestSourceIt->editedContent << endl; + cout << "Rev: " << editRequestSourceIt->description << endl; + cout << "Tags: "; + vector::iterator tagsSourceIt = editRequestSourceIt->editedTags.begin(); + vector::iterator tagsDestIt = editRequestSourceIt->editedTags.end(); + if (tagsSourceIt != tagsDestIt) { + cout << *tagsSourceIt; + tagsSourceIt++; + } + for (; tagsSourceIt != tagsDestIt; tagsSourceIt++) + cout << ", " << *tagsSourceIt; + cout << endl; + + string choice; + while (true) { + cout << "will you accept this edit?(y:yes, n:no, e:exit): "; + cin.ignore(numeric_limits::max(), '\n'); + getline(cin, choice); + if (choice == "y" || choice == "Y") { + questionIt->title = editRequestSourceIt->editedTitle; + questionIt->content = editRequestSourceIt->editedContent; + questionIt->tags.clear(); + questionIt->tags.insert(questionIt->tags.end(), + editRequestSourceIt->editedTags.begin(), editRequestSourceIt->editedTags.end()); + questionIt->editors.emplace_back(editRequestSourceIt->editorName, + editRequestSourceIt->editDate, editRequestSourceIt->description); + editRequestSourceIt = this->questionEditRequests.erase(editRequestSourceIt); + cout << "done :)" << endl; + break; + } + else if (choice == "n" || choice == "N") { + editRequestSourceIt++; + break; + } + else if (choice == "e" || choice == "E") { + return; + } + else { + cout << "not an option!" << endl; + } + } + } +} + +void User::deleteQuestion(string title, string author) { + vector::iterator questionIt; + vector::iterator userIt; + searchAllQuestions(title, author, userIt, questionIt); + if (userIt == users.end()) + throw NoSuchQuestionException(); + + if (this->username != questionIt->author) { + cout << "this isn't your question" << endl; + return; + } + + userIt->contents["Question"].erase(questionIt); + cout << "done :)" << endl; +} + +void User::upvoteQuestion(string title, string author) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(title, author, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator questionSourceIt = this->upvoted.begin(); + vector::iterator questionDestIt = this->upvoted.end(); + for (questionSourceIt; questionSourceIt != questionDestIt; questionSourceIt++) + if (questionSourceIt->title == title) + break; + if (questionSourceIt != questionDestIt) + cout << "you upvoted this question before" << endl; + else { + vector::iterator downvotedSourceIt = this->downvoted.begin(); + vector::iterator downvotedDestIt = this->downvoted.end(); + for (downvotedSourceIt; downvotedSourceIt != downvotedDestIt; downvotedSourceIt++) + if (downvotedSourceIt->title == title) { + this->downvoted.erase(downvotedSourceIt); + questionIt->numReputation++; + break; + } + if (downvotedSourceIt == downvotedDestIt) { + questionIt->numReputation++; + this->upvoted.emplace_back(questionIt->author, questionIt->title, + questionIt->content, questionIt->creationDate, questionIt->tags, + questionIt->numReputation, questionIt->numViews, questionIt->numFavorite, questionIt->duplicate); + } + } +} + +void User::downvoteQuestion(string title, string author) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(title, author, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator questionSourceIt = this->downvoted.begin(); + vector::iterator questionDestIt = this->downvoted.end(); + for (questionSourceIt; questionSourceIt != questionDestIt; questionSourceIt++) + if (questionSourceIt->title == title) + break; + if (questionSourceIt != questionDestIt) + cout << "you downvoted this question before" << endl; + else { + vector::iterator upvotedSourceIt = this->upvoted.begin(); + vector::iterator upvotedDestIt = this->upvoted.end(); + for (upvotedSourceIt; upvotedSourceIt != upvotedDestIt; upvotedSourceIt++) + if (upvotedSourceIt->title == title) { + this->upvoted.erase(upvotedSourceIt); + questionIt->numReputation--; + break; + } + if (upvotedSourceIt == upvotedDestIt) { + questionIt->numReputation--; + this->downvoted.emplace_back(questionIt->author, questionIt->title, + questionIt->content, questionIt->creationDate, questionIt->tags, + questionIt->numReputation, questionIt->numViews, questionIt->numFavorite, questionIt->duplicate); + } + } +} + +void User::addQuestionToFavs(string title, string author) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(title, author, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator questionSourceIt = this->thumbedUp.begin(); + vector::iterator questionDestIt = this->thumbedUp.end(); + for (questionSourceIt; questionSourceIt != questionDestIt; questionSourceIt++) + if (questionSourceIt->title == title) + break; + if (questionSourceIt != questionDestIt) + cout << "you thumbed up this question before" << endl; + else { + vector::iterator thumbedDownSourceIt = this->thumbedDown.begin(); + vector::iterator thumbedDownDestIt = this->thumbedDown.end(); + for (thumbedDownSourceIt; thumbedDownSourceIt != thumbedDownDestIt; thumbedDownSourceIt++) + if (thumbedDownSourceIt->title == title) { + this->thumbedDown.erase(thumbedDownSourceIt); + questionIt->numFavorite++; + break; + } + if (thumbedDownSourceIt == thumbedDownDestIt) { + questionIt->numFavorite++; + this->thumbedUp.emplace_back(questionIt->author, questionIt->title, + questionIt->content, questionIt->creationDate, questionIt->tags, + questionIt->numReputation, questionIt->numViews, questionIt->numFavorite, questionIt->duplicate); + } + } +} + +void User::deleteQuestionFromFavs(string title, string author) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(title, author, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator questionSourceIt = this->thumbedDown.begin(); + vector::iterator questionDestIt = this->thumbedDown.end(); + for (questionSourceIt; questionSourceIt != questionDestIt; questionSourceIt++) + if (questionSourceIt->title == title) + break; + if (questionSourceIt != questionDestIt) + cout << "you didn't thumb it up so you can't thumb it down" << endl; + else { + vector::iterator thumbedUpSourceIt = this->thumbedUp.begin(); + vector::iterator thumbedUpDestIt = this->thumbedUp.end(); + for (thumbedUpSourceIt; thumbedUpSourceIt != thumbedUpDestIt; thumbedUpSourceIt++) + if (thumbedUpSourceIt->title == title) { + this->thumbedUp.erase(thumbedUpSourceIt); + questionIt->numFavorite--; + this->thumbedDown.emplace_back(questionIt->author, questionIt->title, + questionIt->content, questionIt->creationDate, questionIt->tags, + questionIt->numReputation, questionIt->numViews, questionIt->numFavorite, questionIt->duplicate); + break; + } + if (thumbedUpSourceIt == thumbedUpDestIt) { + cout << "you didn't thumb it up so you can't thumb it down" << endl; + } + } +} + +void User::viewFavQuestions() { + for (vector::iterator + questionsSourceIt = this->thumbedUp.begin(), questionsDestIt = this->thumbedUp.end(); + questionsSourceIt != questionsDestIt; + questionsSourceIt++) { + cout << questionsSourceIt->title << " asked " << + questionsSourceIt->creationDate << " by " << questionsSourceIt->author << endl; + return; + } + cout << "You have no favorite questions" << endl; +} + +void User::viewQuestions(string title, string author) { + vector::iterator userIt; + vector::iterator questionIt; + searchAllQuestions(title, author, userIt, questionIt); + if (userIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator seenUsersIt = questionIt->seenUsers.begin(); + vector::iterator destSeenUsersIt = questionIt->seenUsers.end(); + for (seenUsersIt; seenUsersIt != destSeenUsersIt; seenUsersIt++) + if (*seenUsersIt == this->username) + break; + if (seenUsersIt == destSeenUsersIt) { // this user still doesn't see question + questionIt->numViews++; + questionIt->seenUsers.emplace_back(this->username); + } + for (vector::iterator + objfavoriteQuestionIt = this->thumbedUp.begin(), objDestfavoriteQuestionsIt = this->thumbedUp.end(); + objfavoriteQuestionIt != objDestfavoriteQuestionsIt; + objfavoriteQuestionIt++) + if (objfavoriteQuestionIt->title == title) + cout << "you chose this question as your favorite one before" << endl; + if (questionIt->duplicate == true) + cout << "duplicat to: " << questionIt->duplicateTo.title << " from " << questionIt->duplicateTo.author << endl; + cout << "title: " << questionIt->title << endl; + cout << "content: " << questionIt->content << endl; + cout << "tags: "; + vector::iterator tagsSourceIt = questionIt->tags.begin(); + vector::iterator tagsDestIt = questionIt->tags.end(); + if (tagsSourceIt != tagsDestIt) { // prevent last excess comma such as in (a, b, ) + cout << *tagsSourceIt; + tagsSourceIt++; + } + for (tagsSourceIt; tagsSourceIt != tagsDestIt; tagsSourceIt++) + cout << ", " << *tagsSourceIt; + cout << endl; + cout << "asked " << questionIt->creationDate << endl; + cout << "user: " << userIt->username << endl; + for (vector::iterator + editsSourceIt = questionIt->editors.begin(), editsDestIt = questionIt->editors.end(); + editsSourceIt != editsDestIt; + editsSourceIt++) + cout << "edited " << editsSourceIt->editDate << endl << editsSourceIt->editorName << endl; + cout << "viewed " << questionIt->numViews << " times" << endl; + cout << "reputation: " << questionIt->numReputation << endl; + cout << "favorites: " << questionIt->numFavorite << endl; + + cout << "answers:" << endl << endl; + for (vector::iterator + answerSourceIt = questionIt->answers.begin(), answerDestIt = questionIt->answers.end(); + answerSourceIt != answerDestIt; + answerSourceIt++) { + cout << "content: " << answerSourceIt->content << endl; + cout << "answered " << answerSourceIt->creationDate << endl; + cout << "user: " << answerSourceIt->author << endl; + for (vector::iterator + editsSourceIt = answerSourceIt->editors.begin(), editsDestIt = answerSourceIt->editors.end(); + editsSourceIt != editsDestIt; + editsSourceIt++) + cout << "edited " << editsSourceIt->editDate << endl << "by: " << editsSourceIt->editorName << endl; + cout << "reputation: " << answerSourceIt->numReputation << endl; + cout << endl; + } +} + +void User::changeStatus(string copyTitle, string copyAuthor, string originalTitle, string originalAuthor) { + if (this->type != UserType::ADMIN) + throw NotUserPropertyException(); + + vector::iterator userIt; + vector::iterator copyQuestionIt; + searchAllQuestions(copyTitle, copyAuthor, userIt, copyQuestionIt); + if (userIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator originalQuestionIt; + searchAllQuestions(originalTitle, originalAuthor, userIt, originalQuestionIt); + if (userIt == users.end()) + throw NoSuchQuestionException(); + + copyQuestionIt->duplicateTo.title = originalTitle; + copyQuestionIt->duplicateTo.author = originalAuthor; + copyQuestionIt->duplicate = true; + cout << "done :)" << endl; +} + +void User::addAnswer(string questionTitle, string questionAuthor, string answerContent) { + vector::iterator userIt; + vector::iterator questionIt; + searchAllQuestions(questionTitle, questionAuthor, userIt, questionIt); + if (userIt == users.end()) + throw NoSuchQuestionException(); + + for (vector::iterator + answerSourceIt = questionIt->answers.begin(), answerDestIt = questionIt->answers.end(); + answerSourceIt != answerDestIt; + answerSourceIt++) + if (answerSourceIt->author == this->username) { + cout << "You answered this question before" << endl; + return; + } + + questionIt->answers.emplace_back(this->username, answerContent, currentDateTime(), 0); + cout << "done :)" << endl; +} + +void User::editAnswer(string questionTitle, string questionAuthor, string answerAuthor, string answerNewContent, string description) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(questionTitle, questionAuthor, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator answerIt; + searchQuestionAnswers(answerAuthor, answerIt, questionIt); + if (answerIt == questionIt->answers.end()) + throw NoSuchAnswerException(); + + else if (answerIt->author != this->username) { // need to be accept by admin + for (vector::iterator + usersSourceIt = users.begin(), usersDestIt = users.end(); + usersSourceIt != usersDestIt; + usersSourceIt++) + if (usersSourceIt->type == UserType::ADMIN) { + cout << "your request is sended to ADMIN " << usersSourceIt->username << endl; + usersSourceIt->answerEditRequests.emplace_back(questionAuthor, questionTitle, + answerAuthor, this->username, answerNewContent, currentDateTime(), description); + } + } + + else if (answerIt->author == this->username) { // don't need admin acception + answerIt->content = answerNewContent; + answerIt->editors.emplace_back(this->username, currentDateTime(), description); + cout << "done :)" << endl; + } +} + +void User::handleAnswerEditRequest() { + if (this->type != UserType::ADMIN) + throw NotUserPropertyException(); + + vector::iterator editRequestSourceIt = this->answerEditRequests.begin(); + while (editRequestSourceIt != this->answerEditRequests.end()) { + vector::iterator userIt; + vector::iterator questionIt; + searchAllQuestions(editRequestSourceIt->questionTitle, editRequestSourceIt->questionAuthor, userIt, questionIt); + + vector::iterator answerIt; + searchQuestionAnswers(editRequestSourceIt->answerAuthor, answerIt, questionIt); + + cout << "main answer was:" << endl; + cout << "content: " << answerIt->content << endl; + cout << "answered " << answerIt->creationDate << endl; + cout << "user: " << answerIt->author << endl; + for (vector::iterator + previousEditsSourceIt = answerIt->editors.begin(), previousEditsDestIt = answerIt->editors.end(); + previousEditsSourceIt != previousEditsDestIt; + previousEditsSourceIt++) + cout << "edited " << previousEditsSourceIt->editDate << endl << "by: " << previousEditsSourceIt->editorName << endl; + cout << "reputation: " << answerIt->numReputation << endl; + cout << endl; + + cout << "edited answer is:" << endl; + cout << "Edited: " << editRequestSourceIt->editDate << endl; + cout << "By: " << editRequestSourceIt->editorName << endl; + cout << "Body: " << editRequestSourceIt->editedContent << endl; + cout << "Rev: " << editRequestSourceIt->description << endl; + + string choice; + while (true) { + cout << "will you accept this edit?(y:yes, n:no, e:exit): "; + cin.ignore(numeric_limits::max(), '\n'); + getline(cin, choice); + if (choice == "y" || choice == "Y") { + answerIt->content = editRequestSourceIt->editedContent; + answerIt->editors.emplace_back(editRequestSourceIt->editorName, + editRequestSourceIt->editDate, editRequestSourceIt->description); + editRequestSourceIt = this->answerEditRequests.erase(editRequestSourceIt); + cout << "done :)" << endl; + break; + } + else if (choice == "n" || choice == "N") { + editRequestSourceIt++; + break; + } + else if (choice == "e" || choice == "E") { + return; + } + else { + cout << "not an option!" << endl; + } + } + } +} + +void User::deleteAnswer(string questionTitle, string questionAuthor, string answerAuthor) { + vector::iterator questionIt; + vector::iterator userIt; + searchAllQuestions(questionTitle, questionAuthor, userIt, questionIt); + if (userIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator answerIt; + searchQuestionAnswers(answerAuthor, answerIt, questionIt); + if (answerIt == questionIt->answers.end()) + throw NoSuchAnswerException(); + + if (this->username != answerIt->author) { + cout << "this isn't your answer" << endl; + return; + } + + questionIt->answers.erase(answerIt); + cout << "done :)" << endl; +} + +void User::upvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(questionTitle, questionAuthor, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator answerIt; + searchQuestionAnswers(answerAuthor, answerIt, questionIt); + if (answerIt == questionIt->answers.end()) + throw NoSuchAnswerException(); + + vector::iterator answerSourceIt = this->upvotedAnswers.begin(); + vector::iterator answerDestIt = this->upvotedAnswers.end(); + for (; answerSourceIt != answerDestIt; answerSourceIt++) + if (answerSourceIt->author == answerAuthor) + break; + if (answerSourceIt != answerDestIt) + cout << "you upvoted this answer before" << endl; + else { + vector::iterator downvotedSourceIt = this->downvotedAnswers.begin(); + vector::iterator downvotedDestIt = this->downvotedAnswers.end(); + for (downvotedSourceIt; downvotedSourceIt != downvotedDestIt; downvotedSourceIt++) + if (downvotedSourceIt->author == answerAuthor) { + this->downvotedAnswers.erase(downvotedSourceIt); + answerIt->numReputation++; + break; + } + if (downvotedSourceIt == downvotedDestIt) { + answerIt->numReputation++; + this->upvotedAnswers.emplace_back + (answerIt->author,answerIt->content,answerIt->creationDate,answerIt->numReputation); + } + } +} + +void User::downvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor) { + vector::iterator usersIt; + vector::iterator questionIt; + searchAllQuestions(questionTitle, questionAuthor, usersIt, questionIt); + if (usersIt == users.end()) + throw NoSuchQuestionException(); + + vector::iterator answerIt; + searchQuestionAnswers(answerAuthor, answerIt, questionIt); + if (answerIt == questionIt->answers.end()) + throw NoSuchAnswerException(); + + vector::iterator answerSourceIt = this->downvotedAnswers.begin(); + vector::iterator answerDestIt = this->downvotedAnswers.end(); + for (; answerSourceIt != answerDestIt; answerSourceIt++) + if (answerSourceIt->author == answerAuthor) + break; + if (answerSourceIt != answerDestIt) + cout << "you downvoted this answer before" << endl; + else { + vector::iterator upvotedSourceIt = this->upvotedAnswers.begin(); + vector::iterator upvotedDestIt = this->upvotedAnswers.end(); + for (; upvotedSourceIt != upvotedDestIt; upvotedSourceIt++) + if (upvotedSourceIt->author == answerAuthor) { + this->upvotedAnswers.erase(upvotedSourceIt); + answerIt->numReputation--; + break; + } + if (upvotedSourceIt == upvotedDestIt) { + answerIt->numReputation--; + this->downvotedAnswers.emplace_back + (answerIt->author, answerIt->content, answerIt->creationDate, answerIt->numReputation); + } + } } \ No newline at end of file diff --git a/User.h b/User.h index 6b239d9..5ec1f21 100644 --- a/User.h +++ b/User.h @@ -1,28 +1,62 @@ #pragma once #include +#include +#include #include "AbstractUser.h" -class User : public AbstractUser { -public: - User(string username, string password, string email, UserType type); - - static void init(const string &salt); - -public: - void set_password(string password); - bool check_password(string password); - -public: - bool authenticate(string username, string password); - void deleteAccount(); +string currentDateTime(); +class User : public AbstractUser { public: - static User& login(string username, string password); - static User& signup(string username, string password, string email); + // Login related + User(string username, string password, string email, UserType type); + static void init(const string &salt); + void set_password(string password); + bool check_password(string password); + bool authenticate(string username, string password); + void deleteAccount(); + static User& login(string username, string password); + static User& signup(string username, string password, string email); + + // Question related + void createQuestion(string title, string content, vector tags); + void editQuestion(string title, string author, + string newTitle, string newContent, vector newTags, string description); + void handleQuestionEditRequests(); + void deleteQuestion(string title, string author); + void upvoteQuestion(string title ,string author); + void downvoteQuestion(string title, string author); + void addQuestionToFavs(string title, string author); + void deleteQuestionFromFavs(string title, string author); + void viewFavQuestions(); + void viewQuestions(string title, string author); + void changeStatus(string copyTitle, string copyAuthor, string originalTitle, string originalAuthor); + + // Answer related + void addAnswer(string questionTitle, string questionAuthor, string answerContent); + void editAnswer(string questionTitle, string questionAuthor, string answerAuthor, string answerNewContent, string description); + void handleAnswerEditRequest(); + void deleteAnswer(string questionTitle, string questionAuthor, string answerAuthor); + void upvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor); + void downvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor); + +protected: + // Question related + void searchAllQuestions( + string title, + string author, + vector::iterator& userIt, + vector::iterator& questionIt); + + // Answer related + void searchQuestionAnswers( + string author, + vector::iterator& answerIt, + vector::iterator& questionIt); private: - static string salt; - static vector users; - -}; + // Login related + static string salt; + static vector users; +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index 01eac03..87638a9 100755 --- a/main.cpp +++ b/main.cpp @@ -14,110 +14,435 @@ using namespace std; enum MenuState { - START, - LOGGED_IN, - END + START, + LOGGED_IN, + END }; int main() { - User::init("SECRET_KEY"); - User * loggedInUser = nullptr; - MenuState menuState = MenuState::START; - string last_message; + User::init("SECRET_KEY"); + User * loggedInUser = nullptr; + MenuState menuState = MenuState::START; + string last_message; - char choice; - while(menuState != MenuState::END) { - system(CLEAR); - if (!last_message.empty()) - cout << last_message << endl; - last_message = ""; - switch (menuState) { - case MenuState::START: { - cout << "1. login\n2. signup\ne. exit\n"; - cin >> choice; - switch (choice) { - case '1': { // login - try { - string username, password; - cout << "Enter Username: "; - cin >> username; - cout << "Enter Password: "; - cin >> password; - loggedInUser = &User::login(username,password); - menuState = MenuState::LOGGED_IN; - } catch (WrongUsernameOrPasswordException &e) { - last_message = e.what(); - } - break; - } - case '2': { // signup - try { - string username, password, email; - cout << "Enter Email: "; - cin >> email; - cout << "Enter Username: "; - cin >> username; - cout << "Enter Password: "; - cin >> password; - loggedInUser = &User::signup(username, password, email); - menuState = MenuState::LOGGED_IN; - last_message = "User signed up!\n"; - } catch (UsernameAlreadyExistsException &e) { - last_message = e.what(); - break; + char choice; + while (menuState != MenuState::END) { + // system(CLEAR); + if (!last_message.empty()) + cout << last_message << endl; + last_message = ""; + switch (menuState) { + case MenuState::START: { + cout << "1. login\n2. signup\ne. exit\n"; + cin >> choice; + switch (choice) { + case '1': { // login + try { + string username, password; + cout << "Enter Username: "; + cin >> username; + cout << "Enter Password: "; + cin >> password; + loggedInUser = &User::login(username, password); + menuState = MenuState::LOGGED_IN; + } + catch (WrongUsernameOrPasswordException &e) { + last_message = e.what(); + } + break; + } + case '2': { // signup + try { + string username, password, email; + cout << "Enter Email: "; + cin >> email; + cout << "Enter Username: "; + cin >> username; + cout << "Enter Password: "; + cin >> password; + loggedInUser = &User::signup(username, password, email); + menuState = MenuState::LOGGED_IN; + last_message = "User signed up!\n"; + } + catch (UsernameAlreadyExistsException &e) { + last_message = e.what(); + break; - } catch (EmailAlreadyExistsException &e) { - last_message = e.what(); - } - break; - } - case 'e': { // exit - menuState = MenuState::END; - break; - } - default: { // unknown input - last_message = "Unknown Input\n"; - break; - } - } - break; - } - case MenuState::LOGGED_IN: { - cout << "d.delete account\nl. logout\ne. exit\n"; - cin >> choice; - switch (choice) { - case 'd': { - try { - loggedInUser->deleteAccount(); - cout << "Account successfully deleted\n"; - loggedInUser = nullptr; - menuState = MenuState::START; - } - catch (DeleteAdminException &e) { - last_message = e.what(); - } - break; - } - case 'l': { // logout - loggedInUser = nullptr; - menuState = MenuState::START; - last_message = "GOOD BYE\n"; - break; - } - case 'e': { // exit - menuState = MenuState::END; - break; - } - default: { // unknown input - last_message = "Unknown Input\n"; - break; - } + } + catch (EmailAlreadyExistsException &e) { + last_message = e.what(); + } + break; + } + case 'e': { // exit + menuState = MenuState::END; + break; + } + default: { // unknown input + last_message = "Unknown Input\n"; + break; + } + } + break; + } + case MenuState::LOGGED_IN: { + cout << "d. delete account\nl. logout\ne. exit" + "\na. add question\nu. update/edit question\nh. handle questions edit requests(Admin only)\no. omit/delete question" + "\ng. good/upvote question\nb. bad/downvote question" + "\ni. include/add question to your favorits\nr. remove question from your favorits\nf. see your favorit questions" + "\nv. view question\ns. change status(Admin only)" + "\nA. answer question\nU. update/edit answer\nH. handle answers edit requests(Admin only)" + "\nO. omit/delete answer\nG. good/upvote answer\nB. bad/downvote answer\n"; + cin >> choice; + switch (choice) { + case 'd': { // delete account + try { + loggedInUser->deleteAccount(); + cout << "Account successfully deleted\n"; + loggedInUser = nullptr; + menuState = MenuState::START; + } + catch (DeleteAdminException &e) { + last_message = e.what(); + } + break; + } + case 'l': { // logout + loggedInUser = nullptr; + menuState = MenuState::START; + last_message = "GOOD BYE\n"; + break; + } + case 'a': { // add question + cin.ignore(numeric_limits::max(), '\n'); // clean cin + string title, content, t; + vector tags; + cout << "about the question you want to add enter the followings:" << endl; + cout << "title: "; + getline(cin, title); + cout << "content: "; + getline(cin, content); + cout << "tags(seperated by space/newline) and then press EOF or press t to see allowed tags: "; + while (cin >> t) { + if (t == "t") { + cout << "C,C++,Python,Django,MySQL,Java,JavaScript,Go,Socket" << endl; + continue; + } + if (t == "C" || t == "C++" || t == "Python" || t == "Django" || t == "MySQL" || + t == "Java" || t == "JavaScript" || t == "Go" || t == "Socket") + tags.emplace_back(t); + else + cout << t << "is not allowed as a tag" << endl; + } + cin.clear(); // cleaning cin after EOF in C++ is needed + try { + loggedInUser->createQuestion(title, content, tags); + } + catch (RepetitiveQuestionException e) { + last_message = e.what(); + } + break; + } + case 'u': { // update/edit question + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor, newTitle, newContent, description, t; + vector newTags; + cout << "about the question you want to update enter the followings:" << endl; + cout << "title: "; + getline(cin, questionTitle); + cout << "author: "; + getline(cin, questionAuthor); + cout << "new title: "; + getline(cin, newTitle); + cout << "new content: "; + getline(cin, newContent); + cout << "new tags(seperated by space/newline) and the press EOF or press t to see allowed tags: "; + while (cin >> t) { + if (t == "t") { + cout << "C,C++,Python,Django,MySQL,Java,JavaScript,Go,Socket" << endl; + continue; + } + if (t == "C" || t == "C++" || t == "Python" || t == "Django" || t == "MySQL" || + t == "Java" || t == "JavaScript" || t == "Go" || t == "Socket") + newTags.emplace_back(t); + else + cout << t << "is not allowed as a tag" << endl; + } + cin.clear(); + cout << "description: "; + getline(cin, description); + try { + loggedInUser->editQuestion(questionTitle, questionAuthor, newTitle, newContent, newTags, description); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case 'h': { // handle questions edit requests + try { + loggedInUser->handleQuestionEditRequests(); + } + catch (NotUserPropertyException e) { + last_message = e.what(); + } + break; + } + case'g': { // good/upvote question + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor; + cout << "about the question you want to upvote enter the followings:" << endl; + cout << "title: "; + getline(cin, questionTitle); + cout << "author: "; + getline(cin, questionAuthor); + try { + loggedInUser->upvoteQuestion(questionTitle,questionAuthor); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case'b': { // bad/downvote question + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor; + cout << "about the question you wnt to downvote enter the followings:" << endl; + cout << "title: "; + getline(cin, questionTitle); + cout << "author: "; + getline(cin, questionAuthor); + try { + loggedInUser->downvoteQuestion(questionTitle,questionAuthor); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case 'o': { // omit/delete question + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor; + cout << "about the question you want to delete enter the followings:" << endl; + cout << "title: "; + getline(cin, questionTitle); + cout << "author: "; + getline(cin, questionAuthor); + try { + loggedInUser->deleteQuestion(questionTitle,questionAuthor); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case'i': { // include/add question to favorits + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor; + cout << "about the question you want add to your favorite list enter the followings: " << endl; + cout << "title: "; + getline(cin, questionTitle); + cout << "author: "; + getline(cin, questionAuthor); + try { + loggedInUser->addQuestionToFavs(questionTitle,questionAuthor); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case'r': { // remove question from favorits + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor; + cout << "about the question you want to delete it from your favorite list enter the followings:" << endl; + cout << "title: "; + getline(cin, questionTitle); + cout << "author: "; + getline(cin, questionAuthor); + try { + loggedInUser->deleteQuestionFromFavs(questionTitle,questionAuthor); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case'f': { // see favorit list + loggedInUser->viewFavQuestions(); + break; + } + case 'v': { // view question + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor; + cout << "about the question you want to see enter the followings:" << endl; + cout << "title: "; + getline(cin, questionTitle); + cout << "author: "; + getline(cin, questionAuthor); + try { + loggedInUser->viewQuestions(questionTitle,questionAuthor); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case 's': { // change status of question + string copyTitle, copyAuthor, originalTitle, originalAuthor; + cin.ignore(numeric_limits::max(), '\n'); + cout << "about copy question enter the followings:" << endl; + cout << "title: "; + getline(cin, copyTitle); + cout << "author: "; + getline(cin, copyAuthor); - } - } - } - } - system(CLEAR); - cout << "GOODBYE" << endl; - return 0; -} + cout << "about original question enter the followings:" << endl; + cout << "title: "; + getline(cin, originalTitle); + cout << "author: "; + getline(cin, originalAuthor); + try { + loggedInUser->changeStatus(copyTitle,copyAuthor,originalTitle,originalAuthor); + } + catch (NoSuchQuestionException e1) { + last_message = e1.what(); + } + catch (NotUserPropertyException e2) { + last_message = e2.what(); + } + break; + } + case 'A': { // answer question + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor, answerContent; + cout << "about the question you want to answer enter the followings:" << endl; + cout << "question title: "; + getline(cin, questionTitle); + cout << "question author: "; + getline(cin, questionAuthor); + cout << "answer body: "; + getline(cin, answerContent); + try { + loggedInUser->addAnswer(questionTitle, questionAuthor, answerContent); + } + catch (NoSuchQuestionException e) { + last_message = e.what(); + } + break; + } + case 'U': { // update/edit answer + cin.ignore(numeric_limits::max(), '\n'); + cout << "about the answer you want to edit enter the followings:" << endl; + string questionTitle, questionAuthor, answerAuthor, answerNewContent, description; + vector newTags; + cout << "question title: "; + getline(cin, questionTitle); + cout << "question author: "; + getline(cin, questionAuthor); + cout << "answer author: "; + getline(cin, answerAuthor); + cout << "answer new content: "; + getline(cin, answerNewContent); + cout << "description: "; + getline(cin, description); + try { + loggedInUser->editAnswer(questionTitle, questionAuthor, answerAuthor, answerNewContent, description); + } + catch (NoSuchQuestionException e1) { + last_message = e1.what(); + } + catch (NoSuchAnswerException e2) { + last_message = e2.what(); + } + break; + } + case 'H': { // handle answers edit requests + try { + loggedInUser->handleAnswerEditRequest(); + } + catch (NotUserPropertyException e) { + last_message = e.what(); + } + break; + } + case 'O': { // omit/delete answer + cin.ignore(numeric_limits::max(), '\n'); + cout << "about the answer you want to delete enter the followings:" << endl; + string questionTitle, questionAuthor, answerAuthor; + cout << "question title: "; + getline(cin, questionTitle); + cout << "question author: "; + getline(cin, questionAuthor); + cout << "answer author: "; + getline(cin, answerAuthor); + try { + loggedInUser->deleteAnswer(questionTitle, questionAuthor, answerAuthor); + } + catch (NoSuchQuestionException e1) { + last_message = e1.what(); + } + catch (NoSuchAnswerException e2) { + last_message = e2.what(); + } + break; + } + case'G': { // good/upvote answer + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor, answerAuthor; + cout << "about the answer you want to upvote enter the followings:" << endl; + cout << "question title: "; + getline(cin, questionTitle); + cout << "question author: "; + getline(cin, questionAuthor); + cout << "answer author: "; + getline(cin, answerAuthor); + try { + loggedInUser->upvoteAnswer(questionTitle, questionAuthor, answerAuthor); + } + catch (NoSuchQuestionException e1) { + last_message = e1.what(); + } + catch (NoSuchAnswerException e2) { + last_message = e2.what(); + } + break; + } + case'B': { // bad/downvote answer + cin.ignore(numeric_limits::max(), '\n'); + string questionTitle, questionAuthor, answerAuthor; + cout << "about the answer you want to downvote enter the followings:" << endl; + cout << "question title: "; + getline(cin, questionTitle); + cout << "question author: "; + getline(cin, questionAuthor); + cout << "answer author: "; + getline(cin, answerAuthor); + try { + loggedInUser->downvoteAnswer(questionTitle, questionAuthor, answerAuthor); + } + catch (NoSuchQuestionException e1) { + last_message = e1.what(); + } + catch (NoSuchAnswerException e2) { + last_message = e2.what(); + } + break; + } + case 'e': { // exit + menuState = MenuState::END; + break; + } + default: { // unknown input + last_message = "Unknown Input\n"; + break; + } + + } + } + } + } + system(CLEAR); + cout << "GOODBYE" << endl; + return 0; +} \ No newline at end of file From 0a08786fc5d0975b7bd40c72ff89ef6d7fcfcd44 Mon Sep 17 00:00:00 2001 From: s_arme Date: Sun, 20 Jan 2019 01:14:15 +0330 Subject: [PATCH 4/5] Add logger class --- CMakeLists.txt | 2 +- logger.cpp | 5 +++++ logger.h | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 logger.cpp create mode 100644 logger.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b6ce60..726851c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,4 +10,4 @@ add_executable(stackoverflow_in_cpp Exceptions.h main.cpp User.cpp - User.h) + User.h logger.cpp logger.h) diff --git a/logger.cpp b/logger.cpp new file mode 100644 index 0000000..fc27764 --- /dev/null +++ b/logger.cpp @@ -0,0 +1,5 @@ +// +// Created by SARM on 20/01/2019. +// + +#include "logger.h" diff --git a/logger.h b/logger.h new file mode 100644 index 0000000..b1fda00 --- /dev/null +++ b/logger.h @@ -0,0 +1,14 @@ +// +// Created by SARM on 20/01/2019. +// + +#ifndef STACKOVERFLOW_IN_CPP_LOGGER_H +#define STACKOVERFLOW_IN_CPP_LOGGER_H + + +class logger { + +}; + + +#endif //STACKOVERFLOW_IN_CPP_LOGGER_H From 9e5bd9ba37ac9bd55e1a1b294a6b084e3274a679 Mon Sep 17 00:00:00 2001 From: ALi Date: Sun, 20 Jan 2019 02:25:29 +0330 Subject: [PATCH 5/5] Add logger class --- CMakeLists.txt | 2 +- Logger.cpp | 31 +++++++++++++++++++++++++++++++ Logger.h | 25 +++++++++++++++++++++++++ User.cpp | 6 +++++- User.h | 8 +++++++- logger.cpp | 5 ----- logger.h | 14 -------------- 7 files changed, 69 insertions(+), 22 deletions(-) create mode 100644 Logger.cpp create mode 100644 Logger.h delete mode 100644 logger.cpp delete mode 100644 logger.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 726851c..7605ad7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,4 +10,4 @@ add_executable(stackoverflow_in_cpp Exceptions.h main.cpp User.cpp - User.h logger.cpp logger.h) + User.h Logger.cpp Logger.h) diff --git a/Logger.cpp b/Logger.cpp new file mode 100644 index 0000000..272359a --- /dev/null +++ b/Logger.cpp @@ -0,0 +1,31 @@ +// +// Created by SARM on 20/01/2019. +// + +#include +#include "Logger.h" + +Logger *Logger::getInstance() { + if (!instance) + instance = new Logger(); + return instance; +} + +Logger *Logger::instance = nullptr; + +Logger Logger::operator<<(std::string s) const { + char buf[100]; + std::stringstream ss(buf); + ss << "log." << logCount << ".txt"; + std::string k = ss.str(); + std::fstream file(ss.str().c_str(), std::fstream::app | std::fstream::in | std::fstream::out); + file << s << " "; + file.close(); + return *this; +} + +void Logger::inc() { + logCount++; +} + + diff --git a/Logger.h b/Logger.h new file mode 100644 index 0000000..4b7d37e --- /dev/null +++ b/Logger.h @@ -0,0 +1,25 @@ +// +// Created by SARM on 20/01/2019. +// + +#ifndef STACKOVERFLOW_IN_CPP_LOGGER_H +#define STACKOVERFLOW_IN_CPP_LOGGER_H + + +#include + +class Logger { + int logCount = 0; + static Logger *instance; + + Logger() = default; + +public: + static Logger *getInstance(); + + Logger operator<<(std::string s) const; + void inc(); +}; + + +#endif //STACKOVERFLOW_IN_CPP_LOGGER_H diff --git a/User.cpp b/User.cpp index 6abd20f..bd58d87 100644 --- a/User.cpp +++ b/User.cpp @@ -25,6 +25,7 @@ string currentDateTime() { vector User::users; string User::salt; +Logger User::log = *Logger::getInstance(); User::User(string username, string password, string email, UserType type) { lower(username); @@ -77,8 +78,10 @@ void User::deleteAccount() { } User& User::login(string username, string password) { + log.inc(); for (auto &user : users) { if (user.authenticate(username, password)) { + log << user.email << user.username << currentDateTime() ; return user; } } @@ -720,4 +723,5 @@ void User::downvoteAnswer(string questionTitle, string questionAuthor, string an (answerIt->author, answerIt->content, answerIt->creationDate, answerIt->numReputation); } } -} \ No newline at end of file +} + diff --git a/User.h b/User.h index 5ec1f21..37b3b05 100644 --- a/User.h +++ b/User.h @@ -4,11 +4,15 @@ #include #include #include "AbstractUser.h" +#include "Logger.h" string currentDateTime(); class User : public AbstractUser { public: + //Logger related + static Logger log; + // Login related User(string username, string password, string email, UserType type); static void init(const string &salt); @@ -41,6 +45,8 @@ class User : public AbstractUser { void upvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor); void downvoteAnswer(string questionTitle, string questionAuthor, string answerAuthor); + + protected: // Question related void searchAllQuestions( @@ -59,4 +65,4 @@ class User : public AbstractUser { // Login related static string salt; static vector users; -}; \ No newline at end of file +}; diff --git a/logger.cpp b/logger.cpp deleted file mode 100644 index fc27764..0000000 --- a/logger.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by SARM on 20/01/2019. -// - -#include "logger.h" diff --git a/logger.h b/logger.h deleted file mode 100644 index b1fda00..0000000 --- a/logger.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by SARM on 20/01/2019. -// - -#ifndef STACKOVERFLOW_IN_CPP_LOGGER_H -#define STACKOVERFLOW_IN_CPP_LOGGER_H - - -class logger { - -}; - - -#endif //STACKOVERFLOW_IN_CPP_LOGGER_H