From 1097ab98fcb1a9afbab732271acf9ea1b5ee8e1c Mon Sep 17 00:00:00 2001 From: BuildTools Date: Tue, 3 Sep 2024 16:15:42 +0200 Subject: [PATCH] Spielablauf umgesetzt --- FWLAZ.sln | 2 +- FWLAZ_Web/.dockerignore | 30 +++ FWLAZ_Web/Components/Layout/MainLayout.razor | 2 +- FWLAZ_Web/Components/Layout/NavMenu.razor | 18 +- FWLAZ_Web/Components/Pages/Counter.razor | 18 -- FWLAZ_Web/Components/Pages/Home.razor | 157 ++++++++++++++- FWLAZ_Web/Components/Pages/QuizRun.razor | 140 +++++++++++++ .../Components/Pages/Settings/QuizEdit.razor | 4 + .../Pages/Settings/QuizQuestions.razor | 33 +-- .../Components/Pages/Settings/Quizzes.razor | 2 +- FWLAZ_Web/Components/Pages/Weather.razor | 63 ------ FWLAZ_Web/Data/Answer.cs | 2 + FWLAZ_Web/Data/Question.cs | 5 + FWLAZ_Web/Data/QuestionGroup.cs | 3 + FWLAZ_Web/Data/Quiz.cs | 1 + FWLAZ_Web/Data/quiz.db | Bin 114688 -> 114688 bytes FWLAZ_Web/Dockerfile | 30 +++ FWLAZ_Web/FWLAZ_Web.csproj | 15 ++ .../20240901192908_AddQuizType.Designer.cs | 190 ++++++++++++++++++ .../Migrations/20240901192908_AddQuizType.cs | 29 +++ .../Migrations/LocalDbContextModelSnapshot.cs | 3 + FWLAZ_Web/Objects/GameState.cs | 55 +++++ FWLAZ_Web/Objects/QuestionAnswer.cs | 23 +++ FWLAZ_Web/Objects/QuestionGroupSelect.cs | 17 ++ FWLAZ_Web/Objects/QuestionGroupSummary.cs | 19 ++ FWLAZ_Web/Objects/SessionState.cs | 34 ++++ FWLAZ_Web/Program.cs | 4 + FWLAZ_Web/Properties/launchSettings.json | 75 ++++--- FWLAZ_Web/appsettings.json | 2 +- 29 files changed, 828 insertions(+), 148 deletions(-) create mode 100644 FWLAZ_Web/.dockerignore delete mode 100644 FWLAZ_Web/Components/Pages/Counter.razor create mode 100644 FWLAZ_Web/Components/Pages/QuizRun.razor delete mode 100644 FWLAZ_Web/Components/Pages/Weather.razor create mode 100644 FWLAZ_Web/Dockerfile create mode 100644 FWLAZ_Web/Migrations/20240901192908_AddQuizType.Designer.cs create mode 100644 FWLAZ_Web/Migrations/20240901192908_AddQuizType.cs create mode 100644 FWLAZ_Web/Objects/GameState.cs create mode 100644 FWLAZ_Web/Objects/QuestionAnswer.cs create mode 100644 FWLAZ_Web/Objects/QuestionGroupSelect.cs create mode 100644 FWLAZ_Web/Objects/QuestionGroupSummary.cs create mode 100644 FWLAZ_Web/Objects/SessionState.cs diff --git a/FWLAZ.sln b/FWLAZ.sln index 5154577..4a8ab4a 100644 --- a/FWLAZ.sln +++ b/FWLAZ.sln @@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWLAZ", "FWLAZ\FWLAZ.csproj EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWLAZData", "FWLAZData\FWLAZData.csproj", "{EE7C2DA0-9B00-4A6E-9333-DB3FF265AB61}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FWLAZ_Web", "FWLAZ_Web\FWLAZ_Web.csproj", "{8109C64A-C8EC-48E2-9356-E6599ABB0B6F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWLAZ_Web", "FWLAZ_Web\FWLAZ_Web.csproj", "{8109C64A-C8EC-48E2-9356-E6599ABB0B6F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/FWLAZ_Web/.dockerignore b/FWLAZ_Web/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/FWLAZ_Web/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/FWLAZ_Web/Components/Layout/MainLayout.razor b/FWLAZ_Web/Components/Layout/MainLayout.razor index 5a24bb1..ceb96dc 100644 --- a/FWLAZ_Web/Components/Layout/MainLayout.razor +++ b/FWLAZ_Web/Components/Layout/MainLayout.razor @@ -7,7 +7,7 @@
- About + @* About *@
diff --git a/FWLAZ_Web/Components/Layout/NavMenu.razor b/FWLAZ_Web/Components/Layout/NavMenu.razor index 2cf31ca..c2cea05 100644 --- a/FWLAZ_Web/Components/Layout/NavMenu.razor +++ b/FWLAZ_Web/Components/Layout/NavMenu.razor @@ -1,6 +1,6 @@  @@ -10,24 +10,12 @@ diff --git a/FWLAZ_Web/Components/Pages/Counter.razor b/FWLAZ_Web/Components/Pages/Counter.razor deleted file mode 100644 index ef23cb3..0000000 --- a/FWLAZ_Web/Components/Pages/Counter.razor +++ /dev/null @@ -1,18 +0,0 @@ -@page "/counter" - -Counter - -

Counter

- -

Current count: @currentCount

- - - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/FWLAZ_Web/Components/Pages/Home.razor b/FWLAZ_Web/Components/Pages/Home.razor index 9001e0b..ce80dc4 100644 --- a/FWLAZ_Web/Components/Pages/Home.razor +++ b/FWLAZ_Web/Components/Pages/Home.razor @@ -1,7 +1,160 @@ @page "/" +@using FWLAZ_Web.Data +@using FWLAZ_Web.Objects +@using Microsoft.EntityFrameworkCore +@inject Blazored.LocalStorage.ILocalStorageService localStorage; +@inject IDbContextFactory DbFactory; +@inject SessionState CurrentSession; +@inject NavigationManager nav; Home -

Hello, world!

+@if (CurrentSession.LoadGame != null) +{ +

Vorherigen Lauf fortsetzen

+
@CurrentSession.LoadGame.Title
+ + +} -Welcome to your new app. + +

Neues Quiz beginnen

+ +
+ @foreach (Quiz quiz in QuizList) + { + + } +
+ + + + @SelectedQuiz?.Name; + + + @if (SelectedQuiz != null && NewGame != null) + { +@*
+ + +
*@ + +
+ + +
+ +
+ + +
+ +
+ + +
+ } + +
+ @if (NewGame != null) + { + + } + +
+
+ +@code { + public List QuizList { get; set; } = new(); + private LocalDbContext? DbContext; + private ModalComponent modal = null!; + private GameState? NewGame; + private GameState? LoadGame; + private Quiz? SelectedQuiz; + private static Random rng = new(); + + private int _QuestionGroupId; + public int QuestionGroupId + { + get + { + return _QuestionGroupId; + } + set + { + _QuestionGroupId = value; + QuestionCountMax = SelectedQuiz?.Questiongroups.Single(qg => qg.Id == value).Questions.Count ?? 0; + QuestionCountValue = QuestionCountMax; + // QuestionCountValue = Math.Min(QuestionCountMax, QuestionCountValue); + } + } + public int QuestionCountMax { get; set; } = 1; + public int QuestionCountValue { get; set; } = 20; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) await CurrentSession.LoadAsync(); + } + + protected override async Task OnInitializedAsync() + { + await FillListAsync(); + } + + private async Task FillListAsync() + { + DbContext ??= await DbFactory.CreateDbContextAsync(); + if (DbContext == null) return; + QuizList = await DbContext.Quiz.OrderBy(q => q.Name).ToListAsync(); + } + + private async Task OpenDialog(Quiz itm) + { + DbContext ??= await DbFactory.CreateDbContextAsync(); + if (DbContext == null) return; + SelectedQuiz = DbContext.Quiz.Include(q => q.Questiongroups).ThenInclude(qg => qg.Questions).Single(q => q.Id == itm.Id); + QuestionGroupId = SelectedQuiz.Questiongroups.First().Id; + NewGame = new GameState(SelectedQuiz); + await InvokeAsync(StateHasChanged); + await modal.ShowAsync(); + } + + private async Task CloseDialog() + { + NewGame = null; + await modal.CloseAsync(); + } + + private async Task StartQuiz(GameState state) + { + if (state.Questions == null) + { + // Bei neuem Spiel - Frageliste füllen + DbContext ??= await DbFactory.CreateDbContextAsync(); + if (DbContext == null || state == null) return; + state.Questions = DbContext.QuestionGroup + .Include(qg => qg.Questions) + .ThenInclude(q => q.Answers) + .Single(qg => qg.Id == QuestionGroupId).Questions.OrderBy(_ => rng.Next()).ToList(); + state.Questions = state.Questions.Take(QuestionCountValue).ToList(); + CurrentSession.LoadGame = state; + await CurrentSession.SaveAsync(); + } + if (state != null) nav.NavigateTo($"quizstart"); + await CloseDialog(); + } + private async Task DeleteGameState(MouseEventArgs e) + { + CurrentSession.LoadGame = null; + await localStorage.RemoveItemAsync(GameState.STORAGEKEY); + + } +} diff --git a/FWLAZ_Web/Components/Pages/QuizRun.razor b/FWLAZ_Web/Components/Pages/QuizRun.razor new file mode 100644 index 0000000..55b7336 --- /dev/null +++ b/FWLAZ_Web/Components/Pages/QuizRun.razor @@ -0,0 +1,140 @@ +@page "/quizstart" +@using Blazored.LocalStorage; +@using FWLAZ_Web.Data +@using FWLAZ_Web.Objects +@inject ILocalStorageService localStorage; +@inject NavigationManager nav; +@inject SessionState CurrentSession; + + +

@CurrentSession.LoadGame?.Title

+ +@if (CurrentSession.LoadGame?.CurrentQuestion != null) +{ +

Frage @(CurrentSession.LoadGame.QuestionAnswers?.Count + 1) / @(CurrentSession.LoadGame.QuestionAnswers?.Count + @CurrentSession.LoadGame.Questions?.Count)

+

@CurrentSession.LoadGame.CurrentQuestion?.Text [Nr. @CurrentSession.LoadGame.CurrentQuestion?.Number]

+
+ @if (CurrentSession.LoadGame?.CurrentQuestion != null) + { + @foreach (Answer aw in CurrentSession.LoadGame.CurrentQuestion.Answers) + { +
+ @if (!ShowSolution) + { + + } + else + { + if (SelectedAnswers.Contains(aw) && aw.IsCorrect) + { + + } + else if (SelectedAnswers.Contains(aw) && !aw.IsCorrect) + { + + } + else if (aw.IsCorrect) + { + + } + else if (!aw.IsCorrect) + { + + } + } +
+ } + } +
+
+ +
+} +else +{ +

Ergebnis

+
+ + + + + + + + + + + + + + + + + +
Fragen Gesamt@(CurrentSession.LoadGame.QuestionAnswers.Count)
Korrekt beantwortet@(CurrentSession.LoadGame.QuestionAnswers.Where(qa => qa.IsCorrect).Count())
Falsch beantwortet@(CurrentSession.LoadGame.QuestionAnswers.Where(qa => !qa.IsCorrect).Count())
+
+} + +@code { + private string PrevURL = "/"; + + private bool ShowSolution { get; set; } + private List SelectedAnswers { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + if (CurrentSession.LoadGame == null) + { + nav.NavigateTo(PrevURL); + return; + } + if (CurrentSession.LoadGame.QuestionAnswers == null) CurrentSession.LoadGame.QuestionAnswers = new(); + await InvokeAsync(StateHasChanged); + } + + private void ToggleAnswer(Answer aw) + { + if (CurrentSession.LoadGame == null) return; + if (CurrentSession.LoadGame.QuizIsMultipleChoice) + { + if (SelectedAnswers.Contains(aw)) + { + SelectedAnswers.Remove(aw); + } + else + { + SelectedAnswers.Add(aw); + } + } + else + { + SelectedAnswers.Clear(); + SelectedAnswers.Add(aw); + } + + } + private async Task NextQuestion(MouseEventArgs e) + { + if (ShowSolution == false) + { + ShowSolution = true; + } + else + { + ShowSolution = false; + if (CurrentSession.LoadGame == null) return; + if (CurrentSession.LoadGame.QuestionAnswers == null) CurrentSession.LoadGame.QuestionAnswers = new(); + if (CurrentSession.LoadGame.CurrentQuestion != null) + { + CurrentSession.LoadGame.QuestionAnswers.Add(new(CurrentSession.LoadGame.CurrentQuestion) + { + GivenAnswers = SelectedAnswers, + }); + } + SelectedAnswers.Clear(); + CurrentSession.LoadGame.NextQuestion(); + await CurrentSession.SaveAsync(); + } + + } +} diff --git a/FWLAZ_Web/Components/Pages/Settings/QuizEdit.razor b/FWLAZ_Web/Components/Pages/Settings/QuizEdit.razor index 52d299d..c705b3a 100644 --- a/FWLAZ_Web/Components/Pages/Settings/QuizEdit.razor +++ b/FWLAZ_Web/Components/Pages/Settings/QuizEdit.razor @@ -12,6 +12,10 @@ +
+ + +

Question Groups

diff --git a/FWLAZ_Web/Components/Pages/Settings/QuizQuestions.razor b/FWLAZ_Web/Components/Pages/Settings/QuizQuestions.razor index afa218f..cf2c7c6 100644 --- a/FWLAZ_Web/Components/Pages/Settings/QuizQuestions.razor +++ b/FWLAZ_Web/Components/Pages/Settings/QuizQuestions.razor @@ -1,5 +1,6 @@ @page "/settings/quizzes/questions/{id}" @using FWLAZ_Web.Data +@using FWLAZ_Web.Objects @using Microsoft.EntityFrameworkCore; @inject IDbContextFactory DbFactory; @inject NavigationManager nav; @@ -32,7 +33,14 @@ {
- +@* @if (SelectedItem.IsMultipleChoice) + { *@ + +@* } + else + { + + } *@
@@ -156,9 +164,11 @@ private void PrepareNewQuestion() { NewQuestion = new(); - if (SelectedItem.Questions.Count > 0) { + if (SelectedItem.Questions.Count > 0) + { NewQuestion.Number = SelectedItem.Questions.Max(q => q.Number) + 1; - } else + } + else { NewQuestion.Number = 1; } @@ -169,6 +179,12 @@ } } + private void SetSingleChoiceAnswer(Question question, Answer answer) + { + question.Answers.ForEach(aw => aw.IsCorrect = false); + answer.IsCorrect = true; + } + private async Task AddQuestion_Enter(KeyboardEventArgs e) { if (e.Code == "Enter" || e.Code == "NumpadEnter") await AddQuestion(new MouseEventArgs()); @@ -243,17 +259,6 @@ } } - public class QuestionGroupSelect - { - public bool Checked { get; set; } - - public QuestionGroup questionGroup { get; } - - public QuestionGroupSelect(QuestionGroup questionGroup) - { - this.questionGroup = questionGroup; - } - } private async Task SaveQuiz(MouseEventArgs e) { foreach (Question question in SelectedItem.Questions) diff --git a/FWLAZ_Web/Components/Pages/Settings/Quizzes.razor b/FWLAZ_Web/Components/Pages/Settings/Quizzes.razor index a72f105..853433f 100644 --- a/FWLAZ_Web/Components/Pages/Settings/Quizzes.razor +++ b/FWLAZ_Web/Components/Pages/Settings/Quizzes.razor @@ -8,7 +8,7 @@ Neues Quiz
- +
@foreach (Quiz q in QuizList) { diff --git a/FWLAZ_Web/Components/Pages/Weather.razor b/FWLAZ_Web/Components/Pages/Weather.razor deleted file mode 100644 index 8eca4cc..0000000 --- a/FWLAZ_Web/Components/Pages/Weather.razor +++ /dev/null @@ -1,63 +0,0 @@ -@page "/weather" - -Weather - -

Weather

- -

This component demonstrates showing data.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ -
- - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - // Simulate asynchronous loading to demonstrate a loading indicator - await Task.Delay(500); - - var startDate = DateOnly.FromDateTime(DateTime.Now); - var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = summaries[Random.Shared.Next(summaries.Length)] - }).ToArray(); - } - - private class WeatherForecast - { - public DateOnly Date { get; set; } - public int TemperatureC { get; set; } - public string? Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } -} diff --git a/FWLAZ_Web/Data/Answer.cs b/FWLAZ_Web/Data/Answer.cs index 6b6c37e..346fe60 100644 --- a/FWLAZ_Web/Data/Answer.cs +++ b/FWLAZ_Web/Data/Answer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Tasks; namespace FWLAZ_Web.Data @@ -13,6 +14,7 @@ namespace FWLAZ_Web.Data public string Text { get; set; } = null!; public bool IsCorrect { get; set; } + [JsonIgnore] public virtual Question Question { get; set; } = null!; } diff --git a/FWLAZ_Web/Data/Question.cs b/FWLAZ_Web/Data/Question.cs index 299508e..cf7a5c4 100644 --- a/FWLAZ_Web/Data/Question.cs +++ b/FWLAZ_Web/Data/Question.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Tasks; namespace FWLAZ_Web.Data @@ -12,7 +13,11 @@ namespace FWLAZ_Web.Data public int Number { get; set; } public string Text { get; set; } = null!; public List Answers { get; set; } = new List(); + + [JsonIgnore] public virtual List QuestionGroups { get; set; } = new(); + + [JsonIgnore] public virtual Quiz Quiz { get; set; } = null!; } } diff --git a/FWLAZ_Web/Data/QuestionGroup.cs b/FWLAZ_Web/Data/QuestionGroup.cs index 959a459..587235e 100644 --- a/FWLAZ_Web/Data/QuestionGroup.cs +++ b/FWLAZ_Web/Data/QuestionGroup.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Tasks; namespace FWLAZ_Web.Data @@ -15,6 +16,8 @@ namespace FWLAZ_Web.Data public string Name { get; set; } = null!; public int Order { get; set; } public List Questions { get; set; } = new List(); + + [JsonIgnore] public virtual Quiz Quiz { get; set; } = null!; public QuestionGroup() { } diff --git a/FWLAZ_Web/Data/Quiz.cs b/FWLAZ_Web/Data/Quiz.cs index a8659ee..3198c81 100644 --- a/FWLAZ_Web/Data/Quiz.cs +++ b/FWLAZ_Web/Data/Quiz.cs @@ -15,6 +15,7 @@ namespace FWLAZ_Web.Data { public int Id { get; set; } public string Name { get; set; } = null!; + public bool IsMultipleChoice { get; set; } public List Questions { get; set; } = new(); public List Questiongroups { get; set; } = new(); diff --git a/FWLAZ_Web/Data/quiz.db b/FWLAZ_Web/Data/quiz.db index 332cb5621b817a293135a3627bfaa79dc5d784a5..6a7a24407f554dc18c0354b8dcf99d3b7e4fe12f 100644 GIT binary patch delta 982 zcmZ{iUuYaf9LHyF_ipy~Zf|lXmnc!QCKVHN-d!%mE%q|v**w(M3(T$1rCuJ4}i@S zy<*KtwH(ok<%{pl^q3Q#q3GGlGBuRUyC zZiifm-0ldc>CNlU@KvI8TT?fNAWC+z=qqme(#_K%Ye$|W<$`jX_25q}h|1U@4%3}m zbLP@0MBVF#oFirS8mwld?RQFRc=-o-bvMicGYBM{`Hce%B34B80Zk0k86CsX54~G4q!$ zcu>$tzNC{x)}9K%9TB>0L53&1Vt+G(KuhsLY^EQ9J*GVbA2NR%f(Pt}?}w)#DmD+X z8Y%O~Vfa+na+ZCff@b8Omq)`fIhWO@vbGY2mpI2W1|Rn#|55ggt&o{czy JCE(zze*>__E2;nh delta 667 zcmXw!O-vI(9L49~EuHSdGOdC=fT4=v%WkRmU}`WGNhlB%suDya73!8xTe59KG@7&= z44ObBb6BDm4|>o*B95}q2x#zt7c}0)5IFdeaL^dz%@}7*m}D|9zyEu0W-=L^$>6&s zP6;7Y%J{;SDPMKxX7ma^q~OT(TA_P(klYUGxF$r#@>3{R=j_ zYA>KZOFn&T^|HnM+hEP^%EPQh&RHaZj3Kf`X2=-Z=ethmSlJEErV#l`=E)RicZju2 zXaG81Rzi*1C?`+IWShj+f}aV9l(io6SozyZ#Bf@5T7*jAXEN2Fh=;U9dRUanIub|- zkw+v&LS&se9Y+WaMY{6*AfT)CeZ*J6;}x!RJmu%AwAfT!?A{TQtFhx`{e@?jaT_3J zfBH(mu~Rk#*=G8Vot2eDkE+FnRvt$C)F7*U9Xm^mAy$=pRy z=vr B$DjZJ diff --git a/FWLAZ_Web/Dockerfile b/FWLAZ_Web/Dockerfile new file mode 100644 index 0000000..cc5721a --- /dev/null +++ b/FWLAZ_Web/Dockerfile @@ -0,0 +1,30 @@ +# Unter https://aka.ms/customizecontainer erfahren Sie, wie Sie Ihren Debugcontainer anpassen und wie Visual Studio dieses Dockerfile verwendet, um Ihre Images für ein schnelleres Debuggen zu erstellen. + +# Diese Stufe wird verwendet, wenn sie von VS im Schnellmodus ausgeführt wird (Standardeinstellung für Debugkonfiguration). +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + + +# Diese Stufe wird zum Erstellen des Dienstprojekts verwendet. +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["FWLAZ_Web.csproj", "."] +RUN dotnet restore "./FWLAZ_Web.csproj" +COPY . . +WORKDIR "/src/." +RUN dotnet build "./FWLAZ_Web.csproj" -c $BUILD_CONFIGURATION -o /app/build + +# Diese Stufe wird verwendet, um das Dienstprojekt zu veröffentlichen, das in die letzte Phase kopiert werden soll. +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./FWLAZ_Web.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +# Diese Stufe wird in der Produktion oder bei Ausführung von VS im regulären Modus verwendet (Standard, wenn die Debugkonfiguration nicht verwendet wird). +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "FWLAZ_Web.dll"] \ No newline at end of file diff --git a/FWLAZ_Web/FWLAZ_Web.csproj b/FWLAZ_Web/FWLAZ_Web.csproj index d1b3da5..0080564 100644 --- a/FWLAZ_Web/FWLAZ_Web.csproj +++ b/FWLAZ_Web/FWLAZ_Web.csproj @@ -4,9 +4,23 @@ net8.0 enable enable + 974b7a0a-88b5-41b5-9ec8-75d75421e174 + Linux + . + + + + + + PreserveNewest + + + + + all @@ -17,6 +31,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/FWLAZ_Web/Migrations/20240901192908_AddQuizType.Designer.cs b/FWLAZ_Web/Migrations/20240901192908_AddQuizType.Designer.cs new file mode 100644 index 0000000..f0b0ff7 --- /dev/null +++ b/FWLAZ_Web/Migrations/20240901192908_AddQuizType.Designer.cs @@ -0,0 +1,190 @@ +// +using FWLAZ_Web.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FWLAZ_Web.Migrations +{ + [DbContext(typeof(LocalDbContext))] + [Migration("20240901192908_AddQuizType")] + partial class AddQuizType + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.8"); + + modelBuilder.Entity("FWLAZ_Web.Data.Answer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsCorrect") + .HasColumnType("INTEGER"); + + b.Property("Position") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("QuestionId") + .HasColumnType("INTEGER"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("QuestionId"); + + b.ToTable("Answers"); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.Question", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("QuizId") + .HasColumnType("INTEGER"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("QuizId"); + + b.ToTable("Question"); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.QuestionGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("QuizId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("QuizId"); + + b.ToTable("QuestionGroup"); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.Quiz", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsMultipleChoice") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Quiz"); + }); + + modelBuilder.Entity("QuestionQuestionGroup", b => + { + b.Property("QuestionGroupsId") + .HasColumnType("INTEGER"); + + b.Property("QuestionsId") + .HasColumnType("INTEGER"); + + b.HasKey("QuestionGroupsId", "QuestionsId"); + + b.HasIndex("QuestionsId"); + + b.ToTable("QuestionQuestionGroup"); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.Answer", b => + { + b.HasOne("FWLAZ_Web.Data.Question", "Question") + .WithMany("Answers") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.Question", b => + { + b.HasOne("FWLAZ_Web.Data.Quiz", "Quiz") + .WithMany("Questions") + .HasForeignKey("QuizId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Quiz"); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.QuestionGroup", b => + { + b.HasOne("FWLAZ_Web.Data.Quiz", "Quiz") + .WithMany("Questiongroups") + .HasForeignKey("QuizId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Quiz"); + }); + + modelBuilder.Entity("QuestionQuestionGroup", b => + { + b.HasOne("FWLAZ_Web.Data.QuestionGroup", null) + .WithMany() + .HasForeignKey("QuestionGroupsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FWLAZ_Web.Data.Question", null) + .WithMany() + .HasForeignKey("QuestionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.Question", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("FWLAZ_Web.Data.Quiz", b => + { + b.Navigation("Questiongroups"); + + b.Navigation("Questions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FWLAZ_Web/Migrations/20240901192908_AddQuizType.cs b/FWLAZ_Web/Migrations/20240901192908_AddQuizType.cs new file mode 100644 index 0000000..880c08d --- /dev/null +++ b/FWLAZ_Web/Migrations/20240901192908_AddQuizType.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FWLAZ_Web.Migrations +{ + /// + public partial class AddQuizType : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsMultipleChoice", + table: "Quiz", + type: "INTEGER", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsMultipleChoice", + table: "Quiz"); + } + } +} diff --git a/FWLAZ_Web/Migrations/LocalDbContextModelSnapshot.cs b/FWLAZ_Web/Migrations/LocalDbContextModelSnapshot.cs index ccf63b0..9db6957 100644 --- a/FWLAZ_Web/Migrations/LocalDbContextModelSnapshot.cs +++ b/FWLAZ_Web/Migrations/LocalDbContextModelSnapshot.cs @@ -95,6 +95,9 @@ namespace FWLAZ_Web.Migrations .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); + b.Property("IsMultipleChoice") + .HasColumnType("INTEGER"); + b.Property("Name") .IsRequired() .HasColumnType("TEXT"); diff --git a/FWLAZ_Web/Objects/GameState.cs b/FWLAZ_Web/Objects/GameState.cs new file mode 100644 index 0000000..9688ed1 --- /dev/null +++ b/FWLAZ_Web/Objects/GameState.cs @@ -0,0 +1,55 @@ +using FWLAZ_Web.Data; +using Blazored.LocalStorage; +using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Components; + +namespace FWLAZ_Web.Objects +{ + public class GameState + { + public enum QuizMode + { + Practice, + Tournament + } + + public const string STORAGEKEY = "QuizSetting"; + public QuizMode Mode { get; set; } = QuizMode.Practice; + + public int QuizId { get; } + public bool QuizIsMultipleChoice { get; set; } + public string? Title { get; set; } = null!; + + + public string? Username { get; set; } + public List? Questions { get; set; } + + public List? QuestionAnswers { get; set; } + public Question? CurrentQuestion + { + get + { + return Questions?.FirstOrDefault(); + } + } + + public GameState() { } + + public GameState(Quiz quiz) + { + QuizId = quiz.Id; + QuizIsMultipleChoice = quiz.IsMultipleChoice; + Title = quiz.Name; + } + + public bool NextQuestion() + { + if (Questions == null || Questions.Count == 0) + { + return false; + } + Questions.Remove(Questions[0]); + return true; + } + } +} diff --git a/FWLAZ_Web/Objects/QuestionAnswer.cs b/FWLAZ_Web/Objects/QuestionAnswer.cs new file mode 100644 index 0000000..bfb93cb --- /dev/null +++ b/FWLAZ_Web/Objects/QuestionAnswer.cs @@ -0,0 +1,23 @@ +using FWLAZ_Web.Data; + +namespace FWLAZ_Web.Objects +{ + public class QuestionAnswer + { + public Question question { get; set; } + public List GivenAnswers { get; set; } = new(); + + public bool IsCorrect + { + get + { + return GivenAnswers.All(a => a.IsCorrect); + } + } + + public QuestionAnswer(Question question) + { + this.question = question; + } + } +} diff --git a/FWLAZ_Web/Objects/QuestionGroupSelect.cs b/FWLAZ_Web/Objects/QuestionGroupSelect.cs new file mode 100644 index 0000000..6bfbf19 --- /dev/null +++ b/FWLAZ_Web/Objects/QuestionGroupSelect.cs @@ -0,0 +1,17 @@ +using FWLAZ_Web.Data; + +namespace FWLAZ_Web.Objects +{ + public class QuestionGroupSelect + { + public bool Checked { get; set; } + + public QuestionGroup questionGroup { get; } + + public QuestionGroupSelect(QuestionGroup questionGroup) + { + this.questionGroup = questionGroup; + } + } + +} diff --git a/FWLAZ_Web/Objects/QuestionGroupSummary.cs b/FWLAZ_Web/Objects/QuestionGroupSummary.cs new file mode 100644 index 0000000..db1772d --- /dev/null +++ b/FWLAZ_Web/Objects/QuestionGroupSummary.cs @@ -0,0 +1,19 @@ +using FWLAZ_Web.Data; + +namespace FWLAZ_Web.Objects +{ + public class QuestionGroupSummary + { + public int QuestionGroupID { get; set; } + public string QuestionGroupName { get; set; } + public int QuestionCount { get; set; } + + public QuestionGroupSummary(QuestionGroup questionGroup) + { + QuestionGroupID = questionGroup.Id; + QuestionGroupName = questionGroup.Name; + QuestionCount = questionGroup.Questions.Count; + } + + } +} diff --git a/FWLAZ_Web/Objects/SessionState.cs b/FWLAZ_Web/Objects/SessionState.cs new file mode 100644 index 0000000..f73464d --- /dev/null +++ b/FWLAZ_Web/Objects/SessionState.cs @@ -0,0 +1,34 @@ +using Blazored.LocalStorage; +namespace FWLAZ_Web.Objects +{ + + + public class SessionState + { + public GameState? LoadGame { get; set; } + + private ILocalStorageService localStorage; + public SessionState(ILocalStorageService localStorage) + { + this.localStorage = localStorage; + } + + + public async Task LoadAsync() + { + try + { + LoadGame = await localStorage.GetItemAsync(nameof(LoadGame)); + } + catch (Exception ex) + { + Console.WriteLine($"Error loading state: {ex.Message}"); + } + } + + public async Task SaveAsync() + { + await localStorage.SetItemAsync(nameof(LoadGame), LoadGame); + } + } +} diff --git a/FWLAZ_Web/Program.cs b/FWLAZ_Web/Program.cs index b0b5739..030f52d 100644 --- a/FWLAZ_Web/Program.cs +++ b/FWLAZ_Web/Program.cs @@ -1,6 +1,8 @@ using FWLAZ_Web.Components; using FWLAZ_Web.Data; using Microsoft.EntityFrameworkCore; +using Blazored.LocalStorage; +using FWLAZ_Web.Objects; var builder = WebApplication.CreateBuilder(args); var connectionstring = builder.Configuration.GetConnectionString("QuizDB"); @@ -9,6 +11,8 @@ var connectionstring = builder.Configuration.GetConnectionString("QuizDB"); builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); builder.Services.AddDbContextFactory(options => options.UseSqlite(connectionstring)); +builder.Services.AddBlazoredLocalStorage(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/FWLAZ_Web/Properties/launchSettings.json b/FWLAZ_Web/Properties/launchSettings.json index 27a1c72..742acb1 100644 --- a/FWLAZ_Web/Properties/launchSettings.json +++ b/FWLAZ_Web/Properties/launchSettings.json @@ -1,38 +1,49 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:44728", - "sslPort": 44312 + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5018" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7281;http://localhost:5018" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" } }, - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "http://localhost:5018", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "environmentVariables": { + "ASPNETCORE_HTTPS_PORTS": "8081", + "ASPNETCORE_HTTP_PORTS": "8080" }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:7281;http://localhost:5018", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:44728", + "sslPort": 44312 } } +} \ No newline at end of file diff --git a/FWLAZ_Web/appsettings.json b/FWLAZ_Web/appsettings.json index a09ca2b..5967930 100644 --- a/FWLAZ_Web/appsettings.json +++ b/FWLAZ_Web/appsettings.json @@ -6,7 +6,7 @@ } }, "ConnectionStrings": { - "QuizDB": "Data Source=Data\\quiz.db" + "QuizDB": "Data Source=Data/quiz.db" }, "AllowedHosts": "*" }