diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..194474c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,43 @@ + +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/powershell +{ + "name": "PowerShell", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/powershell:lts-ubuntu-22.04", + + "features": { + "ghcr.io/devcontainers/features/common-utils:2": {}, + "ghcr.io/devcontainers/features/sshd:1": {}, + "ghcr.io/devcontainers/features/dotnet:1": {}, + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + + "postCreateCommand": "sudo chsh vscode -s \"$(which pwsh)\"", + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.defaultProfile.linux": "pwsh" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-vscode.powershell", + "github.copilot", + "GitHub.copilot-chat", + "mhutchie.git-graph" + ] + } + } + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} + diff --git a/Test/public/convertMeetingMembersToMarkdown.test.ps1 b/Test/public/convertMeetingMembersToMarkdown.test.ps1 new file mode 100644 index 0000000..22bf1c8 --- /dev/null +++ b/Test/public/convertMeetingMembersToMarkdown.test.ps1 @@ -0,0 +1,149 @@ +function Test_ConvertMeetingMembersToMarkdown_SingleCompany { + + # Arrange + $input = "Alice Johnson , `"Bob Smith (AlphaTech)`" " + + # Act + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $input + + # Assert + $expected = @" +- Alphatech + - Alice Johnson + - "Bob Smith (AlphaTech)" +"@ + Assert-AreEqual -Expected $expected -Presented $result -Comment "Single company output should match expected format" +} + +function Test_ConvertMeetingMembersToMarkdown_MultipleCompanies { + + # Arrange + $input = "Alice Johnson , `"Bob Smith (AlphaTech)`" , Alice Johnson , `"Charlie Brown, David`" , `"Emma Wilson, Frank`" , `"Grace Lee, Henry`" " + + # Act + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $input + + # Assert + $expected = @" +- Alphatech + - Alice Johnson + - "Bob Smith (AlphaTech)" +- Betasoft + - Alice Johnson +- Gammatech + - "Charlie Brown, David" + - "Emma Wilson, Frank" + - "Grace Lee, Henry" +"@ + Assert-AreEqual -Expected $expected -Presented $result -Comment "Multiple companies should be sorted alphabetically" +} + +function Test_ConvertMeetingMembersToMarkdown_DuplicateMemberDifferentCompanies { + + # Arrange + $input = "Alice Johnson , Alice Johnson " + + # Act + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $input + + # Assert + # Same person with different emails should appear in both company groups + $expected = @" +- Alphatech + - Alice Johnson +- Betasoft + - Alice Johnson +"@ + Assert-AreEqual -Expected $expected -Presented $result -Comment "Same person with different emails should appear in both companies" +} + +function Test_ConvertMeetingMembersToMarkdown_SpecialCharactersInName { + + # Arrange + $input = "`"Bob Smith (AlphaTech)`" , `"Charlie Brown, David`" " + + # Act + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $input + + # Assert + # Names with special characters (parentheses, commas) should be preserved + $expected = @" +- Alphatech + - "Bob Smith (AlphaTech)" +- Gammatech + - "Charlie Brown, David" +"@ + Assert-AreEqual -Expected $expected -Presented $result -Comment "Names with special characters should be preserved" +} + +function Test_ConvertMeetingMembersToMarkdown_EmptyInput { + + # Arrange + $input = "" + + # Act + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $input + + # Assert + Assert-AreEqual -Expected "" -Presented $result -Comment "Empty input should return empty string" +} + +function Test_ConvertMeetingMembersToMarkdown_WhitespaceOnlyInput { + + # Arrange + $input = " " + + # Act + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $input + + # Assert + Assert-AreEqual -Expected "" -Presented $result -Comment "Whitespace-only input should return empty string" +} + +function Test_ConvertMeetingMembersToMarkdown_SingleMember { + + # Arrange + $input = "John Doe " + + # Act + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $input + + # Assert + $expected = @" +- Example + - John Doe +"@ + Assert-AreEqual -Expected $expected -Presented $result -Comment "Single member should work correctly" +} + +function Test_ConvertMeetingsMembersToMarkdown_Big_sample{ + + $imput = @" +"Alice Anderson" , "Amy Adams (She/Her)" , "Bob Brown" , "Charlie Chen" , david.davis@betasoft.com, "David Dennis" , "Emma Evans, Eric" , emma.edwards@betasoft.com, "Frank Fields, Fiona" , george.garcia@bookings.betasoft.com, george.garcia@betasoft.com, "Grace (AlphaTech) Garcia" , "Henry Harris" , "Iris Ingram" , "Jack Johnson" , "James Jackson" , "Jennifer Jones" , "Kevin Kim" , "Kyle Knight" , lisa.lee@betasoft.com, "Laura Lewis" , "Mark Martinez" +"@ + + $result = Convert-MeetingMembersToMarkdown -MeetingMembers $imput + + Assert-AreEqual -Presented $result -Expected @" +- Alphatech + - "Grace (AlphaTech) Garcia" + - "Henry Harris" + - "Kevin Kim" + - "Laura Lewis" + - "Mark Martinez" +- Betasoft + - "Amy Adams (She/Her)" + - "Bob Brown" + - "Charlie Chen" + - "David Dennis" + - "Iris Ingram" + - "Jack Johnson" + - "James Jackson" + - "Jennifer Jones" + - "Kyle Knight" +- Deltalab + - "Alice Anderson" + - "Emma Evans, Eric" + - "Frank Fields, Fiona" +"@ +} \ No newline at end of file diff --git a/private/parseMemberEmail.ps1 b/private/parseMemberEmail.ps1 new file mode 100644 index 0000000..c0a3509 --- /dev/null +++ b/private/parseMemberEmail.ps1 @@ -0,0 +1,81 @@ + +function parseMemberEmail { + [CmdletBinding()] + param( + [Parameter(Mandatory, ValueFromPipeline)][string]$MemberString + ) + + process { + $memberString = $MemberString.Trim() + + if ([string]::IsNullOrWhiteSpace($memberString)) { + return $null + } + + # Pattern: "Name" or Name + # The display name may contain quotes, commas, parentheses + $pattern = '^(.+?)\s*<([^>]+)>$' + + if ($memberString -match $pattern) { + $displayName = $matches[1].Trim() + $email = $matches[2].Trim() + + # Extract domain from email + $domain = ($email -split '@')[1] + if ($domain) { + # Get company name from domain (first part before any dots) + $company = ($domain -split '\.')[0] + # Capitalize first letter if company has content + if ($company.Length -gt 0) { + $company = $company.Substring(0, 1).ToUpper() + $company.Substring(1).ToLower() + } + else { + $company = "Unknown" + } + } + else { + $company = "Unknown" + } + + return [PSCustomObject]@{ + DisplayName = $displayName + Email = $email + Company = $company + OriginalFormat = $memberString + } + } + + return $null + } +} + +function groupMembersByCompany { + [CmdletBinding()] + param( + [Parameter(Mandatory)][object[]]$Members + ) + + # Filter out null members + $validMembers = $Members | Where-Object { $null -ne $_ } + + if ($validMembers.Count -eq 0) { + return "" + } + + # Group by company and sort by company name + $grouped = $validMembers | Group-Object -Property Company | Sort-Object -Property Name + + $result = @() + + foreach ($group in $grouped) { + # Add company header + $result += "- $($group.Name)" + + # Add members with 4-space indentation + foreach ($member in $group.Group) { + $result += " - $($member.OriginalFormat)" + } + } + + return ($result -join "`n") +} diff --git a/public/convertMeetingMembersToMarkdown.ps1 b/public/convertMeetingMembersToMarkdown.ps1 new file mode 100644 index 0000000..5a22777 --- /dev/null +++ b/public/convertMeetingMembersToMarkdown.ps1 @@ -0,0 +1,58 @@ + +function Convert-MeetingMembersToMarkdown { + [CmdletBinding()] + param( + [Parameter(Position = 0, ValueFromPipeline)][string]$MeetingMembers + ) + + process { + if ([string]::IsNullOrWhiteSpace($MeetingMembers)) { + return "" + } + + # Parse the comma-separated list, handling quoted names with commas + # Split on ', ' followed by a quote or a letter (not inside quotes) + $members = @() + $currentMember = "" + $inQuotes = $false + + for ($i = 0; $i -lt $MeetingMembers.Length; $i++) { + $char = $MeetingMembers[$i] + + if ($char -eq '"') { + $inQuotes = -not $inQuotes + $currentMember += $char + } + elseif ($char -eq ',' -and -not $inQuotes) { + # End of member + if (-not [string]::IsNullOrWhiteSpace($currentMember)) { + $members += $currentMember.Trim() + } + $currentMember = "" + } + else { + $currentMember += $char + } + } + + # Add the last member + if (-not [string]::IsNullOrWhiteSpace($currentMember)) { + $members += $currentMember.Trim() + } + + # Parse each member + $parsedMembers = $members | ForEach-Object { parseMemberEmail -MemberString $_ } + + # Filter out nulls + $parsedMembers = $parsedMembers | Where-Object { $null -ne $_ } + + if ($parsedMembers.Count -eq 0) { + return "" + } + + # Group and format as markdown + $result = groupMembersByCompany -Members $parsedMembers + + return $result + } +} Export-ModuleMember -Function 'Convert-MeetingMembersToMarkdown'