├── License.txt ├── New-ReleaseChangeLog.ps1 ├── PSQuizMaster.psd1 ├── PSQuizmaster.psm1 ├── PrepNewRelease.ps1 ├── README.md ├── assets ├── editor-integration.png ├── invoke-psquiz.png ├── psquickquiz.png └── quiz-results.png ├── changelog.md ├── docs ├── Copy-PSSampleQuiz.md ├── Get-PSQuiz.md ├── Invoke-PSQuickQuiz.md ├── Invoke-PSQuiz.md ├── New-PSQuiz.md ├── New-PSQuizFile.md ├── New-PSQuizQuestion.md ├── Protect-PSQuizFile.md ├── Remove-PSQuizSetting.md ├── Set-PSQuizFile.md ├── Set-PSQuizPath.md └── Unprotect-PSQuizFile.md ├── en-us └── PSQuizmaster-help.xml ├── formats ├── psquiz.format.ps1xml ├── psquizItem.format.ps1xml └── psquizresult.format.ps1xml ├── functions ├── CHANGELOG.md ├── Copy-PSQuizFile.ps1 ├── Get-PSQuiz.ps1 ├── Invoke-PSQuickQuiz.ps1 ├── Invoke-PSQuiz.ps1 ├── New-PSQuiz.ps1 ├── New-PSQuizFile.ps1 ├── New-PSQuizQuestion.ps1 ├── Protect-PSQuizfile.ps1 ├── Remove-PSQuizSetting.ps1 ├── Set-PSQuizFile.ps1 ├── Set-PSQuizPath.ps1 ├── Unprotect-PSQuizFile.ps1 └── private.ps1 ├── psquiz.schema.json └── quizzes ├── Aliases.quiz.json ├── arrays-hashtables.quiz.json ├── demo.quiz.json ├── pshelp.quiz.json └── remoting.quiz.json /License.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/License.txt -------------------------------------------------------------------------------- /New-ReleaseChangeLog.ps1: -------------------------------------------------------------------------------- 1 | #requires -module ChangeLogManagement 2 | 3 | #get ISO8601 date 4 | $dt = Get-Date -format "yyyy-MM-dd HH:mm:ss" 5 | 6 | $moduleName = Split-Path -path $PSScriptRoot -Leaf 7 | #get changelog data 8 | $change = Get-ChangelogData -Path .\ChangeLog.md 9 | 10 | #get latest release 11 | $modVersion = $change.LastVersion 12 | 13 | #get previous releases 14 | [regex]$rx = "(?<=\[)\d\.\d\.\d" 15 | $releases = $change.footer.split("`n") | Select-Object -skip 1 16 | $prev = $rx.match($releases[1]).value 17 | 18 | $link = "[$modVersion]: https://github.com/jdhitsolutions/$moduleName/compare/v$prev...v$modVersion" 19 | 20 | #$change.footer.split("`n").where({$_ -match "\[$modVersion\]"}) 21 | if ($modVersion -as [version]) { 22 | $md = [System.Collections.Generic.list[String]]::new() 23 | $md.add($change.header.split("`n")[0]) 24 | $md.add("## v[$modVersion] - $dt`n") 25 | $md.add($change.ReleaseNotes) 26 | $md.Add("`n$link") 27 | $md | Out-File .\scratch-change.md 28 | code .\scratch-change.md 29 | } 30 | else { 31 | Write-Warning "Changelog does not follow the newer format." 32 | } 33 | 34 | <# 35 | 36 | # Changelog for PSWorkItem 37 | 38 | ## 0.8.0 39 | 40 | ### Added 41 | 42 | - Add database path as a property to workitem and archived workitem objects. 43 | 44 | ### Changed 45 | 46 | - Update default format view to group by database path. 47 | - Help updates. 48 | 49 | ### Fixed 50 | = Merged [PR#4](https://github.com/jdhitsolutions/PSWorkItem/pull/4) that resolves [Issue #3](https://github.com/jdhitsolutions/PSWorkItem/issues/3) Thank you @jbelina. 51 | 52 | #> -------------------------------------------------------------------------------- /PSQuizMaster.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/PSQuizMaster.psd1 -------------------------------------------------------------------------------- /PSQuizmaster.psm1: -------------------------------------------------------------------------------- 1 | #load functions 2 | 3 | $FunFolder =Join-Path -path $PSScriptRoot -ChildPath functions 4 | 5 | Get-ChildItem -Path $FunFolder -filter *.ps1 | 6 | ForEach-Object { . $_.FullName} 7 | 8 | #this is a private variable not exposed to the user 9 | $PSQuizSettingsFile = Join-Path -Path $HOME -ChildPath '.psquizsettings.json' 10 | 11 | if (Test-Path -path $PSQuizSettingsFile) { 12 | $PSQuizSettings = Get-Content -path $PSQuizSettingsFile | ConvertFrom-Json 13 | Set-Variable -name PSQuizPath -value $PSQuizSettings.PSQuizPath 14 | } 15 | else { 16 | Set-Variable -name PSQuizPath -value (Join-Path -Path $PSScriptRoot -ChildPath "quizzes") 17 | } 18 | 19 | #Path to the JSON schema file 20 | #this is an internal variable 21 | $PSQuizSchema = "https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/main/psquiz.schema.json" 22 | #for local testing 23 | # "file:///c://scripts//psquizmaster//psquiz.schema.json" 24 | 25 | Register-ArgumentCompleter -CommandName Invoke-PSQuiz -ParameterName Path -ScriptBlock { 26 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) 27 | 28 | #PowerShell code to populate $WordToComplete 29 | Get-ChildItem -path $PSQuizPath -filter *quiz.json | 30 | Where-Object { $_.Name -like "$wordToComplete*" } | 31 | ForEach-Object { 32 | # completion text,listItem text,result type,Tooltip 33 | [System.Management.Automation.CompletionResult]::new($_.fullName, $_.name, 'ParameterValue', $_.FullName) 34 | } 35 | } 36 | 37 | #add a VSCode menu item to insert the UTC date time into a quiz file 38 | If ($host.Name -eq 'Visual Studio Code Host') { 39 | $sb = { 40 | $utc = "{0:u}" -f (Get-Date).ToUniversalTime() 41 | $Context = $psEditor.GetEditorContext() 42 | $context.CurrentFile.InsertText($utc) 43 | } 44 | Register-EditorCommand -Name QuizDate -DisplayName "Insert PSQuiz date" -ScriptBlock $sb 45 | 46 | <# 47 | #TODO fix these commands and create ISE equivalents 48 | Function MaskQuizAnswer { 49 | Param() 50 | 51 | Function _hideAnswer { 52 | Param([string]$Answer) 53 | [regex]$word = '\S+' 54 | #write-host "private $answer" -fore green 55 | foreach ($part in $Answer) { 56 | $word.Matches($part).Value.ForEach({ 57 | $_.toCharArray().Foreach({ '{0:d3}' -f ($_ -as [int]) }) -join '' 58 | }) -join ' ' 59 | } 60 | } 61 | 62 | $Context = $psEditor.GetEditorContext() 63 | $selected = [Microsoft.PowerShell.EditorServices.Extensions.FileRange, Microsoft.PowerShell.EditorServices]::new($Context.SelectedRange.Start, $Context.SelectedRange.end) 64 | [string]$text = $Context.CurrentFile.GetText($selected) 65 | write-host "masking $text [$($text.Length)]" 66 | [string]$mask = _hideAnswer $text 67 | write-host "with $mask" 68 | $context.CurrentFile.InsertText($mask) 69 | } 70 | 71 | Register-EditorCommand -Name MaskQuizAnswer -DisplayName "Mask Quiz Answer" -Function MaskQuizAnswer 72 | 73 | Function UnMaskQuizAnswer { 74 | Param() 75 | Function _showAnswer { 76 | Param([string]$ProtectedAnswer) 77 | [regex]$number = '\d{3}' 78 | $out = foreach ($part in $ProtectedAnswer.split()) { 79 | $number.Matches($part).Value.ForEach({ 80 | ([int]$_ -as [string][char]) 81 | }) -join '' 82 | } 83 | $out -join ' ' 84 | } 85 | 86 | $Context = $psEditor.GetEditorContext() 87 | $selected = [Microsoft.PowerShell.EditorServices.Extensions.FileRange, Microsoft.PowerShell.EditorServices]::new($Context.SelectedRange.Start, $Context.SelectedRange.end) 88 | [string]$text = $Context.CurrentFile.GetText($selected) 89 | $global:sel = $selected 90 | write-host "unmasking $text [$($text.Length)]" 91 | [string]$plain = _showAnswer $text 92 | write-host "with $plain" 93 | $context.CurrentFile.InsertText($plain) 94 | } 95 | 96 | Register-EditorCommand -Name UnmaskQuizAnswer -DisplayName "Unmask Quiz Answer" -Function UnMaskQuizAnswer 97 | 98 | } 99 | elseif ($host.name -eq 'Windows PowerShell ISE Host') { 100 | #add an ISE menu shortcut 101 | $sb = { 102 | $utc = "{0:u}" -f (Get-Date).ToUniversalTime() 103 | $PSIse.CurrentFile.Editor.InsertText($utc) 104 | } 105 | 106 | [void]$PSIse.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Insert Quiz UTC Date",$sb,$null) 107 | } #> 108 | } 109 | 110 | Export-ModuleMember -Variable PSQuizPath -Alias "Start-PSQuiz","Make-PSQuiz" -------------------------------------------------------------------------------- /PrepNewRelease.ps1: -------------------------------------------------------------------------------- 1 | #requires -module ChangeLogManagement 2 | 3 | #PrepNewRelease.ps1 4 | 5 | #get module version from manifest 6 | $moduleName = Split-Path $PSScriptRoot -Leaf 7 | $data = Import-PowerShellDataFile .\$moduleName.psd1 8 | $ver = $data.ModuleVersion 9 | 10 | Write-Host "Building release for $moduleName version $ver. Has help been updated? Are you ready to continue?" -ForegroundColor Cyan 11 | Pause 12 | #FirstRelease="https://github.com/jdhitsolutions/$moduleName/tree/v{CUR}"; 13 | 14 | Update-Changelog -ReleaseVersion $ver -LinkMode Automatic -LinkPattern @{ 15 | NormalRelease="https://github.com/jdhitsolutions/$moduleName/compare/v{PREV}..v{CUR}"; 16 | Unreleased ="https://github.com/jdhitsolutions/$moduleName/compare/v{CUR}..HEAD" 17 | } 18 | 19 | #verify and touch-up change log 20 | code $PSScriptRoot\changelog.md 21 | 22 | $msg = @' 23 | Next steps: 24 | - Finalize change log 25 | - Run New-ReleaseChangeLog.ps1 26 | - Update ReleaseNotes in module manifest 27 | $c = Get-ChangelogData 28 | "## $($c.lastVersion)`n`n$($c.ReleaseNotes)" | Set-Clipboard 29 | - git add . 30 | - git commit -m "v" 31 | - publish project 32 | - push release 33 | '@ 34 | 35 | Write-Host $msg -foreground yellow 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSQuizMaster 2 | 3 | [![PSGallery Version](https://img.shields.io/powershellgallery/v/PSQuizMaster.png?style=for-the-badge&label=PowerShell%20Gallery)](https://www.powershellgallery.com/packages/PSQuizMaster/) [![PSGallery Downloads](https://img.shields.io/powershellgallery/dt/PSQuizMaster.png?style=for-the-badge&label=Downloads)](https://www.powershellgallery.com/packages/PSQuizMaster/) 4 | 5 | This PowerShell module consists of two parts, commands to generate quizzes or testing material, and commands for taking a test. Quiz files are stored as JSON documents. 6 | 7 | - [New-PSQuizQuestion](docs/New-PSQuizQuestion.md) 8 | - [Invoke-PSQuiz](docs/Invoke-PSQuiz.md) 9 | - [Invoke-PSQuickQuiz](docs/Invoke-PSQuickQuiz.md) 10 | - [Get-PSQuiz](docs/Get-PSQuiz.md) 11 | - [Set-PSQuizFile](docs/Set-PSQuizFile.md) 12 | - [New-PSQuiz](docs/New-PSQuiz.md) 13 | - [Protect-PSQuizFile](docs/Protect-PSQuizFile.md) 14 | - [Unprotect-PSQuizFile](docs/Unprotect-PSQuizFile.md) 15 | 16 | You can install the module from the PowerShell Gallery. 17 | 18 | ```powershell 19 | Install-Module PSQuizMaster 20 | ``` 21 | 22 | or 23 | 24 | ```powershell 25 | Install-PSResource PSQuizMaster -Repository PSGallery -TrustRepository 26 | ``` 27 | 28 | Most commands should work cross-platform. You can find an [introduction to this module on my blog](https://jdhitsolutions.com/blog/powershell/9260/friday-fun-with-psquizzes/). 29 | 30 | ## Design 31 | 32 | Quizzes are stored as JSON files. Each quiz has a set of questions which includes a set of distractors. You can add a note to each question to provide additional insights or information. 33 | 34 | The module commands will look for quiz files in the location defined in the global variable `$PSQuizPath`. The default value is the `Quizzes` folder in the module directory. 35 | 36 | ## Creating a Quiz 37 | 38 | The easiest way to create a quiz is to use the `New-PSQuiz` command. This will guide you through the process of creating a quiz file. 39 | 40 | ```dos 41 | PS C:\> New-PSQuiz 42 | What is the full name of your quiz?: PowerShell Remoting 43 | What is the short quiz name? This will be used as part of the file name: remoting 44 | Enter a quiz description. You can always edit this later: A short quiz on PowerShell remoting 45 | Enter the author name: Jeff Hicks 46 | Enter the question: What command do you run to enter an interactive remoting session? 47 | Enter the answer: Enter-PSSession 48 | Enter a comma-separated list of distractors: New-PSSession,New-CimSession,Enter-CimSession,Winrm 49 | Enter any notes for this question: Don't confuse PSSessions with CimSessions. 50 | Add another question? (Y/N): y 51 | Enter the question: What is the default PowerShell remoting port? 52 | Enter the answer: 5985 53 | Enter a comma-separated list of distractors: 5986,22,80,443 54 | Enter any notes for this question: If using SSL, the default port will be 5986. 55 | Add another question? (Y/N): n 56 | 57 | Name : PowerShell Remoting 58 | Author : Jeff Hicks 59 | Version : 0.1.0 60 | Description : A short quiz on PowerShell remoting concepts. 61 | Questions : 2 62 | Updated : 6/29/2024 10:21:21 AM 63 | Path : C:\Scripts\PSQuizMaster\quizzes\remoting.quiz.json 64 | ``` 65 | 66 | This will create this JSON file. 67 | 68 | ```json 69 | { 70 | "metadata": { 71 | "name": "PowerShell Remoting", 72 | "author": "Jeff Hicks", 73 | "description": "A short quiz on PowerShell remoting concepts.", 74 | "version": "0.1.0", 75 | "id": "32248289-3ca9-4fb6-acde-524c809bf50e", 76 | "updated": "2024-06-29 14:21:21Z" 77 | }, 78 | "questions": [ 79 | { 80 | "question": "What command do you run to enter an interactive remoting session?", 81 | "answer": "Enter-PSSession", 82 | "distractors": [ 83 | "New-PSSession", 84 | "New-CimSession", 85 | "Enter-CimSession", 86 | "Winrm" 87 | ], 88 | "note": "Don't confuse PSSessions with CimSessions." 89 | }, 90 | { 91 | "question": "What is the default PowerShell remoting port?", 92 | "answer": "5985", 93 | "distractors": [ 94 | "5986", 95 | "22", 96 | "80", 97 | "443" 98 | ], 99 | "note": "If using SSL, the default port will be 5986." 100 | } 101 | ] 102 | } 103 | ``` 104 | 105 | Quiz files should follow the naming convention of `.quiz.json`. 106 | 107 | You can also use the `New-PSQuizFile` command to create a quiz file and then use `New-PSQuizQuestion` to create questions. Add the questions to the file using `Set-PSQuizFile`. 108 | 109 | ## Protecting Quizzes 110 | 111 | Because quizzes are stored as plaintext JSON files, the answers are easily discovered. If you want to deter casual "cheating" you can use `Protect-PSQuizFile` to mask the answers. The technique used to hide the answer isn't complicated or fancy. You can use `Unprotect-PSQuizFile` to revert the process. Version 1.3.0 of this module also protects distractors. 112 | 113 | ## Editor Integrations 114 | 115 | The module includes several editor-related features that you might find helpful. Especially if you find it easier or faster to create a quiz file by editing the JSON file directly. 116 | 117 | ### Schema 118 | 119 | The JSON file includes a public schema. If you open the quiz JSON file in VS Code, you can get tab completion and assistance in adding questions to the file or adjusting the metadata. There is no reason to remove the schema reference in the file. 120 | 121 | ### Editor Shortcuts 122 | 123 | When editing a quiz JSON file, the `Updated` property needs to follow a specific format. When you import this module in VSCode, you will get an additional module command called `Insert PSQuiz date.` Set your cursor in the JSON file where you have deleted the updated value. Open the command palette, select `Show Additional Commands from PowerShell Modules`, and then click `Insert PSQuiz Date.` The proper date string will be inserted into the file. 124 | 125 | In the PowerShell ISE, importing this module will create an Add-ons menu called `Insert Quiz UTC Date` that will achieve the same result. 126 | 127 | ### UseEditor 128 | 129 | When using this module in the PowerShell ISE or VS Code, when running `New-PSQuiz` or `New-PSQuizFile`, you can use the `UseEditor` dynamic parameter. This will open the quiz JSON file in the current editor. 130 | 131 | ```powershell 132 | New-PSQuizFile -Name "Using CIM" -ShortName cim -Path c:\work\quizzes -Author "Jeff Hicks" -Description "A quiz on using CIM in PowerShell" -UseEditor 133 | ``` 134 | 135 | ![editor integration](assets/editor-integration.png) 136 | 137 | You could then use code like this to generate quiz questions. 138 | 139 | ```PowerShell 140 | New-PSQuizQuestion | ConvertTo-Json | Set-Clipboard 141 | ``` 142 | 143 | Paste the question into the JSON file and repeat. 144 | 145 | ## Taking a Quiz 146 | 147 | To take a quiz, use `Invoke-PSQuiz`. You need to specify the full path to the JSON file. By default, the function will tab-complete quiz files found in $PSQuizPath. 148 | 149 | ![Invoke-PSQuiz](assets/Invoke-PSQuiz.png) 150 | 151 | The quiz is presented interactively. Questions and answers are presented in random order. 152 | 153 | When you are done, you will see a summary of your results. 154 | 155 | ![Quiz results](assets/quiz-results.png) 156 | 157 | ## PSQuizMaster Settings 158 | 159 | The default quiz location is determined by the value of the global `$PSQuizPath` variable. If you don't want to be constantly updating this variable every time you import the module, you can run `Set-PSQuizPath` and specify a new location. 160 | 161 | ```powershell 162 | Set-PSQuizPath c:\work\quizzes 163 | ``` 164 | 165 | This will create a file under $HOME called `.psquizsettings.json` The path will be stored in this file. The next time you import the module, if this file exists, the module will use the saved location. You should not need to edit or do anything with this file. If you want to remove it, you should use `Remove-PSQuizSetting` which will delete the settings file and set the value of `$PSQuizPath` back to the module default. 166 | 167 | ## Sample Quizzes 168 | 169 | The module contains several sample quizzes you can find in the [Quizzes](quizzes) folder. If you don't change the value of `$PSQuizPath` you should be able to find them with `Get-PSQuiz`. 170 | 171 | If you have changed the path location, you can use `Copy-PSSampleQuiz` to copy the samples to the new destination. 172 | 173 | ## PSQuickQuiz 174 | 175 | The module contains a command to dynamically generate a quiz based on commands found in one or more modules. This is a great way to test your knowledge of PowerShell commands. 176 | 177 | ```dos 178 | PS C:\> Invoke-PSQuickQuiz -Module Microsoft.PowerShell.* 179 | ``` 180 | 181 | This will generate a dynamic quiz based on the commands found in the specified modules. 182 | 183 | ![PSQuickQuiz](assets/psquickquiz.png) 184 | 185 | ## Ideas and Project Road Map 186 | 187 | - Check for the latest version of questions from a GitHub repository or path, including a UNC. 188 | - Store long-term test results somewhere. Maybe use a database like SQLite, or maybe a JSON file. 189 | - Create WPF or TUI front-ends for creating and taking quizzes. 190 | -------------------------------------------------------------------------------- /assets/editor-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/assets/editor-integration.png -------------------------------------------------------------------------------- /assets/invoke-psquiz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/assets/invoke-psquiz.png -------------------------------------------------------------------------------- /assets/psquickquiz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/assets/psquickquiz.png -------------------------------------------------------------------------------- /assets/quiz-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/assets/quiz-results.png -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog for PSQuizmaster 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.3.0] - 2024-10-17 6 | 7 | ### Added 8 | 9 | - Added missing online help links. 10 | 11 | ### Changed 12 | 13 | - Modify masking commands to also obfuscate or reveal distractors. [Issue #3](https://github.com/jdhitsolutions/PSQuizMaster/issues/3) 14 | - Updated `Invoke-PSQuickQuiz` to use `System.Collections.Generic.List[]` instead of an array. 15 | 16 | ## [1.2.0] - 2023-08-25 17 | 18 | ### Added 19 | 20 | - Added format file `psquizresult.format.ps1xml`. 21 | - Added a `TestTime`, `TestVersion`, and `Path` properties to the quiz result from `Invoke-PSQuiz`. 22 | 23 | ### Changed 24 | 25 | - Updated help for `New-PSQuiz` to reflect new parameters. 26 | - Updated `README.md`. 27 | - Updated help documentation. 28 | 29 | ### Fixed 30 | 31 | - Updated masking and unmasking code to handle integer answers. 32 | - Fixed regex pattern in `Protect-PSQuizFile` and `Unprotect-PSQuizFile`. 33 | 34 | ## [1.1.0] - 2023-08-24 35 | 36 | ### Added 37 | 38 | - Added commands `Protect-PSQuizFile` and `Unprotect-PSQuizFile`, along with private helper functions, to mask or unmask answers. [[Issue #3](https://github.com/jdhitsolutions/functions/issues/3)] 39 | 40 | ### Changed 41 | 42 | - Updated `Invoke-PSQuiz to start a quiz based on the quiz name. [[Issue #1](https://github.com/jdhitsolutions/PSQuizMaster/issues/1)] 43 | - Updated `Invoke-PSQuiz` to accept quiz by pipeline input. 44 | - Help documentation updates. 45 | - Modified `Get-PSQuiz` to better support wildcards for the quiz name. [[Issue #2](https://github.com/jdhitsolutions/PSQuizMaster/issues/2)] 46 | - Updated `README.md`. 47 | 48 | ## [1.0.0] - 2023-08-07 49 | 50 | This is the first version published to the PowerShell Gallery. 51 | 52 | ### Added 53 | 54 | - Added a dynamic parameter called `UseEditor` to `New-PSQuizFile` and `New-PSQuiz`. If running either command in VSCode or the PowerShell ISE, you can use the dynamic parameter open the new quiz file in the current editor. 55 | - Added a pshelp sample quiz. 56 | - Added an ISE add-on menu command to insert the UTC formatted datetime into a JSON quiz file. 57 | - Added a VSCode additional command to insert the UTC formatted datetime into a JSON quiz file. 58 | - Added PSHelp sample quiz. 59 | 60 | ### Changed 61 | 62 | - Updated sample quizzes. 63 | - Help updates. 64 | - Updated `README.md` 65 | 66 | ## [0.5.0] - 2023-08-06 67 | 68 | ### Changed 69 | 70 | - Updated `README.me`. 71 | - Updated command help with online links. 72 | 73 | ## [0.4.0] - 2023-08-06 74 | 75 | ### Added 76 | 77 | - Added format file `psquiz.format.ps1xml`. 78 | - Added argument completer for the `Path` parameter in `Set-PSQuizFile`. 79 | - Added support for a JSON schema for the quiz files. 80 | - Added command `Set-PSQuizPath`. 81 | - Added command `Remove-PSQuizSetting`. 82 | - Added command `Copy-PSSampleQuiz` to copy module sample quizzes to a new location. 83 | 84 | ### Changed 85 | 86 | - Modified `Get-PSQuiz` to output a typed object and updated the associated format.ps1xml file. 87 | - Modified manifest to reflect that this module should work in Windows PowerShell and PowerShell 7. 88 | - Updated help documentation. 89 | - Updated quiz JSON schema to use online source. 90 | 91 | - ## v0.3.0 92 | 93 | ### Fixed 94 | 95 | - Updated root module file to export command aliases. 96 | - Fixed path bug in `New-PSQuizFile` 97 | 98 | - ## v0.1.0 99 | 100 | - initial module files 101 | 102 | [Unreleased]: https://github.com/jdhitsolutions/PSQuizMaster/compare/v1.3.0..HEAD 103 | [1.3.0]: https://github.com/jdhitsolutions/PSQuizMaster/compare/v1.2.0..v1.3.0 104 | [1.2.0]: https://github.com/jdhitsolutions/PSQuizMaster/compare/v1.1.0..v1.2.0 105 | [1.1.0]: https://github.com/jdhitsolutions/PSQuizMaster/compare/v1.0.0..v1.1.0 106 | [1.0.0]: https://github.com/jdhitsolutions/PSQuizMaster/compare/v0.5.0..v1.0.0 107 | [0.5.0]: https://github.com/jdhitsolutions/PSQuizMaster/tree/v0.5.0 -------------------------------------------------------------------------------- /docs/Copy-PSSampleQuiz.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3OocjEC 5 | schema: 2.0.0 6 | --- 7 | 8 | # Copy-PSSampleQuiz 9 | 10 | ## SYNOPSIS 11 | 12 | Copy module sample quiz files. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | Copy-PSSampleQuiz [[-Path] ] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | If you decide to create a new location for $PSQuizPath, you can use this command to copy the sample quizzes from the PSQuizMaster module to the new folder. The location must already exist. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> Copy-PSSampleQuiz -Path C:\work\quizzes\ 30 | 31 | Directory: C:\work\quizzes 32 | 33 | Mode LastWriteTime Length Name 34 | ---- ------------- ------ ---- 35 | -a--- 7/6/2024 11:03 AM 2494 Aliases.quiz.json 36 | -a--- 7/7/2024 2:21 PM 6202 demo.quiz.json 37 | -a--- 7/7/2024 2:21 PM 4156 remoting.quiz.json 38 | ``` 39 | 40 | ## PARAMETERS 41 | 42 | ### -Confirm 43 | 44 | Prompts you for confirmation before running the cmdlet. 45 | 46 | ```yaml 47 | Type: SwitchParameter 48 | Parameter Sets: (All) 49 | Aliases: cf 50 | 51 | Required: False 52 | Position: Named 53 | Default value: None 54 | Accept pipeline input: False 55 | Accept wildcard characters: False 56 | ``` 57 | 58 | ### -Path 59 | 60 | Specify the target folder. 61 | It is assumed that the location will be your new value for $PSQuizPath. 62 | The folder must already exist. 63 | 64 | ```yaml 65 | Type: DirectoryInfo 66 | Parameter Sets: (All) 67 | Aliases: Destination 68 | 69 | Required: False 70 | Position: 0 71 | Default value: None 72 | Accept pipeline input: True (ByValue) 73 | Accept wildcard characters: False 74 | ``` 75 | 76 | ### -WhatIf 77 | 78 | Shows what would happen if the cmdlet runs. 79 | The cmdlet is not run. 80 | 81 | ```yaml 82 | Type: SwitchParameter 83 | Parameter Sets: (All) 84 | Aliases: wi 85 | 86 | Required: False 87 | Position: Named 88 | Default value: None 89 | Accept pipeline input: False 90 | Accept wildcard characters: False 91 | ``` 92 | 93 | ### CommonParameters 94 | 95 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 96 | 97 | ## INPUTS 98 | 99 | ### System.IO.DirectoryInfo 100 | 101 | ## OUTPUTS 102 | 103 | ### System.IO.FileInfo 104 | 105 | ## NOTES 106 | 107 | Learn more about PowerShell: 108 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 109 | 110 | ## RELATED LINKS 111 | 112 | [Set-PSQuizPath](Set-PSQuizPath.md) 113 | -------------------------------------------------------------------------------- /docs/Get-PSQuiz.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3OnLjFd 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-PSQuiz 9 | 10 | ## SYNOPSIS 11 | 12 | Get quizzes from the default quiz path. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | Get-PSQuiz [[-Name] ] [-Path ] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Use this command to find quiz files. The default is to search for all quiz files in the $PSQuizPath variable, although you can specify a different path. The quiz file must follow the naming convention of .quiz.json. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> Get-PSQuiz 30 | 31 | Path: C:\work\quizzes\Aliases.quiz.json [7/7/2023 6:28:58 PM] 32 | 33 | Name Version Description Questions 34 | ---- ------- ----------- --------- 35 | PowerShell Aliases 0.6.0 A short quiz on using aliases .... 3 36 | 37 | Path: C:\work\quizzes\demo.quiz.json [6/29/2023 9:15:26 AM] 38 | 39 | Name Version Description Questions 40 | ---- ------- ----------- --------- 41 | My Demo Quiz 0.3.0 This is a demo quiz file 7 42 | 43 | Path: C:\work\quizzes\pshelp.quiz.json [7/7/2023 6:29:22 PM] 44 | 45 | Name Version Description Questions 46 | ---- ------- ----------- --------- 47 | PowerShell Help 0.2.1 How to use help 2 48 | 49 | Path: C:\work\quizzes\remoting.quiz.json [7/5/2023 11:36:18 AM] 50 | 51 | Name Version Description Questions 52 | ---- ------- ----------- --------- 53 | PowerShell Remoting 0.2.1 A short quiz on PowerShell remoti... . 5 54 | ``` 55 | 56 | ### Example 2 57 | 58 | ```powershell 59 | PS C:\> Get-PSQuiz -Name "*remoting*" | Format-List 60 | 61 | Name : PowerShell Remoting 62 | Author : Jeff Hicks 63 | Version : 0.2.0 64 | Description : A short quiz on PowerShell remoting concepts. 65 | Questions : 5 66 | Updated : 7/5/2023 11:36:18 AM 67 | Path : C:\work\quizzes\remoting.quiz.json 68 | ``` 69 | 70 | You can use wildcards with the quiz name. 71 | ## PARAMETERS 72 | 73 | ### -Name 74 | 75 | Specify a quiz name. 76 | 77 | ```yaml 78 | Type: String 79 | Parameter Sets: (All) 80 | Aliases: 81 | 82 | Required: False 83 | Position: 0 84 | Default value: None 85 | Accept pipeline input: False 86 | Accept wildcard characters: True 87 | ``` 88 | 89 | ### -Path 90 | 91 | Enter the path to the folder with quiz json files. The quiz files must follow the naming convention of .quiz.json. 92 | 93 | ```yaml 94 | Type: String 95 | Parameter Sets: (All) 96 | Aliases: 97 | 98 | Required: False 99 | Position: Named 100 | Default value: None 101 | Accept pipeline input: False 102 | Accept wildcard characters: False 103 | ``` 104 | 105 | ### CommonParameters 106 | 107 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 108 | 109 | ## INPUTS 110 | 111 | ### None 112 | 113 | ## OUTPUTS 114 | 115 | ### psQuiz 116 | 117 | ## NOTES 118 | 119 | Learn more about PowerShell: 120 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 121 | 122 | ## RELATED LINKS 123 | 124 | [Invoke-PSQuiz](Invoke-PSQuiz.md) 125 | -------------------------------------------------------------------------------- /docs/Invoke-PSQuickQuiz.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3Ome6tP 5 | schema: 2.0.0 6 | --- 7 | 8 | # Invoke-PSQuickQuiz 9 | 10 | ## SYNOPSIS 11 | 12 | Run a PowerShell quiz 13 | 14 | ## SYNTAX 15 | 16 | ### all (Default) 17 | 18 | ```yaml 19 | Invoke-PSQuickQuiz [-Exclude ] [-NextQuestion] [-Path ] [] 20 | ``` 21 | 22 | ### single 23 | 24 | ```yaml 25 | Invoke-PSQuickQuiz [-ModuleName ] [-NextQuestion] [-Path ] [] 26 | ``` 27 | 28 | ## DESCRIPTION 29 | 30 | Use this script to test your knowledge of PowerShell commands, which given the Verb-Noun naming pattern should be pretty easy. 31 | 32 | The default behavior is to use all cmdlets and functions from installed modules with an option to exclude an array of module names. 33 | Wildcards are allowed. 34 | You also have the option to specify a single module for testing. 35 | 36 | ## EXAMPLES 37 | 38 | ### EXAMPLE 1 39 | 40 | ```powershell 41 | PS C:\> Invoke-PSQuickQuiz 42 | 43 | PowerShell Pop Quiz 44 | 45 | Given this short cmdlet description: 46 | 47 | Creates a new System.Windows.Markup.RoutedEventConverter 48 | 49 | What command would you use? 50 | 51 | [1] Invoke-CauScan 52 | [2] Clear-Tpm 53 | [3] Remove-WindowsCapability 54 | [4] Get-IscsiTargetServerSetting 55 | [5] New-RoutedEventConverter 56 | 57 | Select a menu choice [1-5]: 5 58 | 59 | You are Correct!! 60 | 61 | Do you want another question? 62 | Press any key to continue or Q to quit: q 63 | You scored 1 correct out of 1 for a GPA of 5. 64 | Your grade is A. 65 | ``` 66 | 67 | ### EXAMPLE 2 68 | 69 | ```powershell 70 | PS C:\> Invoke-PSQuickQuiz -module Hyper-V 71 | ``` 72 | 73 | Launch the quiz but only use commands from the Hyper-V module. 74 | 75 | ### EXAMPLE 3 76 | 77 | ```powershell 78 | PS C:\> Invoke-PSQuickQuiz -exclude ISE,WPK,my* 79 | ``` 80 | 81 | Launch the quiz using all modules except ISE, WPK and any modules that start with 'my'. 82 | 83 | ### EXAMPLE 4 84 | 85 | ```powershell 86 | PS C:\> Invoke-PSQuickQuiz -exclude ISE,WPK,my* -path c:\work\quiz.txt 87 | ``` 88 | 89 | Launch the quiz using all modules except ISE, WPK and any modules that start with 'my'. 90 | Record results in a transcript file, C:\Work\quiz.txt. 91 | 92 | ## PARAMETERS 93 | 94 | ### -ModuleName 95 | 96 | You can specify a single module for testing. 97 | The default is all modules. 98 | 99 | ```yaml 100 | Type: String 101 | Parameter Sets: single 102 | Aliases: 103 | 104 | Required: False 105 | Position: Named 106 | Default value: None 107 | Accept pipeline input: False 108 | Accept wildcard characters: False 109 | ``` 110 | 111 | ### -Exclude 112 | 113 | Enter a comma separated list of module names to ignore. 114 | You can use wildcards. 115 | 116 | ```yaml 117 | Type: String[] 118 | Parameter Sets: all 119 | Aliases: 120 | 121 | Required: False 122 | Position: Named 123 | Default value: None 124 | Accept pipeline input: False 125 | Accept wildcard characters: False 126 | ``` 127 | 128 | ### -NextQuestion 129 | 130 | This is used to indicate a continuing test. 131 | You should never need to use this parameter. 132 | 133 | ```yaml 134 | Type: SwitchParameter 135 | Parameter Sets: (All) 136 | Aliases: 137 | 138 | Required: False 139 | Position: Named 140 | Default value: False 141 | Accept pipeline input: False 142 | Accept wildcard characters: False 143 | ``` 144 | 145 | ### -Path 146 | 147 | Enter a path and filename for a quiz transcript. 148 | 149 | ```yaml 150 | Type: String 151 | Parameter Sets: (All) 152 | Aliases: transcript 153 | 154 | Required: False 155 | Position: Named 156 | Default value: None 157 | Accept pipeline input: False 158 | Accept wildcard characters: False 159 | ``` 160 | 161 | ### CommonParameters 162 | 163 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 164 | 165 | ## INPUTS 166 | 167 | ## OUTPUTS 168 | 169 | ### System.Object 170 | 171 | ## NOTES 172 | 173 | Learn more about PowerShell: 174 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 175 | 176 | ## RELATED LINKS 177 | 178 | [Invoke-PSQuiz](Invoke-PSQuiz.md) 179 | -------------------------------------------------------------------------------- /docs/Invoke-PSQuiz.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3OqhUKx 5 | schema: 2.0.0 6 | --- 7 | 8 | # Invoke-PSQuiz 9 | 10 | ## SYNOPSIS 11 | 12 | Start a PowerShell quiz 13 | 14 | ## SYNTAX 15 | 16 | ### path (Default) 17 | 18 | ```yaml 19 | Invoke-PSQuiz [-Path] [] 20 | ``` 21 | 22 | ### name 23 | 24 | ```yaml 25 | Invoke-PSQuiz [-Name ] [] 26 | ``` 27 | 28 | ## DESCRIPTION 29 | 30 | Use this command to start a quiz. You can specify the path to the quiz file or use the name of a quiz that is in the $PSQuizPath location. 31 | 32 | ## EXAMPLES 33 | 34 | ### Example 1 35 | 36 | ```powershell 37 | PS C:\> Invoke-PSQuiz -Path C:\work\quizzes\Aliases.quiz.json 38 | PowerShell Aliases [v0.2.0] 39 | Question 1/2 40 | 41 | What command will display currently defined PowerShell aliases? 42 | --------------------------------------------------------------------------- 43 | [1] Show-Alias 44 | [2] Get-AliasDefinition 45 | [3] $PSAlias 46 | [4] Get-Alias 47 | [5] Find-PSAlias 48 | [6] Quit 49 | --------------------------------------------------------------------------- 50 | Select a menu choice [1-5]: 51 | ``` 52 | 53 | The command will auto-complete quiz files found in $PSQuizPath. 54 | 55 | ### Example 2 56 | 57 | ```powershell 58 | PS C:\> Get-PSQuiz *remoting* | Start-PSQuiz 59 | ``` 60 | 61 | Get a quiz using a wildcard pattern and pipe it to Invoke-PSQuiz, using its alias Start-PQuiz, to begin the quiz. 62 | 63 | ## PARAMETERS 64 | 65 | ### -Path 66 | 67 | Enter the full path to the quiz file. The command will auto-complete quiz files found in $PSQuizPath. 68 | 69 | ```yaml 70 | Type: String 71 | Parameter Sets: path 72 | Aliases: 73 | 74 | Required: True 75 | Position: 0 76 | Default value: None 77 | Accept pipeline input: True (ByPropertyName) 78 | Accept wildcard characters: False 79 | ``` 80 | 81 | ### -Name 82 | Specify the quiz name. 83 | 84 | ```yaml 85 | Type: String 86 | Parameter Sets: name 87 | Aliases: 88 | 89 | Required: False 90 | Position: Named 91 | Default value: None 92 | Accept pipeline input: False 93 | Accept wildcard characters: False 94 | ``` 95 | 96 | ### CommonParameters 97 | 98 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 99 | 100 | ## INPUTS 101 | 102 | ### None 103 | 104 | ## OUTPUTS 105 | 106 | ### pzQuizResult 107 | 108 | ## NOTES 109 | 110 | This command has an alias of Start-PSQuiz. 111 | 112 | Learn more about PowerShell: 113 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 114 | 115 | ## RELATED LINKS 116 | 117 | [Invoke-PSQuickQuiz](Invoke-PSQuickQuiz.md) 118 | 119 | [Get-PSQuiz](Get-PSQuiz.md) 120 | -------------------------------------------------------------------------------- /docs/New-PSQuiz.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3OjtLdk 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PSQuiz 9 | 10 | ## SYNOPSIS 11 | 12 | Create a new quiz. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | New-PSQuiz [[-Path] ] [-MaskAnswer] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | This command serves as a kind of "wizard" to guide a quiz creator through the process of creating a quiz file with questions. You can modify the quiz file later by modifying the JSON file or using Set-PSQuizFile. 23 | 24 | Use the MaskAnswer parameter if you want to hide the answers in the JSON file. 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1 29 | 30 | ```powershell 31 | PS C:\> New-PSQuiz -Path d:\temp 32 | What is the full name of your quiz?: Using PowerShell Help 33 | What is the short quiz name? This will be used as part of the file name: pshelp 34 | Enter a quiz description. You can always edit this later: A brief quiz on using the PowerShell help system 35 | Enter the author name: Jeff Hicks 36 | Enter the question: What command do you use to install the latest version of the help documentation? 37 | Enter the answer: Update-Help 38 | Enter a comma-separated list of distractors: Install-Help,Get-Help,Find-Help,Get-Help -online 39 | Enter any notes for this question: A few errors are to be expected when running Update-Help 40 | Add another question? (Y/N): n 41 | 42 | Name : Using PowerShell Help 43 | Author : Jeff Hicks 44 | Version : 0.1.0 45 | Description : A brief quiz on using the PowerShell help system 46 | Questions : 1 47 | Updated : 07/05/2023 12:53:23 48 | Path : D:\temp\pshelp.quiz.json 49 | ``` 50 | 51 | The default path location is $PSQuizPath. You can specify a different location with the -Path parameter. 52 | 53 | ## PARAMETERS 54 | 55 | ### -Path 56 | 57 | Specify the folder for the new quiz file. 58 | The default is $PSQuizPath. 59 | 60 | ```yaml 61 | Type: String 62 | Parameter Sets: (All) 63 | Aliases: 64 | 65 | Required: False 66 | Position: 0 67 | Default value: None 68 | Accept pipeline input: False 69 | Accept wildcard characters: False 70 | ``` 71 | 72 | ### -MaskAnswer 73 | Mask the answer so it is not displayed in plain text in the JSON file. You can use Unprotect-PSQuizFile to unmask the answers. 74 | 75 | ```yaml 76 | Type: SwitchParameter 77 | Parameter Sets: (All) 78 | Aliases: 79 | 80 | Required: False 81 | Position: Named 82 | Default value: None 83 | Accept pipeline input: False 84 | Accept wildcard characters: False 85 | ``` 86 | 87 | ### CommonParameters 88 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 89 | 90 | ## INPUTS 91 | 92 | ### None 93 | 94 | ## OUTPUTS 95 | 96 | ### None 97 | 98 | ### psQuiz 99 | 100 | ## NOTES 101 | 102 | This command has an alias of Make-Quiz. If you run this command from within the integrated terminal in VSCode or in the PowerShell ISE, you can use the dynamic parameter, UseEditor, to open the new file in the current editor. 103 | 104 | Learn more about PowerShell: 105 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 106 | 107 | ## RELATED LINKS 108 | 109 | [New-PSQuizFile](New-PSQuizFile.md) 110 | 111 | [New-PSQuizQuestion](New-PSQuizQuestion.md) 112 | 113 | [Set-PSQuizFile](Set-PSQuizFile.md) 114 | 115 | [Protect-PSQuizFile](Protect-PSQuizFile.md) 116 | 117 | [Unprotect-PSQuizFile](Unprotect-PSQuizfile.md) 118 | -------------------------------------------------------------------------------- /docs/New-PSQuizFile.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/45jiosW 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PSQuizFile 9 | 10 | ## SYNOPSIS 11 | 12 | Create an empty quiz file 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | New-PSQuizFile [-Name] [-ShortName] -Author -Description [-Version ] [-Path ] [-NoClobber] [-Encoding ] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | This command will create an empty quiz file. You can then use New-PSQuizQuestion to define a new set of questions and add them to the file using Set-PSQuizFile. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> New-PSQuizFile -Name "PowerShell Scripting" -ShortName scripting -Author "Jeff Hicks" -Description "A quick quiz on PowerShell scripting" -Path d:\temp 30 | 31 | Directory: D:\temp 32 | 33 | Mode LastWriteTime Length Name 34 | ---- ------------- ------ ---- 35 | -a--- 07/05/2024 13:03 580 scripting.quiz.json 36 | 37 | PS C:\> Get-Content D:\temp\scripting.quiz.json 38 | { 39 | "metadata": { 40 | "name": "PowerShell Scripting", 41 | "author": "Jeff Hicks", 42 | "description": "A quick quiz on PowerShell scripting", 43 | "version": "0.1.0", 44 | "id": "f3d3b784-b838-405e-84fb-594600c82189", 45 | "updated": "2024-07-05 17:03:45Z" 46 | }, 47 | "questions": [] 48 | } 49 | ``` 50 | 51 | The default location is $PSQuizPath. You can specify a different location with the -Path parameter. 52 | 53 | ## PARAMETERS 54 | 55 | ### -Author 56 | 57 | Enter the quiz author. 58 | 59 | ```yaml 60 | Type: String 61 | Parameter Sets: (All) 62 | Aliases: 63 | 64 | Required: True 65 | Position: Named 66 | Default value: None 67 | Accept pipeline input: False 68 | Accept wildcard characters: False 69 | ``` 70 | 71 | ### -Confirm 72 | 73 | Prompts you for confirmation before running the cmdlet. 74 | 75 | ```yaml 76 | Type: SwitchParameter 77 | Parameter Sets: (All) 78 | Aliases: cf 79 | 80 | Required: False 81 | Position: Named 82 | Default value: None 83 | Accept pipeline input: False 84 | Accept wildcard characters: False 85 | ``` 86 | 87 | ### -Description 88 | 89 | Enter a quiz description 90 | 91 | ```yaml 92 | Type: String 93 | Parameter Sets: (All) 94 | Aliases: 95 | 96 | Required: True 97 | Position: Named 98 | Default value: None 99 | Accept pipeline input: False 100 | Accept wildcard characters: False 101 | ``` 102 | 103 | ### -Encoding 104 | 105 | Specify encoding for the text file. 106 | 107 | ```yaml 108 | Type: String 109 | Parameter Sets: (All) 110 | Aliases: 111 | Accepted values: Unicode, BigEndianUnicode, UTF8, UTF7, UTF32, ASCII, Default, OEM 112 | 113 | Required: False 114 | Position: Named 115 | Default value: None 116 | Accept pipeline input: False 117 | Accept wildcard characters: False 118 | ``` 119 | 120 | ### -Name 121 | 122 | Enter your quiz name. 123 | 124 | ```yaml 125 | Type: String 126 | Parameter Sets: (All) 127 | Aliases: 128 | 129 | Required: True 130 | Position: 0 131 | Default value: None 132 | Accept pipeline input: False 133 | Accept wildcard characters: False 134 | ``` 135 | 136 | ### -NoClobber 137 | 138 | Don't overwrite an existing file with the same name. 139 | 140 | ```yaml 141 | Type: SwitchParameter 142 | Parameter Sets: (All) 143 | Aliases: 144 | 145 | Required: False 146 | Position: Named 147 | Default value: None 148 | Accept pipeline input: False 149 | Accept wildcard characters: False 150 | ``` 151 | 152 | ### -Path 153 | 154 | Enter the path or directory to store the quiz json file. 155 | 156 | ```yaml 157 | Type: String 158 | Parameter Sets: (All) 159 | Aliases: 160 | 161 | Required: False 162 | Position: Named 163 | Default value: $PSQuizPath 164 | Accept pipeline input: False 165 | Accept wildcard characters: False 166 | ``` 167 | 168 | ### -ShortName 169 | 170 | Enter your quiz short name which will be used as part of the filename. 171 | 172 | ```yaml 173 | Type: String 174 | Parameter Sets: (All) 175 | Aliases: 176 | 177 | Required: True 178 | Position: 1 179 | Default value: None 180 | Accept pipeline input: False 181 | Accept wildcard characters: False 182 | ``` 183 | 184 | ### -Version 185 | 186 | Enter a semantic version number for your quiz 187 | 188 | ```yaml 189 | Type: Version 190 | Parameter Sets: (All) 191 | Aliases: 192 | 193 | Required: False 194 | Position: Named 195 | Default value: 0.1.0 196 | Accept pipeline input: False 197 | Accept wildcard characters: False 198 | ``` 199 | 200 | ### -WhatIf 201 | 202 | Shows what would happen if the cmdlet runs. 203 | The cmdlet is not run. 204 | 205 | ```yaml 206 | Type: SwitchParameter 207 | Parameter Sets: (All) 208 | Aliases: wi 209 | 210 | Required: False 211 | Position: Named 212 | Default value: None 213 | Accept pipeline input: False 214 | Accept wildcard characters: False 215 | ``` 216 | 217 | ### CommonParameters 218 | 219 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 220 | 221 | ## INPUTS 222 | 223 | ### None 224 | 225 | ## OUTPUTS 226 | 227 | ### System.IO.FileInfo 228 | 229 | ## NOTES 230 | 231 | If you run this command from within the integrated terminal in VSCode or in the PowerShell ISE, you can use the dynamic parameter, UseEditor, to open the new file in the current editor. 232 | 233 | Learn more about PowerShell: 234 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 235 | 236 | ## RELATED LINKS 237 | 238 | [New-PSQuiz](New-PSQuiz.md) 239 | 240 | [New-PSQuizQuestion](New-PSQuizQuestion.md) 241 | 242 | [Set-PSQuizFile](Set-PSQuizFile.md) 243 | -------------------------------------------------------------------------------- /docs/New-PSQuizQuestion.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3Onr4rm 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PSQuizQuestion 9 | 10 | ## SYNOPSIS 11 | 12 | Create a new quiz question. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | New-PSQuizQuestion [-Question] [-Answer] [-Distractors] [[-Note] ] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Use this command to create a quiz question. You should save it to a variable and then add it to a quiz file with Set-PSQuizFile. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> $q = New-PSQuizQuestion -Question "What is the default script execution policy" -Answer "Restricted" -Distractors "Unrestricted","AllSigned","RemoteSigned","Bypass" -note "Execution policy is not a security setting." 30 | PS C:\> Set-PSQuizFile -Path D:\temp\pshelp.quiz.json -Question $q -Version 0.2.0 31 | ``` 32 | 33 | Create a new question and then add it to a quiz file. The question Note is optional. 34 | 35 | ### Example 2 36 | 37 | ```powershell 38 | PS C:\> New-PSQuizQuestion | ConvertTo-JSON | Set-Clipboard 39 | ``` 40 | 41 | Run through the prompts to setup a new quiz question. The output will be converted to JSON and copied to the Windows clipboard. You can paste the question directly into the quizzes JSON file. 42 | 43 | ## PARAMETERS 44 | 45 | ### -Answer 46 | 47 | Enter the answer. 48 | Enclose in single quotes if using a variable or $_. 49 | Or escape the $. 50 | 51 | ```yaml 52 | Type: String 53 | Parameter Sets: (All) 54 | Aliases: 55 | 56 | Required: True 57 | Position: 1 58 | Default value: None 59 | Accept pipeline input: True (ByPropertyName) 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -Distractors 64 | 65 | Enter an array of distractors. 66 | Do NOT include your answer. 67 | 3-5 distractors is a good target. 68 | 69 | ```yaml 70 | Type: String[] 71 | Parameter Sets: (All) 72 | Aliases: 73 | 74 | Required: True 75 | Position: 2 76 | Default value: None 77 | Accept pipeline input: True (ByPropertyName) 78 | Accept wildcard characters: False 79 | ``` 80 | 81 | ### -Note 82 | 83 | Enter an optional note to be displayed on correct answers. 84 | Enclose in single quotes if using a variable or $_. 85 | Or escape the $. 86 | 87 | ```yaml 88 | Type: String 89 | Parameter Sets: (All) 90 | Aliases: 91 | 92 | Required: False 93 | Position: 3 94 | Default value: None 95 | Accept pipeline input: True (ByPropertyName) 96 | Accept wildcard characters: False 97 | ``` 98 | 99 | ### -Question 100 | 101 | Enter the question text. 102 | 103 | ```yaml 104 | Type: String 105 | Parameter Sets: (All) 106 | Aliases: 107 | 108 | Required: True 109 | Position: 0 110 | Default value: None 111 | Accept pipeline input: True (ByPropertyName) 112 | Accept wildcard characters: False 113 | ``` 114 | 115 | ### CommonParameters 116 | 117 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 118 | 119 | ## INPUTS 120 | 121 | ### System.String 122 | 123 | ### System.String[] 124 | 125 | ## OUTPUTS 126 | 127 | ### psQuizItem 128 | 129 | ## NOTES 130 | 131 | Learn more about PowerShell: 132 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 133 | 134 | ## RELATED LINKS 135 | 136 | [New-PSQuizFile](New-PSQuizFile.md) 137 | 138 | [New-PSQuiz](New-PSQuiz.md) 139 | -------------------------------------------------------------------------------- /docs/Protect-PSQuizFile.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3S1Iv3M 5 | schema: 2.0.0 6 | --- 7 | 8 | # Protect-PSQuizFile 9 | 10 | ## SYNOPSIS 11 | 12 | Mask answers and distractors in a PSQuiz file. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | Protect-PSQuizFile [-Path] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | PSQuizzes are stored by default in plaintext JSON files. You can use this command to mask the answers and distractors. The obfuscation technique is not complicated and anyone with access to this module can easily reveal the answers. But this will deter casual "cheating." 23 | 24 | For best results, your answers should be in text and not a numeric answer. If you must use a numeric answer, it must not be three digits. 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1 29 | 30 | ```powershell 31 | 32 | PS C:\> Protect-PSQuizFile C:\work\quizzes\remoting.quiz.json 33 | ``` 34 | 35 | ## PARAMETERS 36 | 37 | ### -Confirm 38 | Prompts you for confirmation before running the cmdlet. 39 | 40 | ```yaml 41 | Type: SwitchParameter 42 | Parameter Sets: (All) 43 | Aliases: cf 44 | 45 | Required: False 46 | Position: Named 47 | Default value: None 48 | Accept pipeline input: False 49 | Accept wildcard characters: False 50 | ``` 51 | 52 | ### -Path 53 | Enter the path of the quiz json file. 54 | 55 | ```yaml 56 | Type: String 57 | Parameter Sets: (All) 58 | Aliases: 59 | 60 | Required: True 61 | Position: 0 62 | Default value: None 63 | Accept pipeline input: True (ByPropertyName, ByValue) 64 | Accept wildcard characters: False 65 | ``` 66 | 67 | ### -WhatIf 68 | Shows what would happen if the cmdlet runs. 69 | The cmdlet is not run. 70 | 71 | ```yaml 72 | Type: SwitchParameter 73 | Parameter Sets: (All) 74 | Aliases: wi 75 | 76 | Required: False 77 | Position: Named 78 | Default value: None 79 | Accept pipeline input: False 80 | Accept wildcard characters: False 81 | ``` 82 | 83 | ### CommonParameters 84 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 85 | 86 | ## INPUTS 87 | 88 | ### System.String 89 | 90 | ## OUTPUTS 91 | 92 | ### None 93 | 94 | ## NOTES 95 | 96 | Learn more about PowerShell: 97 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 98 | 99 | ## RELATED LINKS 100 | 101 | [Unprotect-PSQuizFile](Unprotect-PSQuizFile.md) 102 | -------------------------------------------------------------------------------- /docs/Remove-PSQuizSetting.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/47nsYks 5 | schema: 2.0.0 6 | --- 7 | 8 | # Remove-PSQuizSetting 9 | 10 | ## SYNOPSIS 11 | 12 | Remove the PSQuizPath settings file. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | Remove-PSQuizSetting [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | When you run Set-PSQuizPath the command creates a JSON file under $HOME called .psquizsettings.json. Use this command if you want to delete the file. This will also set the value of $PSQuizPath back to the module default. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> Remove-PSQuizSetting 30 | ``` 31 | 32 | ## PARAMETERS 33 | 34 | ### -Confirm 35 | 36 | Prompts you for confirmation before running the cmdlet. 37 | 38 | ```yaml 39 | Type: SwitchParameter 40 | Parameter Sets: (All) 41 | Aliases: cf 42 | 43 | Required: False 44 | Position: Named 45 | Default value: None 46 | Accept pipeline input: False 47 | Accept wildcard characters: False 48 | ``` 49 | 50 | ### -WhatIf 51 | 52 | Shows what would happen if the cmdlet runs. 53 | The cmdlet is not run. 54 | 55 | ```yaml 56 | Type: SwitchParameter 57 | Parameter Sets: (All) 58 | Aliases: wi 59 | 60 | Required: False 61 | Position: Named 62 | Default value: None 63 | Accept pipeline input: False 64 | Accept wildcard characters: False 65 | ``` 66 | 67 | ### CommonParameters 68 | 69 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 70 | 71 | ## INPUTS 72 | 73 | ### None 74 | 75 | ## OUTPUTS 76 | 77 | ### None 78 | 79 | ## NOTES 80 | 81 | Learn more about PowerShell: 82 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 83 | 84 | ## RELATED LINKS 85 | 86 | [Set-PSQuizPath](Set-PSQuizPath.md) 87 | -------------------------------------------------------------------------------- /docs/Set-PSQuizFile.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/45kzFlt 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-PSQuizFile 9 | 10 | ## SYNOPSIS 11 | 12 | Update a quiz file. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | Set-PSQuizFile [-Path] [-Name ] [-Author ] [-Version ] [-Description ] [-Updated ] [-Question ] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Use this command to update a quiz file. You can update the author, version, description, and questions. You can also add new questions to the quiz file. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> Get-PSQuiz -Name "Powershell remoting" | Set-PSQuizFile -Updated (Get-Date) -Version 0.2.1 30 | ``` 31 | 32 | ## PARAMETERS 33 | 34 | ### -Author 35 | 36 | Enter a new author for your quiz. 37 | 38 | ```yaml 39 | Type: String 40 | Parameter Sets: (All) 41 | Aliases: 42 | 43 | Required: False 44 | Position: Named 45 | Default value: None 46 | Accept pipeline input: False 47 | Accept wildcard characters: False 48 | ``` 49 | 50 | ### -Confirm 51 | 52 | Prompts you for confirmation before running the cmdlet. 53 | 54 | ```yaml 55 | Type: SwitchParameter 56 | Parameter Sets: (All) 57 | Aliases: cf 58 | 59 | Required: False 60 | Position: Named 61 | Default value: None 62 | Accept pipeline input: False 63 | Accept wildcard characters: False 64 | ``` 65 | 66 | ### -Description 67 | 68 | Enter a new description for your quiz. 69 | 70 | ```yaml 71 | Type: String 72 | Parameter Sets: (All) 73 | Aliases: 74 | 75 | Required: False 76 | Position: Named 77 | Default value: None 78 | Accept pipeline input: False 79 | Accept wildcard characters: False 80 | ``` 81 | 82 | ### -Name 83 | 84 | Enter a new name for your quiz. 85 | 86 | ```yaml 87 | Type: String 88 | Parameter Sets: (All) 89 | Aliases: 90 | 91 | Required: False 92 | Position: Named 93 | Default value: None 94 | Accept pipeline input: False 95 | Accept wildcard characters: False 96 | ``` 97 | 98 | ### -Path 99 | 100 | Enter the path or directory to store the quiz json file. 101 | 102 | ```yaml 103 | Type: String 104 | Parameter Sets: (All) 105 | Aliases: 106 | 107 | Required: True 108 | Position: 0 109 | Default value: $PSQuizPath 110 | Accept pipeline input: True (ByPropertyName, ByValue) 111 | Accept wildcard characters: False 112 | ``` 113 | 114 | ### -Question 115 | 116 | Enter in a one or more question items. 117 | 118 | ```yaml 119 | Type: Object[] 120 | Parameter Sets: (All) 121 | Aliases: 122 | 123 | Required: False 124 | Position: Named 125 | Default value: None 126 | Accept pipeline input: False 127 | Accept wildcard characters: False 128 | ``` 129 | 130 | ### -Updated 131 | 132 | Enter an optional update value. 133 | The default is today. 134 | 135 | ```yaml 136 | Type: DateTime 137 | Parameter Sets: (All) 138 | Aliases: 139 | 140 | Required: False 141 | Position: Named 142 | Default value: None 143 | Accept pipeline input: False 144 | Accept wildcard characters: False 145 | ``` 146 | 147 | ### -Version 148 | 149 | Enter a new version number for your quiz. 150 | 151 | ```yaml 152 | Type: String 153 | Parameter Sets: (All) 154 | Aliases: 155 | 156 | Required: False 157 | Position: Named 158 | Default value: None 159 | Accept pipeline input: False 160 | Accept wildcard characters: False 161 | ``` 162 | 163 | ### -WhatIf 164 | 165 | Shows what would happen if the cmdlet runs. 166 | The cmdlet is not run. 167 | 168 | ```yaml 169 | Type: SwitchParameter 170 | Parameter Sets: (All) 171 | Aliases: wi 172 | 173 | Required: False 174 | Position: Named 175 | Default value: None 176 | Accept pipeline input: False 177 | Accept wildcard characters: False 178 | ``` 179 | 180 | ### CommonParameters 181 | 182 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 183 | 184 | ## INPUTS 185 | 186 | ### None 187 | 188 | ## OUTPUTS 189 | 190 | ### None 191 | 192 | ## NOTES 193 | 194 | Learn more about PowerShell: 195 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 196 | 197 | ## RELATED LINKS 198 | 199 | [Get-PSQuiz](Get-PSQuiz.md) 200 | 201 | [New-PSQuiz](New-PSQuiz.md) 202 | 203 | [New-PSQuizFile](New-PSQuizFile.md) 204 | 205 | -------------------------------------------------------------------------------- /docs/Set-PSQuizPath.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/45cm27E 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-PSQuizPath 9 | 10 | ## SYNOPSIS 11 | 12 | Save the user's PSQuizPath setting to a file. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | Set-PSQuizPath [-Path] [-Passthru] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | The default location of $PSQuizPath is the module sample quiz folder. This global variable is the default for most commands in this module. Use this command to set a new location. This value will be stored in a settings file under $HOME called .psquizsettings.json. You shouldn't need to do anything with this file. If the file exists, the module will set $PSQuizPath to the saved value. 23 | 24 | The $PSQuizPath variable will also be updated to use the new location. 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1 29 | 30 | ```powershell 31 | PS C:\> Set-PSQuizPath C:\work\quizzes\ 32 | PS C:\> $PSQuizPath 33 | C:\work\quizzes\ 34 | ``` 35 | 36 | ## PARAMETERS 37 | 38 | ### -Confirm 39 | 40 | Prompts you for confirmation before running the cmdlet. 41 | 42 | ```yaml 43 | Type: SwitchParameter 44 | Parameter Sets: (All) 45 | Aliases: cf 46 | 47 | Required: False 48 | Position: Named 49 | Default value: None 50 | Accept pipeline input: False 51 | Accept wildcard characters: False 52 | ``` 53 | 54 | ### -Passthru 55 | 56 | Display the new folder location. 57 | 58 | ```yaml 59 | Type: SwitchParameter 60 | Parameter Sets: (All) 61 | Aliases: 62 | 63 | Required: False 64 | Position: Named 65 | Default value: None 66 | Accept pipeline input: False 67 | Accept wildcard characters: False 68 | ``` 69 | 70 | ### -Path 71 | 72 | Specify the new value for $PSQuizPath. 73 | This will be stored as a persistent value until you change it. 74 | The folder must already exist. 75 | 76 | ```yaml 77 | Type: DirectoryInfo 78 | Parameter Sets: (All) 79 | Aliases: 80 | 81 | Required: True 82 | Position: 0 83 | Default value: None 84 | Accept pipeline input: False 85 | Accept wildcard characters: False 86 | ``` 87 | 88 | ### -WhatIf 89 | 90 | Shows what would happen if the cmdlet runs. 91 | The cmdlet is not run. 92 | 93 | ```yaml 94 | Type: SwitchParameter 95 | Parameter Sets: (All) 96 | Aliases: wi 97 | 98 | Required: False 99 | Position: Named 100 | Default value: None 101 | Accept pipeline input: False 102 | Accept wildcard characters: False 103 | ``` 104 | 105 | ### CommonParameters 106 | 107 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 108 | 109 | ## INPUTS 110 | 111 | ### None 112 | 113 | ## OUTPUTS 114 | 115 | ### None 116 | 117 | ### System.IO.DirectoryInfo 118 | 119 | ## NOTES 120 | 121 | Learn more about PowerShell: 122 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 123 | 124 | ## RELATED LINKS 125 | 126 | [Copy-PSQuizFile](Copy-PSQuizFile.md) 127 | 128 | [Remove-PSQuizSetting](Remove-PSQuizSetting.md) 129 | -------------------------------------------------------------------------------- /docs/Unprotect-PSQuizFile.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSQuizmaster-help.xml 3 | Module Name: PSQuizMaster 4 | online version: https://bit.ly/3S1ELPq 5 | schema: 2.0.0 6 | --- 7 | 8 | # Unprotect-PSQuizFile 9 | 10 | ## SYNOPSIS 11 | 12 | Unmask answers in a PSQuiz file. 13 | 14 | ## SYNTAX 15 | 16 | ```yaml 17 | Unprotect-PSQuizFile [-Path] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | If you masked answers in a PSQuiz file, you can use this command to update the file with unmasked answers and distractors. If the file has not been protected, nothing will be done. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> Unprotect-PSQuizFile C:\work\quizzes\remoting.quiz.json 30 | ``` 31 | 32 | ## PARAMETERS 33 | 34 | ### -Confirm 35 | Prompts you for confirmation before running the cmdlet. 36 | 37 | ```yaml 38 | Type: SwitchParameter 39 | Parameter Sets: (All) 40 | Aliases: cf 41 | 42 | Required: False 43 | Position: Named 44 | Default value: None 45 | Accept pipeline input: False 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -Path 50 | Enter the path of the quiz json file. 51 | 52 | ```yaml 53 | Type: String 54 | Parameter Sets: (All) 55 | Aliases: 56 | 57 | Required: True 58 | Position: 0 59 | Default value: None 60 | Accept pipeline input: True (ByPropertyName, ByValue) 61 | Accept wildcard characters: False 62 | ``` 63 | 64 | ### -WhatIf 65 | Shows what would happen if the cmdlet runs. 66 | The cmdlet is not run. 67 | 68 | ```yaml 69 | Type: SwitchParameter 70 | Parameter Sets: (All) 71 | Aliases: wi 72 | 73 | Required: False 74 | Position: Named 75 | Default value: None 76 | Accept pipeline input: False 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### CommonParameters 81 | 82 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 83 | 84 | ## INPUTS 85 | 86 | ### System.String 87 | 88 | ## OUTPUTS 89 | 90 | ### None 91 | 92 | ## NOTES 93 | 94 | Learn more about PowerShell: 95 | http://jdhitsolutions.com/blog/essential-powershell-resources/ 96 | 97 | ## RELATED LINKS 98 | 99 | [Protect-PSQuizFile](Protect-PSQuizFile.md) 100 | -------------------------------------------------------------------------------- /formats/psquiz.format.ps1xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | default 13 | 14 | PSQuiz 15 | 16 | 17 | "{0} [{1}]" -f $_.Path,$_.Updated 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 21 27 | left 28 | 29 | 30 | 31 | 10 32 | left 33 | 34 | 35 | 36 | 47 37 | left 38 | 39 | 40 | 41 | 10 42 | right 43 | 44 | 45 | 46 | 47 | 48 | 49 | Name 50 | 51 | 52 | Version 53 | 54 | 55 | Description 56 | 57 | 58 | Questions 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /formats/psquizItem.format.ps1xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | default 14 | 15 | psQuizItem 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | question 24 | 25 | 26 | 27 | answer 28 | 29 | 30 | 31 | distractors 32 | 33 | 34 | 35 | note 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /formats/psquizresult.format.ps1xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | default 14 | 15 | psQuizResult 16 | 17 | 18 | Path 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 30 28 | left 29 | 30 | 31 | 32 | 11 33 | right 34 | 35 | 36 | 37 | 11 38 | right 39 | 40 | 41 | 42 | 19 43 | right 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | "{0} [{1}]" -f $_.test,$_.testversion 52 | 53 | 54 | 55 | TotalQuestions 56 | 57 | 58 | 59 | 60 | if ($host.Name -match "code|console") { 61 | [double]$grade = ($_.TotalCorrect/$_.TotalQuestions)*100 62 | if ($grade -ge 75) { 63 | 64 | "$([char]27)[92m$($_.TotalCorrect)$([char]27)[0m" 65 | } 66 | elseif ($grade -ge 45) { 67 | 68 | "$([char]27)[93m$($_.TotalCorrect)$([char]27)[0m" 69 | } 70 | else { 71 | 72 | "$([char]27)[91m$($_.TotalCorrect)$([char]27)[0m" 73 | } 74 | } 75 | else { 76 | $_.Correct 77 | } 78 | 79 | 80 | 81 | TestTime 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /functions/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | ### Changed 3 | - Modify masking commands to also obfuscate or reveal distractors 4 | 5 | -------------------------------------------------------------------------------- /functions/Copy-PSQuizFile.ps1: -------------------------------------------------------------------------------- 1 | Function Copy-PSSampleQuiz { 2 | [cmdletbinding(SupportsShouldProcess)] 3 | [OutputType('System.IO.FileInfo')] 4 | Param( 5 | [Parameter( 6 | Position = 0, 7 | ValueFromPipeline, 8 | HelpMessage = "Specify the target folder. It is assumed that the location will be your new value for `$PSQuizPath. The folder must already exist." 9 | )] 10 | [alias("Destination")] 11 | [ValidateNotNullOrEmpty()] 12 | [ValidateScript({Test-Path $_})] 13 | [System.IO.DirectoryInfo]$Path 14 | ) 15 | 16 | Begin { 17 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" 18 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" 19 | $moduleRoot = (Get-Module PSQuizMaster).path | Split-Path 20 | $moduleDefault = Join-Path -Path $moduleRoot -ChildPath quizzes 21 | } #begin 22 | 23 | Process { 24 | if ($Path -ne $moduleDefault) { 25 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Copying sample quiz files from $moduleDefault to $Path " 26 | Get-ChildItem -path $moduleDefault -filter *.json | 27 | Copy-Item -Destination $Path -PassThru 28 | } 29 | else { 30 | #this should almost never happen 31 | Write-Warning "The path must be different than the module default." 32 | } 33 | } #process 34 | 35 | End { 36 | Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" 37 | } #end 38 | 39 | } #close Copy-PSSampleQuiz -------------------------------------------------------------------------------- /functions/Get-PSQuiz.ps1: -------------------------------------------------------------------------------- 1 | #list PSQuiz json files 2 | Function Get-PSQuiz { 3 | [CmdletBinding()] 4 | [OutputType('psQuiz')] 5 | Param( 6 | [Parameter( 7 | Position = 0, 8 | HelpMessage = 'Specify a quiz name' 9 | )] 10 | [SupportsWildcards()] 11 | [ValidateNotNullOrEmpty()] 12 | [string]$Name, 13 | [Parameter(HelpMessage = 'Enter the path to the folder with quiz json files')] 14 | [String]$Path = $PSQuizPath 15 | ) 16 | 17 | Write-Verbose "Starting $($MyInvocation.MyCommand)" 18 | if ($Name) { 19 | Write-Verbose "Searching for quiz '$Name' under $PSQuizPath" 20 | } 21 | else { 22 | Write-Verbose "Searching for all quizzes under $PSQuizPath" 23 | } 24 | 25 | $get = Get-ChildItem -Path $Path -Filter '*.quiz.json' -PipelineVariable pv | 26 | ForEach-Object { 27 | $json = Get-Content -Path $_.FullName | ConvertFrom-Json 28 | #create a typed custom object for the format file 29 | [PSCustomObject]@{ 30 | PSTypeName = 'psQuiz' 31 | Name = $json.metadata.name 32 | Author = $json.metadata.author 33 | Version = $json.metadata.version 34 | Description = $json.metadata.description 35 | Questions = $json.questions.count 36 | Updated = $json.metadata.updated -as [DateTime] 37 | Path = $pv.FullName 38 | } 39 | } #foreach-object 40 | 41 | Write-Verbose "Found $($get.count) total quizzes" 42 | if ($Name) { 43 | $get | Where-Object { $_.Name -Like $Name } 44 | } 45 | else { 46 | $get 47 | } 48 | Write-Verbose "Ending $($MyInvocation.MyCommand)" 49 | } 50 | -------------------------------------------------------------------------------- /functions/Invoke-PSQuickQuiz.ps1: -------------------------------------------------------------------------------- 1 | Function Invoke-PSQuickQuiz { 2 | [CmdletBinding(DefaultParameterSetName = 'all')] 3 | Param( 4 | [Parameter(ParameterSetName = 'single')] 5 | [ValidateScript( { Get-Module $_ -list })] 6 | #You can specify a single module for testing. The default is all modules. 7 | [String]$ModuleName, 8 | [Parameter(ParameterSetName = 'all')] 9 | [Parameter(HelpMessage = 'Enter a comma separated list of module names to ignore. You can use wildcards.')] 10 | [string[]]$Exclude, 11 | [Parameter(HelpMessage = 'This is used to indicate a continuing test. You should never need to use this parameter.')] 12 | [Switch]$NextQuestion, 13 | [Parameter(HelpMessage = 'Enter a path and filename for a quiz transcript.')] 14 | [ValidateScript( { 15 | $parent = Split-Path $_ 16 | if (Test-Path $parent) { 17 | return $True 18 | } 19 | else { 20 | Throw "Failed to find $parent for your transcript." 21 | return $false 22 | } 23 | })] 24 | [alias('transcript')] 25 | [String]$Path 26 | ) 27 | 28 | If ($Path -AND (-not $NextQuestion)) { 29 | #first time through so initialize a new transcript file. 30 | $header = @" 31 | PowerShell Quiz - $(Get-Date) 32 | ------------------------------------- 33 | PowerShell version: $($PSVersionTable.PSVersion) 34 | User : $($env:USERDOMAIN)\$($env:username) 35 | Quiz parameters : 36 | 37 | $(($PSBoundParameters | Out-String).trim()) 38 | ------------------------------------- 39 | "@ 40 | #create a new transcript file 41 | Set-Content -Value $header -Path $Path 42 | 43 | } 44 | Clear-Host 45 | Write-Verbose "Starting $($MyInvocation.MyCommand)" 46 | Write-Verbose 'PSBoundParameters:' 47 | Write-Verbose $($PSBoundParameters | Out-String).Trim() 48 | 49 | if (-Not $NextQuestion) { 50 | Write-Verbose 'First question setup' 51 | #initialize some variables to keep track of correct answers 52 | $global:QuestionCount = 0 53 | $global:CorrectCount = 0 54 | $global:CommandCache = @() 55 | 56 | if ($ModuleName) { 57 | $Status = "Getting commands from module $ModuleName." 58 | Write-Verbose $Status 59 | Write-Progress -Activity $MyInvocation.MyCommand -Status $status -CurrentOperation 'Please wait...' 60 | $global:CommandCache = Get-Command -CommandType Cmdlet, Function -Module $ModuleName 61 | 62 | } 63 | else { 64 | $status = 'Getting commands from all available modules.' 65 | 66 | if ($exclude) { 67 | $status += " Excluding these modules: $($exclude -join ',') " 68 | #define a separate filter because this causes a problem in PowerShell v6 if $exclude is not specified 69 | $filter = { $_.Source -AND $_.source -notmatch $($exclude -join '|') } 70 | } 71 | else { 72 | $filter = { $_.Source } 73 | } 74 | Write-Verbose $status 75 | Write-Progress -Activity $MyInvocation.MyCommand -Status $status -CurrentOperation 'Please wait...' 76 | #get cmdlets and function that have a defined source which should be a module or snapin 77 | $global:CommandCache = (Get-Command -CommandType Cmdlet, Function).Where($filter) 78 | } 79 | Write-Progress -Activity $MyInvocation.MyCommand -Completed 80 | Write-Verbose "Added $(($global:commandCache).count) commands to the command cache" 81 | if ((($global:commandCache).count) -eq 0) { 82 | Write-Warning 'Failed to find commands in any matching modules.' 83 | #bail out 84 | Return 85 | } 86 | } 87 | else { 88 | Write-Verbose 'Continuing the quiz' 89 | Write-Verbose "Current question count: $($global:QuestionCount)" 90 | Write-Verbose "Current correct count: $($global:CorrectCount)" 91 | Write-Verbose "Current command cache: $(($global:commandCache).count)" 92 | } 93 | 94 | #select a random command with a legitimate synopsis 95 | Write-Verbose 'Selecting a random command' 96 | do { 97 | $cmd = $global:CommandCache | Get-Random 98 | $synopsis = ($cmd | Get-Help).synopsis 99 | 100 | } until ($synopsis -notmatch '(This cmdlet is not supported)|\[|(Fill in the Synopsis)' -AND $synopsis -match '\w{4,}') 101 | 102 | #get other noun related commands 103 | #[object[]]$commands = 104 | #@($cmd) 105 | $commands = [System.Collections.Generic.List[object]]::New() 106 | $commands.Add($cmd) 107 | 108 | #$commands += Get-Command -Noun $cmd.noun | Where-Object { $_.name -ne $cmd.name } | Select-Object -First 4 109 | Get-Command -Noun $cmd.noun | Where-Object { $_.name -ne $cmd.name } | Select-Object -First 4 | ForEach-Object { $commands.Add($_) } 110 | 111 | #get additional random commands if there are not enough noun-related 112 | if ($commands.count -lt 5) { 113 | Write-Verbose 'Getting supplemental commands for answers' 114 | While ($commands.count -lt 5) { 115 | $add = Get-Command -CommandType Cmdlet | Get-Random | 116 | Where-Object { $commands.name -NotContains $_.name } 117 | # $commands += $add 118 | $commands.Add($add) 119 | } 120 | } 121 | #randomize 122 | $commands = $commands | Get-Random -Count $commands.count 123 | 124 | $Title = 'PowerShell Pop Quiz' 125 | $Cue = @" 126 | 127 | Given this short cmdlet description: 128 | 129 | $synopsis 130 | 131 | What command would you use? 132 | 133 | 134 | "@ 135 | 136 | for ($i = 1; $i -lt $commands.count + 1; $i++) { 137 | 138 | $Cue += " [$i]`t$($commands[$i-1].Name)`n" 139 | } 140 | 141 | Write-Host $Title -ForegroundColor Cyan 142 | Write-Host $Cue -ForegroundColor Yellow 143 | If ($Path) { 144 | Add-Content -Value $cue -Path $Path 145 | } 146 | 147 | Do { 148 | try { 149 | [ValidateRange(1, 5)][int32]$r = Read-Host -Prompt 'Select a menu choice [1-5]' -ErrorAction stop 150 | Write-Verbose "You entered $r" 151 | } 152 | Catch { 153 | #ignore the error 154 | Write-Warning $_.exception.message 155 | $r = 0 156 | } 157 | } Until ($r -ge 1 -AND $r -le 5) 158 | 159 | #increment the question counter 160 | $global:QuestionCount++ 161 | if ($commands[$r - 1].name -eq $cmd.Name) { 162 | $global:CorrectCount++ 163 | Write-Host "`nYou are Correct!!" -ForegroundColor green 164 | 165 | If ($Path) { 166 | Add-Content -Value "$($cmd.name) is correct!" -Path $Path 167 | } 168 | } 169 | else { 170 | $msg = "`nThe correct answer is $($cmd.name)" 171 | Write-Host $msg -ForegroundColor Red 172 | if ($Path) { 173 | Add-Content -Value $msg -Path $Path 174 | } 175 | } 176 | 177 | [String]$s = Read-Host "`nDo you want another question? Press any key to continue or Q to quit" 178 | if ($s -match '^q') { 179 | 180 | $score = Get-GPA -Correct $global:CorrectCount -Possible $global:QuestionCount 181 | $result = "`nYou scored {0} correct out of {1} for a GPA of {2}. Your grade is {3}." -f $global:CorrectCount, $global:QuestionCount, $score.gpa, $score.Grade 182 | #colorize output based on gpa 183 | if ($score.Grade -match 'A') { 184 | $fg = 'green' 185 | } 186 | elseif ($score.grade -match 'B|C') { 187 | $fg = 'yellow' 188 | } 189 | else { 190 | $fg = 'red' 191 | } 192 | Write-Host $result -ForegroundColor $fg 193 | if ($path) { 194 | Add-Content -Value $result -Path $Path 195 | Add-Content -Value "`nEnding PowerShell Quiz $(Get-Date)" -Path $Path 196 | Write-Host "`nSee $path for a transcript of this quiz." -ForegroundColor Green 197 | } 198 | Remove-Variable CorrectCount, QuestionCount, CommandCache -Scope global 199 | } 200 | else { 201 | if (-Not ($PSBoundParameters.containsKey('NextQuestion'))) { 202 | Write-Verbose 'Flagging for next question' 203 | $PSBoundParameters.add('NextQuestion', $True) 204 | } 205 | Write-Verbose 'Invoking quiz' 206 | #Write-Verbose ($MyInvocation.MyCommand | Out-String) 207 | &$($MyInvocation.MyCommand) @PSBoundParameters 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /functions/Invoke-PSQuiz.ps1: -------------------------------------------------------------------------------- 1 | #load a quiz from a json file 2 | Function Invoke-PSQuiz { 3 | [CmdletBinding(DefaultParameterSetName = 'path')] 4 | [OutputType('pzQuizResult')] 5 | [alias('Start-PSQuiz')] 6 | Param( 7 | [Parameter( 8 | Position = 0, 9 | Mandatory, 10 | ValueFromPipelineByPropertyName, 11 | HelpMessage = 'Enter the full path to the quiz file', 12 | ParameterSetName = 'path' 13 | )] 14 | [ValidateScript({ Test-Path $_ })] 15 | [String]$Path, 16 | 17 | [Parameter( 18 | ParameterSetName = 'name', 19 | HelpMessage = 'Specify the quiz name.' 20 | )] 21 | [ValidateNotNullOrEmpty()] 22 | [ValidateScript({ 23 | if (Get-PSQuiz -Name $_) { 24 | $True 25 | } 26 | else { 27 | Write-Warning "Can't find a quiz with the name in $PSQuizPath." 28 | $False 29 | } 30 | })] 31 | [String]$Name 32 | ) 33 | Begin { 34 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" 35 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" 36 | } #begin 37 | 38 | Process { 39 | if ($PSCmdlet.ParameterSetName -eq 'Name') { 40 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Getting path to $Name quiz" 41 | $Path = (Get-PSQuiz -Name $Name).path 42 | } 43 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Loading test from $Path" 44 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Test Details:" 45 | $test = Get-Content -Path $path | ConvertFrom-Json 46 | Write-Verbose "`n$(($test.metadata | Out-String).Trim())" 47 | 48 | $in = Get-Content -Path $path | ConvertFrom-Json 49 | 50 | $title = '{0} [v{1}]' -f $in.metadata.name, $in.metadata.version 51 | 52 | $QuestionCount = 0 53 | $CorrectCount = 0 54 | #randomize the questions 55 | $AllQuestions = $in.questions | Get-Random -Count $in.questions.count 56 | #$AllCount is used in the private Invoke-QuizQuestion function 57 | $AllCount = $AllQuestions.count 58 | 59 | #capture the quiz start time 60 | $StartTime = Get-Date 61 | 62 | foreach ($question in $AllQuestions) { 63 | $QuestionCount++ 64 | $answer = $question | Invoke-QuizQuestion -title $title 65 | Write-Verbose "Answer = $answer" 66 | if ($answer -is [Int]) { 67 | Write-Verbose 'Ending the quiz.' 68 | #decrease the question count since the last one didn't technically get an answer 69 | $QuestionCount-- 70 | break 71 | } 72 | elseif ($answer) { 73 | $CorrectCount++ 74 | } 75 | Pause 76 | } 77 | 78 | #capture the stop time 79 | $StopTime = Get-Date 80 | #This is the output from this function 81 | [PSCustomObject]@{ 82 | PSTypeName = 'psQuizResult' 83 | Test = $in.metadata.Name 84 | TestVersion = $in.metadata.version -as [Version] 85 | TotalQuestions = $QuestionCount 86 | TotalCorrect = $CorrectCount 87 | Date = (Get-Date) 88 | TestTime = New-TimeSpan -Start $StartTime -End $StopTime 89 | Path = Convert-Path $Path 90 | } 91 | } #process 92 | End { 93 | Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" 94 | } #end 95 | 96 | } 97 | -------------------------------------------------------------------------------- /functions/New-PSQuiz.ps1: -------------------------------------------------------------------------------- 1 | #This is a wizard-like command that will prompt the user for the information needed to create a new quiz. 2 | 3 | Function New-PSQuiz { 4 | [cmdletbinding()] 5 | [OutputType("None","PSQuiz")] 6 | [Alias('Make-PSQuiz')] 7 | Param( 8 | [Parameter( 9 | Position = 0, 10 | HelpMessage = 'Specify the folder for the new quiz file. The default is $PSQuizPath.' 11 | )] 12 | [ValidateScript({ Test-Path $_ })] 13 | [string]$Path = $PSQuizPath, 14 | [Parameter(HelpMessage = "Mask the answer so it is not displayed in plain text in the JSON file.")] 15 | [switch]$MaskAnswer 16 | ) 17 | DynamicParam { 18 | # Open the new file in the current editor 19 | If ($host.name -match 'code|ise') { 20 | 21 | $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary 22 | 23 | # Defining parameter attributes 24 | $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] 25 | $attributes = New-Object System.Management.Automation.ParameterAttribute 26 | $attributes.ParameterSetName = '__AllParameterSets' 27 | $attributes.HelpMessage = 'Open the new quiz file in the current editor.' 28 | $attributeCollection.Add($attributes) 29 | 30 | # Defining the runtime parameter 31 | $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('UseEditor', [Switch], $attributeCollection) 32 | $paramDictionary.Add('UseEditor', $dynParam1) 33 | 34 | return $paramDictionary 35 | } # end if 36 | } #end DynamicParam 37 | 38 | Begin { 39 | Write-Verbose "Starting $($MyInvocation.MyCommand)" 40 | } #Begin 41 | Process { 42 | 43 | $newQuizFileParams = @{ 44 | Name = Read-Host 'What is the full name of your quiz?' 45 | ShortName = Read-Host 'What is the short quiz name? This will be used as part of the file name' 46 | Description = Read-Host 'Enter a quiz description. You can always edit this later' 47 | Author = Read-Host 'Enter the author name' 48 | Path = $Path 49 | } 50 | 51 | Write-Verbose "Creating the new quiz file called $($new.newQuizFileParams.Name)" 52 | $quizFile = New-PSQuizFile @newQuizFileParams 53 | 54 | $questions = [System.Collections.Generic.list[object]]::New() 55 | 56 | Write-Verbose 'Adding questions to the quiz file' 57 | Do { 58 | $newQuestionParams = @{ 59 | Question = Read-Host 'Enter the question' 60 | Answer = Read-Host 'Enter the answer' 61 | Distractors = (Read-Host 'Enter a comma-separated list of distractors') -split ',' 62 | Note = Read-Host 'Enter any notes for this question' 63 | } 64 | 65 | if ($maskAnswer) { 66 | Write-Verbose "Masking answer" 67 | $newQuestionParams.Add('MaskAnswer',$true) 68 | } 69 | $questions.Add((New-PSQuizQuestion @newQuestionParams)) 70 | $R = Read-Host 'Add another question? (Y/N)' 71 | } While ($r -eq 'Y') 72 | 73 | Write-Verbose "Adding $($questions.Count) questions to the quiz file" 74 | Set-PSQuizFile -path $quizFile.FullName -Question $questions 75 | 76 | Write-Verbose "Quiz file created at $($quizFile.FullName)" 77 | if ($PSBoundParameters.ContainsKey("UseEditor")) { 78 | Write-Verbose "Opening $($quizFile.FullName) in the current editor" 79 | psedit $quizFile.FullName 80 | } 81 | else { 82 | Get-PSQuiz -name $newQuizFileParams.Name -path $Path 83 | } 84 | } #Process 85 | 86 | End { 87 | Write-Verbose "Ending $($MyInvocation.MyCommand)" 88 | } #End 89 | } -------------------------------------------------------------------------------- /functions/New-PSQuizFile.ps1: -------------------------------------------------------------------------------- 1 | Function New-PSQuizFile { 2 | #create a new quiz json file 3 | [CmdletBinding(SupportsShouldProcess)] 4 | [OutputType( { 'System.IO.FileInfo' })] 5 | Param ( 6 | [Parameter( 7 | Position = 0, 8 | Mandatory, 9 | HelpMessage = 'Enter your quiz name' 10 | )] 11 | [ValidateNotNullOrEmpty()] 12 | [String]$Name, 13 | 14 | [Parameter( 15 | Position = 1, 16 | Mandatory, 17 | HelpMessage = 'Enter your quiz short name which will be used as part of the filename.' 18 | )] 19 | [ValidateNotNullOrEmpty()] 20 | [String]$ShortName, 21 | 22 | [Parameter( 23 | Mandatory, 24 | HelpMessage = 'Enter the quiz author.' 25 | )] 26 | [ValidateNotNullOrEmpty()] 27 | [String]$Author, 28 | 29 | [Parameter( 30 | Mandatory, 31 | HelpMessage = 'Enter a quiz description' 32 | )] 33 | [ValidateNotNullOrEmpty()] 34 | [String]$Description, 35 | 36 | [Parameter(HelpMessage = 'Enter a semantic version number for your quiz')] 37 | [ValidateNotNullOrEmpty()] 38 | [version]$Version = '0.1.0', 39 | 40 | [Parameter(HelpMessage = 'Enter the path or directory to store the quiz json file.')] 41 | [ValidateNotNullOrEmpty()] 42 | [ValidateScript( { 43 | if (Test-Path -Path $_) { 44 | return $True 45 | } 46 | else { 47 | Throw "Can't verify $_ as a valid path." 48 | Return $false 49 | } 50 | })] 51 | [String]$Path = $PSQuizPath, 52 | 53 | [Parameter(HelpMessage = "Don't overwrite an existing file with the same name.")] 54 | [Switch]$NoClobber, 55 | 56 | [Parameter(HelpMessage = 'Specify encoding for the text file.')] 57 | [ValidateSet('Unicode', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'ASCII', 'Default', 'OEM')] 58 | [String]$Encoding = 'Unicode' 59 | ) 60 | DynamicParam { 61 | # Open the new file in the current editor 62 | If ($host.name -match 'code|ise') { 63 | 64 | $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary 65 | 66 | # Defining parameter attributes 67 | $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] 68 | $attributes = New-Object System.Management.Automation.ParameterAttribute 69 | $attributes.ParameterSetName = '__AllParameterSets' 70 | $attributes.HelpMessage = 'Open the new quiz file in the current editor.' 71 | $attributeCollection.Add($attributes) 72 | 73 | # Defining the runtime parameter 74 | $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('UseEditor', [Switch], $attributeCollection) 75 | $paramDictionary.Add('UseEditor', $dynParam1) 76 | 77 | return $paramDictionary 78 | } # end if 79 | } #end DynamicParam 80 | 81 | Begin { 82 | Write-Verbose "Starting $($MyInvocation.MyCommand)" 83 | Write-Verbose 'Using these parameter values' 84 | $MyInvocation.BoundParameters | Out-String | Write-Verbose 85 | } #Begin 86 | Process { 87 | $QuizPath = Join-Path -Path $Path -ChildPath "$shortname.quiz.json" 88 | $MetaHash = [ordered]@{ 89 | name = $Name 90 | author = $Author 91 | description = $Description 92 | version = $Version.ToString() 93 | id = (New-Guid).guid 94 | updated = '{0:u}' -f (Get-Date).ToUniversalTime() 95 | } 96 | 97 | $QuizFile = [PSCustomObject]@{ 98 | '$schema' = $PSQuizSchema 99 | metadata = $MetaHash 100 | questions = @() 101 | } 102 | 103 | if ($PSCmdlet.ShouldProcess($QuizPath, "Create Quiz file $Name by $Author [$version]")) { 104 | Try { 105 | $QuizFile | 106 | ConvertTo-Json -ErrorAction Stop | 107 | Out-File -FilePath $QuizPath -Encoding $encoding -NoClobber:$NoClobber -ErrorAction Stop 108 | 109 | } 110 | Catch { 111 | Throw $_ 112 | } 113 | #give the file an opportunity to close 114 | Start-Sleep -Seconds 1 115 | if ($PSBoundParameters.ContainsKey("UseEditor")) { 116 | psedit $QuizPath 117 | } 118 | else { 119 | #write the file object to the pipeline 120 | Get-Item -Path $QuizPath 121 | } 122 | } 123 | } #Process 124 | End { 125 | Write-Verbose "Ending $($MyInvocation.MyCommand)" 126 | 127 | } #End 128 | } 129 | -------------------------------------------------------------------------------- /functions/New-PSQuizQuestion.ps1: -------------------------------------------------------------------------------- 1 | Function New-PSQuizQuestion { 2 | #create a new PSQuizQuestion 3 | [CmdletBinding()] 4 | [OutputType('psQuizItem')] 5 | Param( 6 | [Parameter( 7 | Mandatory, 8 | ValueFromPipelineByPropertyName, 9 | HelpMessage = 'Enter the question text' 10 | )] 11 | [String]$Question, 12 | [Parameter( 13 | Mandatory, 14 | ValueFromPipelineByPropertyName, 15 | HelpMessage = "Enter the answer. Enclose in single quotes if using a variable or `$_. Or escape the `$." 16 | )] 17 | [String]$Answer, 18 | [Parameter( 19 | Mandatory, 20 | ValueFromPipelineByPropertyName, 21 | HelpMessage = 'Enter an array of distractors. Do NOT include your answer. 3-5 distractors is a good target.' 22 | )] 23 | [string[]]$Distractors, 24 | [Parameter( 25 | ValueFromPipelineByPropertyName, 26 | HelpMessage = "Enter an optional note to be displayed on correct answers. Enclose in single quotes if using a variable or `$_. Or escape the `$." 27 | )] 28 | [String]$Note, 29 | [Parameter(HelpMessage = "Mask the answer so it is not displayed in plain text in the JSON file.")] 30 | [switch]$MaskAnswer 31 | ) 32 | 33 | Begin { 34 | Write-Verbose "Starting $($MyInvocation.MyCommand)" 35 | } #begin 36 | Process { 37 | Write-Verbose "Processing quiz file $Path" 38 | Write-Verbose 'Using these bound parameters' 39 | $PSBoundParameters | Out-String | Write-Verbose 40 | 41 | #Modified 8/15/2023 to allow for a masked answer. Issue #3 42 | if ($MaskAnswer) { 43 | Write-Verbose "Masking answer $Answer" 44 | $Answer = _hideAnswer $Answer 45 | } 46 | #create a copy of PSBoundParameters 47 | $Data = [PSCustomObject]@{ 48 | PSTypeName = 'psQuizItem' 49 | question = $Question 50 | answer = $Answer 51 | distractors = $Distractors -as [array] 52 | note = $Note 53 | } 54 | 55 | $data 56 | 57 | } #process 58 | End { 59 | Write-Verbose "Ending $($MyInvocation.MyCommand)" 60 | } #end 61 | } 62 | -------------------------------------------------------------------------------- /functions/Protect-PSQuizfile.ps1: -------------------------------------------------------------------------------- 1 | Function Protect-PSQuizFile { 2 | [cmdletbinding(SupportsShouldProcess)] 3 | [OutputType('None')] 4 | Param( 5 | [Parameter( 6 | Position = 0, 7 | Mandatory, 8 | ValueFromPipeline, 9 | ValueFromPipelineByPropertyName, 10 | HelpMessage = 'Enter the path of the quiz json file.' 11 | )] 12 | [ValidateNotNullOrEmpty()] 13 | [ArgumentCompleter({ 14 | (Get-ChildItem -path $PSQuizPath -Filter *.json).fullName 15 | })] 16 | [ValidateScript( { 17 | if (Test-Path -Path $_) { 18 | return $True 19 | } 20 | else { 21 | Throw "Can't verify $_ as a valid path." 22 | Return $false 23 | } 24 | })] 25 | [String]$Path 26 | ) 27 | 28 | Begin { 29 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" 30 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" 31 | } #begin 32 | 33 | Process { 34 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Protecting answers in $Path" 35 | $quiz = Get-Content -path $Path | ConvertFrom-Json 36 | foreach ($question in $quiz.questions) { 37 | #only hide answer if not already hidden 38 | if ($question.answer -notMatch "^(\d{3})+") { 39 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Masking answer: $($question.answer)" 40 | $question.answer = _hideAnswer $question.Answer 41 | } 42 | #17 Oct 2024 mask distractors 43 | $maskedDistractors = @() 44 | foreach ($distractor in $question.distractors) { 45 | if ($distractor -notMatch "^(\d{3})+") { 46 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Masking distractor: $($distractor)" 47 | $maskedDistractors += _hideAnswer $distractor 48 | } 49 | } #foreach distractor 50 | #replace distractors with masked values 51 | $question.distractors = $maskedDistractors 52 | } #foreach question 53 | $quiz | ConvertTo-Json -Depth 5 | Out-File -FilePath $Path -Encoding Unicode -Force 54 | } #process 55 | 56 | End { 57 | Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" 58 | } #end 59 | 60 | } #close Protect-PSQuizFile -------------------------------------------------------------------------------- /functions/Remove-PSQuizSetting.ps1: -------------------------------------------------------------------------------- 1 | Function Remove-PSQuizSetting { 2 | [cmdletbinding(SupportsShouldProcess)] 3 | [OutputType('None')] 4 | Param( ) 5 | 6 | Begin { 7 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" 8 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" 9 | 10 | $moduleRoot = (Get-Module PSQuizMaster).path | Split-Path 11 | $moduleDefault = Join-Path -Path $moduleRoot -ChildPath quizzes 12 | } #begin 13 | 14 | Process { 15 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Removing $PSQuizSettingsFile" 16 | if (Test-Path -path $PSQuizSettingsFile) { 17 | #delete the file 18 | Remove-Item -path $PSQuizSettingsFile 19 | #set PSQuizPath back to module default 20 | Set-Variable -Name PSQuizPath -Value $moduleDefault -Scope Global 21 | } 22 | else { 23 | Write-Warning "Can't verify $PSQuizSettingsFile." 24 | } 25 | 26 | } #process 27 | 28 | End { 29 | Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" 30 | } #end 31 | 32 | } #close Remove-PSQuizSetting -------------------------------------------------------------------------------- /functions/Set-PSQuizFile.ps1: -------------------------------------------------------------------------------- 1 | #update a quiz file with new metadata and or questions 2 | Function Set-PSQuizFile { 3 | [CmdletBinding(SupportsShouldProcess)] 4 | Param( 5 | [Parameter( 6 | Position = 0, 7 | Mandatory, 8 | ValueFromPipeline, 9 | ValueFromPipelineByPropertyName, 10 | HelpMessage = 'Enter the path of the quiz json file.')] 11 | [ValidateNotNullOrEmpty()] 12 | [ArgumentCompleter({ 13 | (Get-ChildItem -path $PSQuizPath -Filter *.json).fullName 14 | })] 15 | [ValidateScript( { 16 | if (Test-Path -Path $_) { 17 | return $True 18 | } 19 | else { 20 | Throw "Can't verify $_ as a valid path." 21 | Return $false 22 | } 23 | })] 24 | [String]$Path, 25 | [Parameter(HelpMessage = 'Enter a new name for your quiz')] 26 | [ValidateNotNullOrEmpty()] 27 | [String]$Name, 28 | [Parameter(HelpMessage = 'Enter a new author for your quiz')] 29 | [ValidateNotNullOrEmpty()] 30 | [String]$Author, 31 | [Parameter(HelpMessage = 'Enter a new version number for your quiz')] 32 | [ValidateNotNullOrEmpty()] 33 | [String]$Version, 34 | [Parameter(HelpMessage = 'Enter a new description for your quiz')] 35 | [ValidateNotNullOrEmpty()] 36 | [String]$Description, 37 | [ValidateNotNullOrEmpty()] 38 | [Parameter(HelpMessage = 'Enter an optional update value. The default is today.')] 39 | [DateTime]$Updated = $(Get-Date), 40 | [Parameter(HelpMessage = 'Enter in a one or more question items')] 41 | [ValidateNotNullOrEmpty()] 42 | [object[]]$Question 43 | ) 44 | Begin { 45 | Write-Verbose "Starting $($MyInvocation.MyCommand)" 46 | } #begin 47 | Process { 48 | Write-Verbose "Getting file content from $Path - converted to json" 49 | $content = Get-Content -Path $Path | ConvertFrom-Json 50 | Write-Verbose 'Updating metadata' 51 | $PSBoundParameters.Keys.toLower() | Where-Object { $_ -match 'name|author|version|description' } | 52 | ForEach-Object { 53 | Write-Verbose "...$_ $($PSBoundParameters.Item($_))" 54 | $content.metadata.$_ = $PSBoundParameters.Item($_) 55 | } 56 | #set the date in metadata 57 | $updateTime = '{0:u}' -f $Updated.ToUniversalTime() 58 | Write-Verbose "Setting update time to $updateTime" 59 | $content.metadata.updated = $updateTime 60 | 61 | if ($content.questions -AND $PSBoundParameters.ContainsKey('question')) { 62 | Write-Verbose 'Appending to existing questions' 63 | $content.questions += $question 64 | } 65 | elseif ($PSBoundParameters.ContainsKey('question')) { 66 | Write-Verbose 'Defining new questions' 67 | $content | Add-Member -MemberType NoteProperty -Name questions -Value $question -Force 68 | } 69 | 70 | $set = [PSCustomObject]@{ 71 | '$schema' = $PSQuizSchema 72 | metadata = $content.metadata 73 | questions = $content.questions 74 | } 75 | if ($PSCmdlet.ShouldProcess($path)) { 76 | $set | ConvertTo-Json -Depth 5 | Out-File -FilePath $Path -Encoding Unicode -Force 77 | } 78 | } #process 79 | End { 80 | Write-Verbose "Ending $($MyInvocation.MyCommand)" 81 | } #3nd 82 | } 83 | -------------------------------------------------------------------------------- /functions/Set-PSQuizPath.ps1: -------------------------------------------------------------------------------- 1 | Function Set-PSQuizPath { 2 | [cmdletbinding(SupportsShouldProcess)] 3 | [OutputType('None','System.IO.DirectoryInfo')] 4 | Param( 5 | [Parameter( 6 | Position = 0, 7 | Mandatory, 8 | HelpMessage= "Specify the new value for `$PSQuizPath. This will be stored as a persistent value until you change it. The folder must already exist." 9 | )] 10 | [ValidateScript({Test-Path $_})] 11 | [System.IO.DirectoryInfo]$Path, 12 | [switch]$Passthru 13 | ) 14 | 15 | Begin { 16 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" 17 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" 18 | } #begin 19 | 20 | Process { 21 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Updating `$PSQuizPath to $Path" 22 | $settings = [PSCustomObject]@{ 23 | PSQuizPath = $Path.FullName 24 | Updated = (Get-Date).ToString() 25 | Computername = [System.Environment]::MachineName 26 | } 27 | #PSQuizSettingsFile is a private variable defined in PSQuizMaster.psm1 28 | $settings | ConvertTo-Json | Out-File -FilePath $PSQuizSettingsFile 29 | 30 | if ($PSCmdlet.ShouldProcess($Path.FullName)) { 31 | #update the variable in case it is using an old value 32 | Set-Variable -name PSQuizPath -value $Path.FullName -Scope Global 33 | } 34 | 35 | if ($Passthru -AND (-Not $WhatIfPreference)) { 36 | Get-Item -path $Path 37 | } 38 | } #process 39 | 40 | End { 41 | Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" 42 | } #end 43 | 44 | } #close Set-PSQuizPath -------------------------------------------------------------------------------- /functions/Unprotect-PSQuizFile.ps1: -------------------------------------------------------------------------------- 1 | Function Unprotect-PSQuizFile { 2 | [cmdletbinding(SupportsShouldProcess)] 3 | [OutputType('None')] 4 | Param( 5 | [Parameter( 6 | Position = 0, 7 | Mandatory, 8 | ValueFromPipeline, 9 | ValueFromPipelineByPropertyName, 10 | HelpMessage = 'Enter the path of the quiz json file.')] 11 | [ValidateNotNullOrEmpty()] 12 | [ArgumentCompleter({ 13 | (Get-ChildItem -Path $PSQuizPath -Filter *.json).fullName 14 | })] 15 | [ValidateScript( { 16 | if (Test-Path -Path $_) { 17 | return $True 18 | } 19 | else { 20 | Throw "Can't verify $_ as a valid path." 21 | Return $false 22 | } 23 | })] 24 | [String]$Path 25 | ) 26 | 27 | Begin { 28 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)" 29 | Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)" 30 | } #begin 31 | 32 | Process { 33 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Unmasking answers in $Path" 34 | $quiz = Get-Content -Path $Path | ConvertFrom-Json 35 | foreach ($question in $quiz.questions) { 36 | if ($question.answer -Match '^(\d{3})+') { 37 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] From: $($question.answer)" 38 | $question.answer = _showAnswer $question.Answer 39 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] To: $($question.answer)" 40 | } 41 | #17 Oct 2024 mask distractors 42 | $unmaskedDistractors = @() 43 | foreach ($distractor in $question.distractors) { 44 | if ($distractor -Match '^(\d{3})+') { 45 | Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Unmasking distractor: $($distractor)" 46 | $unmaskedDistractors += _showAnswer $distractor 47 | } 48 | } #foreach distractor 49 | #replace distractors with masked values 50 | $question.distractors = $unmaskedDistractors 51 | } #foreach Question 52 | 53 | $quiz | ConvertTo-Json -Depth 5 | Out-File -FilePath $Path -Encoding Unicode -Force 54 | } #process 55 | 56 | End { 57 | Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)" 58 | } #end 59 | 60 | } #close Protect-PSQuizFile -------------------------------------------------------------------------------- /functions/private.ps1: -------------------------------------------------------------------------------- 1 | #private functions for this module 2 | 3 | Function Invoke-QuizQuestion { 4 | [CmdletBinding()] 5 | Param( 6 | [Parameter(mandatory, ValueFromPipelineByPropertyName)] 7 | [ValidateNotNullOrEmpty()] 8 | [String]$Question, 9 | [Parameter(mandatory, ValueFromPipelineByPropertyName)] 10 | [ValidateNotNullOrEmpty()] 11 | [String]$Answer, 12 | [Parameter(mandatory, ValueFromPipelineByPropertyName)] 13 | [ValidateNotNullOrEmpty()] 14 | [string[]]$Distractors, 15 | [Parameter(ValueFromPipelineByPropertyName)] 16 | [String]$Note, 17 | [String]$Title = 'PowerShell Quiz' 18 | ) 19 | 20 | Begin { 21 | Write-Verbose "Starting $($MyInvocation.MyCommand)" 22 | } 23 | 24 | Process { 25 | Write-Verbose $Question 26 | 27 | #Modified 8/15/2023 to allow for a masked answer. Issue #3 28 | if ($answer -match "^[\s\d+]+$") { 29 | Write-Verbose 'Unmasking Answer' 30 | $answer = _showAnswer -ProtectedAnswer $answer 31 | } 32 | 33 | Write-Verbose "Detected $($distractors.count) distractors" 34 | #17 Oct 2024 unmask distractors if protected 35 | $Distractors = foreach ($distractor in $Distractors) { 36 | if ($distractor -match "^[\s\d+]+$") { 37 | Write-Verbose "Unmasking distractor: $distractor" 38 | _showAnswer -ProtectedAnswer $distractor 39 | } 40 | else { 41 | $distractor 42 | } 43 | } 44 | $possible = @($Answer, $Distractors) | Get-Random -Count ($Distractors.count + 1) 45 | 46 | $cue = @" 47 | 48 | $Question 49 | $('-'*75) 50 | 51 | "@ 52 | 53 | for ($i = 1; $i -lt $possible.count + 1; $i++) { 54 | $cue += "[$i] $($possible[$i-1])`n" 55 | } 56 | 57 | $cue += "[$i] Quit`n" 58 | $cue += $('-' * 75) 59 | Write-Host $Title -ForegroundColor Cyan 60 | Write-Host ('Question {0}/{1}' -f $QuestionCount, $AllCount) -ForegroundColor green 61 | Write-Host $cue 62 | 63 | $count = $Distractors.count + 1 64 | Write-Verbose "$count answers" 65 | Do { 66 | try { 67 | [ValidateScript( { $_ -ge 1 -AND $_ -le $count + 1 })][int32]$r = Read-Host -Prompt 'Select a menu choice [1-5]' -ErrorAction stop 68 | Write-Verbose "You entered $r" 69 | } 70 | Catch { 71 | #ignore the error 72 | #Write-Warning $_.exception.message 73 | Write-Warning "Please select a value between 1 and $($count+1)" 74 | $r = 0 75 | } 76 | } Until ($r -gt 0) 77 | 78 | if ($possible[$r - 1] -eq $answer) { 79 | Write-Host 'Correct!' -ForegroundColor green 80 | $True 81 | } 82 | elseif ($r -eq $count + 1) { 83 | Write-Verbose 'You selected Quit' 84 | return -1 85 | } 86 | else { 87 | Write-Host "The correct answer is: $answer" -ForegroundColor magenta 88 | $false 89 | } 90 | 91 | if ($Note) { 92 | Write-Host "`nAdditional Notes" -ForegroundColor yellow 93 | Write-Host '----------------' -ForegroundColor yellow 94 | Write-Host $Note -ForegroundColor Yellow 95 | Write-Host "`n" 96 | } 97 | } #process 98 | End { 99 | Write-Verbose "Ending $($MyInvocation.MyCommand)" 100 | } 101 | 102 | } #close function 103 | 104 | Function Get-GPA { 105 | [CmdletBinding()] 106 | Param([int32]$Correct, [int32]$Possible) 107 | 108 | $grades = [ordered]@{ 109 | 'A' = 4 110 | 'A-' = 3.7 111 | 'B+' = 3.3 112 | 'B' = 3 113 | 'B-' = 2.7 114 | 'C+' = 2.3 115 | 'C' = 2.0 116 | 'C-' = 1.7 117 | 'D+' = 1.3 118 | 'D' = 1 119 | 'D-' = .7 120 | 'F' = 0 121 | } 122 | 123 | $pct = ($Correct / $Possible) * 100 124 | $gpa = [math]::round(($pct / 20), 1) 125 | $grade = $grades.GetEnumerator() | Where-Object { $_.value -le $gpa } | Select-Object -First 1 126 | 127 | [PSCustomObject]@{ 128 | Grade = $grade.name 129 | Minimum = $grade.Value 130 | GPA = $GPA 131 | } 132 | } #end function 133 | 134 | #functions to obfuscate the answer 135 | 136 | <# 137 | [regex]$word = "\b\S+\b" 138 | $message = "I am foo" 139 | $test = foreach ($part in $message) { 140 | $word.Matches($part).Value.ForEach({ 141 | $_.toCharArray().Foreach({ "{0:d3}" -f ($_ -as [int])}) -join '' 142 | }) -join ' ' 143 | } 144 | 145 | 146 | [regex]$number = "\d{3}" 147 | $out = foreach ($part in $test.split()) { 148 | #write-host $part -fore cyan 149 | $number.Matches($part).Value.ForEach({ 150 | ([int]$_ -as [string][char]) 151 | }) -join '' 152 | } 153 | $out -join ' ' 154 | 155 | #> 156 | 157 | Function _hideAnswer { 158 | Param([string]$Answer) 159 | [regex]$word = '\S+' 160 | foreach ($part in $Answer) { 161 | $word.Matches($part).Value.ForEach({ 162 | $_.toCharArray().Foreach({ '{0:d3}' -f ($_ -as [int]) }) -join '' 163 | }) -join ' ' 164 | } 165 | } 166 | 167 | Function _showAnswer { 168 | Param([string]$ProtectedAnswer) 169 | [regex]$number = '\d{3}' 170 | $out = foreach ($part in $ProtectedAnswer.split()) { 171 | $number.Matches($part).Value.ForEach({ 172 | ([int]$_ -as [string][char]) 173 | }) -join '' 174 | } 175 | $out -join ' ' 176 | } 177 | 178 | Function _GetSelected { 179 | $Context = $psEditor.GetEditorContext() 180 | $selected = [Microsoft.PowerShell.EditorServices.Extensions.FileRange, Microsoft.PowerShell.EditorServices]::new($Context.SelectedRange.Start, $Context.SelectedRange.end) 181 | [string]$text = $Context.CurrentFile.GetText($selected) 182 | [PSCustomObject]@{ 183 | File = $Context.CurrentFile.Path 184 | SelectedText = $text 185 | Start = $Selected.Start 186 | End = $selected.end 187 | Context = $Context 188 | } 189 | } -------------------------------------------------------------------------------- /psquiz.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": {}, 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$id": "https://example.com/object1688651416.json", 5 | "title": "PSQuiz Schema", 6 | "description": "Schema configuration for PSQuiz files that are part of the PSQuizMaster PowerShell module.", 7 | "type": "object", 8 | "required": [ 9 | "metadata", 10 | "questions" 11 | ], 12 | "properties": { 13 | "metadata": { 14 | "$id": "#root/metadata", 15 | "title": "metadata", 16 | "description": "Required metadata about the quiz", 17 | "type": "object", 18 | "required": [ 19 | "name", 20 | "author", 21 | "description", 22 | "version", 23 | "id", 24 | "updated" 25 | ], 26 | "properties": { 27 | "name": { 28 | "$id": "#root/metadata/name", 29 | "title": "name", 30 | "description": "What is the long name of the quiz", 31 | "type": "string", 32 | "default": "", 33 | "examples": [ 34 | "PowerShell Aliases", 35 | "Intro to PSRemoting" 36 | ], 37 | "pattern": "^.*$" 38 | }, 39 | "author": { 40 | "$id": "#root/metadata/author", 41 | "title": "author", 42 | "description": "Enter the author's name or contact information.", 43 | "type": "string", 44 | "default": "", 45 | "examples": [ 46 | "Jeff Hicks" 47 | ], 48 | "pattern": "^.*$" 49 | }, 50 | "description": { 51 | "$id": "#root/metadata/description", 52 | "title": "description", 53 | "description": "Enter a brief quiz description", 54 | "type": "string", 55 | "default": "", 56 | "examples": [ 57 | "A short quiz on using aliases in PowerShell." 58 | ], 59 | "pattern": "^.*$" 60 | }, 61 | "version": { 62 | "$id": "#root/metadata/version", 63 | "title": "version", 64 | "description": "Enter a semantic version number", 65 | "type": "string", 66 | "default": "", 67 | "examples": [ 68 | "0.2.0", 69 | "1.0.2" 70 | ], 71 | "pattern": "^\\d+\\." 72 | }, 73 | "id": { 74 | "$id": "#root/metadata/id", 75 | "title": "id", 76 | "description": "Enter a GUID id for the test", 77 | "type": "string", 78 | "default": "", 79 | "examples": [ 80 | "68d30b11-971e-4104-9d76-d862bcef6a37" 81 | ], 82 | "pattern": "[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}" 83 | }, 84 | "updated": { 85 | "$id": "#root/metadata/updated", 86 | "title": "updated", 87 | "description": "Enter the last update time as UTC. \"{0:u}\" -f (Get-Date).ToUniversalTime()", 88 | "type": "string", 89 | "default": "", 90 | "examples": [ 91 | "2023-06-27 22:26:48Z" 92 | ], 93 | "pattern": "^\\d{4}(\\-\\d{2}){2}\\s(\\d{2}:){2}\\d{2}Z$" 94 | } 95 | } 96 | }, 97 | "questions": { 98 | "$id": "#root/questions", 99 | "title": "questions", 100 | "description": "Enter an array of question objects", 101 | "type": "array", 102 | "default": [], 103 | "items": { 104 | "title": "items", 105 | "type": "object", 106 | "required": [ 107 | "question", 108 | "answer", 109 | "distractors" 110 | ], 111 | "properties": { 112 | "question": { 113 | "$id": "#root/questions/items/question", 114 | "title": "question", 115 | "description": "Enter your multiple choice question.", 116 | "type": "string", 117 | "default": "", 118 | "examples": [ 119 | "What command will display currently defined PowerShell aliases?" 120 | ], 121 | "pattern": "^.*$" 122 | }, 123 | "answer": { 124 | "$id": "#root/questions/items/answer", 125 | "title": "answer", 126 | "description": "Enter the answer", 127 | "type": "string", 128 | "default": "", 129 | "examples": [ 130 | "Get-Alias" 131 | ], 132 | "pattern": "^.*$" 133 | }, 134 | "distractors": { 135 | "$id": "#root/questions/items/distractors", 136 | "title": "distractors", 137 | "description": "Enter a comma separated list of 3-5 wrong answers", 138 | "type": "array", 139 | "default": [], 140 | "items": { 141 | "$id": "#root/questions/items/distractors/items", 142 | "title": "items", 143 | "type": "string", 144 | "default": "", 145 | "examples": [ 146 | "Show-Alias" 147 | ], 148 | "pattern": "^.*$" 149 | } 150 | }, 151 | "note": { 152 | "$id": "#root/questions/items/note", 153 | "title": "note", 154 | "description": "Enter optional notes or information", 155 | "type": "string", 156 | "default": "", 157 | "examples": [ 158 | "" 159 | ] 160 | } 161 | } 162 | } 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /quizzes/Aliases.quiz.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/quizzes/Aliases.quiz.json -------------------------------------------------------------------------------- /quizzes/arrays-hashtables.quiz.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/quizzes/arrays-hashtables.quiz.json -------------------------------------------------------------------------------- /quizzes/demo.quiz.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/quizzes/demo.quiz.json -------------------------------------------------------------------------------- /quizzes/pshelp.quiz.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/quizzes/pshelp.quiz.json -------------------------------------------------------------------------------- /quizzes/remoting.quiz.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/PSQuizMaster/406703e7afe18d24a2218909a84c641d0b5e4bcb/quizzes/remoting.quiz.json --------------------------------------------------------------------------------