Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h> //needed for associative array implementation
#include <sqlite3.h> //using sqlite3 library for database integration
//small helper functions
int lengthCArray( char** CArray )
{
return sizeof CArray / sizeof *CArray ;
}
int lengthCArray( int** CArray )
{
return sizeof CArray / sizeof *CArray ;
}
//main program functions
static int displayRecordsCallback(void* data, int numColumns, char** columnValue, char** columnName)
{
std::cout << std::endl;
std::cerr << (char*)data << std::endl; //echo any additional custom arguments passed in the callback
for (int i = 0; i < numColumns; i++) {
std::cout << columnName[i] << "\t" << columnValue[i] << "\t";
}
return 0;
}
static int displayFormattedClassesCallback(void* data, int numColumns, char** columnValue, char** columnName)
{
int time_24hrs, length_hrs;
std::string room_id, module_code;
std::string class_type;
std::cout << std::endl;
std::cerr << (char*)data << std::endl; //echo any additional custom arguments passed in the callback
//store all record values in their appropriate variable to "cache" them for easier later use inside this function
for (int i = 0; i < numColumns; i++) {
if (columnName[i] == "time_24hrs") {
time_24hrs = (int)columnValue[i];
}
else if (columnName[i] == "length_hrs") {
length_hrs = (int)columnValue[i];
}
else if (columnName[i] == "room_id") {
room_id = columnValue[i];
}
else if (columnName[i] == "module_code") {
module_code = columnValue[i];
}
else if (columnName[i] == "class_type") {
class_type = columnValue[i];
}
}
//records should be ordered by time ascending
//always print time first
std::cout << time_24hrs << "\t+-- ";
//print room and module code second on the first line of the timetabled class
std::cout << module_code << "\t" << room_id;
std::cout << std::endl << "\t| "; //start a new line for the second line of this timetabled class
//print lecturer and class type on the second line
std::cout << class_type << "\t";
//continue printing a vertical line until the end of the timetabled class
for (int i = length_hrs-1; i > 0; i--) {
std::cout << std::endl << time_24hrs + (length_hrs - i) << "\t| ";
std::cout << std::endl << "\t| ";
}
return 0;
}
static int generateTimetableModulesCallback(char** moduleCodes, int numColumns, char** columnValue, char**columnName)
{
for (int i = 0; i < lengthCArray(moduleCodes); i++) {
std::cerr << moduleCodes[i] << std::endl;
if (moduleCodes[i] == "") {
moduleCodes[i] = columnValue[0]; //we effectively populate the array declared in the calling scope with values from the database
std::cerr << moduleCodes[i] << std::endl;
break;
}
}
return 0;
}
static int generateTimetableRoomsCallback(char** roomIds, int numColumns, char** columnValue, char** columnName)
{
for (int i = 0; i < lengthCArray(roomIds); i++) {
std::cerr << roomIds[i] << std::endl;
if (roomIds[i] == "") {
roomIds[i] = columnValue[0]; //we effectively populate the array declared in the calling scope with values from the database
std::cerr << roomIds[i] << std::endl;
break;
}
}
return 0;
}
static int generateTimetableLecturersCallback(char** lecturerIds, int numColumns, char** columnValue, char** columnName)
{
for (int i = 0; i < lengthCArray(lecturerIds); i++) {
std::cerr << lecturerIds[i] << std::endl;
if (lecturerIds[i] == "") {
lecturerIds[i] = columnValue[0]; //we effectively populate the array declared in the calling scope with values from the database
std::cerr << lecturerIds[i] << std::endl;
break;
}
}
return 0;
}
int displayTable( sqlite3* db, int exit, char* sqlErrorMessage )
{
std::string tableName;
char query[100]; //use C string so it can be formatted using snprintf
std::cout << "Enter table name: ";
std::cin >> tableName;
snprintf(query, 100, "SELECT * FROM %s ;", tableName.c_str());
//execute sql commands
//sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
//arguments taken from https://www.geeksforgeeks.org/sql-using-c-c-and-sqlite/
exit = sqlite3_exec(db, query, displayRecordsCallback, NULL, &sqlErrorMessage );
//report back to user if execution was successful, and if not why
if (exit != SQLITE_OK) {
std::cerr << "Some errors occured during select:" << std::endl;
std::cerr << sqlErrorMessage << std::endl;
}
std::cout << std::endl;
std::cout << "Done." << std::endl;
return 0;
}
int displayTimetable( sqlite3* db, int exit, char* sqlErrorMessage )
{
std::string query = "SELECT * FROM Classes ORDER BY time_24hrs ASC ;";
//execute sql commands
//sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
//arguments taken from https://www.geeksforgeeks.org/sql-using-c-c-and-sqlite/
exit = sqlite3_exec(db, query.c_str(), displayFormattedClassesCallback, NULL, &sqlErrorMessage );
//report back to user if execution was successful, and if not why
if (exit != SQLITE_OK) {
std::cerr << "Some errors occured during select:" << std::endl;
std::cerr << sqlErrorMessage << std::endl;
}
std::cout << std::endl;
std::cout << "Done." << std::endl;
return 0;
}
int generateTimetable( sqlite3* db, int exit, char* sqlErrorMessage )
{
/* the main outer loop for the algorithm is going to be the list of modules
we are going to loop through each module and assign classes as we go
until no more will fit
since this is an overview timetable (as opposed to being for a specific person)
multiple classes can be assigned at the same time as long as they don't use the same room or lecturer etc.
disadvantage to approach: bias will be created towards allocation of earlier modules over later modules
this can be modified to our liking by changing the order returned in the sql query
*/
int lengthHrsPerClass;
int startAllocationTime;
int endByTime;
int classesPerModule;
char* moduleCodes[20]; //use pointer arrays so these can be remotely populated from the SQL query callback functions
char* roomIds[20];
char* lecturerIds[20];
//initially fill the arrays with dummy values so we can identify unallocated elements
for (int i = 0; i < lengthCArray(moduleCodes); i++) {
moduleCodes[i] = ""; roomIds[i] = ""; lecturerIds[i] = "";
}
//get some user input to affect how we generate the timetable
std::cout << "Enter the desired length of each class (in hours) (must be a whole number):" << std::endl;
std::cin >> lengthHrsPerClass;
std::cout << "Enter the earliest class allocation time (in 24 hours) (must be whole number):" << std::endl;
std::cin >> startAllocationTime;
std::cout << "Enter the latest time a class can run until (in 24 hours) (must be whole number):" << std::endl;
std::cin >> endByTime;
std::cout << "Enter the desired number of classes per module:" << std::endl;
std::cin >> classesPerModule;
//execute sql commands
//sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
//arguments taken from https://www.geeksforgeeks.org/sql-using-c-c-and-sqlite/
exit = sqlite3_exec(db, "SELECT module_code FROM Modules ;", generateTimetableModulesCallback, moduleCodes, &sqlErrorMessage );
exit = sqlite3_exec(db, "SELECT room_id FROM Rooms ;", generateTimetableRoomsCallback, roomIds, &sqlErrorMessage );
exit = sqlite3_exec(db, "SELECT lecturer_id FROM Lecturers ;", generateTimetableLecturersCallback, lecturerIds, &sqlErrorMessage );
//report back to user if execution was successful, and if not why
if (exit != SQLITE_OK) {
std::cerr << "Some errors occured during select:" << std::endl;
std::cerr << sqlErrorMessage << std::endl;
return 1;
}
//associative array using map type definition achieved with help from example code at https://www.geeksforgeeks.org/associative-arrays-in-cpp/
std::vector< std::map<std::string, std::string> > allocatedClasses;
int tempClassId = 11; //this is incremented after a successful allocation
std::string tempRoomId;
std::string tempLecturerId;
bool allocationSuccessful;
char query[250]; //use C string so it can be formatted using snprintf
int numClassesSuccessful = 0;
int numClassesFailed = 0;
//start allocating timetable classes, outer loop iterating through module codes
for (int i = 0; i < lengthCArray(moduleCodes); i++) {
if (moduleCodes[i] != "") {
for (int moduleCycle = 0; moduleCycle < classesPerModule; moduleCycle++) {
//attempt to allocate timetabled class
tempRoomId = "";
tempLecturerId = "";
allocationSuccessful = false;
for (int tempTime = startAllocationTime; tempTime <= endByTime - lengthHrsPerClass; tempTime++) {
//iterate through room indexes
for (int ri = 0; ri < lengthCArray(roomIds); ri++) {
tempRoomId = roomIds[ri];
//iterate through allocated classes indexes
for (int ci = 0; ci < allocatedClasses.size(); ci++) {
//check if times overlap, and if they do cancel out this room allocation
if ((int)allocatedClasses[ci]["time_24hrs"].c_str() + lengthHrsPerClass > tempTime
&& (int)allocatedClasses[ci]["time_24hrs"].c_str() < tempTime + lengthHrsPerClass
&& allocatedClasses[ci]["room_id"] == roomIds[ri]) {
tempRoomId = "";
}
}
if (tempRoomId != "")
break; //a free room has been selected
}
//iterate through lecturer indexes
for (int li = 0; li < lengthCArray(lecturerIds); li++) {
tempLecturerId = lecturerIds[li];
//iterate through allocated classes indexes
for (int ci = 0; ci < allocatedClasses.size(); ci++) {
//check if times overlap, if so cancel out this lecturer allocation
if ((int)allocatedClasses[ci]["time_24hrs"].c_str() + lengthHrsPerClass > tempTime
&& (int)allocatedClasses[ci]["time_24hrs"].c_str() < tempTime + lengthHrsPerClass
&& allocatedClasses[ci]["lecturer_id"] == lecturerIds[li]) {
tempLecturerId = "";
}
}
if (tempLecturerId != "")
break; //a free lecturer has been selected
}
if (tempRoomId != "" && tempLecturerId != "") {
//we have a valid time for our class!
//execute the sql first, so if for whatever reason an error occurs, our tracked allocated classes isn't updated
std::cerr << "Valid allocation found! Attempting to insert into database." << std::endl;
snprintf(query, 250, "INSERT INTO Classes VALUES ( %d , \"%s\" , %s , \"%s\" , %s , %d , %d ) ;", tempClassId, tempRoomId.c_str(), tempLecturerId.c_str(),
moduleCodes[i], "NULL", tempTime, lengthHrsPerClass);
//execute sql commands
//sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
//arguments taken from https://www.geeksforgeeks.org/sql-using-c-c-and-sqlite/
exit = sqlite3_exec(db, query, NULL, NULL, &sqlErrorMessage );
//report back to user if execution was successful, and if not why
//if it fails, loop back around and try the next time like always
if (exit != SQLITE_OK) {
std::cerr << "A valid allocation was found, but some errors occured during insert into database:" << std::endl;
std::cerr << sqlErrorMessage << std::endl;
std::cerr << "A new valid allocation will attempt to be found." << std::endl;
}
else {
//then append to keep track of the new class, and break out of this class allocation loop
std::cerr << "Insert successful. Attempting to append to tracked." << std::endl;
allocatedClasses.push_back( { {"class_id", (char*)&tempClassId}, {"room_id", tempRoomId}, {"lecturer_id", tempLecturerId},
{"module_code", moduleCodes[i]}, {"class_type", "NULL"}, {"time_24hrs", (char*)&tempTime}, {"length_hrs", (char*)&lengthHrsPerClass} } );
tempClassId++;
numClassesSuccessful++;
allocationSuccessful = true;
std::cout << "Appended to tracked." << std::endl;
break;
}
}
}
//if the program natually reached here instead of breaking out of the loop, then no valid class allocation could be found
if (!allocationSuccessful) {
numClassesFailed++;
std::cout << "A class could not be allocated on the timetable. Module: " << moduleCodes[i] << " cycle number: " << moduleCycle << std::endl;
}
}
}
}
//end of timetable classes allocation algorithm
//display statistics
std::cout << std::endl;
std::cout << "Timetable generation complete! Statistics:" << std::endl;
std::cout << "\ttotal Classes Attempted: " << numClassesSuccessful + numClassesFailed << "\tnum Classes Successful: " << numClassesSuccessful
<< "\tnum Classes Failed: " << numClassesFailed << std::endl;
return 0;
}
int main()
{
sqlite3* db;
int exit = 0;
char* sqlErrorMessage;
int menuChoice;
//open connection to database
exit = sqlite3_open("data.db", &db); //will return 0 if succeeded
//check connection was successful before continuing
//example code taken from https://www.geeksforgeeks.org/sql-using-c-c-and-sqlite/
if (exit) {
std::cerr << "Error opening database: " << sqlite3_errmsg(db) << std::endl;
return (-1);
}
else
std::cout << "Opened Database Successfully!" << std::endl;
//end of example code
//use while loop so multiple choices can be made before exiting
while (menuChoice != 0) {
//display user menu
std::cout << "Main Menu\n---------" << std::endl;
std::cout << "0 : Close connection and exit" << std::endl;
std::cout << "1 : Display database table" << std::endl;
std::cout << "2 : Display timetable" << std::endl;
std::cout << "3 : Generate new timetable (overwrite)" << std::endl;
std::cin >> menuChoice;
switch (menuChoice)
{
case 0:
break;
case 1:
displayTable(db, exit, sqlErrorMessage);
break;
case 2:
displayTimetable(db, exit, sqlErrorMessage);
break;
case 3:
generateTimetable(db, exit, sqlErrorMessage);
break;
default:
std::cerr << "Invalid choice." << std::endl;
break;
}
}
sqlite3_close(db); //close connection to the database once finished
std::cout << "Closed connection to database." << std::endl;
return (0);
}