generated from karsten.jeppesen/KAJE-Template
455 lines
17 KiB
C#
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("/");
|
|
}
|
|
|
|
}
|
|
} |