void* allocPage() { void* res; if (pool == NULL) { initPages(); } res = next_free_page; if (res == NULL) { error("error: all pages already allocated", ""); } next_free_page = *((void**)next_free_page); assert(res != NULL); return res; }
void Global::initDatabase(QString filename) { GASSERT(_db == NULL, "Cannot initialize database twice"); QFile fd (filename); if (!fd.open(QIODevice::ReadOnly)) GEXITDIALOG("Could not open project file for reading " + filename); QTextStream file (&fd); // The first five lines contain the following information // VERSION=%s // IMAGE_PATH=%s // QUESTIONS_PER_STUDENT=%d // PAGES_PER_STUDENT=%d // TOTAL_STUDENTS=%d // SAVE_DATE_TIME=%s // NUM_IMAGES=%d struct _keyvalue { QString key; QString string; size_t number; }; _keyvalue intro[7]; QString header_names[7] = { "VERSION", "IMAGE_PATH", "QUESTIONS_PER_STUDENT", "PAGES_PER_STUDENT", "TOTAL_STUDENTS", "SAVE_DATE_TIME", "NUM_IMAGES" }; bool expect_numbers[7] = { false, false, true, true, true, false, true }; for (size_t i = 0; i < 7; i++) { if (file.atEnd()) GEXITDIALOG(QString("Encountered end of file reading line %1").arg(i)); QString in = file.readLine(); QStringList fields = in.split("="); if (fields.size() != 2) GEXITDIALOG(QString("Could not find key/value pair separated by = in [%1] reading line %2").arg(in).arg(i)); intro[i].key = fields.at(0); if (header_names[i] != fields.at(0)) GEXITDIALOG(QString("Line %1 header was not [%2] but found instead [%3]").arg(i).arg(header_names[i]).arg(intro[i].key)); intro[i].string = fields.at(1); if (fields.at(0).size() == 0) GEXITDIALOG(QString("Key in [%1] was empty reading line %2").arg(in).arg(i)); if (fields.at(1).size() == 0) GEXITDIALOG(QString("Value in [%1] was empty reading line %2").arg(in).arg(i)); intro[i].number = -1; if (expect_numbers[i]) { bool ok; int num = fields.at(1).toInt(&ok); if (!ok) GEXITDIALOG(QString("Value [%1] in [%2] was not an integer").arg(fields.at(1)).arg(in)); if (num <= 0) GEXITDIALOG(QString("Value [%1] in [%2] was not a positive integer").arg(fields.at(1)).arg(in)); intro[i].number = num; } if (expect_numbers[i]) GDEBUG ("Parsed key [%s] = number [%zu]", qPrintable(intro[i].key), intro[i].number); else GDEBUG ("Parsed key [%s] = string [%s]", qPrintable(intro[i].key), qPrintable(intro[i].string)); } QString version = intro[0].string; QString image_path = intro[1].string; size_t numQuestions = intro[2].number; size_t numPagesPerStudent = intro[3].number; size_t numStudents = intro[4].number; QString unused_date_time = intro[5].string; size_t numImages = intro[6].number; // Initialize the database since we know all the dimensions now initDatabase(numStudents, numQuestions, numPagesPerStudent); // Check the version is what we are expecting if (version != DB_VERSION) GEXITDIALOG(QString("Found version [%1] when only version [%2] is supported").arg(version).arg(DB_VERSION)); // Image paths should always be . right now, nothing else is supported if (image_path != ".") GEXITDIALOG("Non-empty path found for image loading"); int slash = filename.lastIndexOf("/"); if (slash < 0) GEXITDIALOG("Could not find ending slash in file name"); QString full_image_path = filename.left(slash); GDEBUG("Using image path [%s] from input file [%s]", qPrintable(full_image_path), qPrintable(filename)); initPages(full_image_path); // Check that the number of images loaded matches what we are expecting GDEBUG ("Loaded in %zu images from [%s], input file says %zu images", getPages()->size(), qPrintable(image_path), numImages); if (getPages()->size() != numImages) GEXITDIALOG(QString("Found different number of images than was used for the save")); // Now populate the database. Each entry is of the following form: // IDNUM,STUDENTID,STUDENTNAME,TOTAL,Q0,Q1,..,QN,Feed0,Feed1,..,FeedN // All fields will contain something except for Feed0..FeedN for (int student = -2; student < (int)numStudents; student++) { if (file.atEnd()) GEXITDIALOG(QString("Encountered end of file reading student %1 of %2").arg(student+1).arg(numStudents)); QString in = file.readLine(); QStringList fields = in.split("\t"); if (fields.size() != (2*(int)numQuestions + 4)) GEXITDIALOG(QString("Found only %1 columns in [%2] for student %3 of %4 when expected 2x%5+4 columns").arg(fields.size()).arg(in).arg(student+1).arg(numStudents).arg(numQuestions)); for (size_t col = 4; col < numQuestions+4; col++) // Do not search student id/name, or any feedback, since these can be empty if (fields.at(col) == "") GEXITDIALOG(QString("Found empty column %1 for student %3 of %4").arg(col+1).arg(student+1).arg(numStudents)); bool ok; { int num = fields.at(0).toInt(&ok); if ((!ok) || (num != student)) GEXITDIALOG(QString("Expected row number %1 but found [%2] instead for student %3 of %4").arg(student).arg(fields.at(0)).arg(student+1).arg(numStudents)); } if (student == -2) { if ((fields.at(1) != "PAGE") && (fields.at(2) != "PAGE")) GEXITDIALOG(QString("Expected PAGE headers for first row of data, but found [%1] [%2] instead").arg(fields.at(1).arg(fields.at(2)))); for (size_t q = 0; q < numQuestions; q++) { int page = fields.at(q+4).toInt(&ok); if ((!ok) || (page < -1)) GEXITDIALOG(QString("Page value [%1] for question %2 was not valid for maximum values").arg(fields.at(q+4)).arg(q+1)); Global::db()->setQuestionPage(q, page, false); } int total = fields.at(3).toInt(&ok); if ((!ok) || (total != -1)) GEXITDIALOG(QString("Page total in file [%1] was not -1").arg(fields.at(3))); } else if (student == -1) { if ((fields.at(1) != "MAX") && (fields.at(2) != "MAX")) GEXITDIALOG(QString("Expected MAX headers for first row of data, but found [%1] [%2] instead").arg(fields.at(1).arg(fields.at(2)))); for (size_t q = 0; q < numQuestions; q++) { int score = fields.at(q+4).toInt(&ok); if ((!ok) || (score < -1)) GEXITDIALOG(QString("Score value [%1] for question %2 was not valid for maximum values").arg(fields.at(q+4)).arg(q+1)); Global::db()->setQuestionMaximum(q, score, false); } int total = fields.at(3).toInt(&ok); if ((!ok) || (total != Global::db()->getTotalMaximum())) GEXITDIALOG(QString("Maximum total in file [%1] did not match calculated maximum total %2").arg(fields.at(3)).arg(Global::db()->getTotalMaximum())); } else { Student &entry = Global::db()->getStudent(student); entry.setStudentId(fields.at(1), false); entry.setStudentName(fields.at(2), false); for (size_t q = 0; q < numQuestions; q++) { int score = fields.at(q+4).toInt(&ok); if ((!ok) || (score < -1) || (score > Global::db()->getQuestionMaximum(q))) GEXITDIALOG(QString("Score value [%1] for question %2 was not valid student %3 of %4").arg(fields.at(q+4)).arg(q+1).arg(student+1).arg(numStudents)); entry.setGrade(q, score, false); } for (size_t q = 0; q < numQuestions; q++) entry.setFeedback(q, fields.at(q+4+numQuestions), false); int total = fields.at(3).toInt(&ok); if ((!ok) || (total != entry.getTotal())) GEXITDIALOG(QString("Total in file [%1] did not match calculated total %2").arg(fields.at(3)).arg(entry.getTotal())); } } }
DataStorage::DataStorage(){ initPages(); initBooks(); initAuthors(); }