using Microsoft.AspNetCore.Http.HttpResults; 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.Diagnostics; using System.IdentityModel.Tokens.Jwt; using System.Text; namespace Quizzer.Pages { public class QuestionsModel : PageModel { [BindProperty] public OpenIDUCNData ucnData { get; set; } [BindProperty] public string UserID { get; set; } [BindProperty] public string CatMenu { get; set; } [BindProperty] public bool isSubmittable { get; set; } [BindProperty] public List questionsInCat { get; set; } [BindProperty] public List mcQuestionsInCat { get; set; } [BindProperty] public MCQuestion questionCookie { get; set; } [BindProperty] public IFormFile Upload { get; set; } [BindProperty] public string myFullName { get; set; } private bool isDebug; private string KCUserID { get; set; } private DbOps qManager = new DbOps(); private OpenIDConnectUtils OpenIDUtils = new(); private const string cookieName = "QuestionCookie"; private void GetMyCookie() { string jsonString = HttpContext.Request.Cookies[cookieName]; if (jsonString != null) { questionCookie = JsonConvert.DeserializeObject(jsonString); return; } // Here if no cookie found questionCookie = new MCQuestion { QuestionText = "", AList = new List { new Alternative { Aok = false, Atxt = "", Counter = 0 } } }; // Save the new cookie SetMyCookie(); } private void SetMyCookie() { string jsonResult = JsonConvert.SerializeObject(questionCookie); Response.Cookies.Append(cookieName, jsonResult, new Microsoft.AspNetCore.Http.CookieOptions { Secure = true, HttpOnly = true, SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, }); } private void RemoveMyCookie() { Response.Cookies.Delete(cookieName); } public void AlwaysIn() { 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"); GetMyCookie(); } public void AlwaysOut() { CatMenu = qManager.MakeCList(UserID, "DropDown", questionCookie.Category); isSubmittable = true; if (questionCookie.Category == "" || questionCookie.Category == null) { isSubmittable = false; questionsInCat = null; } else { questionsInCat = qManager.GetQuestions(questionCookie.Category); mcQuestionsInCat = new(); foreach ( var item in questionsInCat ) { MCQuestion newQ= new MCQuestion(); newQ.QuestionID = item.QuestionID; newQ.QuestionText = item.QuestionText; newQ.AList = JsonConvert.DeserializeObject>(item.ContentJson); mcQuestionsInCat.Add(newQ); } } try { isSubmittable = questionCookie.QuestionText != "" && questionCookie.AList.Where(x => x.Aok == true).Count() == 1 && questionCookie.Category != null; } catch { RemoveMyCookie(); GetMyCookie(); isSubmittable = false; } } /// /// Will attempt to extract questions encoded in the Eiken format. /// The answer letters (A,B,C etc.) and the word "ANSWER" must be capitalised as shown below, otherwise the import will fail. /// What is True? /// A. True /// B. False /// ANSWER: A /// /// What is True? /// A) True /// B) False /// ANSWER: A /// /// /// /// A byte array of the uploaded text file /// private void EikenDecode(byte[] theFile) { string[] lines = System.Text.Encoding.Default.GetString(theFile).Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); char state = 'q'; // state: looking for question text MCQuestion theQuestion = new(); ; foreach (string theLine in lines) { if (state == 'q') { if (theLine.Length == 0) continue; // something here theQuestion = new(); theQuestion.QuestionText = theLine; state = 'A'; // looking for first alternative continue; } if (theLine[0] == state && (theLine[1] == '.' || theLine[1] == ')')) { // alternative theQuestion.AList.Add(new Alternative { Aok = false, Atxt = theLine.Substring(3, theLine.Length - 3), }); state++; continue; } if (theLine.StartsWith("ANSWER: ")) { int correct = theLine[8] - 'A'; theQuestion.AList[correct].Aok = true; theQuestion.Category = questionCookie.Category; theQuestion.QuestionType = questionCookie.QuestionType; qManager.InsertMCQuestion(theQuestion); state = 'q'; continue; } } } public IActionResult OnGet() { AlwaysIn(); AlwaysOut(); if (ucnData.theRole != "teacher" && ucnData.theRole != "developer") return Redirect("/"); return Page(); } // Will also accept ChatGPT/CoPilot format //What is the main difference between IPv4 and IPv6? // //A) IPv4 uses 32-bit addresses, IPv6 uses 128-bit addresses //B) IPv4 supports multicast, IPv6 does not //C) IPv4 is used for local networks, IPv6 is used for the internet //D) IPv4 uses hexadecimal notation, IPv6 uses binary notation //E) IPv4 is faster than IPv6 private void OnPostScan(string qText) { char[] delim = { '\r', '\n' }; string[] myStrings = qText.Split(delim); int counter = 0; // Identify the Question part while (counter < myStrings.Length) { if (myStrings[counter] != "") { questionCookie.QuestionText = myStrings[counter]; counter++; break; } counter++; } if (counter >= myStrings.Length) return; // start from scratch questionCookie.AList.Clear(); while (counter < myStrings.Length) { if (myStrings[counter] != "") { string alt = myStrings[counter].Trim(); if (alt.Length > 3) if (alt[1] == ')' || alt[1] == '.') alt = alt.Remove(0, 3); questionCookie.AList.Add(new Alternative { Atxt = alt, Aok = false }); } counter++; } questionCookie.AList.Add(new Alternative { Atxt = "", Aok = false }); } public IActionResult OnPost(string action = "NA", string tochange = "", string changeTo = "", string newCategory = "") { AlwaysIn(); if (ucnData.theRole != "teacher" && ucnData.theRole != "developer") return Redirect("/"); switch (action) { case "changeCategory": questionCookie.Category = newCategory; break; case "changeQText": questionCookie.QuestionText = changeTo; break; case "changeAText": questionCookie.AList[int.Parse(tochange)].Atxt = changeTo; if (int.Parse(tochange) == questionCookie.AList.Count - 1) { questionCookie.AList.Add(new Alternative { Atxt = "", Aok = false }); } break; case "changeAOk": // tochange: "0" changeTo: on questionCookie.AList[int.Parse(tochange)].Aok = (changeTo == "on"); break; case "create": qManager.InsertMCQuestion(questionCookie); questionCookie.QuestionText = ""; questionCookie.QuestionID = ""; questionCookie.AList.Clear(); break; case "scan": OnPostScan(tochange); break; case "delete": if (tochange != "") qManager.DeleteQuestion(tochange); break; case "download": Cat cat = qManager.GetCat(questionCookie.Category); string output = qManager.ExportQuestionsEiken(questionCookie.Category); string fileName = cat.Txt + "-Eiken.txt"; /* Not such good an idea. just keep as subjects */ /* while (cat.CParentId != "/") { cat = qManager.GetCat(cat.CParentId); fileName = cat.Txt + "-" + fileName; } */ var contentType = "text/plain"; var bytes = Encoding.UTF8.GetBytes(output); var result = new FileContentResult(bytes, contentType); result.FileDownloadName = fileName; SetMyCookie(); return result; break; case "upload": // https://www.learnrazorpages.com/razor-pages/forms/file-upload may benefit from async if (Upload != null) { Stream myStream = Upload.OpenReadStream(); byte[] myBuffer = new byte[myStream.Length]; myStream.Position = 0; myStream.Read(myBuffer, 0, myBuffer.Length); myStream.Close(); EikenDecode(myBuffer); } break; default: break; } CatMenu = qManager.MakeCList(UserID, "DropDown", questionCookie.Category); isSubmittable = questionCookie.AList.Where(x => x.Aok == true).Count() == 1 && questionCookie.Category != null; SetMyCookie(); AlwaysOut(); return Page(); } } }