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.
 
 

89 lines
3.2 KiB

<#
.SYNOPSIS
Dumps the Daniel/Ahmed 1:1 notebook tracker (.docx) as plain text via Word COM.
.DESCRIPTION
Word re-applies the document's MIP sensitivity label on every save, which wraps the
.docx in an encrypted OLE container. That means the file cannot be read by unzipping
it once it has been written once. Always read it through this script.
Output is one line per paragraph, indented by list level, with a "---" rule before
each dated entry.
.PARAMETER DocPath
Full path to the tracker .docx.
.PARAMETER Last
Emit only the N most recent dated entries, newest first, skipping the Template and
Example blocks. Omit to dump the whole document.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$DocPath,
[int]$Last = 0
)
$ErrorActionPreference = 'Stop'
if (-not (Test-Path -LiteralPath $DocPath)) { throw "Tracker not found: $DocPath" }
$word = $null
$doc = $null
$openedByUs = $false
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, [ref]$false, [ref]$true); $openedByUs = $true }
$rows = @()
$paras = $doc.Paragraphs
for ($i = 1; $i -le $paras.Count; $i++) {
$p = $paras.Item($i)
$text = ($p.Range.Text -replace "[\r\a]", '').TrimEnd()
$lvl = $p.Range.ListFormat.ListLevelNumber
$isDate = $text.Trim() -match '^\d{1,2}/\d{1,2}/\d{4}$'
$prefix = if ($lvl -ge 2) { ' - ' } elseif ($lvl -eq 1 -and $text -and -not $isDate) { ' ' } else { '' }
$rows += [pscustomobject]@{ IsDate = $isDate; Raw = $text.Trim(); Text = "$prefix$text" }
}
if ($Last -le 0) {
foreach ($r in $rows) {
if ($r.IsDate) { Write-Output '---' }
if ($r.Text.Trim()) { Write-Output $r.Text }
}
return
}
# Group into dated blocks. The first dated block belongs to the "Example:" section.
$blocks = @()
$current = $null
for ($i = 0; $i -lt $rows.Count; $i++) {
if ($rows[$i].IsDate) {
if ($current) { $blocks += $current }
$parsed = [datetime]::MinValue
[void][datetime]::TryParse($rows[$i].Raw, [ref]$parsed)
$current = [pscustomobject]@{ Date = $parsed; Label = $rows[$i].Raw; Lines = @() }
}
elseif ($current -and $rows[$i].Text.Trim()) {
$current.Lines += $rows[$i].Text
}
}
if ($current) { $blocks += $current }
if ($blocks.Count -le 1) { Write-Output '(no entries yet - only the Example block is present)'; return }
$entries = @($blocks[1..($blocks.Count - 1)] | Sort-Object -Property Date -Descending)
foreach ($e in ($entries | Select-Object -First $Last)) {
Write-Output '---'
Write-Output $e.Label
foreach ($l in $e.Lines) { Write-Output $l }
}
}
finally {
if ($doc -and $openedByUs) { $doc.Close([ref]$false) | Out-Null }
if ($word -and $openedByUs -and $word.Documents.Count -eq 0) { $word.Quit() | Out-Null }
[System.GC]::Collect()
}