AI skills in use in my daily
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

187 lines
6.9 KiB

<#
.SYNOPSIS
Appends a dated entry to the Daniel/Ahmed 1:1 notebook tracker (.docx) using Word COM.
.DESCRIPTION
Clones the formatting of the existing "Example:" block (numbered question headings at
list level 1, lettered content bullets at list level 2) so new entries match the
tracker's look exactly, without touching the document's MIP sensitivity label or any
existing content.
Every entry starts on a new page: a manual page break (Ctrl+Enter) is emitted ahead of
the date heading.
Entry content is supplied as a JSON file so the caller never has to fight PowerShell
quoting. Shape:
{
"achievements": ["bullet one", "bullet two"],
"next": ["bullet"],
"lessons": ["bullet"],
"topics": ["bullet"]
}
Any section may be omitted or empty; it renders a single "None." bullet.
.PARAMETER DocPath
Full path to the tracker .docx.
.PARAMETER JsonPath
Full path to the UTF-8 JSON file described above.
.PARAMETER EntryDate
Date heading for the entry, MM/DD/yyyy. Defaults to today.
.PARAMETER Position
Top - insert as the newest entry, immediately after the Example block (default).
Bottom - append at the end of the document (chronological).
.PARAMETER Preview
Render the entry to the console and exit without modifying the document.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$DocPath,
[Parameter(Mandatory = $true)][string]$JsonPath,
[string]$EntryDate = (Get-Date -Format 'MM/dd/yyyy'),
[ValidateSet('Top', 'Bottom')][string]$Position = 'Top',
[switch]$Preview
)
$ErrorActionPreference = 'Stop'
$SectionHeadings = @(
'What are my Achievements, Impact and Key Results?',
"What's next? / What am I working on?",
'Lessons Learned/Things to Improve',
'Topics to discuss/What I need help with'
)
if (-not (Test-Path -LiteralPath $DocPath)) { throw "Tracker not found: $DocPath" }
if (-not (Test-Path -LiteralPath $JsonPath)) { throw "Entry JSON not found: $JsonPath" }
$entry = Get-Content -LiteralPath $JsonPath -Raw -Encoding UTF8 | ConvertFrom-Json
function Get-Bullets($value) {
if ($null -eq $value) { return , @('None.') }
$list = @($value | Where-Object { $_ -and $_.ToString().Trim() } | ForEach-Object { $_.ToString().Trim() })
if ($list.Count -eq 0) { return , @('None.') }
return , $list
}
$sections = @(
(Get-Bullets $entry.achievements),
(Get-Bullets $entry.next),
(Get-Bullets $entry.lessons),
(Get-Bullets $entry.topics)
)
if ($Preview) {
Write-Output $EntryDate
for ($i = 0; $i -lt 4; $i++) {
Write-Output (" {0}. {1}" -f ($i + 1), $SectionHeadings[$i])
foreach ($b in $sections[$i]) { Write-Output " - $b" }
}
return
}
# --- Word COM -------------------------------------------------------------
$wdCharacter = 1
$word = $null
$doc = $null
$openedByUs = $false
function Get-ParaText($para) { return ($para.Range.Text -replace "[\r\a]", '') }
function Set-ParaText($para, [string]$text) {
$r = $para.Range
if ($r.End - $r.Start -gt 0) { [void]$r.MoveEnd($wdCharacter, -1) }
$r.Text = $text
}
try {
try { $word = [Runtime.InteropServices.Marshal]::GetActiveObject('Word.Application') }
catch { $word = New-Object -ComObject Word.Application; $word.Visible = $false }
$full = (Resolve-Path -LiteralPath $DocPath).Path
foreach ($d in $word.Documents) { if ($d.FullName -eq $full) { $doc = $d; break } }
if (-not $doc) { $doc = $word.Documents.Open($full); $openedByUs = $true }
# Locate the Example block: the first bare MM/DD/YYYY paragraph through the paragraph
# before the next bare-date paragraph (or the last non-empty paragraph).
$paras = $doc.Paragraphs
$dateIdx = @()
for ($i = 1; $i -le $paras.Count; $i++) {
if ((Get-ParaText $paras.Item($i)).Trim() -match '^\d{1,2}/\d{1,2}/\d{4}$') { $dateIdx += $i }
}
if ($dateIdx.Count -eq 0) { throw 'Could not find the Example block (no MM/DD/YYYY paragraph) in the tracker.' }
$exStart = $dateIdx[0]
$exEnd = if ($dateIdx.Count -gt 1) { $dateIdx[1] - 1 } else { $paras.Count }
while ($exEnd -gt $exStart -and (Get-ParaText $paras.Item($exEnd)).Trim() -eq '') { $exEnd-- }
# Formatting seeds harvested from the Example block.
$seedPlain = $null # bare paragraph (the date line)
$seedHead = $null # list level 1 (the four questions)
$seedBullet = $null # list level 2 (content under a question)
for ($i = $exStart; $i -le $exEnd; $i++) {
$p = $paras.Item($i)
$lvl = $p.Range.ListFormat.ListLevelNumber
if ($i -eq $exStart) { $seedPlain = $p.Range }
elseif ($lvl -eq 1 -and -not $seedHead) { $seedHead = $p.Range }
elseif ($lvl -eq 2 -and -not $seedBullet) { $seedBullet = $p.Range }
}
if (-not $seedPlain -or -not $seedHead -or -not $seedBullet) {
throw 'Example block is missing a plain / level-1 / level-2 paragraph to use as a formatting seed.'
}
# Snapshot the seeds' formatted text before inserting (live ranges shift as we edit).
$fmtPlain = $seedPlain.FormattedText
$fmtHead = $seedHead.FormattedText
$fmtBullet = $seedBullet.FormattedText
# Insertion point: end of the Example block (Top) or end of the document (Bottom).
if ($Position -eq 'Top') {
$pos = $paras.Item($exEnd).Range.End
}
else {
$lastIdx = $paras.Count
while ($lastIdx -gt 1 -and (Get-ParaText $paras.Item($lastIdx)).Trim() -eq '') { $lastIdx-- }
$pos = $paras.Item($lastIdx).Range.End
}
# Emit one paragraph cloned from a seed, set its text, advance the cursor.
$emit = {
param($fmt, [string]$text)
$r = $doc.Range($pos, $pos)
$r.FormattedText = $fmt
$p = $doc.Range($r.Start, $r.End).Paragraphs.Item(1)
Set-ParaText $p $text
$script:pos = $p.Range.End
}
# Each entry starts on its own page: chr(12) is Word's manual page break (Ctrl+Enter),
# embedded at the head of the date line so no extra paragraph is introduced.
& $emit $fmtPlain ("$([char]12)$EntryDate")
for ($s = 0; $s -lt 4; $s++) {
& $emit $fmtHead $SectionHeadings[$s]
foreach ($b in $sections[$s]) { & $emit $fmtBullet $b }
}
# Strip stray page-break characters Word may leave in trailing empty paragraphs,
# which would otherwise render a blank page at the end of the document.
for ($i = $doc.Paragraphs.Count; $i -ge 1; $i--) {
$t = $doc.Paragraphs.Item($i).Range.Text -replace "[\r\a]", ''
if ($t.Trim() -ne '') { break }
if ($t -match [string][char]12) { Set-ParaText $doc.Paragraphs.Item($i) '' }
}
$doc.Save()
Write-Output "Wrote entry $EntryDate to $full (position: $Position)."
}
finally {
if ($doc -and $openedByUs) { $doc.Close([ref]$true) | Out-Null }
if ($word -and $openedByUs -and $word.Documents.Count -eq 0) { $word.Quit() | Out-Null }
[System.GC]::Collect()
}