#include "main_window.h"
#include <cmath>
bool MainWindow::saveChangesBeforeProceeding(QString parentFunction, bool close)
{
if (current_db_open && this->isWindowModified()) {
switch (QMessageBox::information(this, parentFunction, tr("Save changes before proceeding?"), tr("&Save"), tr("&Discard"), tr("Cancel"), 0, 2)) {
case 0: current_db_open = false;
save(); if (close) {closeDB();}; return false;
break;
case 1: current_db_open = false;
if (close) {closeDB();}; return false;
break;
case 2: return true;
break;
}
} else if (current_db_open && (!this->isWindowModified())) {
if (close) {closeDB();}; return false;
}
return false;
}
void MainWindow::newDB()
{
this->setEnabled(false);
bool cancelled = saveChangesBeforeProceeding(tr("New database"), true);
if (cancelled) { this->setEnabled(true); return; }
bool ok;
QString db_name = QInputDialog::getText(this, tr("New database"), tr("Database name:"), QLineEdit::Normal, tr("Untitled database"), &ok);
QString saveDBName;
if (!ok) { this->setEnabled(true); return; }
if (!db_name.isEmpty()) {
saveDBName = QFileDialog::getSaveFileName(this, tr("Create database file"), tr("%1.itdb").arg(db_name), tr("iTest databases (*.itdb)"));
} else {
saveDBName = QFileDialog::getSaveFileName(this, tr("Create database file"), tr("untitled.itdb"), tr("iTest databases (*.itdb)"));
}
if (saveDBName.isNull() || saveDBName.isEmpty()) { this->setEnabled(true); return; }
QFile file(saveDBName);
if (!file.open(QFile::WriteOnly | QFile::Text))
{
QMessageBox::critical(this, tr("Create database file"), tr("Cannot write file %1:\n%2.").arg(saveDBName).arg(file.errorString()));
this->setWindowTitle(tr("iTestServer"));
this->setEnabled(true); return;
}
setProgress(10); QTextStream sfile(&file);
sfile.setCodec("UTF-8");
sfile << "[ITEST_VERSION]\n" << f_ver << endl;
sfile << "[ITEST_DB_VERSION]\n" << f_itdb_ver << endl;
sfile << "[DB_NAME]\n" << db_name << endl;
sfile << "[DB_DATE]\n" << endl;
sfile << "[DB_DATE_ULSD]\ntrue" << endl;
sfile << "[DB_COMMENTS]\n" << endl;
sfile << "[DB_QNUM]\n0" << endl;
sfile << "[DB_SNUM]\n0" << endl;
sfile << "[DB_FLAGS]" << endl;
sfile << "--------------------\n";
for (int i = 0; i < 20; ++i) {sfile << "[DB_F" << i << "]\n" << endl;}
sfile << "[DB_FLAGS_END]";
setProgress(50); enableAll(); clearAll();
DBIDatabaseNameLineEdit->setText( db_name );
current_db_file = saveDBName;
current_db_name = db_name;
current_db_open = true;
addRecent(saveDBName);
this->setWindowTitle(tr("%1[*] - iTestServer").arg(current_db_name));
this->setWindowModified(false);
statusBar()->showMessage(tr("Ready"), 10000);
setProgress(100); setProgress(-1); actionEdit_questions->setChecked(true);
actionUse_last_save_date->setChecked(true);
DBIUseLastSaveDateCheckBox->setChecked(true);
DBIDateEdit->setDateTime(QDateTime::currentDateTime());
DBIDateEdit->setEnabled(false);
setPage(actionEdit_questions);
this->setEnabled(true);
}
void MainWindow::save() { saveDB(current_db_file); }
void MainWindow::saveAs()
{
QString db_name = DBIDatabaseNameLineEdit->text();
QString saveDBName = QFileDialog::getSaveFileName(this, tr("Save database"), QString("%1.itdb").arg(db_name), tr("iTest databases (*.itdb)"));
if (!saveDBName.isNull() || !saveDBName.isEmpty()) { addRecent(saveDBName); saveDB(saveDBName); }
}
void MainWindow::saveCopy()
{
if (this->isWindowModified()) {
switch (QMessageBox::information(this, tr("Save a copy"), tr("It is necessary to save any changes you have made to the database before proceeding."), tr("&Save"), tr("Cancel"), 0, 1)) {
case 0: save(); break;
case 1: return; break;
}
}
QString db_name = DBIDatabaseNameLineEdit->text();
QString saveDBName = QFileDialog::getSaveFileName(this, tr("Save a copy"), QString("%1.itdb").arg(db_name), tr("iTest databases (*.itdb)"));
if (!saveDBName.isNull() || !saveDBName.isEmpty())
{ addRecent(saveDBName); saveDB(saveDBName, true, false); }
}
void MainWindow::saveBackup()
{
if (this->isWindowModified()) {
switch (QMessageBox::information(this, tr("Save a backup"), tr("It is necessary to save any changes you have made to the database before proceeding."), tr("&Save"), tr("Cancel"), 0, 1)) {
case 0: save(); break;
case 1: return; break;
}
}
QString db_name = DBIDatabaseNameLineEdit->text();
QString saveDBName = QFileDialog::getSaveFileName(this, tr("Save a backup"), QString("%1.itdb").arg(db_name), tr("iTest databases (*.itdb)"));
if (!saveDBName.isNull() || !saveDBName.isEmpty())
{ addRecent(saveDBName); saveDB(saveDBName, true, true); }
}
void MainWindow::saveDB(QString db_file_name, bool savecopy, bool allsessions)
{
this->setEnabled(false); qApp->processEvents();
if (LQListWidget->currentIndex().isValid()) {
applyQuestionChanges();
}
QString db_name = DBIDatabaseNameLineEdit->text();
if (db_name != current_db_name) {
QSettings archive(QSettings::IniFormat, QSettings::UserScope, "Michal Tomlein", "iTest");
QStringList dbs, sns; QString buffer;
switch (QMessageBox::information(this, tr("iTestServer"), tr("Are you sure you want to change the database name?\nIf you do so, any archived sessions associated to this database\non other computers will not load, unless you change it back.\nThis computer's archive will be updated."), tr("&Change"), tr("Do ¬ change"), 0, 1)) {
case 0: dbs = archive.value("databases").toStringList();
if (!dbs.contains(current_db_name)) { break; }
dbs.removeAll(current_db_name);
dbs << db_name;
archive.setValue("databases", dbs);
sns = archive.value(QString("%1/sessions").arg(current_db_name)).toStringList();
archive.setValue(QString("%1/sessions").arg(db_name), sns);
for (int i = 0; i < sns.count(); ++i) {
buffer = archive.value(QString("%1/%2").arg(current_db_name).arg(sns.at(i))).toString();
archive.setValue(QString("%1/%2").arg(db_name).arg(sns.at(i)), buffer);
}
archive.remove(current_db_name);
break;
case 1: db_name = current_db_name;
DBIDatabaseNameLineEdit->setText(current_db_name);
break;
}
}
QString use_last_save_date; QString db_date;
if (actionUse_last_save_date->isChecked()) {
db_date = QDateTime::currentDateTime().toString("yyyy.MM.dd-hh:mm");
use_last_save_date = "true";
} else {
db_date = DBIDateEdit->dateTime().toString("yyyy.MM.dd-hh:mm");
use_last_save_date = "false";
}
QString db_comments = removeLineBreaks(ECTextEdit->toHtml());
QMap<QDateTime, Session *> sessions = current_db_sessions;
if (allsessions) {
QMapIterator<QDateTime, ArchivedSession *> a(current_db_archivedsessions);
while (a.hasNext()) { a.next();
if (!sessions.contains(a.key())) {
sessions.insert(a.key(), a.value());
}
}
}
uint db_snum = (uint)sessions.size();
QFile file(db_file_name);
if (!file.open(QFile::WriteOnly | QFile::Text))
{
QMessageBox::critical(this, tr("Save database"), tr("Cannot write file %1:\n%2.").arg(db_file_name).arg(file.errorString()));
this->setEnabled(true); return;
}
setProgress(5); QTextStream sfile(&file); QString q_file_name;
sfile.setCodec("UTF-8");
sfile << "[ITEST_VERSION]\n" << f_ver << endl;
sfile << "[ITEST_DB_VERSION]\n" << f_itdb_ver << endl;
sfile << "[DB_NAME]\n" << db_name << endl;
sfile << "[DB_DATE]\n" << db_date << endl;
sfile << "[DB_DATE_ULSD]\n" << use_last_save_date << endl;
sfile << "[DB_COMMENTS]\n" << db_comments << endl;
sfile << "[DB_QNUM]\n" << current_db_questions.size() << endl;
sfile << "[DB_SNUM]\n" << db_snum << endl;
sfile << "[DB_FLAGS]" << endl;
for (int i = 0; i < 20; ++i)
{if (current_db_fe[i]) {sfile << "+";} else {sfile << "-";}} sfile << endl;
for (int i = 0; i < 20; ++i)
{sfile << "[DB_F" << i << "]" << endl; sfile << current_db_f[i] << endl;}
sfile << "[DB_FLAGS_END]" << endl;
setProgress(10); uint count = (uint)current_db_questions.size() + (uint)sessions.size();
uint n = current_db_questions.size();
for (int i = 0; i < LQListWidget->count(); ++i) {
sfile << current_db_questions.value(LQListWidget->item(i))->allProperties() << endl;
setProgress((90/(i+1)*count)+10); }
QMapIterator<QDateTime, Session *> i(sessions);
while (i.hasNext()) { i.next(); n++;
sfile << i.value()->sessionData() << endl;
for (int s = 0; s < i.value()->numStudents(); ++s) {
sfile << i.value()->student(s)->studentData() << endl;
}
setProgress((90/n*count)+10); }
while (!current_db_queued_sessions.isEmpty()) {
ArchivedSession * session = current_db_queued_sessions.dequeue();
if (session->status() == ArchivedSession::Remove) {
session->removeFromArchive();
session->destruct();
delete session;
} else if (session->status() == ArchivedSession::Archive) {
session->archive();
session->setStatus(ArchivedSession::Default);
}
}
if (!savecopy) {
current_db_name = db_name;
current_db_date = db_date;
current_db_comments = db_comments;
current_db_file = db_file_name;
if (actionUse_last_save_date->isChecked()) {
DBIDateEdit->setDateTime(QDateTime::fromString(current_db_date, "yyyy.MM.dd-hh:mm"));
}
}
this->setWindowTitle(tr("%1[*] - iTestServer").arg(current_db_name));
this->setWindowModified(false);
statusBar()->showMessage(tr("Database saved"), 10000);
setProgress(100); setProgress(-1); this->setEnabled(true);
}
void MainWindow::open()
{
bool cancelled = saveChangesBeforeProceeding(tr("Open database"), true);
if (!cancelled) {
QString openDBName = QFileDialog::getOpenFileName(this, tr("Open database file"), "", tr("iTest databases (*.itdb);;All files (*.*)"));
if (!openDBName.isNull()) { addRecent(openDBName); openDB(openDBName); }
}
}
void MainWindow::openRecent()
{
if (recentDBsListWidget->currentIndex().isValid()) {
addRecent(recentDBsListWidget->currentItem()->text());
openDB(recentDBsListWidget->currentItem()->text());
}
}
void MainWindow::openRecent(QListWidgetItem * item)
{
QString buffer = item->text();
addRecent(buffer);
openDB(buffer);
}
void MainWindow::openDB(QString openDBName, bool useCP1250)
{
if (openDBName.isNull()) { return; }
this->setEnabled(false); qApp->processEvents();
try {
QFile file(openDBName);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::critical(this, tr("Open database"), tr("Cannot read file %1:\n%2.").arg(openDBName).arg(file.errorString()));
this->setEnabled(true); return;
}
setProgress(3); QTextStream rfile(&file);
if (useCP1250) { rfile.setCodec("CP 1250"); }
else { rfile.setCodec("UTF-8"); }
QString db_buffer; float version; float db_version; QString db_name;
QString db_date; QString db_comments; int db_qnum; bool db_fxxe[20];
QString db_f[20]; QString q_file_name; QString db_ulsd; int s_lenum;
QuestionItem * item; QStringList answers; int db_snum; int s_snum;
QStringList bufferlist; int ans_flag; SvgItem * svg;
QuestionItem::Answer c_ans; QuestionItem::Answer ans;
if (rfile.readLine() != "[ITEST_VERSION]") { throw xInvalidDBFile(); }
version = rfile.readLine().toFloat();
if (rfile.readLine() != "[ITEST_DB_VERSION]") { throw xInvalidDBFile(); }
db_version = rfile.readLine().toFloat();
if ((version > f_ver) && (db_version == f_itdb_ver))
{ QMessageBox::information(this, tr("iTest version notice"), tr("There is a newer version of iTest available.\nNonetheless, this version is able to open the database file you selected,\nbut you are most probably missing a whole bunch of cool new features.")); }
if ((version > f_ver) && (db_version > f_itdb_ver))
{ QMessageBox::critical(this, tr("iTest version notice"), tr("You need a newer version of iTest to open this database file.")); this->setEnabled(true); return; }
if (!useCP1250) {
if (db_version == 1.0) { openDB(openDBName, true); return; }
}
if (rfile.readLine() != "[DB_NAME]") { throw xInvalidDBFile(); }
db_name = rfile.readLine();
if (rfile.readLine() != "[DB_DATE]") { throw xInvalidDBFile(); }
db_date = rfile.readLine();
if (rfile.readLine() != "[DB_DATE_ULSD]") { throw xInvalidDBFile(); }
db_ulsd = rfile.readLine();
if (rfile.readLine() != "[DB_COMMENTS]") { throw xInvalidDBFile(); }
db_comments = rfile.readLine();
if (rfile.readLine() != "[DB_QNUM]") { throw xInvalidDBFile(); }
db_qnum = rfile.readLine().toInt();
if (rfile.readLine() != "[DB_SNUM]") { throw xInvalidDBFile(); }
db_snum = rfile.readLine().toInt();
if (rfile.readLine() != "[DB_FLAGS]") { throw xInvalidDBFile(); }
setProgress(6); db_buffer = rfile.readLine();
for (int i = 0; i < 20; ++i) {
if (db_buffer.at(i) == '+') {db_fxxe[i] = true;} else if (db_buffer.at(i) == '-')
{db_fxxe[i] = false;} else { throw xInvalidDBFile(); }
}
for (int i = 0; i < 20; ++i) {
if (rfile.readLine() != QString("[DB_F%1]").arg(i)) { throw xInvalidDBFile(); }
db_f[i] = rfile.readLine();
}
if (rfile.readLine() != "[DB_FLAGS_END]") { throw xInvalidDBFile(); }
setProgress(10); int count = db_qnum + db_snum;
for (int i = 0; i < db_qnum; ++i) {
answers.clear();
if (rfile.readLine() != "[Q_NAME]") { throw xInvalidDBFile(); }
item = new QuestionItem (rfile.readLine());
if (rfile.readLine() != "[Q_FLAG]") { throw xInvalidDBFile(); }
item->setFlag(rfile.readLine().toInt());
if (db_version >= 1.2) {
if (rfile.readLine() != "[Q_GRP]") { throw xInvalidDBFile(); }
item->setGroup(rfile.readLine());
}
if (db_version >= 1.2) { if (rfile.readLine() != "[Q_DIF]") { throw xInvalidDBFile(); } }
else { if (rfile.readLine() != "[Q_DIFFICULTY]") { throw xInvalidDBFile(); } }
item->setDifficulty(rfile.readLine().toInt());
if (rfile.readLine() != "[Q_TEXT]") { throw xInvalidDBFile(); }
item->setText(rfile.readLine());
if (rfile.readLine() != "[Q_ANSA]") { throw xInvalidDBFile(); }
answers << rfile.readLine();
if (db_version < 1.2) { if (rfile.readLine() != "[Q_ANSA_C]") { throw xInvalidDBFile(); } }
item->setAnsACorrect(rfile.readLine() == "true");
if (rfile.readLine() != "[Q_ANSB]") { throw xInvalidDBFile(); }
answers << rfile.readLine();
if (db_version < 1.2) { if (rfile.readLine() != "[Q_ANSB_C]") { throw xInvalidDBFile(); } }
item->setAnsBCorrect(rfile.readLine() == "true");
if (rfile.readLine() != "[Q_ANSC]") { throw xInvalidDBFile(); }
answers << rfile.readLine();
if (db_version < 1.2) { if (rfile.readLine() != "[Q_ANSC_C]") { throw xInvalidDBFile(); } }
item->setAnsCCorrect(rfile.readLine() == "true");
if (rfile.readLine() != "[Q_ANSD]") { throw xInvalidDBFile(); }
answers << rfile.readLine();
if (db_version < 1.2) { if (rfile.readLine() != "[Q_ANSD_C]") { throw xInvalidDBFile(); } }
item->setAnsDCorrect(rfile.readLine() == "true");
if (db_version < 1.2) { if (rfile.readLine() != "[Q_ICNT]") { throw xInvalidDBFile(); } }
else { if (rfile.readLine() != "[Q_ICCNT]") { throw xInvalidDBFile(); } }
item->setIncorrectAnsCount(rfile.readLine().toUInt());
if (db_version < 1.2) { if (rfile.readLine() != "[Q_CCNT]") { throw xInvalidDBFile(); } }
item->setCorrectAnsCount(rfile.readLine().toUInt());
if (db_version >= 1.2) {
if (rfile.readLine() != "[Q_HID]") { throw xInvalidDBFile(); }
item->setHidden(rfile.readLine() == "true");
}
if (db_version > 1.25) {
if (rfile.readLine() != "[Q_SVG]") { throw xInvalidDBFile(); }
int numsvgitems = rfile.readLine().toInt();
for (int g = 0; g < numsvgitems; ++g) {
db_buffer = rfile.readLine();
svg = new SvgItem(db_buffer, rfile.readLine());
item->addSvgItem(svg);
}
}
if (db_version < 1.25) {
if (rfile.readLine() != "[Q_END]") { throw xInvalidDBFile(); }
}
item->setAnswers(answers);
QListWidgetItem * q_item = new QListWidgetItem (item->group().isEmpty() ? item->name() : QString("[%1] %2").arg(item->group()).arg(item->name()));
LQListWidget->addItem(q_item);
current_db_questions.insert(q_item, item);
setQuestionItemIcon(q_item, item->difficulty());
setQuestionItemColour(q_item, item->flag());
hideQuestion(q_item, item);
setProgress((85/(i+1)*count)+10); }
for (int i = 0; i < db_snum; ++i) {
if (rfile.atEnd()) { break; }
if (rfile.readLine() != "[SESSION]") { continue; }
Session * session = new Session;
session->setName(rfile.readLine());
session->setDateTimeFromString(rfile.readLine());
if (db_version >= 1.2) {
if (rfile.readLine() != "[PASSMARK]") { throw xInvalidDBFile(); }
PassMark pm(rfile.readLine().toInt());
int pm_count = rfile.readLine().toInt(); int pm_c;
for (int i = 0; i < pm_count; ++i) {
pm_c = rfile.readLine().toInt();
pm.addCondition(pm_c, rfile.readLine().toInt());
}
session->setPassMark(pm);
} else {
session->setPassMark(PassMark(rfile.readLine().toInt()));
}
s_snum = rfile.readLine().toInt();
s_lenum = rfile.readLine().toInt();
for (int le = 0; le < s_lenum; ++le) {
bufferlist.clear();
bufferlist = rfile.readLine().split(';');
if (bufferlist.count() == 6) {
session->addLogEntry(bufferlist.at(0).toInt(),
bufferlist.at(1).toInt(),
bufferlist.at(2).toInt(),
bufferlist.at(3).toInt(),
bufferlist.at(4).toInt(),
bufferlist.at(5).toInt(),
rfile.readLine());
} else {
session->addLogEntry(255, 255, 255, 0, 0, 0, rfile.readLine());
}
}
for (int s = 0; s < s_snum; ++s) {
if (rfile.atEnd()) { break; }
if (rfile.readLine() != "[STUDENT]") { continue; }
Student * student = new Student (rfile.readLine());
student->setReady(rfile.readLine() == "true");
if (db_version >= 1.2) {
student->setPassed(rfile.readLine() == "true");
}
student->setNumber(rfile.readLine().toInt());
student->setScore(rfile.readLine().toInt());
int numresults = rfile.readLine().toInt();
QMap<QString, QuestionAnswer> * results = new QMap<QString, QuestionAnswer>;
for (int a = 0; a < numresults; ++a) {
db_buffer = rfile.readLine();
if (db_version >= 1.2) {
ans_flag = rfile.readLine().toInt();
} else {
QuestionItem * item = NULL;
QMapIterator<QListWidgetItem *, QuestionItem *> q(current_db_questions);
while (q.hasNext()) { q.next();
if (q.value()->name() == db_buffer) { item = q.value(); break; }
}
if (item == NULL) {
ans_flag = -1;
} else {
ans_flag = item->flag();
}
}
if (db_version < 1.27) {
ans = QuestionItem::convertOldAnsNumber(rfile.readLine().toInt());
c_ans = QuestionItem::convertOldAnsNumber(rfile.readLine().toInt());
} else {
ans = (QuestionItem::Answer)rfile.readLine().toInt();
c_ans = (QuestionItem::Answer)rfile.readLine().toInt();
}
QuestionAnswer qans(ans_flag, c_ans, ans);
results->insert(db_buffer, qans);
}
student->setResults(results);
if (db_version < 1.2) {
student->setPassed(session->passMark().check(student->results(), ¤t_db_questions));
}
session->addStudent(student);
}
current_db_sessions.insert(session->dateTime(), session);
QListWidgetItem * item = new QListWidgetItem (QString("%1 - %2").arg(session->dateTimeToString()).arg(session->name()));
VSSLSListWidget->insertItem(0, item);
item->setData(Qt::UserRole, session->dateTime());
setProgress((85/(db_qnum+i+1)*count)+10); }
DBIDatabaseNameLineEdit->setText( db_name );
DBIDateEdit->setDateTime( QDateTime::fromString(db_date, "yyyy.MM.dd-hh:mm") );
if (db_ulsd == "true") {
DBIUseLastSaveDateCheckBox->setChecked(true);
actionUse_last_save_date->setChecked(true);
DBIDateEdit->setEnabled(false);
} else if (db_ulsd == "false") {
DBIUseLastSaveDateCheckBox->setChecked(false);
actionUse_last_save_date->setChecked(false);
DBIDateEdit->setEnabled(true);
}
ECTextEdit->setHtml( db_comments );
for (int i = 0; i < 20; ++i) {current_db_fe[i] = db_fxxe[i];}
for (int i = 0; i < 20; ++i) {current_db_f[i] = db_f[i];}
setProgress(97); setFlags(); loadFlags();
setProgress(98); enableAll();
setProgress(99); current_db_file = openDBName;
current_db_name = db_name;
current_db_date = db_date;
current_db_comments = db_comments;
current_db_open = true;
this->setWindowTitle(tr("%1[*] - iTestServer").arg(current_db_name));
this->setWindowModified(false);
statusBar()->showMessage(tr("Database open"), 10000);
setProgress(100); setProgress(-1); actionEdit_questions->setChecked(true);
mainStackedWidget->setCurrentIndex(1);
this->setEnabled(true);
openArchive();
}
catch (xInvalidDBFile) {
errorInvalidDBFile(tr("Open database"), openDBName);
}
catch (...) {
QMessageBox::critical(this, tr("iTestServer"), tr("Error opening database."));
clearAll(); disableAll(); current_db_open = false;
this->setWindowTitle(tr("iTestServer"));
this->setWindowModified(false); setProgress(-1);
mainStackedWidget->setCurrentIndex(0);
this->setEnabled(true);
}
}
void MainWindow::closeDB()
{
if (saveChangesBeforeProceeding(tr("Close database"), false)) { return; }
clearAll(); disableAll(); current_db_open = false;
this->setWindowTitle(tr("iTestServer"));
this->setWindowModified(false);
setProgress(100); setProgress(-1); mainStackedWidget->setCurrentIndex(0);
statusBar()->showMessage(tr("Database closed"), 10000);
}