mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-13 01:14:56 +03:00
Auto-build: copilot (python) from 7fa279c
This commit is contained in:
@@ -0,0 +1,484 @@
|
||||
# mxl-info v1.0 — Analyze 1C spreadsheet structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Alias('Path')]
|
||||
[string]$TemplatePath,
|
||||
[string]$ProcessorName,
|
||||
[string]$TemplateName,
|
||||
[string]$SrcDir = "src",
|
||||
[ValidateSet("text", "json")]
|
||||
[string]$Format = "text",
|
||||
[switch]$WithText,
|
||||
[int]$MaxParams = 10,
|
||||
[int]$Limit = 150,
|
||||
[int]$Offset = 0
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve template path ---
|
||||
|
||||
if (-not $TemplatePath) {
|
||||
if (-not $ProcessorName -or -not $TemplateName) {
|
||||
Write-Error "Specify -TemplatePath or both -ProcessorName and -TemplateName"
|
||||
exit 1
|
||||
}
|
||||
$TemplatePath = Join-Path (Join-Path (Join-Path (Join-Path (Join-Path $SrcDir $ProcessorName) "Templates") $TemplateName) "Ext") "Template.xml"
|
||||
}
|
||||
|
||||
if (-not (Test-Path $TemplatePath)) {
|
||||
Write-Error "File not found: $TemplatePath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Load XML ---
|
||||
|
||||
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||
$xmlDoc.PreserveWhitespace = $false
|
||||
$xmlDoc.Load((Resolve-Path $TemplatePath).Path)
|
||||
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$nsMgr.AddNamespace("d", "http://v8.1c.ru/8.2/data/spreadsheet")
|
||||
$nsMgr.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
|
||||
$nsMgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
|
||||
$root = $xmlDoc.DocumentElement
|
||||
|
||||
# --- Column sets ---
|
||||
|
||||
$columnSets = @()
|
||||
$defaultColCount = 0
|
||||
|
||||
foreach ($cols in $root.SelectNodes("d:columns", $nsMgr)) {
|
||||
$sizeNode = $cols.SelectSingleNode("d:size", $nsMgr)
|
||||
$idNode = $cols.SelectSingleNode("d:id", $nsMgr)
|
||||
$size = if ($sizeNode) { [int]$sizeNode.InnerText } else { 0 }
|
||||
|
||||
if ($idNode) {
|
||||
$columnSets += @{ Id = $idNode.InnerText; Size = $size }
|
||||
} else {
|
||||
$defaultColCount = $size
|
||||
}
|
||||
}
|
||||
|
||||
# --- Rows: collect row data ---
|
||||
|
||||
$rowNodes = $root.SelectNodes("d:rowsItem", $nsMgr)
|
||||
$totalRows = $rowNodes.Count
|
||||
|
||||
$heightNode = $root.SelectSingleNode("d:height", $nsMgr)
|
||||
$docHeight = if ($heightNode) { [int]$heightNode.InnerText } else { $totalRows }
|
||||
|
||||
# --- Named items ---
|
||||
|
||||
$namedAreas = @()
|
||||
$namedDrawings = @()
|
||||
|
||||
foreach ($ni in $root.SelectNodes("d:namedItem", $nsMgr)) {
|
||||
$niType = $ni.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
$name = $ni.SelectSingleNode("d:name", $nsMgr).InnerText
|
||||
|
||||
if ($niType -like "*NamedItemCells*") {
|
||||
$area = $ni.SelectSingleNode("d:area", $nsMgr)
|
||||
$areaType = $area.SelectSingleNode("d:type", $nsMgr).InnerText
|
||||
$beginRow = [int]$area.SelectSingleNode("d:beginRow", $nsMgr).InnerText
|
||||
$endRow = [int]$area.SelectSingleNode("d:endRow", $nsMgr).InnerText
|
||||
$beginCol = [int]$area.SelectSingleNode("d:beginColumn", $nsMgr).InnerText
|
||||
$endCol = [int]$area.SelectSingleNode("d:endColumn", $nsMgr).InnerText
|
||||
$colsId = $null
|
||||
$colsIdNode = $area.SelectSingleNode("d:columnsID", $nsMgr)
|
||||
if ($colsIdNode) { $colsId = $colsIdNode.InnerText }
|
||||
|
||||
$namedAreas += @{
|
||||
Name = $name
|
||||
AreaType = $areaType
|
||||
BeginRow = $beginRow
|
||||
EndRow = $endRow
|
||||
BeginCol = $beginCol
|
||||
EndCol = $endCol
|
||||
ColumnsID = $colsId
|
||||
}
|
||||
} elseif ($niType -like "*NamedItemDrawing*") {
|
||||
$drawId = $ni.SelectSingleNode("d:drawingID", $nsMgr).InnerText
|
||||
$namedDrawings += @{ Name = $name; DrawingID = $drawId }
|
||||
}
|
||||
}
|
||||
|
||||
# --- Scan rows for parameters and text ---
|
||||
|
||||
# Build row index map: rowIndex -> XmlNode
|
||||
$rowMap = @{}
|
||||
foreach ($ri in $rowNodes) {
|
||||
$idx = [int]$ri.SelectSingleNode("d:index", $nsMgr).InnerText
|
||||
$rowMap[$idx] = $ri
|
||||
}
|
||||
|
||||
function Get-CellData {
|
||||
param($rowNode, [System.Xml.XmlNamespaceManager]$ns, [bool]$includeText)
|
||||
|
||||
$row = $rowNode.SelectSingleNode("d:row", $ns)
|
||||
if (-not $row) { return @() }
|
||||
|
||||
$results = @()
|
||||
foreach ($cGroup in $row.SelectNodes("d:c", $ns)) {
|
||||
$cell = $cGroup.SelectSingleNode("d:c", $ns)
|
||||
if (-not $cell) { continue }
|
||||
|
||||
$param = $cell.SelectSingleNode("d:parameter", $ns)
|
||||
$detail = $cell.SelectSingleNode("d:detailParameter", $ns)
|
||||
$tl = $cell.SelectSingleNode("d:tl", $ns)
|
||||
|
||||
if ($param) {
|
||||
$entry = @{ Kind = "Parameter"; Value = $param.InnerText }
|
||||
if ($detail) { $entry.Detail = $detail.InnerText }
|
||||
$results += $entry
|
||||
}
|
||||
|
||||
if ($tl) {
|
||||
$content = $tl.SelectSingleNode("v8:item/v8:content", $ns)
|
||||
if ($content -and $content.InnerText) {
|
||||
$text = $content.InnerText
|
||||
$isTemplate = $text -match '\[.+\]'
|
||||
|
||||
if ($isTemplate) {
|
||||
# Always extract parameter names from [Param] placeholders
|
||||
# Skip numeric-only like [5] — these are footnote refs in legal forms
|
||||
foreach ($m in [regex]::Matches($text, '\[([^\]]+)\]')) {
|
||||
$val = $m.Groups[1].Value
|
||||
if ($val -notmatch '^\d+$') {
|
||||
$results += @{ Kind = "TemplateParam"; Value = $val }
|
||||
}
|
||||
}
|
||||
# Full template text only with -WithText
|
||||
if ($includeText) {
|
||||
$results += @{ Kind = "Template"; Value = $text }
|
||||
}
|
||||
} elseif ($includeText) {
|
||||
$results += @{ Kind = "Text"; Value = $text }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $results
|
||||
}
|
||||
|
||||
function Get-AreaCellData {
|
||||
param(
|
||||
[hashtable]$area,
|
||||
[hashtable]$rowMap,
|
||||
[System.Xml.XmlNamespaceManager]$ns,
|
||||
[bool]$includeText
|
||||
)
|
||||
|
||||
$params = @()
|
||||
$details = @()
|
||||
$texts = @()
|
||||
$templates = @()
|
||||
|
||||
$startRow = $area.BeginRow
|
||||
$endRow = $area.EndRow
|
||||
if ($startRow -eq -1) { $startRow = 0 }
|
||||
if ($endRow -eq -1) { $endRow = $docHeight - 1 }
|
||||
|
||||
for ($r = $startRow; $r -le $endRow; $r++) {
|
||||
if ($rowMap.ContainsKey($r)) {
|
||||
$cells = Get-CellData -rowNode $rowMap[$r] -ns $ns -includeText $includeText
|
||||
foreach ($c in $cells) {
|
||||
switch ($c.Kind) {
|
||||
"Parameter" {
|
||||
$params += $c.Value
|
||||
if ($c.Detail) { $details += "$($c.Value)->$($c.Detail)" }
|
||||
}
|
||||
"TemplateParam" { $params += "$($c.Value) [tpl]" }
|
||||
"Text" { $texts += $c.Value }
|
||||
"Template" { $templates += $c.Value }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @{ Params = $params; Details = $details; Texts = $texts; Templates = $templates }
|
||||
}
|
||||
|
||||
# Sort areas by position: Rows by beginRow, Columns by beginCol, Rectangle by beginRow
|
||||
$namedAreas = $namedAreas | Sort-Object {
|
||||
if ($_.AreaType -eq "Columns") { $_.BeginCol } else { $_.BeginRow }
|
||||
}, { $_.Name }
|
||||
|
||||
# Collect data for each area
|
||||
$areaData = @()
|
||||
$coveredRows = @{}
|
||||
|
||||
foreach ($area in $namedAreas) {
|
||||
$data = Get-AreaCellData -area $area -rowMap $rowMap -ns $nsMgr -includeText $WithText
|
||||
$areaData += @{
|
||||
Area = $area
|
||||
Params = $data.Params
|
||||
Details = $data.Details
|
||||
Texts = $data.Texts
|
||||
Templates = $data.Templates
|
||||
}
|
||||
|
||||
# Track covered rows
|
||||
$sr = $area.BeginRow
|
||||
$er = $area.EndRow
|
||||
if ($sr -ne -1 -and $er -ne -1) {
|
||||
for ($r = $sr; $r -le $er; $r++) {
|
||||
$coveredRows[$r] = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Find parameters outside named areas
|
||||
$outsideParams = @()
|
||||
$outsideDetails = @()
|
||||
$outsideTexts = @()
|
||||
$outsideTemplates = @()
|
||||
|
||||
foreach ($r in $rowMap.Keys | Sort-Object) {
|
||||
if (-not $coveredRows.ContainsKey($r)) {
|
||||
$cells = Get-CellData -rowNode $rowMap[$r] -ns $nsMgr -includeText $WithText
|
||||
foreach ($c in $cells) {
|
||||
switch ($c.Kind) {
|
||||
"Parameter" {
|
||||
$outsideParams += $c.Value
|
||||
if ($c.Detail) { $outsideDetails += "$($c.Value)->$($c.Detail)" }
|
||||
}
|
||||
"TemplateParam" { $outsideParams += "$($c.Value) [tpl]" }
|
||||
"Text" { $outsideTexts += $c.Value }
|
||||
"Template" { $outsideTemplates += $c.Value }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Counts ---
|
||||
|
||||
$mergeCount = $root.SelectNodes("d:merge", $nsMgr).Count
|
||||
$drawingNodes = $root.SelectNodes("d:drawing", $nsMgr)
|
||||
$drawingCount = $drawingNodes.Count
|
||||
|
||||
# --- Output ---
|
||||
|
||||
function Truncate-List {
|
||||
param([string[]]$items, [int]$max)
|
||||
if ($items.Count -le $max) {
|
||||
return ($items -join ", ")
|
||||
}
|
||||
$shown = ($items[0..($max - 1)] -join ", ")
|
||||
$remaining = $items.Count - $max
|
||||
return "$shown, ... (+$remaining)"
|
||||
}
|
||||
|
||||
# Determine template name from path
|
||||
$templateName = [System.IO.Path]::GetFileName([System.IO.Path]::GetDirectoryName([System.IO.Path]::GetDirectoryName($TemplatePath)))
|
||||
|
||||
if ($Format -eq "json") {
|
||||
$result = @{
|
||||
name = $templateName
|
||||
rows = $docHeight
|
||||
columns = $defaultColCount
|
||||
columnSets = @($columnSets)
|
||||
areas = @()
|
||||
outsideParams = @($outsideParams)
|
||||
mergeCount = $mergeCount
|
||||
drawingCount = $drawingCount
|
||||
}
|
||||
|
||||
foreach ($ad in $areaData) {
|
||||
$areaObj = @{
|
||||
name = $ad.Area.Name
|
||||
type = $ad.Area.AreaType
|
||||
beginRow = $ad.Area.BeginRow
|
||||
endRow = $ad.Area.EndRow
|
||||
beginCol = $ad.Area.BeginCol
|
||||
endCol = $ad.Area.EndCol
|
||||
params = @($ad.Params)
|
||||
}
|
||||
if ($ad.Area.ColumnsID) { $areaObj.columnsID = $ad.Area.ColumnsID }
|
||||
if ($WithText) {
|
||||
$areaObj.texts = @($ad.Texts)
|
||||
$areaObj.templates = @($ad.Templates)
|
||||
}
|
||||
$result.areas += $areaObj
|
||||
}
|
||||
|
||||
if ($WithText) {
|
||||
$result.outsideTexts = @($outsideTexts)
|
||||
$result.outsideTemplates = @($outsideTemplates)
|
||||
}
|
||||
|
||||
foreach ($nd in $namedDrawings) {
|
||||
$result.areas += @{
|
||||
name = $nd.Name
|
||||
type = "Drawing"
|
||||
drawingID = $nd.DrawingID
|
||||
}
|
||||
}
|
||||
|
||||
$result | ConvertTo-Json -Depth 5
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Text format output ---
|
||||
|
||||
$lines = @()
|
||||
|
||||
$lines += "=== $templateName ==="
|
||||
$lines += " Rows: $docHeight, Columns: $defaultColCount"
|
||||
|
||||
if ($columnSets.Count -eq 0) {
|
||||
$lines += " Column sets: 1 (default only)"
|
||||
} else {
|
||||
$lines += " Column sets: $($columnSets.Count + 1) (default=$defaultColCount cols + $($columnSets.Count) additional)"
|
||||
foreach ($cs in $columnSets) {
|
||||
$lines += " $($cs.Id.Substring(0,8))...: $($cs.Size) cols"
|
||||
}
|
||||
}
|
||||
|
||||
$lines += ""
|
||||
$lines += "--- Named areas ---"
|
||||
|
||||
foreach ($ad in $areaData) {
|
||||
$a = $ad.Area
|
||||
$paramCount = $ad.Params.Count
|
||||
$rowRange = ""
|
||||
|
||||
switch ($a.AreaType) {
|
||||
"Rows" { $rowRange = "rows $($a.BeginRow)-$($a.EndRow)" }
|
||||
"Columns" { $rowRange = "cols $($a.BeginCol)-$($a.EndCol)" }
|
||||
"Rectangle" { $rowRange = "rows $($a.BeginRow)-$($a.EndRow), cols $($a.BeginCol)-$($a.EndCol)" }
|
||||
}
|
||||
|
||||
$colsInfo = ""
|
||||
if ($a.ColumnsID) {
|
||||
$csSize = ""
|
||||
foreach ($cs in $columnSets) {
|
||||
if ($cs.Id -eq $a.ColumnsID) { $csSize = " $($cs.Size)cols"; break }
|
||||
}
|
||||
$colsInfo = " [colset$csSize]"
|
||||
}
|
||||
|
||||
$paramInfo = "($paramCount params)"
|
||||
$nameStr = $a.Name.PadRight(25)
|
||||
$typeStr = $a.AreaType.PadRight(12)
|
||||
$lines += " $nameStr $typeStr $rowRange $paramInfo$colsInfo"
|
||||
}
|
||||
|
||||
foreach ($nd in $namedDrawings) {
|
||||
$nameStr = $nd.Name.PadRight(25)
|
||||
$lines += " $nameStr Drawing drawingID=$($nd.DrawingID)"
|
||||
}
|
||||
|
||||
# Detect intersection pairs (Rows + Columns areas that overlap)
|
||||
$rowsAreas = $areaData | Where-Object { $_.Area.AreaType -eq "Rows" }
|
||||
$colsAreas = $areaData | Where-Object { $_.Area.AreaType -eq "Columns" }
|
||||
$intersections = @()
|
||||
if ($rowsAreas -and $colsAreas) {
|
||||
foreach ($ra in $rowsAreas) {
|
||||
foreach ($ca in $colsAreas) {
|
||||
$intersections += "$($ra.Area.Name)|$($ca.Area.Name)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($intersections.Count -gt 0) {
|
||||
$lines += ""
|
||||
$lines += "--- Intersections (use with GetArea) ---"
|
||||
foreach ($pair in $intersections) {
|
||||
$lines += " $pair"
|
||||
}
|
||||
}
|
||||
|
||||
# Parameters by area
|
||||
$hasParams = ($areaData | Where-Object { $_.Params.Count -gt 0 }) -or ($outsideParams.Count -gt 0)
|
||||
|
||||
if ($hasParams) {
|
||||
$lines += ""
|
||||
$lines += "--- Parameters by area ---"
|
||||
foreach ($ad in $areaData) {
|
||||
if ($ad.Params.Count -gt 0) {
|
||||
$paramStr = Truncate-List -items $ad.Params -max $MaxParams
|
||||
$lines += " $($ad.Area.Name): $paramStr"
|
||||
# Show detailParameters if any
|
||||
if ($ad.Details.Count -gt 0) {
|
||||
$detailStr = Truncate-List -items $ad.Details -max $MaxParams
|
||||
$lines += " detail: $detailStr"
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($outsideParams.Count -gt 0) {
|
||||
$paramStr = Truncate-List -items $outsideParams -max $MaxParams
|
||||
$lines += " (outside areas): $paramStr"
|
||||
if ($outsideDetails.Count -gt 0) {
|
||||
$detailStr = Truncate-List -items $outsideDetails -max $MaxParams
|
||||
$lines += " detail: $detailStr"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# WithText sections
|
||||
if ($WithText) {
|
||||
$hasText = ($areaData | Where-Object { $_.Texts.Count -gt 0 -or $_.Templates.Count -gt 0 }) -or ($outsideTexts.Count -gt 0) -or ($outsideTemplates.Count -gt 0)
|
||||
|
||||
if ($hasText) {
|
||||
$lines += ""
|
||||
$lines += "--- Text content ---"
|
||||
foreach ($ad in $areaData) {
|
||||
if ($ad.Texts.Count -gt 0 -or $ad.Templates.Count -gt 0) {
|
||||
$lines += " $($ad.Area.Name):"
|
||||
if ($ad.Texts.Count -gt 0) {
|
||||
$textItems = $ad.Texts | ForEach-Object { "`"$_`"" }
|
||||
$textStr = Truncate-List -items $textItems -max $MaxParams
|
||||
$lines += " Text: $textStr"
|
||||
}
|
||||
if ($ad.Templates.Count -gt 0) {
|
||||
$tplItems = $ad.Templates | ForEach-Object { "`"$_`"" }
|
||||
$tplStr = Truncate-List -items $tplItems -max $MaxParams
|
||||
$lines += " Templates: $tplStr"
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($outsideTexts.Count -gt 0 -or $outsideTemplates.Count -gt 0) {
|
||||
$lines += " (outside areas):"
|
||||
if ($outsideTexts.Count -gt 0) {
|
||||
$textItems = $outsideTexts | ForEach-Object { "`"$_`"" }
|
||||
$textStr = Truncate-List -items $textItems -max $MaxParams
|
||||
$lines += " Text: $textStr"
|
||||
}
|
||||
if ($outsideTemplates.Count -gt 0) {
|
||||
$tplItems = $outsideTemplates | ForEach-Object { "`"$_`"" }
|
||||
$tplStr = Truncate-List -items $tplItems -max $MaxParams
|
||||
$lines += " Templates: $tplStr"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$lines += ""
|
||||
$lines += "--- Stats ---"
|
||||
$lines += " Merges: $mergeCount"
|
||||
$lines += " Drawings: $drawingCount"
|
||||
|
||||
# --- Truncation protection ---
|
||||
|
||||
$totalLines = $lines.Count
|
||||
|
||||
if ($Offset -gt 0) {
|
||||
if ($Offset -ge $totalLines) {
|
||||
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
|
||||
exit 0
|
||||
}
|
||||
$lines = $lines[$Offset..($totalLines - 1)]
|
||||
}
|
||||
|
||||
if ($lines.Count -gt $Limit) {
|
||||
$shown = $lines[0..($Limit - 1)]
|
||||
foreach ($l in $shown) { Write-Host $l }
|
||||
$remaining = $totalLines - $Offset - $Limit
|
||||
Write-Host ""
|
||||
Write-Host "[TRUNCATED] Shown $Limit of $totalLines lines. Use -Offset $($Offset + $Limit) to continue."
|
||||
} else {
|
||||
foreach ($l in $lines) { Write-Host $l }
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
#!/usr/bin/env python3
|
||||
# mxl-info v1.0 — Analyze 1C spreadsheet structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from lxml import etree
|
||||
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
sys.stderr.reconfigure(encoding="utf-8")
|
||||
|
||||
# --- Argument parsing ---
|
||||
parser = argparse.ArgumentParser(description="Analyze 1C spreadsheet (MXL) structure", allow_abbrev=False)
|
||||
parser.add_argument("-TemplatePath", "-Path", default="", help="Path to Template.xml")
|
||||
parser.add_argument("-ProcessorName", default="", help="Processor name (used with -TemplateName)")
|
||||
parser.add_argument("-TemplateName", default="", help="Template name (used with -ProcessorName)")
|
||||
parser.add_argument("-SrcDir", default="src", help="Source directory (default: src)")
|
||||
parser.add_argument("-Format", choices=["text", "json"], default="text", help="Output format")
|
||||
parser.add_argument("-WithText", action="store_true", default=False, help="Include text content")
|
||||
parser.add_argument("-MaxParams", type=int, default=10, help="Max parameters to show per area")
|
||||
parser.add_argument("-Limit", type=int, default=150, help="Max lines to show")
|
||||
parser.add_argument("-Offset", type=int, default=0, help="Lines to skip")
|
||||
args = parser.parse_args()
|
||||
|
||||
# --- Resolve template path ---
|
||||
template_path = args.TemplatePath
|
||||
|
||||
if not template_path:
|
||||
if not args.ProcessorName or not args.TemplateName:
|
||||
print("Specify -TemplatePath or both -ProcessorName and -TemplateName", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
template_path = os.path.join(args.SrcDir, args.ProcessorName, "Templates", args.TemplateName, "Ext", "Template.xml")
|
||||
|
||||
if not os.path.isabs(template_path):
|
||||
template_path = os.path.join(os.getcwd(), template_path)
|
||||
|
||||
if not os.path.isfile(template_path):
|
||||
print(f"File not found: {template_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# --- Load XML ---
|
||||
tree = etree.parse(template_path, etree.XMLParser(remove_blank_text=True))
|
||||
root = tree.getroot()
|
||||
|
||||
NS = {
|
||||
"d": "http://v8.1c.ru/8.2/data/spreadsheet",
|
||||
"v8": "http://v8.1c.ru/8.1/data/core",
|
||||
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||
}
|
||||
|
||||
XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"
|
||||
|
||||
# --- Column sets ---
|
||||
column_sets = []
|
||||
default_col_count = 0
|
||||
|
||||
for cols in root.findall("d:columns", NS):
|
||||
size_node = cols.find("d:size", NS)
|
||||
id_node = cols.find("d:id", NS)
|
||||
size = int(size_node.text) if size_node is not None and size_node.text else 0
|
||||
|
||||
if id_node is not None:
|
||||
column_sets.append({"Id": id_node.text or "", "Size": size})
|
||||
else:
|
||||
default_col_count = size
|
||||
|
||||
# --- Rows: collect row data ---
|
||||
row_nodes = root.findall("d:rowsItem", NS)
|
||||
total_rows = len(row_nodes)
|
||||
|
||||
height_node = root.find("d:height", NS)
|
||||
doc_height = int(height_node.text) if height_node is not None and height_node.text else total_rows
|
||||
|
||||
# --- Named items ---
|
||||
named_areas = []
|
||||
named_drawings = []
|
||||
|
||||
for ni in root.findall("d:namedItem", NS):
|
||||
ni_type = ni.get(f"{{{XSI_NS}}}type", "")
|
||||
name_node = ni.find("d:name", NS)
|
||||
name = name_node.text if name_node is not None else ""
|
||||
|
||||
if "NamedItemCells" in ni_type:
|
||||
area = ni.find("d:area", NS)
|
||||
area_type_node = area.find("d:type", NS)
|
||||
area_type = area_type_node.text if area_type_node is not None else ""
|
||||
begin_row = int(area.find("d:beginRow", NS).text)
|
||||
end_row = int(area.find("d:endRow", NS).text)
|
||||
begin_col = int(area.find("d:beginColumn", NS).text)
|
||||
end_col = int(area.find("d:endColumn", NS).text)
|
||||
cols_id = None
|
||||
cols_id_node = area.find("d:columnsID", NS)
|
||||
if cols_id_node is not None:
|
||||
cols_id = cols_id_node.text
|
||||
|
||||
named_areas.append({
|
||||
"Name": name,
|
||||
"AreaType": area_type,
|
||||
"BeginRow": begin_row,
|
||||
"EndRow": end_row,
|
||||
"BeginCol": begin_col,
|
||||
"EndCol": end_col,
|
||||
"ColumnsID": cols_id,
|
||||
})
|
||||
elif "NamedItemDrawing" in ni_type:
|
||||
draw_id_node = ni.find("d:drawingID", NS)
|
||||
draw_id = draw_id_node.text if draw_id_node is not None else ""
|
||||
named_drawings.append({"Name": name, "DrawingID": draw_id})
|
||||
|
||||
# --- Scan rows for parameters and text ---
|
||||
|
||||
# Build row index map: rowIndex -> XmlNode
|
||||
row_map = {}
|
||||
for ri in row_nodes:
|
||||
idx_node = ri.find("d:index", NS)
|
||||
if idx_node is not None and idx_node.text:
|
||||
idx = int(idx_node.text)
|
||||
row_map[idx] = ri
|
||||
|
||||
|
||||
def get_cell_data(row_node, include_text):
|
||||
row = row_node.find("d:row", NS)
|
||||
if row is None:
|
||||
return []
|
||||
|
||||
results = []
|
||||
for c_group in row.findall("d:c", NS):
|
||||
cell = c_group.find("d:c", NS)
|
||||
if cell is None:
|
||||
continue
|
||||
|
||||
param = cell.find("d:parameter", NS)
|
||||
detail = cell.find("d:detailParameter", NS)
|
||||
tl = cell.find("d:tl", NS)
|
||||
|
||||
if param is not None:
|
||||
entry = {"Kind": "Parameter", "Value": param.text or ""}
|
||||
if detail is not None:
|
||||
entry["Detail"] = detail.text or ""
|
||||
results.append(entry)
|
||||
|
||||
if tl is not None:
|
||||
content = tl.find("v8:item/v8:content", NS)
|
||||
if content is not None and content.text:
|
||||
text = content.text
|
||||
is_template = bool(re.search(r'\[.+\]', text))
|
||||
|
||||
if is_template:
|
||||
# Extract parameter names from [Param] placeholders
|
||||
# Skip numeric-only like [5]
|
||||
for m in re.finditer(r'\[([^\]]+)\]', text):
|
||||
val = m.group(1)
|
||||
if not re.match(r'^\d+$', val):
|
||||
results.append({"Kind": "TemplateParam", "Value": val})
|
||||
# Full template text only with -WithText
|
||||
if include_text:
|
||||
results.append({"Kind": "Template", "Value": text})
|
||||
elif include_text:
|
||||
results.append({"Kind": "Text", "Value": text})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def get_area_cell_data(area, row_map_ref, include_text):
|
||||
params = []
|
||||
details = []
|
||||
texts = []
|
||||
templates = []
|
||||
|
||||
start_row = area["BeginRow"]
|
||||
end_row = area["EndRow"]
|
||||
if start_row == -1:
|
||||
start_row = 0
|
||||
if end_row == -1:
|
||||
end_row = doc_height - 1
|
||||
|
||||
for r in range(start_row, end_row + 1):
|
||||
if r in row_map_ref:
|
||||
cells = get_cell_data(row_map_ref[r], include_text)
|
||||
for c in cells:
|
||||
kind = c["Kind"]
|
||||
if kind == "Parameter":
|
||||
params.append(c["Value"])
|
||||
if "Detail" in c:
|
||||
details.append(f"{c['Value']}->{c['Detail']}")
|
||||
elif kind == "TemplateParam":
|
||||
params.append(f"{c['Value']} [tpl]")
|
||||
elif kind == "Text":
|
||||
texts.append(c["Value"])
|
||||
elif kind == "Template":
|
||||
templates.append(c["Value"])
|
||||
|
||||
return {"Params": params, "Details": details, "Texts": texts, "Templates": templates}
|
||||
|
||||
|
||||
# Sort areas by position: Rows by beginRow, Columns by beginCol, Rectangle by beginRow
|
||||
def area_sort_key(a):
|
||||
if a["AreaType"] == "Columns":
|
||||
return (a["BeginCol"], a["Name"])
|
||||
return (a["BeginRow"], a["Name"])
|
||||
|
||||
named_areas.sort(key=area_sort_key)
|
||||
|
||||
# Collect data for each area
|
||||
area_data = []
|
||||
covered_rows = set()
|
||||
|
||||
for area in named_areas:
|
||||
data = get_area_cell_data(area, row_map, args.WithText)
|
||||
area_data.append({
|
||||
"Area": area,
|
||||
"Params": data["Params"],
|
||||
"Details": data["Details"],
|
||||
"Texts": data["Texts"],
|
||||
"Templates": data["Templates"],
|
||||
})
|
||||
|
||||
# Track covered rows
|
||||
sr = area["BeginRow"]
|
||||
er = area["EndRow"]
|
||||
if sr != -1 and er != -1:
|
||||
for r in range(sr, er + 1):
|
||||
covered_rows.add(r)
|
||||
|
||||
# Find parameters outside named areas
|
||||
outside_params = []
|
||||
outside_details = []
|
||||
outside_texts = []
|
||||
outside_templates = []
|
||||
|
||||
for r in sorted(row_map.keys()):
|
||||
if r not in covered_rows:
|
||||
cells = get_cell_data(row_map[r], args.WithText)
|
||||
for c in cells:
|
||||
kind = c["Kind"]
|
||||
if kind == "Parameter":
|
||||
outside_params.append(c["Value"])
|
||||
if "Detail" in c:
|
||||
outside_details.append(f"{c['Value']}->{c['Detail']}")
|
||||
elif kind == "TemplateParam":
|
||||
outside_params.append(f"{c['Value']} [tpl]")
|
||||
elif kind == "Text":
|
||||
outside_texts.append(c["Value"])
|
||||
elif kind == "Template":
|
||||
outside_templates.append(c["Value"])
|
||||
|
||||
# --- Counts ---
|
||||
merge_count = len(root.findall("d:merge", NS))
|
||||
drawing_nodes = root.findall("d:drawing", NS)
|
||||
drawing_count = len(drawing_nodes)
|
||||
|
||||
# --- Output ---
|
||||
|
||||
def truncate_list(items, max_count):
|
||||
if len(items) <= max_count:
|
||||
return ", ".join(items)
|
||||
shown = ", ".join(items[:max_count])
|
||||
remaining = len(items) - max_count
|
||||
return f"{shown}, ... (+{remaining})"
|
||||
|
||||
|
||||
# Determine template name from path
|
||||
template_name = os.path.basename(os.path.dirname(os.path.dirname(template_path)))
|
||||
|
||||
if args.Format == "json":
|
||||
result = {
|
||||
"name": template_name,
|
||||
"rows": doc_height,
|
||||
"columns": default_col_count,
|
||||
"columnSets": [{"id": cs["Id"], "size": cs["Size"]} for cs in column_sets],
|
||||
"areas": [],
|
||||
"outsideParams": list(outside_params),
|
||||
"mergeCount": merge_count,
|
||||
"drawingCount": drawing_count,
|
||||
}
|
||||
|
||||
for ad in area_data:
|
||||
area_obj = {
|
||||
"name": ad["Area"]["Name"],
|
||||
"type": ad["Area"]["AreaType"],
|
||||
"beginRow": ad["Area"]["BeginRow"],
|
||||
"endRow": ad["Area"]["EndRow"],
|
||||
"beginCol": ad["Area"]["BeginCol"],
|
||||
"endCol": ad["Area"]["EndCol"],
|
||||
"params": list(ad["Params"]),
|
||||
}
|
||||
if ad["Area"]["ColumnsID"]:
|
||||
area_obj["columnsID"] = ad["Area"]["ColumnsID"]
|
||||
if args.WithText:
|
||||
area_obj["texts"] = list(ad["Texts"])
|
||||
area_obj["templates"] = list(ad["Templates"])
|
||||
result["areas"].append(area_obj)
|
||||
|
||||
if args.WithText:
|
||||
result["outsideTexts"] = list(outside_texts)
|
||||
result["outsideTemplates"] = list(outside_templates)
|
||||
|
||||
for nd in named_drawings:
|
||||
result["areas"].append({
|
||||
"name": nd["Name"],
|
||||
"type": "Drawing",
|
||||
"drawingID": nd["DrawingID"],
|
||||
})
|
||||
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
sys.exit(0)
|
||||
|
||||
# --- Text format output ---
|
||||
lines = []
|
||||
|
||||
lines.append(f"=== {template_name} ===")
|
||||
lines.append(f" Rows: {doc_height}, Columns: {default_col_count}")
|
||||
|
||||
if len(column_sets) == 0:
|
||||
lines.append(" Column sets: 1 (default only)")
|
||||
else:
|
||||
lines.append(f" Column sets: {len(column_sets) + 1} (default={default_col_count} cols + {len(column_sets)} additional)")
|
||||
for cs in column_sets:
|
||||
lines.append(f" {cs['Id'][:8]}...: {cs['Size']} cols")
|
||||
|
||||
lines.append("")
|
||||
lines.append("--- Named areas ---")
|
||||
|
||||
for ad in area_data:
|
||||
a = ad["Area"]
|
||||
param_count = len(ad["Params"])
|
||||
row_range = ""
|
||||
|
||||
if a["AreaType"] == "Rows":
|
||||
row_range = f"rows {a['BeginRow']}-{a['EndRow']}"
|
||||
elif a["AreaType"] == "Columns":
|
||||
row_range = f"cols {a['BeginCol']}-{a['EndCol']}"
|
||||
elif a["AreaType"] == "Rectangle":
|
||||
row_range = f"rows {a['BeginRow']}-{a['EndRow']}, cols {a['BeginCol']}-{a['EndCol']}"
|
||||
|
||||
cols_info = ""
|
||||
if a["ColumnsID"]:
|
||||
cs_size = ""
|
||||
for cs in column_sets:
|
||||
if cs["Id"] == a["ColumnsID"]:
|
||||
cs_size = f" {cs['Size']}cols"
|
||||
break
|
||||
cols_info = f" [colset{cs_size}]"
|
||||
|
||||
param_info = f"({param_count} params)"
|
||||
name_str = a["Name"].ljust(25)
|
||||
type_str = a["AreaType"].ljust(12)
|
||||
lines.append(f" {name_str} {type_str} {row_range} {param_info}{cols_info}")
|
||||
|
||||
for nd in named_drawings:
|
||||
name_str = nd["Name"].ljust(25)
|
||||
lines.append(f" {name_str} Drawing drawingID={nd['DrawingID']}")
|
||||
|
||||
# Detect intersection pairs (Rows + Columns areas that overlap)
|
||||
rows_areas = [ad for ad in area_data if ad["Area"]["AreaType"] == "Rows"]
|
||||
cols_areas = [ad for ad in area_data if ad["Area"]["AreaType"] == "Columns"]
|
||||
intersections = []
|
||||
if rows_areas and cols_areas:
|
||||
for ra in rows_areas:
|
||||
for ca in cols_areas:
|
||||
intersections.append(f"{ra['Area']['Name']}|{ca['Area']['Name']}")
|
||||
|
||||
if intersections:
|
||||
lines.append("")
|
||||
lines.append("--- Intersections (use with GetArea) ---")
|
||||
for pair in intersections:
|
||||
lines.append(f" {pair}")
|
||||
|
||||
# Parameters by area
|
||||
has_params = any(len(ad["Params"]) > 0 for ad in area_data) or len(outside_params) > 0
|
||||
|
||||
if has_params:
|
||||
lines.append("")
|
||||
lines.append("--- Parameters by area ---")
|
||||
for ad in area_data:
|
||||
if len(ad["Params"]) > 0:
|
||||
param_str = truncate_list(ad["Params"], args.MaxParams)
|
||||
lines.append(f" {ad['Area']['Name']}: {param_str}")
|
||||
# Show detailParameters if any
|
||||
if len(ad["Details"]) > 0:
|
||||
detail_str = truncate_list(ad["Details"], args.MaxParams)
|
||||
lines.append(f" detail: {detail_str}")
|
||||
if len(outside_params) > 0:
|
||||
param_str = truncate_list(outside_params, args.MaxParams)
|
||||
lines.append(f" (outside areas): {param_str}")
|
||||
if len(outside_details) > 0:
|
||||
detail_str = truncate_list(outside_details, args.MaxParams)
|
||||
lines.append(f" detail: {detail_str}")
|
||||
|
||||
# WithText sections
|
||||
if args.WithText:
|
||||
has_text = any(len(ad["Texts"]) > 0 or len(ad["Templates"]) > 0 for ad in area_data) or len(outside_texts) > 0 or len(outside_templates) > 0
|
||||
|
||||
if has_text:
|
||||
lines.append("")
|
||||
lines.append("--- Text content ---")
|
||||
for ad in area_data:
|
||||
if len(ad["Texts"]) > 0 or len(ad["Templates"]) > 0:
|
||||
lines.append(f" {ad['Area']['Name']}:")
|
||||
if len(ad["Texts"]) > 0:
|
||||
text_items = [f'"{t}"' for t in ad["Texts"]]
|
||||
text_str = truncate_list(text_items, args.MaxParams)
|
||||
lines.append(f" Text: {text_str}")
|
||||
if len(ad["Templates"]) > 0:
|
||||
tpl_items = [f'"{t}"' for t in ad["Templates"]]
|
||||
tpl_str = truncate_list(tpl_items, args.MaxParams)
|
||||
lines.append(f" Templates: {tpl_str}")
|
||||
if len(outside_texts) > 0 or len(outside_templates) > 0:
|
||||
lines.append(" (outside areas):")
|
||||
if len(outside_texts) > 0:
|
||||
text_items = [f'"{t}"' for t in outside_texts]
|
||||
text_str = truncate_list(text_items, args.MaxParams)
|
||||
lines.append(f" Text: {text_str}")
|
||||
if len(outside_templates) > 0:
|
||||
tpl_items = [f'"{t}"' for t in outside_templates]
|
||||
tpl_str = truncate_list(tpl_items, args.MaxParams)
|
||||
lines.append(f" Templates: {tpl_str}")
|
||||
|
||||
lines.append("")
|
||||
lines.append("--- Stats ---")
|
||||
lines.append(f" Merges: {merge_count}")
|
||||
lines.append(f" Drawings: {drawing_count}")
|
||||
|
||||
# --- Truncation protection ---
|
||||
total_lines = len(lines)
|
||||
|
||||
if args.Offset > 0:
|
||||
if args.Offset >= total_lines:
|
||||
print(f"[INFO] Offset {args.Offset} exceeds total lines ({total_lines}). Nothing to show.")
|
||||
sys.exit(0)
|
||||
lines = lines[args.Offset:]
|
||||
|
||||
if len(lines) > args.Limit:
|
||||
shown = lines[:args.Limit]
|
||||
for l in shown:
|
||||
print(l)
|
||||
remaining = total_lines - args.Offset - args.Limit
|
||||
print("")
|
||||
print(f"[TRUNCATED] Shown {args.Limit} of {total_lines} lines. Use -Offset {args.Offset + args.Limit} to continue.")
|
||||
else:
|
||||
for l in lines:
|
||||
print(l)
|
||||
Reference in New Issue
Block a user