"""Эндпоинты для работы с тестом «Карта интересов» (А.Е. Гломшток, модификация А.А. Азабель, А.Г. Грецова). 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///.svg. """ from fastapi import APIRouter, HTTPException import uuid from bd import get_session 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", "-", "--"] @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/." ), }