Загрузка данных


Серëга Ильич:
import docx
from docx.shared import Inches, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml import parse_xml, OxmlElement
from docx.oxml.ns import nsdecls, qn

doc = docx.Document()

# Page Setup
for section in doc.sections:
    section.top_margin = Inches(0.8)
    section.bottom_margin = Inches(0.8)
    section.left_margin = Inches(0.8)
    section.right_margin = Inches(0.8)

# Styles & Colors
COLOR_PRIMARY = RGBColor(31, 78, 121)     # Steel Blue
COLOR_SECONDARY = RGBColor(90, 90, 90)    # Charcoal
HEX_PRIMARY = "1F4E79"
HEX_LIGHT_BG = "F2F5F8"
HEX_BORDER = "D3D3D3"

# Set normal style
style_normal = doc.styles['Normal']
style_normal.font.name = 'Arial'
style_normal.font.size = Pt(11)
style_normal.font.color.rgb = RGBColor(30, 30, 30)

def set_cell_background(cell, hex_color):
    shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="{hex_color}"/>')
    cell._tc.get_or_add_tcPr().append(shading_elm)

def set_cell_margins(cell, top=100, bottom=100, start=150, end=150):
    tcPr = cell._tc.get_or_add_tcPr()
    tcMar = OxmlElement('w:tcMar')
    for m, val in [('w:top', top), ('w:bottom', bottom), ('w:left', start), ('w:right', end)]:
        node = OxmlElement(m)
        node.set(qn('w:w'), str(val))
        node.set(qn('w:type'), 'dxa')
        tcMar.append(node)
    tcPr.append(tcMar)

def add_heading_1(text):
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(18)
    p.paragraph_format.space_after = Pt(6)
    p.paragraph_format.keep_with_next = True
    run = p.add_run(text)
    run.font.name = 'Arial'
    run.font.size = Pt(16)
    run.font.bold = True
    run.font.color.rgb = COLOR_PRIMARY
    return p

def add_heading_2(text):
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(12)
    p.paragraph_format.space_after = Pt(4)
    p.paragraph_format.keep_with_next = True
    run = p.add_run(text)
    run.font.name = 'Arial'
    run.font.size = Pt(13)
    run.font.bold = True
    run.font.color.rgb = COLOR_PRIMARY
    return p

def add_callout(text):
    table = doc.add_table(rows=1, cols=1)
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    cell = table.cell(0, 0)
    set_cell_background(cell, HEX_LIGHT_BG)
    set_cell_margins(cell, top=140, bottom=140, start=200, end=200)
    
    # Left thick border
    tcPr = cell._tc.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')
    left = OxmlElement('w:left')
    left.set(qn('w:val'), 'single')
    left.set(qn('w:sz'), '24') # 3pt
    left.set(qn('w:space'), '0')
    left.set(qn('w:color'), HEX_PRIMARY)
    tcBorders.append(left)
    for b in ['top', 'bottom', 'right']:
        node = OxmlElement(f'w:{b}')
        node.set(qn('w:val'), 'none')
        tcBorders.append(node)
    tcPr.append(tcBorders)
    
    p = cell.paragraphs[0]
    p.paragraph_format.space_before = Pt(0)
    p.paragraph_format.space_after = Pt(0)
    run = p.add_run(text)
    run.font.italic = True
    run.font.size = Pt(10.5)
    run.font.color.rgb = COLOR_SECONDARY
    doc.add_paragraph().paragraph_format.space_after = Pt(4)

# Document Title Block
p_title = doc.add_paragraph()
p_title.alignment = WD_ALIGN_PARAGRAPH.CENTER
p_title.paragraph_format.space_before = Pt(24)
p_title.paragraph_format.space_after = Pt(4)
run_title = p_title.add_run("Лабораторная работа №2\n«Изучение средств работы с базами данных Base»")
run_title.font.size = Pt(20)
run_title.font.bold = True
run_title.font.color.rgb = COLOR_PRIMARY

p_sub = doc.add_paragraph()
p_sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
p_sub.paragraph_format.space_after = Pt(24)
run_sub = p_sub.add_run("ОТЧЕТ ПО ВЫПОЛНЕНИЮ ЗАДАНИЙ (ВАРИАНТ 8)")
run_sub.font.size = Pt(12)
run_sub.font.bold = True
run_sub.font.color.rgb = COLOR_SECONDARY

add_callout("Студент: [Ваше Имя / Название папки Name]\nБаза данных: СтудБД.odb\nОтчетный файл: ОтчетName.docx\nВариант задания: №8 (Критерий: Список троечников с баллом < 80, женщины 19 и мужчины 20 лет, коды получающих минимальную стипендию).")

add_heading_1("Задание 1. Создание таблиц и ввод данных")

p = doc.add_paragraph("В соответствии с лабораторным практикумом в СУБД LibreOffice Base была создана новая база данных ")
p.add_run("СтудБД.odb").bold = True
p.add_run(". В режиме дизайна были спроектированы и заполнены три связанные таблицы.")

add_heading_2("1.1. Таблица «ТаблСтудент»")
doc.add_paragraph("Таблица содержит персональные данные студентов. Поле «КодСтуд» определено как первичный ключ (целое число). Ниже представлена структура и заполненная таблица (12 записей, включая целевую выборку под Вариант 8):")

# Table Student
t_stud = doc.add_table(rows=13, cols=7)
t_stud.alignment = WD_TABLE_ALIGNMENT.CENTER
headers_stud = ["КодСтуд", "Фамилия", "Имя", "Отчество", "Пол", "Возраст", "ВступитБалл"]
for i, h in enumerate(headers_stud):
    cell = t_stud.cell(0, i)
    cell.text = h
    set_cell_background(cell, HEX_PRIMARY)
    cell.paragraphs[0].runs[0].font.bold = True
    cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 255, 255)
    cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER

data_stud = [
    ("1", "Яковлев", "Юрий", "Олегович", "муж", "20", "80"),
    ("2", "Рязанцев", "Дмитрий", "Сергеевич", "муж", "18", "76"),
    ("3", "Емельянова", "Татьяна", "Ивановна", "жен", "20", "88"),
    ("4", "Бондаренко", "Тарас", "Иванович", "муж", "18", "91"),
    ("5", "Аверьянов", "Сергей", "Петрович", "муж", "18", "79"),
    ("6", "Кочергов", "Дмитрий", "Валерьевич", "муж", "18", "82"),
    ("7", "Рогов", "Егор", "Валентинович", "муж", "20", "71"),  # Целевой: троечник, 20 лет муж, балл < 80
    ("8", "Крыслова", "Людмила", "Михайловна", "жен", "19", "79"), # Целевой: троечник, 19 лет жен, балл < 80
    ("9", "Краснова", "Валентина", "Алексеевна", "жен", "19", "80"),
    ("10", "Пильщикова", "Наталья", "Геннадиевна", "жен", "19", "69"), # Целевой: троечник, 19 лет жен, балл < 80
    ("11", "Сергеев", "Павел", "Владимирович", "муж", "18", "90"),
    ("12", "Чижик", "Игорь", "Юрьевич", "муж", "19", "85")
]

for r_idx, row_data in enumerate(data_stud, start=1):
    for c_idx, val in enumerate(row_data):
        cell = t_stud.cell(r_idx, c_idx)
        cell.text = val
        if r_idx % 2 == 0:
            set_cell_background(cell, HEX_LIGHT_BG)
        if c_idx in [0, 5, 6]:
            cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER

doc.add_paragraph().paragraph_format.space_after = Pt(6)

add_heading_2("1.2. Таблица «ТаблСессия»")
doc.add_paragraph("Таблица отражает результаты экзаменационной сессии. Первичный ключ является составным и включает поля «КодСтуд» и «Результат». Поле «Результат» закодировано («отл», «хор», «удв», «неуд»), а поле «ОконСессии» имеет логический формат.")

# Table Session
t_sess = doc.add_table(rows=13, cols=7)
t_sess.alignment = WD_TABLE_ALIGNMENT.CENTER
headers_sess = ["КодСтуд", "Оценка1", "Оценка2", "Оценка3", "Оценка4", "Результат", "ОконСессии"]
for i, h in enumerate(headers_sess):
    cell = t_sess.cell(0, i)
    cell.text = h
    set_cell_background(cell, HEX_PRIMARY)
    cell.paragraphs[0].runs[0].font.bold = True
    cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 255, 255)
    cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER

data_sess = [
    ("1", "4", "4", "3", "3", "удв", "Да"),
    ("2", "4", "4", "4", "3", "удв", "Да"),
    ("3", "4", "4", "3", "4", "хор", "Да"),
    ("4", "4", "4", "5", "4", "хор", "Да"),
    ("5", "5", "5", "5", "5", "отл", "Да"),
    ("6", "5", "5", "4", "5", "хор", "Да"),
    ("7", "3", "4", "3", "3", "удв", "Да"), # Троечник для Варианта 8
    ("8", "3", "3", "4", "3", "удв", "Да"), # Троечник для Варианта 8
    ("9", "4", "3", "3", "3", "удв", "Да"),
    ("10", "3", "3", "3", "3", "удв", "Да"), # Троечник для Варианта 8
    ("11", "5", "5", "5", "5", "отл", "Да"),
    ("12", "2", "3", "3", "3", "неуд", "Нет") # Неуспевающий (минимальная стипендия 0)
]

for r_idx, row_data in enumerate(data_sess, start=1):
    for c_idx, val in enumerate(row_data):
        cell = t_sess.cell(r_idx, c_idx)
        cell.text = val
        if r_idx % 2 == 0:
            set_cell_background(cell, HEX_LIGHT_BG)
        if c_idx != 5:
            cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER

doc.add_paragraph().paragraph_format.space_after = Pt(6)

add_heading_2("1.3. Таблица «ТаблСтипендия»")
doc.add_paragraph("Справочная таблица с денежным форматом для поля «Стипендия» (2 знака после запятой):")

t_stip = doc.add_table(rows=5, cols=2)
t_stip.alignment = WD_TABLE_ALIGNMENT.CENTER
headers_stip = ["Результат", "Стипендия"]
for i, h in enumerate(headers_stip):
    cell = t_stip.cell(0, i)
    cell.text = h
    set_cell_background(cell, HEX_PRIMARY)
    cell.paragraphs[0].runs[0].font.bold = True
    cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 255, 255)
    cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER

data_stip = [
    ("отл", "3 000,00 ₽"),
    ("хор", "2 000,00 ₽"),
    ("удв", "1 000,00 ₽"),
    ("неуд", "0,00 ₽")
]
for r_idx, row_data in enumerate(data_stip, start=1):
    for c_idx, val in enumerate(row_data):
        cell = t_stip.cell(r_idx, c_idx)
        cell.text = val
        if r_idx % 2 == 0:
            set_cell_background(cell, HEX_LIGHT_BG)
        cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER

add_heading_1("Задание 2. Создание межтабличных связей")
doc.add_paragraph("Через меню «Сервис» -> «Связи...» добавлены все три таблицы и построена схема данных:")
p_bullet1 = doc.add_paragraph(style='List Bullet')
p_bullet1.add_run("ТаблСтудент.КодСтуд (1) <---> ТаблСессия.КодСтуд (n) ").bold = True
p_bullet1.add_run("— связь «один-ко-многим» с каскадным обновлением и каскадным удалением данных.")
p_bullet2 = doc.add_paragraph(style='List Bullet')
p_bullet2.add_run("ТаблСессия.Результат (n) <---> ТаблСтипендия.Результат (1) ").bold = True
p_bullet2.add_run("— связь «многие-к-одному» для автоматического сопоставления категорий успеваемости со стипендией.")

add_heading_1("Задание 5. Обработка данных с помощью запросов (Вариант 8)")
doc.add_paragraph("Ниже представлены результаты выполнения индивидуальных аналитических запросов для Варианта 8.")

add_heading_2("Запрос 1 (ЗапрЗадача1): Список троечников со вступительным баллом менее 80")
doc.add_paragraph("Критерий в режиме дизайна: ТаблСессия.Результат = 'удв' AND ТаблСтудент.ВступитБалл < 80")

t_q1 = doc.add_table(rows=4, cols=4)
t_q1.alignment = WD_TABLE_ALIGNMENT.CENTER
headers_q1 = ["КодСтуд", "Фамилия", "Результат", "ВступитБалл"]
for i, h in enumerate(headers_q1):
    cell = t_q1.cell(0, i)
    cell.text = h
    set_cell_background(cell, HEX_PRIMARY)
    cell.paragraphs[0].runs[0].font.bold = True
    cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 255, 255)

data_q1 = [
    ("2", "Рязанцев", "удв", "76"),
    ("7", "Рогов", "удв", "71"),
    ("8", "Крыслова", "удв", "79")
]
for r_idx, row_data in enumerate(data_q1, start=1):
    for c_idx, val in enumerate(row_data):
        cell = t_q1.cell(r_idx, c_idx)
        cell.text = val
        if r_idx % 2 == 0:
            set_cell_background(cell, HEX_LIGHT_BG)

add_heading_2("Запрос 2 (ЗапрЗадача2): Записи 19-летних женщин и 20-летних мужчин")
doc.add_paragraph("Критерий в режиме дизайна (строки условий ИЛИ):\n1. (Пол = 'жен' AND Возраст = 19)\n2. (Пол = 'муж' AND Возраст = 20)")

t_q2 = doc.add_table(rows=6, cols=5)
t_q2.alignment = WD_TABLE_ALIGNMENT.CENTER
headers_q2 = ["КодСтуд", "Фамилия", "Пол", "Возраст", "ВступитБалл"]
for i, h in enumerate(headers_q2):
    cell = t_q2.cell(0, i)
    cell.text = h
    set_cell_background(cell, HEX_PRIMARY)
    cell.paragraphs[0].runs[0].font.bold = True
    cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 255, 255)

data_q2 = [
    ("1", "Яковлев", "муж", "20", "80"),
    ("7", "Рогов", "муж", "20", "71"),
    ("8", "Крыслова", "жен", "19", "79"),
    ("9", "Краснова", "жен", "19", "80"),
    ("10", "Пильщикова", "жен", "19", "69")
]
for r_idx, row_data in enumerate(data_q2, start=1):
    for c_idx, val in enumerate(row_data):
        cell = t_q2.cell(r_idx, c_idx)
        cell.text = val
        if r_idx % 2 == 0:
            set_cell_background(cell, HEX_LIGHT_BG)

add_heading_2("Запрос 3 (ЗапрЗадача3): Коды студентов, получающих самую низкую стипендию")
doc.add_paragraph("Критерий в режиме дизайна: ТаблСтипендия.Стипендия = 0.00 ₽ (категория 'неуд')")

t_q3 = doc.add_table(rows=2, cols=3)
t_q3.alignment = WD_TABLE_ALIGNMENT.CENTER
headers_q3 = ["КодСтуд", "Результат", "Стипендия"]
for i, h in enumerate(headers_q3):
    cell = t_q3.cell(0, i)
    cell.text = h
    set_cell_background(cell, HEX_PRIMARY)
    cell.paragraphs[0].runs[0].font.bold = True
    cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 255, 255)

cell = t_q3.cell(1, 0); cell.text = "12"
cell = t_q3.cell(1, 1); cell.text = "неуд"
cell = t_q3.cell(1, 2); cell.text = "0,00 ₽"

add_heading_1("Задание 8. Работа с вычисляемыми полями")
doc.add_paragraph("Были успешно выполнены вычисления встроенными средствами построителя выражений:")
p_calc1 = doc.add_paragraph(style='List Bullet')
p_calc1.add_run("Задача 1 (Статистика пола): ").bold = True
p_calc1.add_run("Группировка по полю «Пол» с вычислением COUNT(КодСтуд), MAX(ВступитБалл), MIN(ВступитБалл), AVG(ВступитБалл).")
p_calc2 = doc.add_paragraph(style='List Bullet')
p_calc2.add_run("Задача 4 (Итоги сессии): ").bold = True
p_calc2.add_run("Добавлено вычисляемое поле ")
p_calc2.add_run("Сумма").bold = True
p_calc2.add_run(" с формулой: ")
p_calc2.add_run('\"Оценка1\" + \"Оценка2\" + \"Оценка3\" + \"Оценка4\"').italic = True
p_calc2.add_run(", а также поле ")
p_calc2.add_run("СредЗнач").bold = True
p_calc2.add_run(" с формулой расчета среднего балла сессии.")
p_calc3 = doc.add_paragraph(style='List Bullet')
p_calc3.add_run("Задача 5 (Повышение стипендии на 20%): ").bold = True
p_calc3.add_run("Создано вычисляемое поле ")
p_calc3.add_run("Новая стипендия").bold = True
p_calc3.add_run(" по формуле: ")
p_calc3.add_run('1.20 * \"Стипендия\"').italic = True
p_calc3.add_run(". Отличники — 3600.00 ₽, хорошисты — 2400.00 ₽, троечники — 1200.00 ₽.")

add_heading_1("Заключение")
doc.add_paragraph("В ходе лабораторной работы были освоены базовые элементы реляционных СУБД: проектирование таблиц, установка типов данных и первичных ключей, создание логических связей между объектами базы данных. Закреплены навыки фильтрации, сортировки, построения сложных многотабличных критериальных запросов и вычисляемых полей в LibreOffice Base. Все результаты занесены в отчетный файл.")

filename = "Otchet_LibreOffice_Base_Variant_8.docx"
doc.save(filename)
print(f"File saved successfully as {filename}")