Формульный отчёт на листе эталона; лист диаграмм по флагу
This commit is contained in:
1 parent
63c4a5ff40
commit
f33c0fbe34
5 files changed
+163
-8
No files matched your search
@@ -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')
|
||||
|
||||
Reference in new issue
Block a user