### Get Product Packages Source: https://context7.com/usix79/openquiz/llms.txt Retrieves a list of all available product packages. ```APIDOC ## GET /api/packages/products ### Description Retrieves a list of all available product packages. ### Method GET ### Endpoint /api/packages/products ### Response #### Success Response (200) - **PackageId** (integer) - The ID of the package. - **Name** (string) - The name of the package. #### Response Example ```json [ { "PackageId": 101, "Name": "Science Facts" }, { "PackageId": 102, "Name": "History Trivia" } ] ``` ``` -------------------------------- ### F# Implement OpenQuiz Package Creation and Update API Calls Source: https://context7.com/usix79/openquiz/llms.txt Demonstrates how to interact with the `IMainApi` interface in F# to create and update question packages. The examples show the structure of the request arguments for `createPackage` and `updateProdPackageCard`, including defining slips with questions and answers. ```fsharp // Create package with questions let! packageResp = mainApi.createPackage { Token = ""; Arg = () } // Update package with question content let! updateResp = mainApi.updateProdPackageCard { Token = "" Arg = { PackageId = 67890 Name = "History Quiz Round 1" Slips = [ Single { Caption = "Question 1" Question = Solid "Who was the first president of the United States?" QuestionMedia = None Answer = OpenAnswer "George Washington" AnswerMedia = None Comment = "First president, served 1789-1797" Points = 10M JeopardyPoints = Some 20M WithChoice = false Seconds = Some 60 EndOfTour = false } Single { Caption = "Question 2" Question = Split [ "Name the capital city" "Name the largest city" "Name the population" ] QuestionMedia = Some { Key = "qw/france-map.jpg"; Type = Image } Answer = ChoiceAnswer [ { Text = "Paris"; IsCorrect = true } { Text = "London"; IsCorrect = false } { Text = "Berlin"; IsCorrect = false } { Text = "Madrid"; IsCorrect = false } ] AnswerMedia = None Comment = "France geography" Points = 15M JeopardyPoints = None WithChoice = true Seconds = Some 45 EndOfTour = true } ] } } ``` -------------------------------- ### Bash Deployment Commands for OpenQuiz Source: https://context7.com/usix79/openquiz/llms.txt Provides essential bash commands for managing the OpenQuiz project's development workflow, client build, and deployment to AWS. These commands automate tasks like dependency restoration, local environment setup, and cloud deployment. ```bash # Development workflow dotnet fsi make.fsx restore dotnet fsi make.fsx devenv dotnet fsi make.fsx run # Build client for production npm run build:client # Deploy to AWS dotnet fsi make.fsx deploy ``` -------------------------------- ### F# Implement OpenQuiz Package Sharing API Call Source: https://context7.com/usix79/openquiz/llms.txt Illustrates how to share a question package with another user using the `sharePackage` function from the `IMainApi` interface in F#. This example shows the required arguments, including the `PackageId` and the `UserId` of the recipient. ```fsharp // Share package with another user let! shareResp = mainApi.sharePackage { Token = "" Arg = {| PackageId = 67890; UserId = "other-user-sub-456" |} } ``` -------------------------------- ### Create and Manage Teams in F# Source: https://context7.com/usix79/openquiz/llms.txt Provides F# code for creating single teams, batch creation of teams, and updating team statuses. It includes type definitions for team management and examples of API calls. ```fsharp // Shared types type TeamStatus = | New | Admitted | Rejected type AdminModels.TeamCard = { TeamId: int TeamName: string TeamStatus: TeamStatus EntryToken: string RegistrationDate: DateTime } // API interface type IAdminApi = { getTeams: REQ -> ARESP createTeam: REQ<{| TeamName: string |}> -> ARESP<{| Record: TeamRecord |}> createTeamBatch: REQ<{| TeamNames: string list |}> -> ARESP getTeamCard: REQ<{| TeamId: int |}> -> ARESP updateTeamCard: REQ -> ARESP changeTeamStatus: REQ<{| TeamId: int; TeamStatus: TeamStatus |}> -> ARESP // ... } // Server implementation - AdminService.fs let createTeam env quiz req = let teamName = req.TeamName.Trim() let creator teamsInQuiz teamId = match Domain.Teams.validateTeamUpdate true teamName teamsInQuiz quiz with | Some txt -> Error txt | None -> Domain.Teams.createNewAdmin teamId teamName quiz Domain.Admitted |> Ok Data2.Teams.getDescriptors env quiz.QuizId |> AR.bind (fun teamsInQuiz -> Data2.Teams.create env quiz.QuizId (creator teamsInQuiz) |> AR.map (fun team -> {| Record = Admin.teamRecord team.Dsc |})) |> AR.side (fun _ -> PublishResults quiz.QuizId |> env.PublisherAgent |> AR.retn) // Create single team let! teamResp = adminApi.createTeam { Token = "" Arg = {| TeamName = "The Quiz Masters" |} } // Create multiple teams at once let! batchResp = adminApi.createTeamBatch { Token = "" Arg = {| TeamNames = [ "Team Alpha" "Team Beta" "Team Gamma" "The Brainiacs" "Quiz Wizards" ] |} } // Change team status (admit/reject) let! statusResp = adminApi.changeTeamStatus { Token = "" Arg = {| TeamId = 101 TeamStatus = Admitted |} } // Expected result: // { Record = { TeamId = 101 // QuizId = 12345 // TeamName = "The Quiz Masters" // Status = Admitted // RegistrationDate = 2025-10-15T14:30:00Z } } ``` -------------------------------- ### F# Environment Configuration with AWS Systems Manager Source: https://context7.com/usix79/openquiz/llms.txt Configures the application environment using AWS Systems Manager Parameter Store. It defines an interface for configuration and builds the environment by fetching values from configuration sections and AWS Parameter Store. This setup is crucial for managing application settings across different environments. ```fsharp // Env.fs - Configuration interfaces type IConfigurer = abstract DynamoTablePrefix: string abstract JwtSecret: string abstract UserPoolClientId: string abstract LoginUrl: string abstract AppUrl: string abstract BucketName: string abstract BucketUrl: string abstract AppSyncCfg: AppSyncConfig // Server.fs - Build environment from AWS Parameter Store let buildEnvironment envName logger (cfg: IConfiguration) = let overrides = cfg.GetSection("Overrides") let getValue name = match overrides.[name] with | null -> cfg.[name] | txt -> txt let configurer = { new Env.IConfigurer with member _.DynamoTablePrefix = match overrides.["DynamoPrefix"] with | null -> "OpenQuiz-" + envName | txt -> txt member _.JwtSecret = getValue "JwtSecret" member _.UserPoolClientId = getValue "UserPoolClientId" member _.LoginUrl = getValue "LoginUrl" member _.AppUrl = getValue "AppUrl" member _.BucketName = getValue "BucketName" member _.BucketUrl = getValue "BucketUrl" member _.AppSyncCfg = { Endpoint = getValue "AppSyncEndpoint" Region = getValue "AppSyncRegion" ApiKey = getValue "AppSyncApiKey" } } // Create environment with agents let publisherAgent = Agents.publisherAgent configurer logger let answersAgent = Agents.answersAgent configurer logger { Configurer = configurer Logger = logger PublisherAgent = publisherAgent AnswersAgent = answersAgent } // Server.fs - Routing setup with Fable.Remoting let appRouter env = choose [ route "/ping" >=> text "root pong" route "/api/login" >=> loginHandler env route "/api/ping" >=> text "api pong" apiHandler <| SecurityService.api env apiHandler <| MainService.api env apiHandler <| AdminService.api env apiHandler <| TeamService.api env apiHandler <| RegService.api env apiHandler <| AudService.api env ] let apiHandler api = Remoting.createApi() |> Remoting.withRouteBuilder (Infra.routeBuilder "") |> Remoting.fromContext api |> Remoting.withErrorHandler errorHandler |> Remoting.buildHttpHandler // Route pattern: /api/{ServiceName}/{MethodName} // Examples: // POST /api/OpenQuiz.IMainApi/createQuiz // POST /api/OpenQuiz.IAdminApi/getAnswers // POST /api/OpenQuiz.ITeamApi/answers ``` -------------------------------- ### Quiz Lifecycle Management in F# Source: https://context7.com/usix79/openquiz/llms.txt Demonstrates F# code for controlling the lifecycle of a quiz, including changing its status, setting question packages, starting countdowns, and progressing through tours and questions. It defines relevant types and API interfaces. ```fsharp // Shared types type QuizStatus = | Setup | Live | Finished type TourStatus = | Announcing | Countdown | Settled type TourCard = { Idx: int Name: string Sec: int // seconds for countdown TS: TourStatus Slip: SlipCard ST: DateTime option // start time } type QuizControlCard = { QuizId: int Name: string Status: QuizStatus StreamUrl: string option Tour: TourCard option Package: PackageRecord option } // API interface type IAdminApi = { getQuizCard: REQ -> ARESP changeQuizStatus: REQ<{| QuizStatus: QuizStatus |}> -> ARESP changeStreamUrl: REQ -> ARESP setPackage: REQ<{| PackageId: int option |}> -> ARESP // Question control startCountDown: REQ -> ARESP pauseCountDown: REQ -> ARESP settleTour: REQ -> ARESP nextTour: REQ -> ARESP nextQuestion: REQ -> ARESP nextQuestionPart: REQ -> ARESP showMedia: REQ -> ARESP showQuestion: REQ -> ARESP // ... } // Change quiz status to Live let! liveResp = adminApi.changeQuizStatus { Token = "" Arg = {| QuizStatus = Live |} } // Attach question package to quiz let! packageResp = adminApi.setPackage { Token = "" Arg = {| PackageId = Some 67890 |} } // Start countdown for current question let! countdownResp = adminApi.startCountDown { Token = "" Arg = currentQuizCard } // Server implementation of question progression let nextQuestion env quiz (card: QuizControlCard) req = let logic (quiz: Domain.Quiz) = quiz |> Domain.Quizzes.nextQuestion req.Slip req.QwIdx |> Ok Data2.Quizzes.update env quiz.QuizId logic |> AR.map Admin.quizCard |> AR.side (fun _ -> PublishResults quiz.QuizId |> env.PublisherAgent) // Show question to teams let! showResp = adminApi.showQuestion { Token = "" Arg = currentQuizCard } // Settle current tour (end question, show results) let! settleResp = adminApi.settleTour { Token = "" Arg = () } // Move to next tour let! nextTourResp = adminApi.nextTour { Token = "" Arg = () } // Finish quiz let! finishResp = adminApi.changeQuizStatus { Token = "" Arg = {| QuizStatus = Finished |} } ``` -------------------------------- ### Update Quiz Configuration - F# Source: https://context7.com/usix79/openquiz/llms.txt This F# code snippet demonstrates how to update the configuration of an existing quiz. It includes the shared type definition for `QuizProdCard`, the server-side implementation for updating the quiz details, and a client-side example of how to call the update function with new card information. ```fsharp // Shared types type QuizProdCard = { QuizId: int Name: string StartTime: DateTime option ImgKey: string WelcomeText: string FarewellText: string RegText: string InfoText: string WithPremoderation: bool EventPage: string MixlrCode: string AdminUrl: string RegUrl: string AudUrl: string ResultsUrl: string } // Server implementation let updateProdQuizCard env expert card = let logic (quiz: Domain.Quiz) = Domain.Quizzes.authorize expert.Id quiz.Dsc |> Result.map (fun _ -> let dsc = { quiz.Dsc with Name = card.Name StartTime = card.StartTime ImgKey = card.ImgKey WelcomeText = card.WelcomeText FarewellText = card.FarewellText RegText = card.RegText InfoText = card.InfoText WithPremoderation = card.WithPremoderation EventPage = card.EventPage MixlrCode = if card.MixlrCode.Trim() = "" then None else Some card.MixlrCode } { quiz with Dsc = dsc }) Data2.Quizzes.update env card.QuizId logic |> AR.map (fun quiz -> quiz.Dsc |> Main.quizProdRecord) // Client usage let! resp = mainApi.updateProdQuizCard { Token = "" Arg = { QuizId = 12345 Name = "My Trivia Night" StartTime = Some (DateTime(2025, 11, 15, 19, 0, 0)) ImgKey = "qz/abc123.jpg" WelcomeText = "Welcome to our quiz!" FarewellText = "Thanks for playing!" RegText = "Please register your team" InfoText = "Rules: Be honest and have fun!" WithPremoderation = true EventPage = "https://example.com/event" MixlrCode = "mixlr-embed-code" AdminUrl = "" RegUrl = "" AudUrl = "" ResultsUrl = "" } } ``` -------------------------------- ### Get Product Package Card Source: https://context7.com/usix79/openquiz/llms.txt Retrieves the full details of a specific product package card. ```APIDOC ## GET /api/packages/products/{PackageId} ### Description Retrieves the full details of a specific product package card. ### Method GET ### Endpoint /api/packages/products/{PackageId} ### Parameters #### Path Parameters - **PackageId** (integer) - Required - The ID of the package to retrieve. ### Response #### Success Response (200) - **PackageId** (integer) - The ID of the package. - **Name** (string) - The name of the package. - **Slips** (Array) - The list of slips (questions and answers) in the package. #### Response Example ```json { "PackageId": 67890, "Name": "History Quiz Round 1", "Slips": [ { "Caption": "Question 1", "Question": {"Type": "Solid", "Text": "Who was the first president of the United States?"}, "Answer": {"Type": "OpenAnswer", "Text": "George Washington"}, "Comment": "First president, served 1789-1797", "Points": 10, "JeopardyPoints": 20, "WithChoice": false, "Seconds": 60, "EndOfTour": false } ] } ``` ``` -------------------------------- ### Handling Real-Time Quiz Updates in UI (F#) Source: https://context7.com/usix79/openquiz/llms.txt This F# snippet demonstrates how to use the client-side AppSync subscription to handle real-time quiz updates within a UI component. It dispatches actions to update the application state based on the received `QuizChangedEvent`, such as starting a countdown or showing tour results. ```fsharp // Client usage in Team interface (Team.fs) let subscription = AppSync.subscribe appSyncCfg teamUser.QuizId listenToken (fun event -> // Handle real-time quiz updates match event.T with | Some tour when tour.TS = Countdown -> // Update countdown display dispatch (CountdownStarted tour) | Some tour when tour.TS = Settled -> // Show results dispatch (TourSettled tour) | _ -> // Refresh quiz state dispatch RefreshState) ``` -------------------------------- ### F# Admin Answer Review and Grading API and Logic Source: https://context7.com/usix79/openquiz/llms.txt Provides F# code for an administrator's interface to manage quiz answers. It defines an API for retrieving answer bundles, updating results for submitted answers, and updating results for teams that did not submit an answer. Includes examples for fetching all answers and grading multiple submissions. ```fsharp // Admin API for answer management type IAdminApi = { getAnswers: REQ -> ARESP updateResults: REQ<{| TeamId: int; QwKey: QwKey; Res: decimal option |} list> -> ARESP updateResultsWithoutAnswer: REQ<{| TeamId: int QwKey: QwKey Res: decimal option IsAccepted: bool |} list> -> ARESP // ... } type AnswersBundle = { Teams: TeamAnswersRecord list Tour: TourCard option } type TeamAnswersRecord = { TeamId: int TeamName: string Answer: Answer option Vote: bool option } // Get all answers for current question let! answersResp = adminApi.getAnswers { Token = "" Arg = None // Get all, or Some (start, count) for pagination } // Expected response: // { Teams = [ // { TeamId = 101 // TeamName = "The Quiz Masters" // Answer = Some { Txt = "George Washington" // Jpd = false // RT = 2025-10-25T19:15:30Z // Res = Some 10M // IsA = true // UT = Some 2025-10-25T19:16:00Z } // Vote = Some true } // { TeamId = 102 // TeamName = "Brainiacs" // Answer = Some { Txt = "Washington" // Jpd = true // RT = 2025-10-25T19:15:45Z // Res = Some 20M // IsA = true // UT = None } // Vote = None } // ] // Tour = Some { Idx = 0; Name = "Round 1"; ... } } // Update results for multiple teams let! updateResp = adminApi.updateResults { Token = "" Arg = [ {| TeamId = 101 QwKey = { TourIdx = 0; QwIdx = 0 } Res = Some 10M |} // Full points {| TeamId = 102 QwKey = { TourIdx = 0; QwIdx = 0 } Res = Some 20M |} // Jeopardy bonus {| TeamId = 103 QwKey = { TourIdx = 0; QwIdx = 0 } Res = Some 0M |} // Incorrect ] } // Award points to team that didn't submit answer let! manualResp = adminApi.updateResultsWithoutAnswer { Token = "" Arg = [ {| TeamId = 104 QwKey = { TourIdx = 0; QwIdx = 0 } Res = Some 10M IsAccepted = true |} ] } ``` -------------------------------- ### F# DynamoDB Expert Data Access with AsyncResult Source: https://context7.com/usix79/openquiz/llms.txt This F# module, Data2.Experts, handles DynamoDB interactions for expert data. It defines functions to get, update, and create expert records asynchronously using the AsyncResult pattern. These functions interact with a `DataLibrary` for the actual database operations and `Expert` type for data representation. ```fsharp module Data2 = module Experts = let get env expertId : ARES = async { let! result = DataLibrary.getItem env.TableName expertId match result with | Ok attrs -> return Expert.fromAttrs attrs |> Ok | Error e -> return Error e } let update env expertId logic : ARES = async { let! current = get env expertId match current with | Ok expert -> match logic expert with | Ok updated -> let attrs = Expert.toAttrs updated let! writeResult = DataLibrary.putItem env.TableName expertId attrs match writeResult with | Ok _ -> return Ok updated | Error e -> return Error e | Error e -> return Error e | Error e -> return Error e } let create env creator : ARES = async { let expertId = Guid.NewGuid().ToString() let expert = creator expertId let attrs = Expert.toAttrs expert let! result = DataLibrary.putItem env.TableName expertId attrs match result with | Ok _ -> return Ok expert | Error e -> return Error e } ``` -------------------------------- ### PUT /api/quizzes/{quizId}/card Source: https://context7.com/usix79/openquiz/llms.txt Updates the configuration of an existing quiz. This endpoint allows modification of quiz details such as name, start time, and text content. ```APIDOC ## PUT /api/quizzes/{quizId}/card ### Description Updates the configuration for a specific quiz identified by its ID. Allows modification of various quiz settings. ### Method PUT ### Endpoint /api/quizzes/{quizId}/card ### Parameters #### Path Parameters - **quizId** (integer) - Required - The ID of the quiz to update. #### Request Body - **cardUpdate** (object) - Required - Contains the updated quiz card information. - **QuizId** (integer) - Required - The ID of the quiz being updated. - **Name** (string) - Optional - The new name for the quiz. - **StartTime** (string or null) - Optional - The new scheduled start time for the quiz (ISO 8601 format). - **ImgKey** (string) - Optional - The new image key for the quiz. - **WelcomeText** (string) - Optional - The updated welcome message. - **FarewellText** (string) - Optional - The updated farewell message. - **RegText** (string) - Optional - The updated registration text. - **InfoText** (string) - Optional - The updated information text. - **WithPremoderation** (boolean) - Optional - Whether to enable or disable pre-moderation. - **EventPage** (string) - Optional - The URL for the event page. - **MixlrCode** (string) - Optional - The updated Mixlr embed code. An empty string will clear the code. ### Request Example ```json { "QuizId": 12345, "Name": "My Trivia Night", "StartTime": "2025-11-15T19:00:00Z", "ImgKey": "qz/abc123.jpg", "WelcomeText": "Welcome to our quiz!", "FarewellText": "Thanks for playing!", "RegText": "Please register your team", "InfoText": "Rules: Be honest and have fun!", "WithPremoderation": true, "EventPage": "https://example.com/event", "MixlrCode": "mixlr-embed-code" } ``` ### Response #### Success Response (200) - **Record** (object) - The updated quiz record details. - **QuizId** (integer) - The unique identifier for the quiz. - **Producer** (string) - The ID of the quiz producer. - **Name** (string) - The name of the quiz. - **Status** (string) - The current status of the quiz. - **StartTime** (string or null) - The scheduled start time of the quiz. - **ImgKey** (string) - The key for the quiz image. - **WelcomeText** (string) - Welcome message for participants. - **FarewellText** (string) - Farewell message for participants. - **RegText** (string) - Registration text. - **InfoText** (string) - Information text about the quiz. - **WithPremoderation** (boolean) - Whether pre-moderation is enabled. - **EventPage** (string) - URL for the event page. - **MixlrCode** (string or null) - Mixlr embed code. - **PackageId** (integer or null) - Associated package ID. - **Version** (integer) - The version of the quiz record. ### Response Example ```json { "Record": { "QuizId": 12345, "Producer": "user-sub-123", "Name": "My Trivia Night", "Status": "Setup", "StartTime": "2025-11-15T19:00:00Z", "ImgKey": "qz/abc123.jpg", "WelcomeText": "Welcome to our quiz!", "FarewellText": "Thanks for playing!", "RegText": "Please register your team", "InfoText": "Rules: Be honest and have fun!", "WithPremoderation": true, "EventPage": "https://example.com/event", "MixlrCode": "mixlr-embed-code", "PackageId": null, "Version": 1 } } ``` ``` -------------------------------- ### Client-Side S3 Upload and Media URL Construction (F#) Source: https://context7.com/usix79/openquiz/llms.txt This F# code demonstrates the client-side usage of a pre-signed S3 upload URL. It includes fetching the upload URL, performing a PUT request with the file content, handling the upload response, and then constructing the final media URL for display using a media host and the saved key. This involves using `fetch` API and conditional logic for external URLs. ```fsharp // Client usage - upload image for quiz let! urlResp = mainApi.getUploadUrl { Token = "" Arg = {| Cat = QuizImg |} } match urlResp.Value with | Ok urlData -> // Upload file directly to S3 using pre-signed URL let! uploadResp = fetch urlData.Url [ Method "PUT" Body (U3.Case3 imageBlob) Headers [ ContentType "image/jpeg" ] ] if uploadResp.Ok then // Save media key for later use let mediaKey = urlData.BucketKey // e.g., "qz/abc-123-def" // Update quiz with new image let! updateResp = mainApi.updateProdQuizCard { Token = "" Arg = { currentCard with ImgKey = mediaKey } } printfn "Quiz image updated successfully" else printfn "Upload failed: %s" uploadResp.StatusText | Error err -> printfn "Failed to get upload URL: %s" err // Retrieve media URL for display let urlForMedia mediaHost key = if key.Contains(".") then key // External URL else sprintf "%s/media/%s" mediaHost key // CloudFront CDN // Display image in UI let imageUrl = urlForMedia settings.MediaHost "qz/abc-123-def" // Result: "https://d1234.cloudfront.net/media/qz/abc-123-def" ``` -------------------------------- ### Create Package Source: https://context7.com/usix79/openquiz/llms.txt Creates a new quiz package with a name and a list of slips (questions and answers). ```APIDOC ## POST /api/packages ### Description Creates a new quiz package. ### Method POST ### Endpoint /api/packages ### Parameters #### Request Body - **Name** (string) - Required - The name of the package. - **Slips** (Array) - Required - A list of slips, where each slip contains question details. ### Request Example ```json { "Name": "Sample Quiz", "Slips": [ { "Caption": "Question 1", "Question": {"Type": "Solid", "Text": "What is 2+2?"}, "Answer": {"Type": "OpenAnswer", "Text": "4"}, "Points": 10, "WithChoice": false, "EndOfTour": false } ] } ``` ### Response #### Success Response (200) - **PackageId** (integer) - The ID of the newly created package. - **Name** (string) - The name of the package. - **Slips** (Array) - The list of slips in the package. #### Response Example ```json { "PackageId": 12345, "Name": "Sample Quiz", "Slips": [ { "Caption": "Question 1", "Question": {"Type": "Solid", "Text": "What is 2+2?"}, "Answer": {"Type": "OpenAnswer", "Text": "4"}, "Points": 10, "WithChoice": false, "EndOfTour": false } ] } ``` ``` -------------------------------- ### F# DynamoDB CRUD Operations (PutItem, GetItem) Source: https://context7.com/usix79/openquiz/llms.txt Implements essential DynamoDB operations in F# for putting and getting items. It utilizes the custom attribute value conversion and handles potential errors during DynamoDB interactions, returning results asynchronously. ```fsharp // DynamoDB operations let putItem tableName key (attrs: Attr list) : Async> = async { let request = PutItemRequest() request.TableName <- tableName request.Item <- attrs |> List.map (fun (k, v) -> k, toAttributeValue v) |> dict try let! response = dynamoClient.PutItemAsync(request) |> Async.AwaitTask return Ok () with ex -> return Error ex.Message } let getItem tableName key : Async> = async { let request = GetItemRequest() request.TableName <- tableName request.Key <- dict [ "Id", AttributeValue(S = key) ] try let! response = dynamoClient.GetItemAsync(request) |> Async.AwaitTask let attrs = response.Item |> Seq.map (fun kvp -> kvp.Key, fromAttributeValue kvp.Value) |> List.ofSeq return Ok attrs with ex -> return Error ex.Message } ``` -------------------------------- ### F# Workflow for Creating a Quiz with AsyncResult Composition Source: https://context7.com/usix79/openquiz/llms.txt This F# code demonstrates a workflow for creating a quiz and updating an expert's quiz list using the AsyncResult pattern. It composes multiple asynchronous operations, including quiz creation and expert record updates, chaining them together with `AR.side` and `AR.map` for sequential execution and transformation. ```fsharp // Domain logic composition example let createQuizWorkflow env expert = // Create quiz Data2.Quizzes.create env (fun id -> Domain.Quizzes.createNew id expert.Id) // Update expert's quiz list |> AR.side (fun quiz -> Data2.Experts.update env expert.Id (Domain.Experts.addQuiz quiz.Dsc.QuizId)) // Transform to response model |> AR.map (fun quiz -> Main.quizProdCard quiz) // Usage with error handling let! result = createQuizWorkflow env expert match result with | Ok card -> printfn "Quiz created: %s" card.Name | Error msg -> printfn "Error: %s" msg ``` -------------------------------- ### AWS Infrastructure Overview with CDK Source: https://context7.com/usix79/openquiz/llms.txt Outlines the expected AWS infrastructure components managed by AWS CDK for the production environment. This includes compute, storage, database, CDN, authentication, API, and logging services. ```bash # Expected infrastructure (Cdk/ProductionStack.fs): # - Elastic Beanstalk environment # - DynamoDB tables (Experts, Quizzes, Teams, Packages, Answers, RefreshTokens) # - S3 bucket for media storage # - CloudFront distribution for CDN # - Cognito User Pool for authentication # - AppSync GraphQL API for real-time subscriptions # - CloudWatch logging # - Systems Manager Parameter Store for configuration ``` -------------------------------- ### Initialize Quiz Results Page (JavaScript) Source: https://github.com/usix79/openquiz/blob/master/src/Client/public/results.html This snippet initializes the quiz results page on document ready. It sets default parameters, parses URL parameters, retrieves localization strings, sets the page title and meta descriptions, updates the quiz logo and name, and fetches the results JSON data to display. Dependencies include jQuery and a hypothetical 'getL10n' function and '$.parseParams'. ```javascript $(document).ready(function() { var defaults = { quiz : 0, quizName : "", quizImg : "", teamId : 0, token : "", url: "https://www.open-quiz.com" } var parameters = $.parseParams(location.search.substring(1)) var settings = $.extend({}, defaults, parameters); var l10n = getL10n() // alert(JSON.stringify(l10n)) var title = settings.quizName + " - " + l10n.results document.title = title $("meta[name='description']").attr("content", title) $("meta[property='og\\:description']").attr("content", title) $("meta[property='og\\:url']").attr("content", window.location.href) if (settings.quizImg != ""){ $("#quizLogo").attr("src", settings.url + "/media/" + settings.quizImg); } $("#quizName").text(settings.quizName); var isEmbeded = settings.who === "emb" if (!isEmbeded){ $(".hero-head").show() $(".hero-foot").show() } var url = settings.url + "/static/" + settings.quiz + "-" + settings.token + "/results.json?nocache=" + (new Date()).getTime() $.getJSON(url, function(data) { displayResults(data, settings.teamId, l10n) }); }); ``` -------------------------------- ### Create New Quiz - F# Source: https://context7.com/usix79/openquiz/llms.txt This F# code defines the API interface for quiz creation, the server-side implementation for creating a new quiz, the domain model for a quiz, and the client-side command to initiate quiz creation. It outlines the process of generating a new quiz record and associating it with an expert. ```fsharp // Shared type - API interface type IMainApi = { createQuiz: REQ -> ARESP<{| Record: QuizProdRecord Card: QuizProdCard |}> getProdQuizzes: REQ -> ARESP getProdQuizCard: REQ<{| QuizId: int |}> -> ARESP updateProdQuizCard: REQ -> ARESP deleteQuiz: REQ<{| QuizId: int |}> -> ARESP // ... other methods } // Server implementation - MainService.fs let createQuiz env expert _ = let creator quizId = Domain.Quizzes.createNew quizId expert.Id expert.DefaultImg expert.DefaultMixlr let logic (quiz: Domain.Quiz) expert = expert |> Domain.Experts.addQuiz quiz.Id |> Ok Data2.Quizzes.create env creator |> AR.side (fun quiz -> Data2.Experts.update env expert.Id (logic quiz)) |> AR.map (fun quiz -> {| Record = quiz.Dsc |> Main.quizProdRecord Card = Main.quizProdCard quiz |}) // Domain model - Domain.fs module Quizzes = let createNew quizId producerId defaultImg defaultMixlr = let dsc = { QuizId = quizId Producer = producerId Name = sprintf "Quiz%i" quizId Status = Setup AdminToken = generateRandomToken() ListenToken = generateRandomToken() StartTime = None ImgKey = defaultImg WelcomeText = "" FarewellText = "" RegText = "" InfoText = "" WithPremoderation = false EventPage = "" MixlrCode = defaultMixlr PackageId = None Version = 0 } { Dsc = dsc; Tours = []; Version = 0 } : Quiz // Client usage - MainProdQuizzes.fs let createQuizCmd (api: IMainApi) = Cmd.OfAsync.either (fun _ -> api.createQuiz { Token = ""; Arg = () }) () (fun resp -> match resp.Value with | Ok result -> QuizCreated result | Error txt -> ErrorOccurred txt) (fun ex -> ErrorOccurred ex.Message) // Expected result: // { Record = { QuizId = 12345 // Producer = "user-sub-123" // Name = "Quiz12345" // Status = Setup // StartTime = None // ... } // Card = { QuizId = 12345 // Name = "Quiz12345" // AdminUrl = "https://open-quiz.com/admin/12345/abc-token" // RegUrl = "https://open-quiz.com/reg/12345/xyz-token" // ... } // } ``` -------------------------------- ### Localization Function for Quiz Results Source: https://github.com/usix79/openquiz/blob/master/src/Client/public/results.html Provides a function to get localized strings for quiz result elements based on the browser's language settings. It supports English, Russian, Ukrainian, Azerbaijani, and Uzbek languages. Returns an object with localized labels. ```javascript function getL10n() { function getLang() { for (var i = 0; i < window.navigator.languages.length; i++) { var lang = window.navigator.languages[i] if (lang.startsWith("en")) { return "en" } if (lang.startsWith("ru")) { return "ru" } if (lang.startsWith("uk")) { return "uk" } if (lang.startsWith("az")) { return "az" } if (lang.startsWith("uz")) { return "uz" } } return "en" } var lang = getLang() function l(en, ru, uk, az, uz) { if (lang == "ru") { return ru } if (lang == "uk") { return uk } if (lang == "az") { return az } if (lang == "uz") { return uz } return en } var l10n = { results: l("Results", "Результаты", "Результати", "Nəticələr", "Натижалар"), team: l("Team", "Команда", "Команда", "Komanda", "Жамоа"), points: l("Points", "Очки", "Очки", "Xallar", "Очколар"), ratingOfDifficulty: l("Rating", "Рейтинг", "Рейтинг", "Reytinq", "Рейтинг"), place: l("Place", "Место", "Місце", "Yer", "Ўрин"), questionDifficulty: l("Question difficulty", "Сложность вопроса", "Складність питання", "Sualın mürəkkəbliyi", "Савол қийинлиги"), questionVotes: l("Question karma", "Карма вопроса", "Карма питання", "Sualın karma", "Савол кармаси"), questionRating: l("Question rating", "Рейтинг вопроса", "Рейтинг питання", "Sualın reytinqi", "Савол рейтинги"), toShortView: l('less details', "скрыть детали", "сховати подробицi", "daha çox", "деталларни беркитиш"), toFullView: l('more details', "показать детали", "показати подробицi", "daha az", "батафсил"), tour: l('Tour', "Тур", "Тур", "Tur", "Босқич") } return l10n } ``` -------------------------------- ### Aquire Package Source: https://context7.com/usix79/openquiz/llms.txt Allows a user to acquire a package using a transfer token. ```APIDOC ## POST /api/packages/{PackageId}/aquire ### Description Allows a user to acquire a package using a transfer token. ### Method POST ### Endpoint /api/packages/{PackageId}/aquire ### Parameters #### Path Parameters - **PackageId** (integer) - Required - The ID of the package to acquire. #### Request Body - **TransferToken** (string) - Required - The token required to acquire the package. ### Request Example ```json { "TransferToken": "your-secure-transfer-token" } ``` ### Response #### Success Response (200) - **PackageId** (integer) - The ID of the acquired package. - **Name** (string) - The name of the acquired package. #### Response Example ```json { "PackageId": 98765, "Name": "Acquired Special Package" } ``` ``` -------------------------------- ### Client-Side GraphQL Subscription for Real-Time Quiz Updates (F#) Source: https://context7.com/usix79/openquiz/llms.txt This F# code sets up a client-side subscription to AWS AppSync for real-time quiz updates. It uses the Amplify library to establish the subscription and processes incoming messages, parsing them into `QuizChangedEvent` objects. The `onMessage` callback function handles the received events. ```fsharp // Client-side subscription setup (AppSync.fs) module AppSync = open Amplify open Amplify.Core open Amplify.API let subscribe config quizId token onMessage = let query = """ subscription OnQuizMessage($quizId: Int!, $token: String!) { onQuizMessage(quizId: $quizId, token: $token) { body quizId token version } } """ Amplify.API.GraphQL.subscribe(query, {| quizId = quizId token = token |}) |> Observable.subscribe (fun response -> match response.data with | Some data -> let event = Json.parse data.body onMessage event | None -> ()) ``` -------------------------------- ### F# Define Question and Package Structures for OpenQuiz Source: https://context7.com/usix79/openquiz/llms.txt Defines the F# type definitions for questions, answers, slips, and packages. These structures are fundamental for organizing and storing quiz content within the OpenQuiz system. They include various question formats (single text, split text) and answer types (free text, multiple choice). ```fsharp // Shared types for questions type Question = | Solid of string // Single question text | Split of string list // Multi-part question type SlipAnswer = | OpenAnswer of string // Free text answer | ChoiceAnswer of ChoiceAnswer list // Multiple choice type ChoiceAnswer = { Text: string; IsCorrect: bool } type SingleSlip = { Caption: string Question: Question QuestionMedia: MediaDsc option Answer: SlipAnswer AnswerMedia: MediaDsc option Comment: string Points: decimal JeopardyPoints: decimal option WithChoice: bool Seconds: int option EndOfTour: bool } type Slip = | Single of SingleSlip | Multiple of string * SingleSlip list type PackageCard = { PackageId: int Name: string Slips: Slip list } // API interface type IMainApi = { createPackage: REQ -> ARESP<{| Record: PackageRecord Card: PackageCard |}> getProdPackages: REQ -> ARESP getProdPackageCard: REQ<{| PackageId: int |}> -> ARESP updateProdPackageCard: REQ -> ARESP deletePackage: REQ<{| PackageId: int |}> -> ARESP sharePackage: REQ<{| PackageId: int; UserId: string |}> -> ARESP removePackageShare: REQ<{| PackageId: int; UserId: string |}> -> ARESP aquirePackage: REQ<{| PackageId: int; TransferToken: string |} -> ARESP // ... } ```