Формульный отчёт на листе эталона; лист диаграмм по флагу

This commit is contained in:
apuc committed 2026-10-20 00:26:55 +03:00
1 parent 63c4a5ff40
commit f33c0fbe34
5 files changed
+163 -8

No files matched your search

+53 -2
View File
@@ -5,6 +5,7 @@ import json
import re
import unicodedata
import argparse
import zipfile
import pymupdf
import openpyxl
from PIL import Image, ImageDraw
@@ -66,6 +67,44 @@ for (sheet,column),name in expected_dv.items():
assert all(r[1] in depts for r in rows),( 'подразделение вне списка')
assert all(r[2] in cats for r in rows),'категория вне списка'
assert all(r[5] in statuses for r in rows),'статус вне списка'
# Формульный отчёт на листе «Эталон отчёта» (с 15-й строки) должен сходиться со статическим эталоном.
rep=book['Эталон отчёта']
static={rep.cell(r,1).value:(rep.cell(r,2).value,rep.cell(r,3).value,rep.cell(r,4).value,rep.cell(r,5).value)
for r in range(4,9)}
assert len(static)==5
for r in range(17,22):
dept=rep.cell(r,1).value
done,late,soon,total=static[dept]
assert (rep.cell(r,2).value,rep.cell(r,3).value,rep.cell(r,4).value)==(total,done,late),(dept,'отчёт на формулах')
assert rep.cell(r,5).value==soon,(dept,'в срок')
assert abs(rep.cell(r,6).value-late/(late+soon))<1e-9,(dept,'доля просроченных среди открытых')
assert (rep.cell(22,2).value,rep.cell(22,3).value,rep.cell(22,4).value,rep.cell(22,5).value)==(60,24,18,18)
assert abs(rep.cell(22,6).value-.5)<1e-9
assert rep.cell(24,2).value=='Совпадает' and rep.cell(25,2).value=='Совпадает'
report_formulas=openpyxl.load_workbook(OUT/'excel-practice.xlsx',data_only=False)['Эталон отчёта']
assert all(report_formulas.cell(r,2).data_type=='f' for r in list(range(17,23))+[24,25])
assert 'COUNTIFS' in report_formulas.cell(17,3).value and 'Реестр' in report_formulas.cell(17,3).value
# Лист с диаграммами включается флагом --with-chart-sheet. Проверяем его только если он есть.
chart_sheet=book['Диаграмма'] if 'Диаграмма' in book.sheetnames else None
if chart_sheet is None:
chart_status='лист «Диаграмма» не включён (ожидаемо: диаграммы строятся на лекции вживую)'
with zipfile.ZipFile(OUT/'excel-practice.xlsx') as archive:
assert not [n for n in archive.namelist() if n.startswith('xl/charts/')],'найдены диаграммы без листа'
else:
for n,dept in enumerate(static):
r=2+n
done,late,soon,total=static[dept]
assert chart_sheet.cell(r,1).value==dept
assert (chart_sheet.cell(r,2).value,chart_sheet.cell(r,3).value,chart_sheet.cell(r,4).value)==(done,late,soon),(dept,'блок диаграммы')
assert (chart_sheet.cell(7,2).value,chart_sheet.cell(7,3).value,chart_sheet.cell(7,4).value)==(24,18,18)
with zipfile.ZipFile(OUT/'excel-practice.xlsx') as archive:
chart_parts=[n for n in archive.namelist() if n.startswith('xl/charts/chart') and n.endswith('.xml')]
assert len(chart_parts)==2,chart_parts
blob=''.join(archive.read(n).decode('utf-8') for n in chart_parts)
assert 'Диаграмма!' in blob and 'Реестр!' not in blob,'диаграммы должны ссылаться на лист Диаграмма'
assert 'Диаграмма!$A$2:$A$6' in blob,'нет категорий по подразделениям'
assert 'Диаграмма!$B$7:$D$7' in blob,'круговая диаграмма должна брать строку ИТОГО'
chart_status='лист «Диаграмма»: блок данных и две диаграммы совпадают с эталоном'
for r in rows:
expected='Завершено' if r[5]=='Выполнено' else ('Просрочено' if r[4]<book['Параметры']['B2'].value else 'В срок')
assert expected==r[7]
@@ -79,7 +118,19 @@ if args.recalculated_workbook:
assert all(recalculated['Тесты'].cell(n,4).value==recalculated['Тесты'].cell(n,5).value for n in range(2,6))
assert [recalculated['Старт'].cell(r,8).value for r in range(2,62)]==simple
assert [recalculated['Старт 2'].cell(r,8).value for r in range(2,62)]==nested
recalc_status='independent recalculation matches 60 rows, both teaching sheets, totals and boundary tests'
recalc_report=recalculated['Эталон отчёта']
for r in range(17,23):
for col in range(2,7):
a,b=recalc_report.cell(r,col).value,rep.cell(r,col).value
same=abs(a-b)<1e-9 if isinstance(a,(int,float)) and isinstance(b,(int,float)) else a==b
assert same,(r,col,a,b)
assert recalc_report.cell(24,2).value=='Совпадает' and recalc_report.cell(25,2).value=='Совпадает'
if chart_sheet is not None:
recalc_charts=recalculated['Диаграмма']
for r in range(2,8):
for col in range(2,5):
assert recalc_charts.cell(r,col).value==chart_sheet.cell(r,col).value,(r,col)
recalc_status='independent recalculation matches 60 rows, both teaching sheets, formula report, totals and boundary tests'
pdf=pymupdf.open(OUT/'excel-ai-lecture.pdf')
assert len(pdf)==26
@@ -130,7 +181,7 @@ except ImportError:
report={
'slides':26,'total_seconds':3600,'notes_embedded':True,'pdf_pages':len(pdf),
'synthetic_rows':60,'completed':24,'open':36,'overdue_open':18,'overdue_share_open':.5,
'qr':qr_status,'render_text_warnings':warnings,
'qr':qr_status,'render_text_warnings':warnings,'chart_sheet':chart_status,
'recalculated_workbook':recalc_status,
}
(OUT/'validation.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')