Compare commits

..

2 Commits

Author SHA1 Message Date
Karsten Jeppesen
15445b6e4a Quizzer: Redesigned journal
Some checks reported errors
DMA-CSD-CICD/QuizSystem/pipeline/head Something is wrong with the build of this commit
UCN/QuizSystem/pipeline/head This commit looks good
Will now handle infinite size quizzes
2026-04-30 19:51:19 +02:00
Karsten Jeppesen
5df799fbe8 Done: students can now clear a quiz
All checks were successful
UCN/QuizSystem/pipeline/head This commit looks good
DMA-CSD-CICD/QuizSystem/pipeline/head This commit looks good
Only if Anonymous
Coverage will be omitted
2026-04-16 21:05:25 +02:00
3 changed files with 440 additions and 181 deletions

View File

@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Quizzer.Models
{
/// <summary>
/// This is the database model for the journal, which is used to store the quiz results and other related information. It is used to display the quiz results in the journal page and to store the quiz results in the database.
/// </summary>
public class Journal
{
public string PID { get; set; }
@@ -11,6 +14,26 @@ namespace Quizzer.Models
public bool Anonymous { get; set; }
public int Score { get; set; }
public int ScoreAcc { get; set; }
// This is the JSON string that contains the quiz results, which is used to display the quiz results in the journal page. It is also used to store the quiz results in the database.
// The JSON string is in the following format:
// {
// "items": [
// {
// "itemID": "1",
// "isOpen": 1,
// "score": 1,
// "maxScore": 1,
// "replyMC": [true, false, false, false]
// },
// {
// "itemID": "2",
// "isOpen": 0,
// "score": 0,
// "maxScore": 1,
// "replyMC": [false, false, false, false]
// }
// ]
// }
public string JournalJson { get; set; }
}
@@ -23,9 +46,44 @@ namespace Quizzer.Models
public List<bool> ReplyMC { get; set; }
}
/// <summary>
/// Used for web page building
/// </summary>
public class JournalHtml
{
public Journal Journal { get; set; }
public Journal2 Journal { get; set; }
public string Text { get; set; }
}
/// <summary>
/// Table for the journal. One is created for each test available to the PID.
/// </summary>
[Table("Journal2")]
public class Journal2
{
public string PID { get; set; } // VARCHAR 256 Primary key
public string QuizzID { get; set; } // VARCHAR 50 Primary key
public string Category { get; set; } // VARCHAR 50
public int IsOpen { get; set; } // INT 11
public bool Anonymous { get; set; } // INT 11
public int Score { get; set; } // INT 11
public int ScoreAcc { get; set; } // INT 11
public List<Journal2Item> Items { get; set; } // This field does not store in the DB, but is serialized to JSON and stored in the JournalJson field of the JournalHead table. The JSON string is in the following format: {"items": [{"itemID": "1", "isOpen": 1, "score": 1, "maxScore": 1, "replyMC": [true, false, false, false]}, {"itemID": "2", "isOpen": 0, "score": 0, "maxScore": 1, "replyMC": [false, false, false, false]}]}
}
/// <summary>
/// One is created for each question in the test, and linked to the JournalHead by the QuizzID and PID. The ItemID is used to identify the question in the test, and the IsOpen, Score, MaxScore, and ReplyMCJson are used to store the quiz results for that question.
/// </summary>
[Table("Journal2Item")]
public class Journal2Item
{
public string PID { get; set; } // VARCHAR 256, Foreign key to the JournalHead table. Primary key
public string QuizzID { get; set; } // VARCHAR 50, Foreign key to the JournalHead table. Primary key
public required string QuestionID { get; set; } // VARCHAR 50, used to identify the question in the test. Primary key
public int IsOpen { get; set; } // INT 11, used to indicate whether the question is open or not.
public int Score { get; set; } // INT 11, used to store the score for the question.
public int MaxScore { get; set; } // INT 11, used to store the maximum score for the question.
public List<bool> ReplyMC { get; set; } // This field does not store in the DB, but is serialized to JSON and stored in the JournalJson field of the JournalHead table. The JSON string is in the following format: [true, false, false, false]
public string ReplyMCJson { get; set; } // VARCHAR 512, used to store the JSON string for the ReplyMC field. The JSON string is in the following format: [true, false, false, false]
}
}

View File

@@ -133,11 +133,11 @@ namespace Quizzer.Pages
foreach (Quizz theQuizz in dbManager.GetQuizzesClass(theClass))
{
// Make sure that for a quizz is generated for this individual
Journal theJournal = dbManager.GetJournal(UserID, theQuizz.QuizzId);
Journal2 theJournal = dbManager.GetJournal2(UserID, theQuizz.QuizzId);
if (theJournal == null)
{
// Make up a new journal
Journal newJournal = new()
Journal2 newJournal = new()
{
PID = UserID,
QuizzID = theQuizz.QuizzId,
@@ -148,7 +148,7 @@ namespace Quizzer.Pages
ScoreAcc = 0,
};
//List<Item>items = JsonConvert.DeserializeObject<List<Item>>(theJournal.JournalJson);
List<Item> myItemList = new();
newJournal.Items = new();
List<Question> myQuestions = PopulateQList(theQuizz.Category);
//List<Question> myQuestions = dbManager.GetQuestions(theQuizz.Category);
int theSize = (myQuestions.Count < theQuizz.QSize) ? myQuestions.Count : theQuizz.QSize;
@@ -168,16 +168,15 @@ namespace Quizzer.Pages
foreach (Question question in myQuestions)
{
newJournal.ScoreAcc++;
myItemList.Add(new Item
newJournal.Items.Add(new Journal2Item
{
ItemID = question.QuestionID,
QuestionID = question.QuestionID,
IsOpen = 1,
Score = 0,
MaxScore = 1,
});
}
newJournal.JournalJson = JsonConvert.SerializeObject(myItemList);
dbManager.PostJournal(newJournal);
dbManager.PostJournal2(newJournal);
}
}
}
@@ -185,8 +184,8 @@ namespace Quizzer.Pages
private void BuildTestSelection()
{
List<JournalHtml> myTests = new();
List<Journal> myJournals = dbManager.GetJournal(UserID);
foreach (Journal myJournal in myJournals)
List<Journal2> myJournals = dbManager.GetJournal2(UserID);
foreach (Journal2 myJournal in myJournals)
{
Quizz theQuizz = dbManager.GetQuizz(myJournal.QuizzID);
if (theQuizz == null) continue;
@@ -202,20 +201,20 @@ namespace Quizzer.Pages
private bool PresentQuestion()
{
Journal myJournal = dbManager.GetJournal(UserID, PageQuizzID);
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
if (myJournal == null)
{
Response.Cookies.Delete("PageQuizzID");
return false;
}
List<Item> myItems = JsonConvert.DeserializeObject<List<Item>>(myJournal.JournalJson);
//List<Item> myItems = JsonConvert.DeserializeObject<List<Item>>(myJournal.JournalJson);
Random rnd = new Random();
foreach (var item in myItems)
foreach (var item in myJournal.Items)
{
if (item.IsOpen == 1)
{
// Get the question
MCQuestion myMCQuestion = dbManager.GetMCQuestion(item.ItemID);
MCQuestion myMCQuestion = dbManager.GetMCQuestion(item.QuestionID);
List<int> myRndSeq = new List<int>();
for (int indx = 0; indx < myMCQuestion.AList.Count(); indx++)
{
@@ -239,23 +238,21 @@ namespace Quizzer.Pages
private void FinalizeTest()
{
Journal myJournal = dbManager.GetJournal(UserID, PageQuizzID);
List<Item> myItems = JsonConvert.DeserializeObject<List<Item>>(myJournal.JournalJson);
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
myJournal.IsOpen = 0;
dbManager.UpdateJournal(myJournal);
dbManager.UpdateJournal2(myJournal);
Response.Cookies.Delete("PageQuizzID");
}
private void RegisterAnswer()
{
Journal myJournal = dbManager.GetJournal(UserID, PageQuizzID);
List<Item> myItems = JsonConvert.DeserializeObject<List<Item>>(myJournal.JournalJson);
foreach (var item in myItems)
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
foreach (var item in myJournal.Items)
{
if (item.IsOpen == 1)
{
// Get the question
MCQuestion myMCQuestion = dbManager.GetMCQuestion(item.ItemID);
MCQuestion myMCQuestion = dbManager.GetMCQuestion(item.QuestionID);
if (myMCQuestion != null)
{
int theScore = item.MaxScore;
@@ -271,15 +268,14 @@ namespace Quizzer.Pages
item.Score = theScore;
myJournal.Score += theScore;
item.IsOpen = 0;
myJournal.JournalJson = JsonConvert.SerializeObject(myItems);
dbManager.UpdateJournal(myJournal);
dbManager.UpdateJournal2(myJournal);
// Now register statistics
List<int> counter = new List<int>();
for (int indx = 0; indx < myMCQuestion.AList.Count(); indx++)
{
if (PageMCQuestion.AList[indx].Aok) { counter.Add(1); } else { counter.Add(0); }
}
dbManager.UpdateStats(PageQuizzID, item.ItemID, counter);
dbManager.UpdateStats(PageQuizzID, item.QuestionID, counter);
PageMCQuestion.AList.Clear();
}
return;
@@ -293,20 +289,19 @@ namespace Quizzer.Pages
PageMCQuestionReview = new();
PageMCResponseReview = new();
List<StatisticsStudentByCategory> StatStudent = new();
Journal myJournal = dbManager.GetJournal(UserID, PageQuizzID);
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
if (myJournal == null)
{
Response.Cookies.Delete("PageQuizzID");
return;
}
List<Item> myItems = JsonConvert.DeserializeObject<List<Item>>(myJournal.JournalJson);
for (int index = 0; index < myItems.Count; index++)
for (int index = 0; index < myJournal.Items.Count; index++)
{
string background;
MCQuestion question = dbManager.GetMCQuestion(myItems[index].ItemID);
MCQuestion question = dbManager.GetMCQuestion(myJournal.Items[index].QuestionID);
PageMCQuestionReview.Add(question);
PageMCResponseReview.Add(myItems[index].ReplyMC);
PageMCResponseReview.Add(myJournal.Items[index].ReplyMC);
// create statistics
StatisticsStudentByCategory theStat = StatStudent.Find(x => x.CategoryID == question.Category);
if (theStat == null)
@@ -315,7 +310,7 @@ namespace Quizzer.Pages
StatStudent.Add(new StatisticsStudentByCategory { CategoryID = cat.CId, CategoryName = cat.Txt });
theStat = StatStudent[StatStudent.Count - 1];
}
theStat.Update(myItems[index].Score, myItems[index].MaxScore);
theStat.Update(myJournal.Items[index].Score, myJournal.Items[index].MaxScore);
// https://social.msdn.microsoft.com/Forums/en-US/7f96525d-7253-4f08-87f8-88ae526834f9/razor-pages-with-chartjs?forum=aspdotnetcore
PageChartConnectors = new List<ChartConnector>();
//background = "getRandomColor()";
@@ -418,7 +413,11 @@ namespace Quizzer.Pages
{
case "startTest":
// NEW: Coverage: register who takes the test
Quizz theQuizz = dbManager.GetQuizz(value);
if (! theQuizz.Anonymous)
{ // Only register coverage if the test is not anonymous
dbManager.SetCoverage(OpenIDUtils.GetJwtClaim(HttpContext, "email"), OpenIDUtils.GetJwtClaim(HttpContext, "name"), ucnData.theClass.FirstOrDefault());
}
if (isDebug) Console.WriteLine(OpenIDUtils.GetJwtClaim(HttpContext, "name") + ": REMOTE ADDR: " + HttpContext.Request.Headers["X-Forwarded-For"]);
Console.WriteLine(OpenIDUtils.GetJwtClaim(HttpContext, "name") + ": REMOTE ADDR: " + HttpContext.Request.Headers["X-Forwarded-For"]);
PageQuizzID = value;
@@ -435,7 +434,7 @@ namespace Quizzer.Pages
RegisterAnswer();
break;
case "deleteMyTest": //Student should not be able to delete journal as this resets the journal
dbManager.DeleteJournal(UserID, target);
dbManager.DeleteJournal2(UserID, target);
break;
default:
break;

View File

@@ -47,6 +47,8 @@ namespace Quizzer.Persistens
myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `TheClasss` ( `Name` varchar(50) DEFAULT NULL ) ENGINE=InnoDB COLLATE='utf8mb4_general_ci'");
myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Coverage` ( `Email` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', `Name` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', `Date` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', `TheClass` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', PRIMARY KEY (`Email`, `Name`, `Date`) USING BTREE ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB");
myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Images` ( `MD5` VARCHAR(36) NOT NULL DEFAULT 'ERR', `UID` VARCHAR(50) NOT NULL DEFAULT 'ERR', `Encoding` VARCHAR(16) NULL DEFAULT NULL, `Txt` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`MD5`, `UID`) ) COLLATE='utf8mb4_general_ci'");
myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Journal2s` (`PID` VARCHAR(256) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`QuizzID` VARCHAR(50) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`Category` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci',`IsOpen` INT(11) NULL DEFAULT NULL,`Anonymous` INT(11) NULL DEFAULT NULL,`Score` INT(11) NULL DEFAULT NULL,`ScoreAcc` INT(11) NULL DEFAULT NULL,PRIMARY KEY (`PID`, `QuizzID`) USING BTREE) COLLATE='utf8mb4_general_ci'");
myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Journal2Items` (`PID` VARCHAR(256) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`QuizzID` VARCHAR(50) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`QuestionID` VARCHAR(50) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`IsOpen` INT(11) NULL DEFAULT NULL,`Score` INT(11) NULL DEFAULT NULL,`MaxScore` INT(11) NULL DEFAULT NULL,`ReplyMCJson` VARCHAR(512) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci',PRIMARY KEY (`PID`, `QuizzID`, `QuestionID`) USING BTREE ) COLLATE='utf8mb4_general_ci'");
}
private string GetGuid()
@@ -54,6 +56,62 @@ namespace Quizzer.Persistens
return System.Guid.NewGuid().ToString();
}
#endregion General
#region Question
public List<Question> GetQuestions()
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
return myDbConnection.GetAll<Question>().AsList();
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public Question GetQuestion(string questionID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
return myDbConnection.QueryFirstOrDefault<Question>("SELECT * FROM Questions WHERE QuestionID = @questionID", new
{
questionID
});
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public List<Question> GetQuestions(string category)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
return myDbConnection.Query<Question>("SELECT * FROM Questions WHERE Category = @theCat ORDER BY QuestionText ASC", new
{
theCat = category
}).AsList();
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
List<Question> GetQuestionList()
{
List<Question> myList = new List<Question>();
@@ -114,154 +172,6 @@ namespace Quizzer.Persistens
return null;
}
private string indentString = "&nbsp;&nbsp;&nbsp;";
public string MakeCSubList(string asForm, string indent, string curSelect, ref List<Cat> cats, string theParent)
{
string myList = string.Empty;
string selTxt;
foreach (var item in cats)
{
if (item.CParentId == theParent)
{
string subList = MakeCSubList(asForm, indent + indentString, curSelect, ref cats, item.CId);
switch (asForm)
{
case "radioBtn":
if (item.CId == curSelect)
selTxt = " checked";
else
selTxt = "";
myList += "<div class=\"form - check\">\n" +
" <input class=\"form - check - input\" type =\"radio\" name =\"categoryParent\" id =\"" +
item.CId +
"\" value =\"" +
item.CId +
"\" onclick=\"javascript: this.form.submit()\"" + selTxt + " > \n" +
" <label class=\"form - check - label\" for=\"" + item.CId + "\" > \n" + indent + item.Txt + " </label>\n" +
"</div>\n";
if (subList != string.Empty)
myList += subList;
break;
case "DropDown":
case "DropDownIndented":
if (item.CId == curSelect)
selTxt = " selected";
else
selTxt = "";
myList = myList + "<option value=\"" + item.CId + "\"" + selTxt + " >" + indent + item.Txt + " </option >";
if (subList != string.Empty)
myList += subList;
break;
default:
break;
}
}
}
return myList;
}
public string MakeCList(string uid, string asForm, string curSelect)
{
DbOps qManager = new();
List<Cat> cats = qManager.GetCats(uid);
switch (asForm)
{
case "radioBtn":
//return "<ul>" + MakeCSubList(asForm, "&nbsp;&nbsp", curSelect, ref cats, "/") + "</ul>";
return "<ul>" + MakeCSubList(asForm, "", curSelect, ref cats, "/") + "</ul>";
case "DropDown":
return "<select name=\"newCategory\" onchange=\"javascript: this.form.submit()\">" +
"<option value=\"\">Please select</option>" +
MakeCSubList(asForm, "&nbsp;&nbsp", curSelect, ref cats, "/") +
"</select>";
case "DropDownIndented":
return "<select name=\"newCategory\" onchange=\"javascript: this.form.submit()\">" +
"<option value=\"\">Please select</option>" +
MakeCSubList(asForm, "&nbsp;&nbsp;", curSelect, ref cats, "/") +
"</select>";
default:
return "<p>MakeCList form error: " + asForm + "</p>";
}
}
public Cat SetCat(string uid, string parentTagID, string newTagName)
{
Cat myCat = new();
myCat.CParentId = parentTagID;
myCat.UID = uid;
myCat.Txt = newTagName;
myCat.CId = GetGuid();
// doesn't map TId fild // myDbConnection.Insert<Tag>(myTag);
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
int rowsAffected = myDbConnection.Execute("INSERT INTO Cats (CId, UID, CParentID, Txt) VALUES(@CId, @UID, @CParentID, @Txt)", myCat);
return myCat;
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
#endregion General
#region Question
public List<Question> GetQuestions()
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
return myDbConnection.GetAll<Question>().AsList();
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public Question GetQuestion(string questionID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
return myDbConnection.QueryFirstOrDefault<Question>("SELECT * FROM Questions WHERE QuestionID = @questionID", new
{
questionID
});
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public List<Question> GetQuestions(string category)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
return myDbConnection.Query<Question>("SELECT * FROM Questions WHERE Category = @theCat ORDER BY QuestionText ASC", new
{
theCat = category
}).AsList();
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public void DeleteQuestion(string questionID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
@@ -391,6 +301,99 @@ namespace Quizzer.Persistens
#region Category
private string indentString = "&nbsp;&nbsp;&nbsp;";
public string MakeCSubList(string asForm, string indent, string curSelect, ref List<Cat> cats, string theParent)
{
string myList = string.Empty;
string selTxt;
foreach (var item in cats)
{
if (item.CParentId == theParent)
{
string subList = MakeCSubList(asForm, indent + indentString, curSelect, ref cats, item.CId);
switch (asForm)
{
case "radioBtn":
if (item.CId == curSelect)
selTxt = " checked";
else
selTxt = "";
myList += "<div class=\"form - check\">\n" +
" <input class=\"form - check - input\" type =\"radio\" name =\"categoryParent\" id =\"" +
item.CId +
"\" value =\"" +
item.CId +
"\" onclick=\"javascript: this.form.submit()\"" + selTxt + " > \n" +
" <label class=\"form - check - label\" for=\"" + item.CId + "\" > \n" + indent + item.Txt + " </label>\n" +
"</div>\n";
if (subList != string.Empty)
myList += subList;
break;
case "DropDown":
case "DropDownIndented":
if (item.CId == curSelect)
selTxt = " selected";
else
selTxt = "";
myList = myList + "<option value=\"" + item.CId + "\"" + selTxt + " >" + indent + item.Txt + " </option >";
if (subList != string.Empty)
myList += subList;
break;
default:
break;
}
}
}
return myList;
}
public string MakeCList(string uid, string asForm, string curSelect)
{
DbOps qManager = new();
List<Cat> cats = qManager.GetCats(uid);
switch (asForm)
{
case "radioBtn":
//return "<ul>" + MakeCSubList(asForm, "&nbsp;&nbsp", curSelect, ref cats, "/") + "</ul>";
return "<ul>" + MakeCSubList(asForm, "", curSelect, ref cats, "/") + "</ul>";
case "DropDown":
return "<select name=\"newCategory\" onchange=\"javascript: this.form.submit()\">" +
"<option value=\"\">Please select</option>" +
MakeCSubList(asForm, "&nbsp;&nbsp", curSelect, ref cats, "/") +
"</select>";
case "DropDownIndented":
return "<select name=\"newCategory\" onchange=\"javascript: this.form.submit()\">" +
"<option value=\"\">Please select</option>" +
MakeCSubList(asForm, "&nbsp;&nbsp;", curSelect, ref cats, "/") +
"</select>";
default:
return "<p>MakeCList form error: " + asForm + "</p>";
}
}
public Cat SetCat(string uid, string parentTagID, string newTagName)
{
Cat myCat = new();
myCat.CParentId = parentTagID;
myCat.UID = uid;
myCat.Txt = newTagName;
myCat.CId = GetGuid();
// doesn't map TId fild // myDbConnection.Insert<Tag>(myTag);
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
int rowsAffected = myDbConnection.Execute("INSERT INTO Cats (CId, UID, CParentID, Txt) VALUES(@CId, @UID, @CParentID, @Txt)", myCat);
return myCat;
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public List<Cat> GetCats(string uid)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
@@ -659,6 +662,12 @@ namespace Quizzer.Persistens
#region Journal
/// <summary>
/// Get the journal for a specific quiz and a specific user. Returns null if no journal exists for that quiz and user.
/// </summary>
/// <param name="thePID"></param>
/// <param name="theQuizzID"></param>
/// <returns></returns>
public Journal GetJournal(string thePID, string theQuizzID)
{
string sql = "SELECT * FROM Journals WHERE PID = '" + thePID + "' AND QuizzID = '" + theQuizzID + "'";
@@ -680,6 +689,11 @@ namespace Quizzer.Persistens
}
}
/// <summary>
/// Get all journals for a specific user. Returns null if no journal exists for that user.
/// </summary>
/// <param name="thePID"></param>
/// <returns></returns>
public List<Journal> GetJournal(string thePID)
{
string sql = "SELECT * FROM Journals WHERE PID = '" + thePID + "'";
@@ -697,6 +711,10 @@ namespace Quizzer.Persistens
}
}
/// <summary>
/// Update journal. Journal is identified by PID and QuizzID. If no journal exists for that PID and QuizzID, nothing happens.
/// </summary>
/// <param name="theJournal"></param>
public void UpdateJournal(Journal theJournal)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
@@ -720,6 +738,10 @@ namespace Quizzer.Persistens
}
}
/// <summary>
/// Insert journal. If a journal already exists for that PID and QuizzID, nothing happens.
/// </summary>
/// <param name="theJournal"></param>
public void PostJournal(Journal theJournal)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
@@ -745,6 +767,11 @@ namespace Quizzer.Persistens
}
}
/// <summary>
/// Get all journals for a specific quiz. Returns null if no journal exists for that quiz.
/// </summary>
/// <param name="quizzID"></param>
/// <returns></returns>
public List<Journal> GetJournals(string quizzID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
@@ -760,6 +787,10 @@ namespace Quizzer.Persistens
}
}
/// <summary>
/// Deletes the journal entry associated with the specified quiz identifier.
/// </summary>
/// <param name="quizzID">The unique identifier of the quiz whose journal entry is to be deleted.</param>
public void DeleteJournalByQuizz(string quizzID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
@@ -773,6 +804,12 @@ namespace Quizzer.Persistens
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// Deletes the journal entry associated with the specified participant identifier and quiz identifier.
/// </summary>
/// <param name="pID"></param>
/// <param name="quizzID"></param>
public void DeleteJournal(string pID, string quizzID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
@@ -787,6 +824,171 @@ namespace Quizzer.Persistens
#endregion Journal
#region Journal2
// JOURNAL2 is an alternative journal table with same structure as Journal, but with different name. This is used for testing purposes, to avoid conflicts with the original Journal table. It is not used in production.
/// <summary>
/// Get the journal for a specific quiz and a specific user. Returns null if no journal exists for that quiz and user.
/// </summary>
/// <param name="thePID"></param>
/// <param name="theQuizzID"></param>
/// <returns></returns>
public Journal2 GetJournal2(string thePID, string theQuizzID)
{
string sqlJ = $"SELECT * FROM Journal2s WHERE PID = '{thePID}' AND QuizzID = '{theQuizzID}'";
string sqlI = $"SELECT * FROM Journal2Items WHERE PID = '{thePID}' AND QuizzID = '{theQuizzID}'";
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
// Get the journal entry, general part.
var theJournal = myDbConnection.QueryFirstOrDefault<Journal2>(sqlJ);
if (theJournal == null) return null;
// Get the journal items.
theJournal.Items = myDbConnection.Query<Journal2Item>(sqlI).AsList();
//theJournal.Items.Sort((x, y) => x.QuestionID.CompareTo(y.QuestionID));
if (theJournal.Items != null)
{
foreach (var item in theJournal.Items)
{
item.ReplyMC = JsonConvert.DeserializeObject<List<bool>>(item.ReplyMCJson);
}
}
return theJournal;
}
catch
{
myDbConnection.Close();
CreateIfNotExist(myDbConnection); // must be in cosed state
return null;
}
}
/// <summary>
/// Get all journals for a specific user. Returns null if no journal exists for that user.
/// </summary>
/// <param name="thePID"></param>
/// <returns></returns>
public List<Journal2> GetJournal2(string thePID)
{
string sqlJ = $"SELECT * FROM Journal2s WHERE PID = '{thePID}'";
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
List<Journal2> result = myDbConnection.Query<Journal2>(sqlJ).AsList();
return result;
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
/// <summary>
/// Update journal. Journal is identified by PID and QuizzID. If no journal exists for that PID and QuizzID, nothing happens.
/// </summary>
/// <param name="theJournal"></param>
public void UpdateJournal2(Journal2 theJournal)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
myDbConnection.Execute("UPDATE Journal2s SET IsOpen = @IsOpen, Score = @Score WHERE PID=@PID AND QuizzID = @QuizzID", theJournal);
foreach (var item in theJournal.Items)
{
item.ReplyMCJson = JsonConvert.SerializeObject(item.ReplyMC);
myDbConnection.Execute("UPDATE Journal2Items SET IsOpen = @IsOpen, Score = @Score, MaxScore = @MaxScore, ReplyMCJson = @ReplyMCJson WHERE PID = @PID AND QuizzID = @QuizzID AND QuestionID = @QuestionID", item);
}
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
return;
}
}
/// <summary>
/// Insert journal. If a journal already exists for that PID and QuizzID, nothing happens.
/// </summary>
/// <param name="theJournal"></param>
public void PostJournal2(Journal2 theJournal)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
myDbConnection.Execute("INSERT INTO Journal2s (PID, QuizzID, Category, Anonymous, IsOpen, Score, ScoreAcc) VALUES(@PID,@QuizzID,@Category,@Anonymous,@IsOpen,@Score,@ScoreAcc)", new
{
PID = theJournal.PID,
QuizzID = theJournal.QuizzID,
Category = theJournal.Category,
Anonymous = theJournal.Anonymous,
IsOpen = theJournal.IsOpen,
Score = theJournal.Score,
ScoreAcc = theJournal.ScoreAcc,
});
// Must populate the Journal2Items table with the items of the journal
if (theJournal.Items != null)
{
foreach (var item in theJournal.Items)
{
item.PID = theJournal.PID;
item.QuizzID = theJournal.QuizzID;
item.ReplyMCJson = JsonConvert.SerializeObject(item.ReplyMC);
myDbConnection.Execute("INSERT INTO Journal2Items (PID, QuizzID, QuestionID, IsOpen, Score, MaxScore, ReplyMCJson) VALUES(@PID, @QuizzID, @QuestionID, @IsOpen, @Score, @MaxScore, @ReplyMCJson)", item);
}
}
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
return;
}
}
/// <summary>
/// Deletes the journal entry associated with the specified quiz identifier.
/// </summary>
/// <param name="quizzID">The unique identifier of the quiz whose journal entry is to be deleted.</param>
public void DeleteJournalByQuizz2(string quizzID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try
{
myDbConnection.Open();
myDbConnection.Delete(quizzID);
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// Deletes the journal entry associated with the specified participant identifier and quiz identifier.
/// </summary>
/// <param name="pID"></param>
/// <param name="quizzID"></param>
public void DeleteJournal2(string pID, string quizzID)
{
using var myDbConnection = new MySqlConnection(GetConnectionString());
try { myDbConnection.Open(); }
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
return;
}
myDbConnection.Execute("DELETE FROM Journal2s WHERE PID = @PID AND QuizzID = @QuizzID", new { PID = pID, QuizzID = quizzID });
myDbConnection.Execute("DELETE FROM Journal2Items WHERE PID = @PID AND QuizzID = @QuizzID", new { PID = pID, QuizzID = quizzID });
}
#endregion Journal2
#region Statistic