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

339 lines
12 KiB
C#

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<Question> questionsInCat { get; set; }
[BindProperty]
public List<MCQuestion> 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<MCQuestion>(jsonString);
return;
}
// Here if no cookie found
questionCookie = new MCQuestion
{
QuestionText = "",
AList = new List<Alternative> { 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<OpenIDUCNData>(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<List<Alternative>>(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;
}
}
/// <summary>
/// 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
///
/// </summary>
/// <param name="theFile">
/// A byte array of the uploaded text file
/// </param>
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();
}
}
}