generated from karsten.jeppesen/KAJE-Template
Quizzer-8: Migrating to .NET 10
This commit is contained in:
53
Quizzer-8/Quizzer/Pages/Categories.cshtml
Normal file
53
Quizzer-8/Quizzer/Pages/Categories.cshtml
Normal file
@@ -0,0 +1,53 @@
|
||||
@page
|
||||
@model Quizzer.Pages.CategoriesModel
|
||||
@Html.AntiForgeryToken()
|
||||
@{
|
||||
ViewData["Title"] = "Quizzer";
|
||||
Layout = "_Layout-teacher";
|
||||
}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lightgreen">
|
||||
<h1><center>Category Management</center></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class=row>
|
||||
<div class="col-xl-12" style="background-color: lemonchiffon">
|
||||
<form class="form-horizontal" method="post">
|
||||
@Html.Raw(Model.CatSelectList)
|
||||
<label for="newTag" class="form-label">New Tag</label>
|
||||
<input class="form-control" type="text" id="newTag" placeholder="Your new category" name="newCategory" aria-label="default input example" />
|
||||
<button type="submit" class="btn btn-primary" name="action" value="newCategory">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.CatSelected != null)
|
||||
{
|
||||
<div class=row>
|
||||
<div class="col-xl-12" style="background-color: lightblue">
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<td style="width:max-content">
|
||||
<h5>@Model.CatSelected.Txt</h5>
|
||||
</td>
|
||||
<td>
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="action" value="removeCategory" />
|
||||
<input type="hidden" name="target" value="@Model.CatSelected.CId" />
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||
Remove category (incl children)
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="categoryParent" value="@Model.CatSelected.CId">
|
||||
<label for="CatSelected_Descr" class="form-label">Quizz description</label>
|
||||
@Html.TextAreaFor(m => Model.CatSelected.Descr, 10,40, htmlAttributes: new {style="width: 100%; max-width: 100%;" })<br>
|
||||
<button type="submit" class="btn btn-primary" name="action" value="editCategory">Change description for @Model.CatSelected.Txt</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
148
Quizzer-8/Quizzer/Pages/Categories.cshtml.cs
Normal file
148
Quizzer-8/Quizzer/Pages/Categories.cshtml.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
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;
|
||||
|
||||
namespace Quizzer.Pages
|
||||
{
|
||||
public class CategoriesModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public OpenIDUCNData ucnData { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string UserID { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string CatSelectList { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public Cat CatSelected { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string myFullName { get; set; }
|
||||
|
||||
private bool isDebug;
|
||||
private string KCUserID { get; set; }
|
||||
|
||||
private DbOps cManager = new();
|
||||
|
||||
private OpenIDConnectUtils OpenIDUtils = new();
|
||||
|
||||
private Cat categoryCookie;
|
||||
|
||||
|
||||
private const string cookieName = "CategoriesCookie";
|
||||
|
||||
private void GetMyCookie()
|
||||
{
|
||||
string jsonString = HttpContext.Request.Cookies[cookieName];
|
||||
if (jsonString != null)
|
||||
{
|
||||
categoryCookie = JsonConvert.DeserializeObject<Cat>(jsonString);
|
||||
return;
|
||||
}
|
||||
// Here if no cookie found
|
||||
categoryCookie = new Cat
|
||||
{
|
||||
CId = "",
|
||||
CParentId = "",
|
||||
Descr = "",
|
||||
Txt = "",
|
||||
};
|
||||
// Save the new cookie
|
||||
SetMyCookie();
|
||||
}
|
||||
|
||||
private void SetMyCookie()
|
||||
{
|
||||
string jsonResult = JsonConvert.SerializeObject(categoryCookie);
|
||||
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);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
public void Always()
|
||||
{
|
||||
Common();
|
||||
GetMyCookie();
|
||||
CatSelectList = cManager.MakeCList(UserID, "radioBtn", categoryCookie.CId);
|
||||
if (categoryCookie.CId != "")
|
||||
{
|
||||
CatSelected = categoryCookie;
|
||||
}
|
||||
}
|
||||
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
Always();
|
||||
if (ucnData.theRole != "teacher" && ucnData.theRole != "developer") return Redirect("/");
|
||||
return Page();
|
||||
}
|
||||
|
||||
public IActionResult OnPost(string action = "na", string categoryParent = "/", string newCategory = "na", string target="na")
|
||||
{
|
||||
//tagPath = JsonConvert.DeserializeObject<List<Tags>>(theTagPath);
|
||||
Common();
|
||||
switch (action)
|
||||
{
|
||||
case "newCategory":
|
||||
categoryCookie = cManager.SetCat(UserID, categoryParent, newCategory);
|
||||
break;
|
||||
case "editCategory":
|
||||
cManager.UpdateCatdescr(categoryParent, CatSelected.Descr);
|
||||
break;
|
||||
case "removeCategory":
|
||||
cManager.DeleteCategory(target);
|
||||
break;
|
||||
default:
|
||||
categoryCookie = cManager.GetCat(categoryParent);
|
||||
break;
|
||||
}
|
||||
SetMyCookie();
|
||||
return Redirect("/Categories");
|
||||
}
|
||||
}
|
||||
}
|
||||
96
Quizzer-8/Quizzer/Pages/Coverage.cshtml
Normal file
96
Quizzer-8/Quizzer/Pages/Coverage.cshtml
Normal file
@@ -0,0 +1,96 @@
|
||||
@page
|
||||
@model Quizzer.Pages.CoverageModel
|
||||
@Html.AntiForgeryToken()
|
||||
@{
|
||||
ViewData["Title"] = "Quizzer";
|
||||
Layout = "_Layout-teacher";
|
||||
}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lightgreen">
|
||||
<h1><center>Quiz Coverage</center></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="selectClass" />
|
||||
<select id="selectedClass" name="selectedClass" onchange="javascript:this.form.submit()">
|
||||
<option>Select Class</option>
|
||||
@foreach (var item in @Model.theClasses)
|
||||
{
|
||||
<option value="@item">@item</option>
|
||||
}
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.currentClass != "" && Model.registrations.Count > 0)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<h3>@Model.registrations[0].TheClass</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
@for (var counter = 0; counter < Model.dates.Count(); counter++)
|
||||
{
|
||||
<th scope="col">@(counter + 1)</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var student in Model.persons)
|
||||
{
|
||||
<tr>
|
||||
<td>@student.Name</td>
|
||||
@foreach (var theDate in Model.dates)
|
||||
{
|
||||
var res = Model.registrations.Where(x => x.Name == student.Name && x.Date == theDate);
|
||||
@if (res.Count() == 0)
|
||||
{
|
||||
<td>-</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td>+</td>
|
||||
}
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-xl-12">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Attendees</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (var index = 0; index < Model.dates.Count(); index++)
|
||||
{
|
||||
<tr>
|
||||
<td>@(index + 1)</td>
|
||||
<td>@Model.dates[index]</td>
|
||||
<td>@Model.registrations.Where(x => x.Date == Model.dates[index]).Count()</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (Model.currentClass != "" && Model.registrations.Count == 0)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<h5>No registrations</h5>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
108
Quizzer-8/Quizzer/Pages/Coverage.cshtml.cs
Normal file
108
Quizzer-8/Quizzer/Pages/Coverage.cshtml.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Newtonsoft.Json;
|
||||
using Quizzer.Extensions;
|
||||
using Quizzer.Implementations;
|
||||
using Quizzer.Models;
|
||||
using Quizzer.OpenIDConnect;
|
||||
using Quizzer.Persistens;
|
||||
using System.Diagnostics;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
namespace Quizzer.Pages
|
||||
{
|
||||
public class CoverageModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public List<Coverage>? registrations { get; set; }
|
||||
[BindProperty]
|
||||
public List<string>? dates { get; set; }
|
||||
[BindProperty]
|
||||
public List<Coverage>? persons { get; set; }
|
||||
[BindProperty]
|
||||
public List<string>? theClasses { get; set; }
|
||||
[BindProperty]
|
||||
public string? currentClass { get; set; }
|
||||
[BindProperty]
|
||||
public string myFullName { get; set; }
|
||||
|
||||
public OpenIDUCNData ucnData { get; set; }
|
||||
private bool isDebug;
|
||||
public string UserID { get; set; }
|
||||
private string KCUserID { get; set; }
|
||||
|
||||
|
||||
|
||||
private DbOps dbMgr = new();
|
||||
private OpenIDConnectUtils OpenIDUtils = new();
|
||||
|
||||
public void Common()
|
||||
{
|
||||
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 GetMyCookie()
|
||||
{
|
||||
currentClass = HttpContext.Request.Cookies["CoverageCookie"];
|
||||
SetMyCookie();
|
||||
}
|
||||
|
||||
private void SetMyCookie()
|
||||
{
|
||||
Response.Cookies.Append("CoverageCookie", currentClass, new Microsoft.AspNetCore.Http.CookieOptions
|
||||
{
|
||||
Secure = true,
|
||||
HttpOnly = true,
|
||||
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None,
|
||||
});
|
||||
}
|
||||
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
Common();
|
||||
if (ucnData.theRole != "teacher" && ucnData.theRole != "developer") return Redirect("/");
|
||||
currentClass = "";
|
||||
// List of classes
|
||||
KeyCloak KCApi = new KeyCloak();
|
||||
List<KCGroups> myGroups = KCApi.KeyCloakGetUsersGroups(KCUserID);
|
||||
theClasses = new();
|
||||
foreach (var group in myGroups)
|
||||
{
|
||||
if (group.name.Contains("-CSD-")) theClasses.Add(group.name);
|
||||
}
|
||||
SetMyCookie();
|
||||
return Page();
|
||||
}
|
||||
|
||||
public IActionResult OnPost(string action = "na", string selectedClass = "")
|
||||
{
|
||||
Common();
|
||||
if (ucnData.theRole != "teacher" && ucnData.theRole != "developer") return Redirect("/");
|
||||
if ( selectedClass != "")
|
||||
{
|
||||
currentClass= selectedClass;
|
||||
SetMyCookie();
|
||||
registrations = dbMgr.GetCoverage(currentClass);
|
||||
// Ascending list of dates
|
||||
dates = dbMgr.GetCoverageDates(currentClass);
|
||||
// Ascending combined list of persons attending
|
||||
persons = dbMgr.GetCoveragePersons(currentClass);
|
||||
// List of classes
|
||||
KeyCloak KCApi = new KeyCloak();
|
||||
List<KCGroups> myGroups = KCApi.KeyCloakGetUsersGroups(KCUserID);
|
||||
theClasses = new();
|
||||
foreach (var group in myGroups)
|
||||
{
|
||||
if (group.name.Contains("-CSD-")) theClasses.Add(group.name);
|
||||
}
|
||||
return Page();
|
||||
}
|
||||
else return Redirect("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Quizzer-8/Quizzer/Pages/Error.cshtml
Normal file
26
Quizzer-8/Quizzer/Pages/Error.cshtml
Normal file
@@ -0,0 +1,26 @@
|
||||
@page
|
||||
@model ErrorModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
27
Quizzer-8/Quizzer/Pages/Error.cshtml.cs
Normal file
27
Quizzer-8/Quizzer/Pages/Error.cshtml.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Quizzer.Pages
|
||||
{
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public class ErrorModel : PageModel
|
||||
{
|
||||
public string? RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
private readonly ILogger<ErrorModel> _logger;
|
||||
|
||||
public ErrorModel(ILogger<ErrorModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
208
Quizzer-8/Quizzer/Pages/Index.cshtml
Normal file
208
Quizzer-8/Quizzer/Pages/Index.cshtml
Normal file
@@ -0,0 +1,208 @@
|
||||
@page
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Quizzer";
|
||||
}
|
||||
<div class="container-fluid">
|
||||
@if (Model.ucnData.theRole == "teacher" || Model.ucnData.theRole == "developer")
|
||||
{
|
||||
<div class="col-lg-12" style="background-color:cornflowerblue">
|
||||
<h1>@Model.myFullName: Teacher</h1>
|
||||
</div>
|
||||
}
|
||||
@if (Model.ucnData.theRole == "student" || Model.ucnData.theRole == "developer")
|
||||
{
|
||||
// STUDENT PATH
|
||||
@if (Model.StudentState == "QuizzSelect")
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-xxl-12" style="background-color: cornflowerblue">
|
||||
<h1 style="text-align:center">Quizz Management</h1>
|
||||
</div>
|
||||
<div class="col-xxl-12" style="background-color: cornflowerblue">
|
||||
@Model.myFullName, @Model.ucnData.theClass.FirstOrDefault()
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Description</th>
|
||||
<th>Your Score</th>
|
||||
<th>Max Score</th>
|
||||
<th>Reset</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var item in Model.PageTests)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="email" value="@item.Journal.PID" />
|
||||
<input type="hidden" name="value" value="@item.Journal.QuizzID" />
|
||||
@if (item.Journal.IsOpen == 1)
|
||||
{
|
||||
<input type="hidden" name="action" value="startTest" />
|
||||
<button type="button" class="btn btn-outline-primary" onclick="javascript:this.form.submit()">Take Quizz</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="hidden" name="action" value="reviewTest" />
|
||||
<button type="button" class="btn btn-outline-primary" onclick="javascript:this.form.submit()">Review</button>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@item.Text
|
||||
</td>
|
||||
<td>
|
||||
@item.Journal.Score
|
||||
</td>
|
||||
<td>
|
||||
@item.Journal.ScoreAcc
|
||||
</td>
|
||||
<td>
|
||||
@if (item.Journal.Anonymous)
|
||||
{
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="email" value="@item.Journal.PID" />
|
||||
<input type="hidden" name="target" value="@item.Journal.QuizzID" />
|
||||
<input type="hidden" name="action" value="deleteMyTest" />
|
||||
<button type="button" class="btn btn-warning" onclick="javascript:this.form.submit()">Reset Test</button>
|
||||
</form>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
// if review has been selected then show it
|
||||
@if (Model.PageMCQuestionReview != null)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h2>Review</h2>
|
||||
</div>
|
||||
</div>
|
||||
@for (int qindex = 0; qindex < Model.PageMCQuestionReview.Count; qindex++)
|
||||
{
|
||||
<div class="row" style="background-color:white; opacity: 0.8">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%"></th>
|
||||
<th>@Model.PageMCQuestionReview[qindex].QuestionText</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@for (int aindex = 0; aindex < Model.PageMCResponseReview[qindex].Count; aindex++)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@if (Model.PageMCResponseReview[qindex][aindex])
|
||||
{
|
||||
<i class="bi bi-caret-right-fill" />
|
||||
}
|
||||
</td>
|
||||
@if (Model.PageMCQuestionReview[qindex].AList[aindex].Aok)
|
||||
{
|
||||
<td style="background-color:lightgreen">
|
||||
@Model.PageMCQuestionReview[qindex].AList[aindex].Atxt
|
||||
</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td>
|
||||
@Model.PageMCQuestionReview[qindex].AList[aindex].Atxt
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (Model.StudentState == "QuizzRunning")
|
||||
{
|
||||
// We are doing a Quizz
|
||||
<div class="row" style="background-color:white; opacity: 0.8">
|
||||
<div class="col-lg-12">
|
||||
<h2 style="text-align:center">@Model.PageMCQuestion.QuestionText</h2>
|
||||
</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="registerAnswer" />
|
||||
<input type="hidden" name="quizzInProgress" value="@Model.PageQuizzID" />
|
||||
<table class="table">
|
||||
@for (int indx = 0; indx < @Model.PageMCQuestion.AList.Count(); indx++)
|
||||
{
|
||||
<tr>
|
||||
<td><input asp-for="@Model.PageMCQuestion.AList[@Model.PageMCRandomized[@indx]].Aok" /></td>
|
||||
<td>@Model.PageMCQuestion.AList[@Model.PageMCRandomized[@indx]].Atxt</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
<button type="submit" class="btn btn-success" name="action" value="create"><i class="bi bi-forward"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (Model.PageChartConnectors != null)
|
||||
{
|
||||
@if (Model.PageChartConnectors.Count > 2)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-xxl-12">
|
||||
<h2>Visual</h2><br />
|
||||
<canvas id="myChart" width="400" height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@section scripts {
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
var chartdata = @Html.Raw(Json.Serialize(Model.PageChartConnectors));
|
||||
var theLabels = [];
|
||||
var theDatas = [];
|
||||
var theBackgrounds = [];
|
||||
var theBorders = [];
|
||||
$.each(chartdata, function (index, value) {
|
||||
theLabels.push(value.chartLabel);
|
||||
theDatas.push(value.chartData);
|
||||
theBackgrounds.push(value.chartBackground.toString())
|
||||
theBorders.push(value.chartBorder.toString())
|
||||
});
|
||||
var ctx = document.getElementById('myChart').getContext('2d');
|
||||
var theOptions = {
|
||||
responsive: false,
|
||||
maintainAspectRatio: true,
|
||||
scale: {
|
||||
ticks: {
|
||||
min: 0,
|
||||
max: 100
|
||||
}
|
||||
}
|
||||
};
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'polarArea',
|
||||
options: theOptions,
|
||||
data: {
|
||||
labels: theLabels,
|
||||
datasets: [{
|
||||
label: 'demo',
|
||||
backgroundColor: theBackgrounds,
|
||||
borderColor: theBorders,
|
||||
data: theDatas
|
||||
}]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
455
Quizzer-8/Quizzer/Pages/Index.cshtml.cs
Normal file
455
Quizzer-8/Quizzer/Pages/Index.cshtml.cs
Normal file
@@ -0,0 +1,455 @@
|
||||
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("/");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
31
Quizzer-8/Quizzer/Pages/Privacy.cshtml
Normal file
31
Quizzer-8/Quizzer/Pages/Privacy.cshtml
Normal file
@@ -0,0 +1,31 @@
|
||||
@page
|
||||
@model Quizzer.Pages.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Privacy Policy";
|
||||
}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lightpink">
|
||||
<h1><center>@ViewData["Title"]</center></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<h5>Opsamlede data</h5>
|
||||
<p>Et personhenførbart datasæt opsamles: [Person for- og efternavn, E-mail adresse, Klasse, Dato for test]</p>
|
||||
<h5>Formål med opsamlingen af datasættet</h5>
|
||||
<p>Kvalitetsvurdering af undervisning og didaktik.</p>
|
||||
<h5>Hvem har adgang til data?</h5>
|
||||
<p>Autentiserede og autoriserede lærere ved UCN (UCN login kræves)</p>
|
||||
<h5>Lovmæssig baggrund for opsamlingen af datasættet</h5>
|
||||
<p>Legitim interesse med henvisning til formål</p>
|
||||
<h5>Hvordan afgøres hvornår datasættet ikke længere tjener et formål?</h5>
|
||||
<p>Når sidste session i undervisningsgang i forløbet er afsluttet</p>
|
||||
<h5>Destruktion eller anonymisering af datasættet</h5>
|
||||
<p>Datasættet destrueres automatisk senest i starten af januar og i starten af juli</p>
|
||||
<h5>Kontaktperson</h5>
|
||||
<p>Lektor Karsten Jeppesen, kaje@ucn.dk</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
19
Quizzer-8/Quizzer/Pages/Privacy.cshtml.cs
Normal file
19
Quizzer-8/Quizzer/Pages/Privacy.cshtml.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Quizzer.Pages
|
||||
{
|
||||
public class PrivacyModel : PageModel
|
||||
{
|
||||
private readonly ILogger<PrivacyModel> _logger;
|
||||
|
||||
public PrivacyModel(ILogger<PrivacyModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
182
Quizzer-8/Quizzer/Pages/Questions.cshtml
Normal file
182
Quizzer-8/Quizzer/Pages/Questions.cshtml
Normal file
@@ -0,0 +1,182 @@
|
||||
@page
|
||||
@model Quizzer.Pages.QuestionsModel
|
||||
@Html.AntiForgeryToken()
|
||||
@{
|
||||
ViewData["Title"] = "Quizzer";
|
||||
Layout = "_Layout-teacher";
|
||||
}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lightgreen">
|
||||
<h1><center>Q Management</center></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lemonchiffon">
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="changeCategory" />
|
||||
Subject: @Html.Raw(Model.CatMenu)<br /><br />
|
||||
</form>
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="changeQText" />
|
||||
<label for="qText">Question Text:</label>
|
||||
<input id="qText" type="text" class="form-control" placeholder="Enter the question" name="changeTo" value="@Model.questionCookie.QuestionText" style="width:100%" onchange="javascript:this.form.submit()">
|
||||
</form>
|
||||
<table class="table table-striped" style="width:100%">
|
||||
<tr>
|
||||
<th>Correct</th>
|
||||
<th>Text</th>
|
||||
</tr>
|
||||
@if (Model.questionCookie.AList != null)
|
||||
{
|
||||
@for (var indx = 0; indx < Model.questionCookie.AList.Count(); indx++)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="changeAOk" />
|
||||
<input type="hidden" name="tochange" value="@indx" />
|
||||
@if (@Model.questionCookie.AList[@indx].Aok)
|
||||
{
|
||||
<input type="hidden" name="changeTo" value="off" />
|
||||
<button type="submit" class="btn btn-outline-success btn-sm" checked onchange="javascript:this.form.submit()"><i class="bi bi-check2-circle"></i></button>
|
||||
//<input type="checkbox" class="form-check-input" name="changeTo" checked onchange="javascript:this.form.submit()">
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="hidden" name="changeTo" value="on" />
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm" checked onchange="javascript:this.form.submit()"><i class="bi bi-x-circle"></i></button>
|
||||
//<input type="checkbox" class="form-check-input" name="changeTo" onchange="javascript:this.form.submit()">
|
||||
}
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="changeAText" />
|
||||
<input type="hidden" name="tochange" value="@indx" />
|
||||
<input id="qText" type="text" class="form-control" placeholder="Alternative text" name="changeTo" value="@Model.questionCookie.AList[@indx].Atxt" style="width:100%" onchange="javascript:this.form.submit()">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</table>
|
||||
<form class="form-horizontal" method="post">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
@if (Model.isSubmittable)
|
||||
{
|
||||
<button type="submit" class="btn btn-success" name="action" value="create">Submit</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="hidden" name="action" value="update" />
|
||||
<button type="submit" class="btn btn-success" name="action" value="create" disabled>Submit</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" style="background-color: lemonchiffon">
|
||||
<h3>Scan Question</h3>
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="action" value="scan" />
|
||||
<label for="q_txt">Question text</label>
|
||||
<div class="form-group green-border-focus">
|
||||
<textarea class="form-control" name="tochange" rows="5" id="q_txt" onchange="javascript:this.form.submit()"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" style="background-color: lemonchiffon">
|
||||
<h3>Edit Question</h3>
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="action" value="edit" />
|
||||
<label for="q_txt">Question text</label>
|
||||
<input type="text" name="txt" id="q_txt" size="100%" onchange="javascript:this.form.submit()" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.questionCookie.Category != null && Model.questionsInCat != null)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12" style="background-color: paleturquoise">
|
||||
<table class="table" style="width:100%">
|
||||
<tr>
|
||||
<td>
|
||||
<h5>Questions in selected category : @Model.questionsInCat.Count()</h5>
|
||||
</td>
|
||||
<td>
|
||||
<table class="table" style="width:100%">
|
||||
<thead>
|
||||
<th>AIKEN format</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>
|
||||
Download
|
||||
</td>
|
||||
<td>
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="action" value="download" />
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm" checked onchange="javascript:this.form.submit()"><i class="bi bi-download"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
UpLoad
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action" value="upload" />
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm" checked onchange="javascript:this.form.submit()"><i class="bi bi-upload"></i></button>
|
||||
<input type="file" asp-for="Upload" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="table table-striped" style="width:100%">
|
||||
<tr>
|
||||
<th>Question</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
@foreach (var item in Model.mcQuestionsInCat)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@item.QuestionText
|
||||
@foreach (var altItem in item.AList)
|
||||
{
|
||||
@if (altItem.Aok)
|
||||
{
|
||||
<li style="background-color:lightgreen">@altItem.Atxt</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li>@altItem.Atxt</li>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="action" value="delete" />
|
||||
<input type="hidden" name="tochange" value="@item.QuestionID" />
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
</div>
|
||||
</div>
|
||||
338
Quizzer-8/Quizzer/Pages/Questions.cshtml.cs
Normal file
338
Quizzer-8/Quizzer/Pages/Questions.cshtml.cs
Normal file
@@ -0,0 +1,338 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
112
Quizzer-8/Quizzer/Pages/Quizzes.cshtml
Normal file
112
Quizzer-8/Quizzer/Pages/Quizzes.cshtml
Normal file
@@ -0,0 +1,112 @@
|
||||
@page
|
||||
@model Quizzer.Pages.QuizzesModel
|
||||
@{
|
||||
ViewData["Title"] = "Quizzer";
|
||||
Layout = "_Layout-teacher";
|
||||
}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lightgreen">
|
||||
<h1><center>Quizz Management</center></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class=row>
|
||||
<div class="col-xl-12" style="background-color: lemonchiffon">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
Select Category
|
||||
</td>
|
||||
<td>
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="changeCategory" />
|
||||
@Html.Raw(Model.CategorySelect)
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Select Class
|
||||
</td>
|
||||
<td>
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="changeClass" />
|
||||
<select name="target" onchange="javascript:this.form.submit()">
|
||||
<option value=""> Select the Class </option>
|
||||
@foreach (var item in @Model.TheClassList)
|
||||
{
|
||||
@if (item == Model.pageQuizz.Class)
|
||||
{
|
||||
<option value="@item" selected> @item </option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@item"> @item </option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form class="form-horizontal" method="post">
|
||||
@*
|
||||
<label for="newQuizz_Class" class="form-label">Class</label>
|
||||
@Html.TextBoxFor(m => m.pageQuizz.Class)
|
||||
@Html.DropDownListFor(m => m.pageQuizz.Class, new SelectList(Model.TheClassList), "Select the Class")
|
||||
*@
|
||||
<br />
|
||||
<label for="newQuizz_Descr" class="form-label">Description</label>
|
||||
@Html.TextAreaFor(m => m.pageQuizz.Descr, 10,40, htmlAttributes: new {style="width: 100%; max-width: 100%;" })<br />
|
||||
<label for="newQuizz_Anonymous" class="form-label">Anonymous quiz</label>
|
||||
@Html.CheckBoxFor(m => m.pageQuizz.Anonymous)<br />
|
||||
<label for="newQuizz_Qsize" class="form-label">Number of questions</label>
|
||||
@Html.TextBoxFor(m => m.pageQuizz.QSize, new { @type = "number" })
|
||||
<button type="submit" class="btn btn-primary" name="action" value="createQuizz">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class=row>
|
||||
<div class="col-xl-12" style="background-color: lightgreen">
|
||||
<h5>Tests in system</h5>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<th>Size</th>
|
||||
<th>Category</th>
|
||||
<th>Anonymous</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var item in @Model.QuizzList)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@item.Class
|
||||
</td>
|
||||
<td>
|
||||
@item.QSize
|
||||
</td>
|
||||
<td>
|
||||
@item.CategoryText
|
||||
</td>
|
||||
<td>
|
||||
@item.Anonymous
|
||||
</td>
|
||||
<td>
|
||||
@item.Descr
|
||||
</td>
|
||||
<td>
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="action" value="delete" />
|
||||
<input type="hidden" name="target" value="@item.QuizzId" />
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
145
Quizzer-8/Quizzer/Pages/Quizzes.cshtml.cs
Normal file
145
Quizzer-8/Quizzer/Pages/Quizzes.cshtml.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Newtonsoft.Json;
|
||||
using Quizzer.Extensions;
|
||||
using Quizzer.Implementations;
|
||||
using Quizzer.Models;
|
||||
using Quizzer.OpenIDConnect;
|
||||
using Quizzer.Persistens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
namespace Quizzer.Pages
|
||||
{
|
||||
public class QuizzesModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public OpenIDUCNData ucnData { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string UserID { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public List<QuizzDN> QuizzList { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string CategorySelect { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public List<string> TheClassList { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public Quizz pageQuizz { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string myFullName { get; set; }
|
||||
|
||||
|
||||
private bool isDebug;
|
||||
private string KCUserID { get; set; }
|
||||
|
||||
private DbOps dbManager = new();
|
||||
private OpenIDConnectUtils OpenIDUtils = new();
|
||||
private Quizz quizzCookie;
|
||||
|
||||
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 GetMyCookie()
|
||||
{
|
||||
string jsonString = HttpContext.Request.Cookies["quizzCookie"];
|
||||
if (jsonString != null)
|
||||
{
|
||||
quizzCookie = JsonConvert.DeserializeObject<Quizz>(jsonString);
|
||||
return;
|
||||
}
|
||||
// Here if no cookie found
|
||||
quizzCookie = new Quizz
|
||||
{
|
||||
Category = "",
|
||||
Class = "",
|
||||
Anonymous = false,
|
||||
Descr = "",
|
||||
QSize = 5,
|
||||
QuizzId = "",
|
||||
};
|
||||
// Save the new cookie
|
||||
SetMyCookie();
|
||||
}
|
||||
|
||||
private void SetMyCookie()
|
||||
{
|
||||
string jsonResult = JsonConvert.SerializeObject(quizzCookie);
|
||||
Response.Cookies.Append("quizzCookie", jsonResult, new Microsoft.AspNetCore.Http.CookieOptions
|
||||
{
|
||||
Secure = true,
|
||||
HttpOnly = true,
|
||||
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None,
|
||||
});
|
||||
}
|
||||
|
||||
private void RemoveMyCookie()
|
||||
{
|
||||
Response.Cookies.Delete("quizzCookie");
|
||||
}
|
||||
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
Common();
|
||||
if (ucnData.theRole != "teacher" && ucnData.theRole != "developer") return Redirect("/");
|
||||
GetMyCookie();
|
||||
pageQuizz = JsonConvert.DeserializeObject<Quizz>(JsonConvert.SerializeObject(quizzCookie));
|
||||
QuizzList = dbManager.GetQuizzesTeacher(UserID);
|
||||
CategorySelect = dbManager.MakeCList(UserID, "DropDownIndented", quizzCookie.Category);
|
||||
TheClassList = new();
|
||||
KeyCloak KCApi = new KeyCloak();
|
||||
List<KCGroups> myGroups = KCApi.KeyCloakGetUsersGroups(KCUserID);
|
||||
foreach (var group in myGroups)
|
||||
{
|
||||
if (group.name.Contains("-CSD-")) TheClassList.Add(group.name);
|
||||
}
|
||||
return Page();
|
||||
}
|
||||
public IActionResult OnPost(string action = "na", string categoryParent = "", string newCategory = "na", string target = "na")
|
||||
{
|
||||
Common();
|
||||
GetMyCookie();
|
||||
//tagPath = JsonConvert.DeserializeObject<List<Tags>>(theTagPath);
|
||||
switch (action)
|
||||
{
|
||||
case "createQuizz":
|
||||
quizzCookie.Descr = pageQuizz.Descr;
|
||||
quizzCookie.Anonymous = pageQuizz.Anonymous;
|
||||
quizzCookie.QSize = pageQuizz.QSize;
|
||||
quizzCookie.Owner = UserID;
|
||||
dbManager.InsertQuizz(quizzCookie);
|
||||
break;
|
||||
case "changeCategory":
|
||||
quizzCookie.Category = newCategory;
|
||||
break;
|
||||
case "changeClass":
|
||||
pageQuizz.Class = quizzCookie.Class = target;
|
||||
break;
|
||||
case "delete":
|
||||
dbManager.DeleteQuizz(target);
|
||||
dbManager.DeleteStatistics(target);
|
||||
dbManager.DeleteJournalByQuizz(target);
|
||||
break;
|
||||
case "na":
|
||||
if (newCategory != "na") quizzCookie.Category = newCategory;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SetMyCookie();
|
||||
return Redirect("/Quizzes");
|
||||
}
|
||||
}
|
||||
}
|
||||
63
Quizzer-8/Quizzer/Pages/Shared/_Layout-teacher.cshtml
Normal file
63
Quizzer-8/Quizzer/Pages/Shared/_Layout-teacher.cshtml
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - Quizzer</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="~/css/site.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" asp-area="" asp-page="/Index">Quizzer</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Questions">Questions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Categories">Categories</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Quizzes">Quizzes</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Statistics">Statistics</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Coverage">Coverage</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="border-top footer text-muted">
|
||||
<div class="container">
|
||||
© 2022 - Quizzer
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
|
||||
@RenderSection("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
48
Quizzer-8/Quizzer/Pages/Shared/_Layout-teacher.cshtml.css
Normal file
48
Quizzer-8/Quizzer/Pages/Shared/_Layout-teacher.cshtml.css
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
for details on configuring this project to bundle and minify static web assets. */
|
||||
|
||||
a.navbar-brand {
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0077cc;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.box-shadow {
|
||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
button.accept-policy {
|
||||
font-size: 1rem;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
line-height: 60px;
|
||||
}
|
||||
67
Quizzer-8/Quizzer/Pages/Shared/_Layout.cshtml
Normal file
67
Quizzer-8/Quizzer/Pages/Shared/_Layout.cshtml
Normal file
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@model IndexModel
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - Quizzer</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="~/css/site.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" asp-area="" asp-page="/Index">Quizzer</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
@if (Model.ucnData.theRole == "teacher" || Model.ucnData.theRole == "developer")
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Questions">Questions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Categories">Categories</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Quizzes">Quizzes</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Statistics">Statistics</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Coverage">Coverage</a>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container-fluid" style="background-image: url('images/quiz.jpg'); height:100vh;">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="border-top footer text-muted">
|
||||
<div class="container">
|
||||
© 2022 - Quizzer
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
|
||||
@RenderSection("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
48
Quizzer-8/Quizzer/Pages/Shared/_Layout.cshtml.css
Normal file
48
Quizzer-8/Quizzer/Pages/Shared/_Layout.cshtml.css
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
for details on configuring this project to bundle and minify static web assets. */
|
||||
|
||||
a.navbar-brand {
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0077cc;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.box-shadow {
|
||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
button.accept-policy {
|
||||
font-size: 1rem;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
line-height: 60px;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||
84
Quizzer-8/Quizzer/Pages/Statistics.cshtml
Normal file
84
Quizzer-8/Quizzer/Pages/Statistics.cshtml
Normal file
@@ -0,0 +1,84 @@
|
||||
@page
|
||||
@model Quizzer.Pages.StatisticsModel
|
||||
@Html.AntiForgeryToken()
|
||||
@{
|
||||
ViewData["Title"] = "Quizzer";
|
||||
Layout = "_Layout-teacher";
|
||||
}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lightgreen">
|
||||
<h1><center>Statistics</center></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Class</th>
|
||||
<th>Quizz</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var item in Model.quizzList)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<form class="form-horizontal" method="post">
|
||||
<input type="hidden" name="action" value="selectQuizz" />
|
||||
<input type="hidden" name="changeTo" value="@item.QuizzId" />
|
||||
<button type="button" class="btn btn-outline-primary" onclick="javascript:this.form.submit()">Stats</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>@item.Class</td>
|
||||
<td>@item.CategoryText</td>
|
||||
<td>
|
||||
<form asp-page="" method="post">
|
||||
<input type="hidden" name="action" value="delete" />
|
||||
<input type="hidden" name="changeTo" value="@item.QuizzId" />
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.PageStatistics != null)
|
||||
{
|
||||
@if (Model.PageStatistics.Questions.Count > 0 && Model.PageStatistics.MCList.Count > 0)
|
||||
{
|
||||
<div class="row">
|
||||
<h3>Statistics</h3><br>
|
||||
@for (int Qindex = 0; Qindex < Model.PageStatistics.Questions.Count; Qindex++)
|
||||
{
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%">Count</th>
|
||||
<th>@Model.PageStatistics.Questions[Qindex].QuestionText</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@for (int Aindex = 0; Aindex < @Model.PageStatistics.Questions[Qindex].AList.Count; Aindex++)
|
||||
{
|
||||
<tr>
|
||||
<td class="align-text-top">@Model.PageStatistics.MCList[Qindex].Counter[Aindex]</td>
|
||||
@if (@Model.PageStatistics.Questions[Qindex].AList[Aindex].Aok)
|
||||
{
|
||||
<td style="background-color:lightgreen">@Model.PageStatistics.Questions[Qindex].AList[Aindex].Atxt</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td>@Model.PageStatistics.Questions[Qindex].AList[Aindex].Atxt</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="row">
|
||||
</div>
|
||||
</div>
|
||||
126
Quizzer-8/Quizzer/Pages/Statistics.cshtml.cs
Normal file
126
Quizzer-8/Quizzer/Pages/Statistics.cshtml.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
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.IdentityModel.Tokens.Jwt;
|
||||
|
||||
namespace Quizzer.Pages
|
||||
{
|
||||
public class StatisticsModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public OpenIDUCNData ucnData { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string UserID { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public List<QuizzDN> quizzList { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public StatisticsMCDN PageStatistics { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string myFullName { get; set; }
|
||||
|
||||
|
||||
private bool isDebug;
|
||||
private string KCUserID { get; set; }
|
||||
|
||||
private DbOps dbManager = new();
|
||||
private OpenIDConnectUtils OpenIDUtils = new();
|
||||
|
||||
|
||||
public string statisticsCookie { get; set; }
|
||||
|
||||
private const string cookieName = "StatisticsCookie";
|
||||
|
||||
private void GetMyCookie()
|
||||
{
|
||||
string jsonString = HttpContext.Request.Cookies[cookieName];
|
||||
if (jsonString != null)
|
||||
{
|
||||
statisticsCookie = JsonConvert.DeserializeObject<string>(jsonString);
|
||||
return;
|
||||
}
|
||||
// Here if no cookie found
|
||||
statisticsCookie = "";
|
||||
// Save the new cookie
|
||||
SetMyCookie();
|
||||
}
|
||||
|
||||
private void SetMyCookie()
|
||||
{
|
||||
string jsonResult = JsonConvert.SerializeObject(statisticsCookie);
|
||||
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 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");
|
||||
}
|
||||
|
||||
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
Common();
|
||||
if (ucnData.theRole != "teacher" && ucnData.theRole != "developer") return Redirect("/");
|
||||
GetMyCookie();
|
||||
quizzList = dbManager.GetQuizzesTeacher(UserID);
|
||||
if (statisticsCookie != "")
|
||||
{
|
||||
// traverse all statistics
|
||||
PageStatistics = dbManager.GetStatisticsMCDN(statisticsCookie);
|
||||
}
|
||||
else
|
||||
{
|
||||
PageStatistics = null;
|
||||
}
|
||||
return Page();
|
||||
}
|
||||
|
||||
public IActionResult OnPost(string action = "na", string changeTo = "na")
|
||||
{
|
||||
Common();
|
||||
GetMyCookie();
|
||||
switch (action)
|
||||
{
|
||||
case "selectQuizz":
|
||||
statisticsCookie = changeTo;
|
||||
break;
|
||||
case "delete":
|
||||
dbManager.DeleteStatistics(changeTo);
|
||||
break;
|
||||
case "na":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SetMyCookie();
|
||||
return Redirect("/Statistics");
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Quizzer-8/Quizzer/Pages/_ViewImports.cshtml
Normal file
3
Quizzer-8/Quizzer/Pages/_ViewImports.cshtml
Normal file
@@ -0,0 +1,3 @@
|
||||
@using Quizzer
|
||||
@namespace Quizzer.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
3
Quizzer-8/Quizzer/Pages/_ViewStart.cshtml
Normal file
3
Quizzer-8/Quizzer/Pages/_ViewStart.cshtml
Normal file
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
14
Quizzer-8/Quizzer/Pages/images.cshtml
Normal file
14
Quizzer-8/Quizzer/Pages/images.cshtml
Normal file
@@ -0,0 +1,14 @@
|
||||
@page
|
||||
@model Quizzer.Pages.QuestionsModel
|
||||
@Html.AntiForgeryToken()
|
||||
@{
|
||||
ViewData["Title"] = "Quizzer";
|
||||
Layout = "_Layout-teacher";
|
||||
}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-12" style="background-color: lightgreen">
|
||||
<h1><center>Image Management</center></h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
12
Quizzer-8/Quizzer/Pages/images.cshtml.cs
Normal file
12
Quizzer-8/Quizzer/Pages/images.cshtml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Quizzer.Pages
|
||||
{
|
||||
public class imagesModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user