Skip to content

Commit

Permalink
Question model and repository.
Browse files Browse the repository at this point in the history
New views and logic for the main dashboard.
  • Loading branch information
cristianfrasineanu committed Nov 29, 2016
1 parent 4030d92 commit a98f06b
Show file tree
Hide file tree
Showing 16 changed files with 415 additions and 15 deletions.
1 change: 1 addition & 0 deletions app/Console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ Console::~Console()

// Dump all the records...
UserModel::dumpFile();
QuestionModel::dumpFile();

// Revert to default terminal and display notice.
clearScreen();
Expand Down
8 changes: 2 additions & 6 deletions app/Model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,10 @@ void Model::attachEntity(string &model)
{
this->repository = new UserRepository();
}
/*else if (model == "question")
else if (model == QuestionRepository::getAlias())
{
this->repository = new QuestionRepository();
}
else if (model == "category")
{
}*/
}

Model::Model()
Expand Down
1 change: 1 addition & 0 deletions app/Model.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "Helpers.h"
#include "RepositoryInterface.h"
#include "UserRepository.h"
#include "QuestionRepository.h"

using namespace std;

Expand Down
1 change: 1 addition & 0 deletions app/ModelInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class ModelInterface {
public:
virtual void save() = 0;
virtual void setAttributes(map<string, string> &) = 0;
virtual void markAs(const string &, int) = 0;

virtual ~ModelInterface();
};
168 changes: 168 additions & 0 deletions app/QuestionModel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#include "QuestionModel.h"

Question QuestionModel::setAfterUserId(int userId)
{
this->io.seekg(0, this->io.beg);

while (this->io.read(reinterpret_cast<char *>(&this->question), sizeof(question)))
{
if (this->question.user_id == userId)
{
return this->question;
}
}

throw invalid_argument("Question not found!");
}

Question QuestionModel::setAfterId(int id)
{
this->io.seekg((id - 1) * sizeof(Question), this->io.beg);
this->io.read(reinterpret_cast<char *>(&this->question), sizeof(Question));

return this->question;
}

bool QuestionModel::questionTitleExists(string &title)
{
Question question;

this->io.seekg(0, this->io.beg);

while (this->io.read(reinterpret_cast<char *>(&question), sizeof(Question)))
{
if (question.title == title)
{
return true;
}
}

return false;
}

void QuestionModel::markAnswered(int id)
{
this->setAfterId(id);

this->question.hasAnswer = true;

this->save();
}

void QuestionModel::markAs(const string &status, int id)
{
this->setAfterId(id);

this->question.active = (status == "active") ? true : false;

this->save();
}

// Serialize the object.
void QuestionModel::save()
{
this->io.seekp((this->question.id - 1) * sizeof(Question), this->io.beg);

this->io.clear();

if (!this->io.write(reinterpret_cast<char *>(&this->question), sizeof(Question)))
{
throw system_error(error_code(3, generic_category()), "Failed persisting data to file!");
}
}

void QuestionModel::setAttributes(map<string, string> &cleanInputs)
{
for (map<string, string>::iterator it = cleanInputs.begin(); it != cleanInputs.end(); it++)
{
if (isInVector(this->protectedAttributes, it->first))
{
continue;
}
else if (it->first == "title")
{
strcpy(this->question.title, it->second.c_str());
}
else if (it->first == "body")
{
strcpy(this->question.body, it->second.c_str());
}
}

// If there's a new user, assign created_at with the current date.
if (cleanInputs.find("action")->second == "create")
{
time_t t = time(nullptr);
strftime(this->question.created_at, sizeof(this->question.created_at), "%c", localtime(&t));
}

this->question.id = ++this->lastId;
}

void QuestionModel::dumpFile()
{
Question question;

ifstream db(QuestionModel::pathToFile, ios::in | ios::binary);
ofstream dump((QuestionModel::pathToFile.substr(0, QuestionModel::pathToFile.find(".store")).append(".txt")), ios::out | ios::trunc);

if (db.is_open() && dump.is_open())
{
db.seekg(0, db.beg);

while (db.read(reinterpret_cast<char *>(&question), sizeof(Question)))
{
dump << question.id << endl
<< question.user_id << endl
<< question.category_id << endl
<< question.title << endl
<< question.body << endl
<< question.created_at << endl
<< question.deleted_at << endl
<< question.hasAnswer << endl;
}

db.close();
dump.close();
}
}

QuestionModel::~QuestionModel()
{
this->io.close();
}

string QuestionModel::pathToFile = "..\\database\\questions.store";
void QuestionModel::openIOStream()
{
this->io.open(QuestionModel::pathToFile, ios::in | ios::out | ios::binary);
this->io.seekp(0, this->io.end);
this->fileSize = this->io.tellp();

if (!this->io.is_open())
{
toast(string("Couldn't open the file stream!"), string("error"));
}
}

void QuestionModel::setLastId()
{
if (this->fileSize == 0)
{
this->lastId = 0;
}
else
{
this->io.seekg((this->fileSize / sizeof(Question) - 1) * sizeof(Question), this->io.beg);
this->io.read(reinterpret_cast<char *>(&this->question), sizeof(Question));

this->lastId = this->question.id;
}
}

QuestionModel::QuestionModel()
{
this->openIOStream();
this->protectedAttributes = { "id", "user_id", "category_id", "created_at", "deleted_at", "votes", "hasAnswer" };
this->setLastId();
}
54 changes: 54 additions & 0 deletions app/QuestionModel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once

#include <iomanip>
#include <ctime>
#include <locale>

#include "ModelInterface.h"

using namespace std;

typedef struct {
int id;
int user_id;
int category_id = 1;

unsigned votes = 0;

char title[255] = "";
char body[1023] = "";
char created_at[50] = "";

// Soft deletes
char deleted_at[50] = "";
bool hasAnswer = 0;
bool active = 0;
} Question;

class QuestionModel : public ModelInterface {
private:
static string pathToFile;

Question question;

void openIOStream();
void setLastId();

long long fileSize;
int lastId;
public:
static void dumpFile();
QuestionModel();

Question setAfterUserId(int);
Question setAfterId(int);

bool questionTitleExists(string &);
void markAnswered(int);
void markAs(const string &, int);
void save();
void setAttributes(map<string, string> &);

// Set the active to false on logout
~QuestionModel();
};
89 changes: 89 additions & 0 deletions app/QuestionRepository.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include "QuestionRepository.h"
#include "Controller.h"

string QuestionRepository::alias = "question";

string &QuestionRepository::getAlias()
{
return QuestionRepository::alias;
}

QuestionRepository::QuestionRepository()
{
this->defineValidation();
}

void QuestionRepository::defineValidation()
{
// Stand back, I am going to try Regex!
this->ValidationRules = {
{ "title", "^.*$" },
{ "body", "^.*$" },
{ "keyword", "^.*$" }
};

this->ValidationErrors = {
{ "title", "..." },
{ "body", "..." },
{ "keyword", "..." }
};

Controller::pushError(string(""));
}

void QuestionRepository::receiveCleanInput(map<string, string> &cleanInput)
{
this->model.setAttributes(cleanInput);

string action = cleanInput.find("action")->second;

if (action == "search")
{
string keyword = cleanInput.find("keyword")->second;

toast(string("Search!"), string("notice"));
}
else if (action == "create")
{
toast(string("Create!"), string("notice"));
}
else
{
Controller::pushError(string("Please provide a valid action!"));
}
}

// Determine what to output via the model.
void QuestionRepository::echo(const string &alias)
{
if (alias == "")
{
}
}

void QuestionRepository::validateItems(map<string, string> &truncatedInput)
{
for (map<string, string>::iterator it = truncatedInput.begin(); it != truncatedInput.end(); it++)
{
try
{
if (!regex_match(it->second.c_str(), regex(this->ValidationRules[it->first.c_str()])))
{
Controller::pushError(this->ValidationErrors[it->first.c_str()]);
}
}
catch (const regex_error &e)
{
toast("\n\n" + string(e.what()) + "\n", string("error"));
}
}

if (Controller::getErrorBag().empty())
{
this->receiveCleanInput(truncatedInput);
}
}

QuestionRepository::~QuestionRepository()
{
}
27 changes: 27 additions & 0 deletions app/QuestionRepository.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <regex>

#include "RepositoryInterface.h"
#include "QuestionModel.h"

using namespace std;

class QuestionRepository : public RepositoryInterface {
private:
static string alias;

QuestionModel model;

void defineValidation();
void receiveCleanInput(map<string, string> &);
public:
static string &getAlias();

QuestionRepository();

void validateItems(map<string, string> &);
void echo(const string &);

~QuestionRepository();
};
Loading

0 comments on commit a98f06b

Please sign in to comment.