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 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 PageMCQuestionReview { get; set; } [BindProperty] public List> PageMCResponseReview { get; set; } [BindProperty] public List PageMCRandomized { get; set; } [BindProperty] public string myFullName { get; set; } [BindProperty] public List PageChartConnectors { get; set; } private readonly ILogger _logger; public IndexModel(ILogger 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(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 PopulateQList(string theCategory) { // Get the questions in the category List list = dbManager.GetQuestions(theCategory); // Now get the subordinate categories List 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, }; //Listitems = JsonConvert.DeserializeObject>(theJournal.JournalJson); newJournal.Items = new(); List myQuestions = PopulateQList(theQuizz.Category); //List 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 myTests = new(); List 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 myItems = JsonConvert.DeserializeObject>(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 myRndSeq = new List(); for (int indx = 0; indx < myMCQuestion.AList.Count(); indx++) { myRndSeq.Add(indx); myMCQuestion.AList[indx].Aok = false; } PageMCRandomized = new List(); 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 counter = new List(); 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 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(); //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("/"); } } }