Files
QuizSystem/Quizzer-8/Quizzer/Pages/Index.cshtml.cs
2026-05-01 11:40:09 +02:00

455 lines
17 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Newtonsoft.Json;
using Quizzer.Extensions;
using Quizzer.Models;
using Quizzer.OpenIDConnect;
using Quizzer.Persistens;
using System.Collections;
using System.IdentityModel.Tokens.Jwt;
namespace Quizzer.Pages
{
public class IndexModel : PageModel
{
// Generat landing page
// Teachers get navigation options
// Students are presented with selection of available tests
[BindProperty]
public string PageQuizzID { get; set; }
[BindProperty]
public List<JournalHtml> PageTests { get; set; }
[BindProperty]
public OpenIDUCNData ucnData { get; set; }
[BindProperty]
public string UserID { get; set; }
[BindProperty]
public string StudentState { get; set; }
[BindProperty]
public MCQuestion PageMCQuestion { get; set; }
[BindProperty]
public List<MCQuestion> PageMCQuestionReview { get; set; }
[BindProperty]
public List<List<bool>> PageMCResponseReview { get; set; }
[BindProperty]
public List<int> PageMCRandomized { get; set; }
[BindProperty]
public string myFullName { get; set; }
[BindProperty]
public List<ChartConnector> PageChartConnectors { get; set; }
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
private bool isDebug;
private string KCUserID { get; set; }
private DbOps dbManager = new();
private OpenIDConnectUtils OpenIDUtils = new();
private void DumpEnvironment()
{
Console.WriteLine("HEADER DUMP START");
foreach (var e in HttpContext.Request.Headers)
{
Console.WriteLine(e.Key + ":" + e.Value);
}
Console.WriteLine("HEADER DUMP END");
Console.WriteLine("ENVIRONMENT DUMP START");
foreach (DictionaryEntry e in System.Environment.GetEnvironmentVariables())
{
Console.WriteLine(e.Key + ":" + e.Value);
}
Console.WriteLine("ENVIRONMENT DUMP END");
}
public void Common()
{
isDebug = MyConfiguration.Get()["DEBUG"] == "Y";
string? ucnDataJson = OpenIDUtils.GetJwtClaim(HttpContext, "ucn");
KCUserID = OpenIDUtils.GetJwtClaim(HttpContext, "sub");
ucnData = JsonConvert.DeserializeObject<OpenIDUCNData>(ucnDataJson);
UserID = OpenIDUtils.GetJwtClaim(HttpContext, "email").HashSHA256();
myFullName = OpenIDUtils.GetJwtClaim(HttpContext, "name");
}
private void Msg(string msg)
{
Boolean debug = true;
if (debug)
{
Console.WriteLine(msg);
}
}
private void Journal(string Msg)
{
Console.WriteLine("JOURNAL:" + Msg);
}
// =============================================================
// From SSO session:
// OIDC_CLAIM_email = "KAJE@ucn.dk"
// OIDC_CLAIM_family_name = "Jeppesen"
// OIDC_CLAIM_given_name = "Karsten"
// OIDC_CLAIM_name = "Karsten Jeppesen"
List<Question> PopulateQList(string theCategory)
{
// Get the questions in the category
List<Question> list = dbManager.GetQuestions(theCategory);
// Now get the subordinate categories
List<Cat> catList = dbManager.GetChildCategories(theCategory);
foreach (Cat cat in catList)
{
list.AddRange(PopulateQList(cat.CId));
}
return list;
}
private void PrepareForUser(string theRole, string theClass)
{
foreach (Quizz theQuizz in dbManager.GetQuizzesClass(theClass))
{
// Make sure that for a quizz is generated for this individual
Journal2 theJournal = dbManager.GetJournal2(UserID, theQuizz.QuizzId);
if (theJournal == null)
{
// Make up a new journal
Journal2 newJournal = new()
{
PID = UserID,
QuizzID = theQuizz.QuizzId,
Category = theQuizz.Category,
Anonymous = theQuizz.Anonymous,
IsOpen = 1,
Score = 0,
ScoreAcc = 0,
};
//List<Item>items = JsonConvert.DeserializeObject<List<Item>>(theJournal.JournalJson);
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;
Random rnd = new Random();
while (theSize < myQuestions.Count)
{
myQuestions.RemoveAt(rnd.Next(myQuestions.Count));
}
// we should now have a random selection of theSize questions
// Lets mix the deck
for (int indx = 0; indx < myQuestions.Count; indx++)
{
int target = rnd.Next(myQuestions.Count);
myQuestions.Add(myQuestions[target]);
myQuestions.RemoveAt(target);
}
foreach (Question question in myQuestions)
{
newJournal.ScoreAcc++;
newJournal.Items.Add(new Journal2Item
{
QuestionID = question.QuestionID,
IsOpen = 1,
Score = 0,
MaxScore = 1,
});
}
dbManager.PostJournal2(newJournal);
}
}
}
private void BuildTestSelection()
{
List<JournalHtml> myTests = new();
List<Journal2> myJournals = dbManager.GetJournal2(UserID);
foreach (Journal2 myJournal in myJournals)
{
Quizz theQuizz = dbManager.GetQuizz(myJournal.QuizzID);
if (theQuizz == null) continue;
JournalHtml myHtml = new()
{
Journal = myJournal,
Text = theQuizz.Descr,
};
myTests.Add(myHtml);
}
PageTests = myTests;
}
private bool PresentQuestion()
{
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
if (myJournal == null)
{
Response.Cookies.Delete("PageQuizzID");
return false;
}
//List<Item> myItems = JsonConvert.DeserializeObject<List<Item>>(myJournal.JournalJson);
Random rnd = new Random();
foreach (var item in myJournal.Items)
{
if (item.IsOpen == 1)
{
// Get the question
MCQuestion myMCQuestion = dbManager.GetMCQuestion(item.QuestionID);
List<int> myRndSeq = new List<int>();
for (int indx = 0; indx < myMCQuestion.AList.Count(); indx++)
{
myRndSeq.Add(indx);
myMCQuestion.AList[indx].Aok = false;
}
PageMCRandomized = new List<int>();
while (myRndSeq.Count > 0)
{
int indx = rnd.Next(myRndSeq.Count);
PageMCRandomized.Add(myRndSeq[indx]);
myRndSeq.RemoveAt(indx);
}
PageMCQuestion = myMCQuestion;
return true;
}
}
FinalizeTest();
return false;
}
private void FinalizeTest()
{
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
myJournal.IsOpen = 0;
dbManager.UpdateJournal2(myJournal);
Response.Cookies.Delete("PageQuizzID");
}
private void RegisterAnswer()
{
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
foreach (var item in myJournal.Items)
{
if (item.IsOpen == 1)
{
// Get the question
MCQuestion myMCQuestion = dbManager.GetMCQuestion(item.QuestionID);
if (myMCQuestion != null)
{
int theScore = item.MaxScore;
int answers = 0;
item.ReplyMC = new();
for (int indx = 0; indx < myMCQuestion.AList.Count(); indx++)
{
item.ReplyMC.Add(PageMCQuestion.AList[indx].Aok);
if (myMCQuestion.AList[indx].Aok != PageMCQuestion.AList[indx].Aok) theScore = 0;
if (PageMCQuestion.AList[indx].Aok) answers++;
}
if (answers != 1) return; // Must have one answer and one only
item.Score = theScore;
myJournal.Score += theScore;
item.IsOpen = 0;
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.QuestionID, counter);
PageMCQuestion.AList.Clear();
}
return;
}
}
return;
}
private void PrepareReview()
{
PageMCQuestionReview = new();
PageMCResponseReview = new();
List<StatisticsStudentByCategory> StatStudent = new();
Journal2 myJournal = dbManager.GetJournal2(UserID, PageQuizzID);
if (myJournal == null)
{
Response.Cookies.Delete("PageQuizzID");
return;
}
for (int index = 0; index < myJournal.Items.Count; index++)
{
string background;
MCQuestion question = dbManager.GetMCQuestion(myJournal.Items[index].QuestionID);
PageMCQuestionReview.Add(question);
PageMCResponseReview.Add(myJournal.Items[index].ReplyMC);
// create statistics
StatisticsStudentByCategory theStat = StatStudent.Find(x => x.CategoryID == question.Category);
if (theStat == null)
{ // need a new one
Cat cat = dbManager.GetCat(question.Category);
StatStudent.Add(new StatisticsStudentByCategory { CategoryID = cat.CId, CategoryName = cat.Txt });
theStat = StatStudent[StatStudent.Count - 1];
}
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()";
var random = new Random();
foreach (StatisticsStudentByCategory item in StatStudent)
{
background = String.Format("#{0:X6}", random.Next(0x1000000)); // = "#A197B9"
PageChartConnectors.Add(new ChartConnector
{
ChartLabel = item.CategoryName,
ChartData = item.percentage.ToString(),
ChartBackground = background,
ChartBorder = "#FFBBBB"
});
}
//PageChartConnectors.Add(new ChartConnector
//{
// ChartLabel = "100%",
// ChartData = "100",
// ChartBackground = "#000000",
// ChartBorder = "#FFBBBB"
//});
//PageChartConnectors.Add(new ChartConnector { ChartLabel = "Jan", ChartData = "31", ChartBackground = "#FF0000", ChartBorder = "#000000" });
}
}
private void CheckPageQuizzID()
{
if (PageQuizzID == null)
{
//PrepareForUser(ucnData.theRole, ucnData.theClass.FirstOrDefault());
foreach (var item in ucnData.theClass)
{
PrepareForUser(ucnData.theRole, item);
}
BuildTestSelection();
if (ucnData.theRole == "student" || ucnData.theRole == "developer") StudentState = "QuizzSelect";
}
else
{
if (!QuizzRunning())
{
//PageQuizzID = null;
if (ucnData.theRole == "student" || ucnData.theRole == "developer") StudentState = "QuizzSelect";
PrepareForUser(ucnData.theRole, ucnData.theClass.FirstOrDefault());
BuildTestSelection();
// Review selected test?
PrepareReview();
}
}
}
private void CheckTheClass()
{
if (ucnData.theClass.Count > 0)
{
foreach (var item in ucnData.theClass)
{
if (item.Contains("-CSD-"))
{
if (dbManager.GetTheClasses().Find(x => x.Name == item) == null) dbManager.CreateTheClass(new TheClass { Name = item });
}
}
}
}
public IActionResult OnGet(string category, string count)
{
try
{
// if the entity can not be categorized then send him to classselect
Common();
}
catch
{
return Redirect("https://ClassSelect.a.ucnit.eu");
}
if ((ucnData.theClass.Count == 0) || (ucnData.theClass[0] == "PleaseSelectAClass"))
{
// No class found
return Redirect("https://ClassSelect.a.ucnit.eu");
}
PageQuizzID = HttpContext.Request.Cookies["PageQuizzID"];
CheckPageQuizzID();
// Next line should be replaced by call to KeyCloakGetUsersGroups
// CheckTheClass();
return Page();
}
private bool QuizzRunning()
{
StudentState = "QuizzRunning";
return PresentQuestion();
}
public IActionResult OnPost(string action, string value, string quizzInProgress, string target)
{
Common();
switch (action)
{
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;
QuizzRunning();
break;
case "reviewTest":
PageQuizzID = value;
break;
case "QuizzRunning":
QuizzRunning();
break;
case "registerAnswer":
PageQuizzID = quizzInProgress;
RegisterAnswer();
break;
case "deleteMyTest": //Student should not be able to delete journal as this resets the journal
dbManager.DeleteJournal2(UserID, target);
break;
default:
break;
}
if (PageQuizzID != null)
{
Response.Cookies.Append("PageQuizzID", PageQuizzID, new Microsoft.AspNetCore.Http.CookieOptions
{
Secure = true,
HttpOnly = true,
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None,
});
}
return Redirect("/");
}
}
}