Files
api-copp/route/glomshtok_crud.py
2026-04-02 14:14:08 +05:00

505 lines
42 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Эндпоинты для работы с тестом «Карта интересов»
(А.Е. Гломшток, модификация А.А. Азабель, А.Г. Грецова).
POST /polls/glomshtok — создать опрос в БД (144 утверждения, 5 вариантов ответа)
POST /polls/glomshtok/{id}/seed-scores — загрузить ключ расшифровки (24 сферы интересов)
GET /polls/glomshtok — список тестов «Карта интересов»
Ключ: вопрос с position=N относится к измерению dimension = ((N - 1) % 24) + 1.
Пример: вопросы 1, 25, 49, 73, 97, 121 → Биология; вопросы 13, 37, 61, 85, 109, 133 → Военные специальности.
Варианты ответа и баллы: ++ = +2 | + = +1 | 0 = 0 | - = 1 | -- = 2
Интерпретация суммы по сфере (6 вопросов × ±2, диапазон 12..+12):
12..6 → область деятельности активно отрицается («Что угодно, только не это!»)
5..1 → область деятельности интереса не вызывает
+1..+4 → интерес выражен слабо
+5..+7 → выраженный интерес
+8..+12 → ярко выраженный интерес
После прохождения теста (POST /polls/{id}/responses) диаграмма строится автоматически
и сохраняется в data/radar/<poll_title>/<user_id>/<result_id>.svg.
"""
from fastapi import APIRouter, HTTPException
import uuid
from bd import make_engine
from bd.tables.poll import Poll
from bd.tables.question import Question
from bd.tables.choice import Choice
from bd.tables.scale import ScaleDimension, ChoiceScore
router = APIRouter(tags=["polls"], prefix="/polls")
# ---------------------------------------------------------------------------
# 144 утверждения «Карты интересов»
# Порядок строго соответствует PDF; индекс + 1 = номер вопроса.
# ---------------------------------------------------------------------------
STATEMENTS: list[str] = [
"Изучать разнообразие животного и растительного мира.", # 1
"Проводить физические эксперименты.", # 2
"Разрабатывать новые технологии химического синтеза.", # 3
"Изучать месторождения полезных ископаемых.", # 4
"Разрабатывать новые методы диагностики и лечения различных болезней.", # 5
"Устанавливать и налаживать работу компьютерных программ.", # 6
"Следить за последовательностью и технологиями строительных работ.", # 7
"Решать задачки из книг типа «Математические игры», «Занимательная математика».", # 8
"Планировать финансовую и производственную деятельность предприятия, фирмы.", # 9
"Помогать делать переводы документации.", # 10
"Управлять машиной, автобусом, трейлером и т. д.", # 11
"Заниматься в секции парашютистов, в авиаклубе или заниматься в секции яхтсменов, аквалангистов.", # 12
"Заниматься в стрелковой секции.", # 13
"Заниматься в историческом клубе, разыгрывать ролевые игры по сюжетам исторических событий.", # 14
"Изучать устройство и режимы работы станков.", # 15
"Писать статьи, фельетоны, очерки в периодические издания и в Интернет.", # 16
"Оказывать юридическую помощь людям, консультируя их по вопросам законодательства.", # 17
"Организовывать для ребят игры и праздники.", # 18
"Консультировать людей при крупных покупках (автомобиль, заграничный тур и т. д.).", # 19
"Тренировать команду спортсменов.", # 20
"Работать на музыкальных радиоканалах, составлять списки произведений для звучания в эфире.", # 21
"Сниматься в кино или играть на сцене.", # 22
"Делать интернет-сайты, веб-страницы.", # 23
"Разрабатывать мероприятия по охране численности редких растений и животных.", # 24
"Участвовать в биологических экспедициях, посещать биологические секции.", # 25
"Изучать законы природы, находить физические закономерности.", # 26
"Проводить опыты с различными веществами, следить за ходом химических реакций.", # 27
"Вести наблюдения за изменениями состояния атмосферы.", # 28
"Читать о том, как люди учились бороться с болезнями и изобретали новые лекарства.", # 29
"Находить и устранять причины сбоя в компьютерах, аппаратуре, приборах.", # 30
"Набрасывать эскизы или выполнять чертежи различных построек.", # 31
"Соревноваться в решении математических задач.", # 32
"Выступать посредником при заключении торговых сделок (искать покупателям продавцов и наоборот).", # 33
"Проводить экскурсии на иностранном языке для гостей нашего города.", # 34
"Управлять пожарной машиной.", # 35
"Управлять самолётом МЧС или скоростным катером.", # 36
"Участвовать в военизированных учениях («Зарница», «Разведчик» и др.).", # 37
"Посещать исторические музеи, изучать исторические памятники разных народов.", # 38
"Регулировать механизмы и заменять их в случае неисправности.", # 39
"Наблюдать и анализировать события, поступки людей, делать об этом репортажи.", # 40
"Искать и фиксировать следы на месте преступления.", # 41
"Заниматься репетиторством и преподавательской деятельностью.", # 42
"Изучать спрос покупателей на определённый товар.", # 43
"Читать специальные спортивные новости в газетах и на сайтах.", # 44
"Исполнять произведения на музыкальных инструментах с использованием различных техник игры.", # 45
"Изучать основы сценического искусства, творчество знаменитых мастеров сцены.", # 46
"Искать наиболее рациональное и эстетическое цветовое решение для интерьеров.", # 47
"Проводить наблюдения и контроль загрязнения окружающей среды.", # 48
"Изучать анатомию и физиологию животных.", # 49
"Собирать установки для проведения физических экспериментов.", # 50
"В лабораторных условиях определять степень загрязнённости почвы химическими веществами.", # 51
"Создавать ландшафтные карты с помощью компьютерных геоинформационных систем.", # 52
"Интересоваться причинами и способами лечения болезни.", # 53
"Прокладывать сетевые кабели и кабели силового питания.", # 54
"Изучать новые технологии строительства.", # 55
"Изучать языки программирования на компьютере.", # 56
"Осуществлять финансовые расчёты между предприятиями.", # 57
"Читать литературу на иностранном языке или смотреть фильмы без перевода.", # 58
"Управлять современным поездом дальнего следования.", # 59
"Оказывать экстренную помощь людям на терпящих бедствие судах.", # 60
"Быть военным инженером или командиром.", # 61
"Обсуждать исторические и текущие политические события России и других стран.", # 62
"Подбирать цветовые оттенки, красить дома, расписывать стены.", # 63
"Добывать эксклюзивную информацию, передавать её читателям и зрителям.", # 64
"Контролировать своевременную уплату налогов фирмами и физическими лицами.", # 65
"Обучать маленьких детей, играя с ними.", # 66
"Встречать и размещать пассажиров в салоне самолёта.", # 67
"Тренироваться в профессиональном спортивном клубе.", # 68
"Играть в музыкальной группе или в оркестре, следуя указаниям дирижёра.", # 69
"Вести концертные программы, объявлять зрителям новые номера программы.", # 70
"Создавать необычные модели одежды.", # 71
"Разрабатывать меры по снижению количества вредных производственных выбросов в окружающую среду.", # 72
"Разрабатывать средства борьбы с возбудителями заболеваний животных и растений.", # 73
"Изучать процессы взаимодействия элементарных частиц в ядерных реакторах.", # 74
"Брать на анализ химические пробы воды, продуктов питания, почвы и пр.", # 75
"Вести разведку месторождений полезных ископаемых: нефти, газа, драгоценных металлов и др.", # 76
"Тренироваться в навыках первой медицинской помощи.", # 77
"Проверять, испытывать, регулировать работу узлов автомобиля, самолёта, корабля и т. д.", # 78
"Готовить растворы, смеси для строительных работ.", # 79
"Разрабатывать программные алгоритмы для выполнения сложных расчётов.", # 80
"Вести финансовую документацию фирмы, предприятия.", # 81
"Изучать иностранные языки.", # 82
"Регулировать движение транспортных потоков на улицах города.", # 83
"Оказывать экстренную помощь людям «с воздуха» (например, управляя вертолётом МЧС).", # 84
"Изучать устройство оружия, военной техники.", # 85
"Анализировать по книгам исторические факты.", # 86
"Обеспечивать в домах систему отопления, исправную работу водопровода.", # 87
"Редактировать тексты книг, статей, выступлений высокопоставленных лиц.", # 88
"Выдвигать обвинения преступникам, назначать им наказания в соответствии с законом.", # 89
"Готовить школьников к олимпиадным заданиям, конкурсам.", # 90
"Помогать подбирать людям наряды и украшения для торжественных церемоний.", # 91
"Сдавать спортивные нормативы.", # 92
"Выступать в качестве солиста перед публикой на концертах, в клубах.", # 93
"Подбирать актёров для съёмки художественного фильма или рекламного клипа.", # 94
"Делать фотоснимки, монтировать фотографии с помощью компьютера.", # 95
"Заниматься мониторингом состояния воздуха на оживлённых городских магистралях.", # 96
"Отбирать и заготавливать семена для посадки, высаживать растения в зимних садах.", # 97
"Моделировать на компьютере физические законы природы.", # 98
"Создавать новые синтетические вещества из нефтепродуктов и других полезных ископаемых.", # 99
"Изучать особенности флоры и фауны различных регионов.", # 100
"Осуществлять уход за человеком во время его выздоровления после болезни.", # 101
"Заниматься в технической или электротехнической секции (например, в авиа- или судомоделировании).", # 102
"Изучать качества и условия применения строительных материалов.", # 103
"Писать компьютерные программы на основе анализа математических алгоритмов.", # 104
"Рассчитывать предполагаемый размер прибыли предприятия.", # 105
"Осуществлять перевод телепередач на русский язык.", # 106
"Консультировать людей по соблюдению правил дорожного движения.", # 107
"Изучать особенности управления яхтой в штормовую погоду.", # 108
"Предупреждать незаконное пересечение государственной границы иностранными гражданами.", # 109
"Разыскивать и собирать материалы, свидетельствующие о событиях прошлого.", # 110
"Обрабатывать и изготовлять металлические детали на фрезерном станке.", # 111
"Работать в средствах массовой информации, вести телепередачи.", # 112
"Выяснять у людей причины незаконных поступков, которые они скрывают.", # 113
"Организовывать походы для школьников.", # 114
"Разрабатывать рекламные акции для продвижения товара в магазинах города.", # 115
"Ездить в качестве участника на спортивные соревнования в другой город.", # 116
"Сочинять музыку для кинофильмов, телепередач, для известных исполнителей.", # 117
"Подготавливать и ставить трюки в кино.", # 118
"Придумывать оригинальные ювелирные изделия, украшения.", # 119
"Изучать взаимоотношения живых организмов с их средой обитания.", # 120
"Заниматься дрессировкой служебных собак или других животных.", # 121
"Читать книги типа «Занимательная физика», «Физики шутят» и т. п.", # 122
"Управлять технологическими процессами производства лекарств.", # 123
"Участвовать в географических экспедициях.", # 124
"Работать врачом на станции «Скорой помощи».", # 125
"Ремонтировать радиоприборы и различную аппаратуру.", # 126
"Продумывать планировку домов, этажей, квартир. Намечать план строительства.", # 127
"Отражать в виде чисел и формул какие-либо события, процессы или явления.", # 128
"Заключать сделки, договоры на выполнение определённых работ.", # 129
"Изучать происхождение слов и различных словосочетаний в разных языках.", # 130
"Тренироваться водить машину или мотоцикл для подготовки к гонке.", # 131
"Изучать особенности управления лёгким самолётом в ветреную погоду.", # 132
"Жить согласно уставу, носить военную форму.", # 133
"Участвовать в археологических экспедициях, работать на раскопках.", # 134
"Делать из дерева различные предметы, мебель и пр.", # 135
"Осуществлять литературную обработку статей в соответствии с жанровым своеобразием.", # 136
"Продумывать новые законы и поправки в кодексы, которые были бы удобны для применения.", # 137
"Руководить одной из секций в доме детского творчества.", # 138
"Помогать человеку улучшить внешность с помощью причёски, макияжа, подбора красивой одежды и т. п.", # 139
"Осуществлять честное и грамотное судейство на спортивных соревнованиях.", # 140
"Изучать творчество выдающихся композиторов, поэтов песен и исполнителей.", # 141
"Создавать творческие проекты, «раскручивать» исполнителей.", # 142
"Заниматься дизайном интерьеров.", # 143
"Следить за качеством воды, поступающей в городскую водопроводную сеть.", # 144
]
# ---------------------------------------------------------------------------
# 24 сферы интересов
# Порядок соответствует столбцам бланка ответов из PDF.
# ---------------------------------------------------------------------------
DIMENSIONS = [
{
"code": "1", "name": "Биология", "position": 1, "color": "#6B8E5E",
"description": (
"Интерес к живой природе, биологии, зоологии, ботанике, экспедициям. "
"Рекомендуемые профессии: биолог, зоолог, ботаник, агроном, ветеринар, эколог."
),
},
{
"code": "2", "name": "Физика", "position": 2, "color": "#4A7FA5",
"description": (
"Интерес к физическим законам, экспериментам, моделированию. "
"Рекомендуемые профессии: физик, инженер-физик, научный сотрудник, метролог."
),
},
{
"code": "3", "name": "Химия", "position": 3, "color": "#9B59B6",
"description": (
"Интерес к химическим процессам, лабораторным опытам, синтезу веществ. "
"Рекомендуемые профессии: химик, технолог, лаборант, провизор."
),
},
{
"code": "4", "name": "География", "position": 4, "color": "#27AE60",
"description": (
"Интерес к изучению Земли, природных ресурсов, экспедициям, геоинформатике. "
"Рекомендуемые профессии: географ, геолог, картограф, метеоролог."
),
},
{
"code": "5", "name": "Медицина", "position": 5, "color": "#E74C3C",
"description": (
"Интерес к здоровью, лечению людей и животных, медицинским технологиям. "
"Рекомендуемые профессии: врач, медсестра, фельдшер, фармацевт."
),
},
{
"code": "6", "name": "Техника и электроника", "position": 6, "color": "#2980B9",
"description": (
"Интерес к приборам, компьютерам, электронике, ремонту техники. "
"Рекомендуемые профессии: инженер, электроник, системный администратор, радиотехник."
),
},
{
"code": "7", "name": "Строительство", "position": 7, "color": "#795548",
"description": (
"Интерес к строительным технологиям, архитектуре, планировке зданий. "
"Рекомендуемые профессии: архитектор, прораб, строитель, проектировщик."
),
},
{
"code": "8", "name": "Математика", "position": 8, "color": "#1ABC9C",
"description": (
"Интерес к числам, формулам, задачам, алгоритмам, программированию. "
"Рекомендуемые профессии: математик, программист, статистик, аналитик данных."
),
},
{
"code": "9", "name": "Экономика", "position": 9, "color": "#F39C12",
"description": (
"Интерес к финансам, торговле, производственному планированию. "
"Рекомендуемые профессии: экономист, бухгалтер, финансист, менеджер."
),
},
{
"code": "10", "name": "Иностранные языки", "position": 10, "color": "#3498DB",
"description": (
"Интерес к языкам, переводу, межкультурным коммуникациям. "
"Рекомендуемые профессии: переводчик, лингвист, преподаватель иностранного языка."
),
},
{
"code": "11", "name": "Транспорт", "position": 11, "color": "#E67E22",
"description": (
"Интерес к управлению транспортом, логистике, ПДД. "
"Рекомендуемые профессии: водитель, машинист, диспетчер, логист."
),
},
{
"code": "12", "name": "Авиация, морское дело", "position": 12, "color": "#16A085",
"description": (
"Интерес к полётам, мореплаванию, экстремальным видам деятельности. "
"Рекомендуемые профессии: лётчик, штурман, капитан, водолаз, парашютист."
),
},
{
"code": "13", "name": "Военные специальности", "position": 13, "color": "#2C3E50",
"description": (
"Интерес к армии, военной технике, патриотическим видам деятельности. "
"Рекомендуемые профессии: офицер, пограничник, военный инженер, командир."
),
},
{
"code": "14", "name": "История", "position": 14, "color": "#8E6B3E",
"description": (
"Интерес к истории, политике, археологии, изучению прошлого. "
"Рекомендуемые профессии: историк, археолог, политолог, архивист."
),
},
{
"code": "15", "name": "Рабочие специальности", "position": 15, "color": "#7F8C8D",
"description": (
"Интерес к ручному труду, ремёслам, работе на станках. "
"Рекомендуемые профессии: слесарь, токарь, столяр, сантехник, маляр."
),
},
{
"code": "16", "name": "Журналистика", "position": 16, "color": "#C0392B",
"description": (
"Интерес к написанию текстов, СМИ, репортажам, медиапроизводству. "
"Рекомендуемые профессии: журналист, редактор, корреспондент, телеведущий."
),
},
{
"code": "17", "name": "Юриспруденция", "position": 17, "color": "#1F3A5F",
"description": (
"Интерес к законам, правопорядку, криминалистике, судопроизводству. "
"Рекомендуемые профессии: юрист, адвокат, судья, следователь, нотариус."
),
},
{
"code": "18", "name": "Педагогика", "position": 18, "color": "#D4A84B",
"description": (
"Интерес к обучению, воспитанию, работе с детьми. "
"Рекомендуемые профессии: учитель, воспитатель, педагог-психолог, тренер."
),
},
{
"code": "19", "name": "Сфера обслуживания, торговля", "position": 19, "color": "#F1948A",
"description": (
"Интерес к торговле, обслуживанию клиентов, маркетингу, сервису. "
"Рекомендуемые профессии: продавец, менеджер по продажам, администратор, стилист."
),
},
{
"code": "20", "name": "Физкультура и спорт", "position": 20, "color": "#28B463",
"description": (
"Интерес к спорту, тренировкам, соревнованиям, физическому развитию. "
"Рекомендуемые профессии: спортсмен, тренер, инструктор, спортивный судья."
),
},
{
"code": "21", "name": "Музыка", "position": 21, "color": "#A569BD",
"description": (
"Интерес к музыке, игре на инструментах, сочинению, звукорежиссуре. "
"Рекомендуемые профессии: музыкант, композитор, дирижёр, звукорежиссёр."
),
},
{
"code": "22", "name": "Сценическое искусство", "position": 22, "color": "#EC407A",
"description": (
"Интерес к театру, кино, сцене, перевоплощению, продюсированию. "
"Рекомендуемые профессии: актёр, режиссёр, сценарист, продюсер."
),
},
{
"code": "23", "name": "Изобразительное искусство", "position": 23, "color": "#FF8C42",
"description": (
"Интерес к рисованию, дизайну, фотографии, созданию визуальных образов. "
"Рекомендуемые профессии: художник, дизайнер, фотограф, ювелир."
),
},
{
"code": "24", "name": "Экология", "position": 24, "color": "#2ECC71",
"description": (
"Интерес к охране природы, экологическому мониторингу, контролю загрязнений. "
"Рекомендуемые профессии: эколог, природоохранный инспектор, санитарный врач."
),
},
]
# ---------------------------------------------------------------------------
# Шкала оценок: позиция варианта ответа → числовой балл
# ---------------------------------------------------------------------------
CHOICE_SCORES: dict[int, float] = {
1: 2.0, # ++
2: 1.0, # +
3: 0.0, # 0
4: -1.0, # -
5: -2.0, # --
}
CHOICE_LABELS: list[str] = ["++", "+", "0", "-", "--"]
def get_session():
from sqlalchemy.orm import sessionmaker
return sessionmaker(bind=make_engine(), future=True)
@router.post("/glomshtok", status_code=201)
def create_glomshtok_poll():
"""
Создаёт тест «Карта интересов» в базе данных (144 утверждения, 5 вариантов ответа).
После создания вызовите POST /polls/glomshtok/{id}/seed-scores для загрузки ключа расшифровки.
"""
Session = get_session()
with Session() as session:
poll = Poll(
title="Карта интересов — А.Е. Гломшток (модификация А.А. Азабель, А.Г. Грецова)",
description=(
"Методика «Карта интересов» для профориентации. Оцените 144 утверждения по шкале: "
"«++» (очень хотелось бы), «+» (нравится), «0» (сомневаюсь), "
"«-» (не нравится), «--» (совершенно не нравится). "
"Определяет выраженность интересов в 24 сферах деятельности."
),
)
session.add(poll)
for idx, text in enumerate(STATEMENTS, start=1):
q = Question(text=text, position=idx, type="single", poll=poll)
session.add(q)
for pos, label in enumerate(CHOICE_LABELS, start=1):
ch = Choice(question=q, text=label, position=pos)
session.add(ch)
session.commit()
session.refresh(poll)
return {"id": str(poll.id), "questions": len(poll.questions)}
@router.get("/glomshtok")
def list_glomshtok_polls():
"""Возвращает список тестов «Карта интересов» Гломштока."""
Session = get_session()
with Session() as session:
rows = session.query(Poll).filter(Poll.title.like("%Гломшток%")).all()
return [{"id": str(p.id), "title": p.title, "description": p.description} for p in rows]
@router.post("/glomshtok/{poll_id}/seed-scores", status_code=201)
def seed_glomshtok_scores(poll_id: str):
"""
Загружает ключ расшифровки «Карты интересов» в базу данных:
- создаёт 24 оси диаграммы (ScaleDimension) для указанного теста
- для каждого из 144 вопросов привязывает 5 вариантов ответа к соответствующей оси (ChoiceScore)
с баллами: ++ = +2 | + = +1 | 0 = 0 | - = 1 | -- = 2
Формула: вопрос с position=N → измерение ((N 1) % 24) + 1
При повторном вызове оси и расшифровки пересоздаются.
"""
Session = get_session()
try:
pid = uuid.UUID(poll_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid poll UUID")
with Session() as session:
poll = session.get(Poll, pid)
if not poll:
raise HTTPException(status_code=404, detail="Poll not found")
questions = (
session.query(Question)
.filter(Question.poll_id == pid)
.order_by(Question.position.asc())
.all()
)
if len(questions) != 144:
raise HTTPException(
status_code=422,
detail=(
f"Ожидается 144 вопроса, найдено {len(questions)}. "
"Убедитесь, что тест создан через POST /polls/glomshtok."
),
)
# Удаляем старые оси (ChoiceScore удаляются каскадно)
old_dims = session.query(ScaleDimension).filter(ScaleDimension.poll_id == pid).all()
for d in old_dims:
session.delete(d)
session.flush()
# Создаём 24 оси: position → объект
dim_map: dict[int, ScaleDimension] = {}
for d_data in DIMENSIONS:
dim = ScaleDimension(
poll_id=pid,
name=d_data["name"],
description=d_data["description"],
color=d_data["color"],
position=d_data["position"],
)
session.add(dim)
session.flush()
dim_map[d_data["position"]] = dim
# Для каждого вопроса привязываем все 5 вариантов к соответствующей оси
scores_created = 0
for q in questions:
dim_idx = ((q.position - 1) % 24) + 1
dim = dim_map.get(dim_idx)
if dim is None:
continue
choices = (
session.query(Choice)
.filter(Choice.question_id == q.id)
.order_by(Choice.position.asc())
.all()
)
for ch in choices:
score_val = CHOICE_SCORES.get(ch.position, 0.0)
session.add(ChoiceScore(choice_id=ch.id, dimension_id=dim.id, score=score_val))
scores_created += 1
session.commit()
return {
"status": "ok",
"dimensions": len(DIMENSIONS),
"choice_scores": scores_created,
"message": (
"Расшифровка «Карты интересов» успешно загружена. "
"После прохождения теста диаграмма строится автоматически и "
"сохраняется в data/radar/."
),
}