├── .gitignore ├── LICENSE ├── README.md ├── colors.py ├── data.csv ├── images ├── add.png ├── delete.png ├── icon.ico ├── sound_OFF.png ├── sound_ON.png ├── start.png └── stop.png ├── knowledge ├── 1.pdf ├── 2.pdf ├── 3.pdf ├── 4.pdf ├── 5.pdf ├── 6.pdf └── 7.pdf ├── main.py ├── manage_db.py ├── models.py ├── my_timers.py ├── requirements.txt ├── settings.py ├── user_statistics.py └── validator.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ivan Zaycev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :bar_chart: Python Interview Assistant 2 | 3 | ![demo](https://github.com/IvanZaycev0717/python_interview_assistant_rus/assets/111955306/1404cff4-b19e-4ef4-af14-eafc8959fecb) 4 | 5 | **Python Interview Assistant** - многопоточное desktop-приложение на Python, которое позволяет подготовиться к собеседованиям по Python. 6 | 7 | ## :exclamation:Важное обновление 8 | Добавлена поддержка Linux. Приложение протестировано на Ubuntu 22.04. **При работе с Linux четко следуйте инструкции по установке** 9 | :mute: Звуковое воспроизведение вопросов отсутствует в версии для Linux в связи с особенностями сторонней библиотеки. 10 | 11 | ## :pager: Технологии 12 | - Python 3.12 13 | - SQLAlchemy 14 | - customtkinter, CTkMessagebox 15 | - PyMuPDF 16 | - Pillow 17 | 18 | ## :newspaper: Содержание приложения 19 | Приложение предназначено для отработки навыка прохождения собеседования и повторения основных тем, связанных с языком Python. Приложение состоит из следующих тем: 20 | 1. Базовый синтаксис Python (217 вопросов) 21 | 2. Объектно-ориентированное программирование (ООП) (110 вопросов) 22 | 3. Правила оформления кода (PEP8, PEP257) (27 вопросов) 23 | 4. Структуры данных на Python (69 вопросов) 24 | 5. Алгоритмы на Python (39 вопросов) 25 | 6. Git (64 вопроса) 26 | 7. Базы данных и SQL запросы (58 вопросов) 27 | 28 | ## :city_sunrise: Демонстрация работы приложения 29 | 30 | Сайт | Ссылка |Описание| 31 | | ------------- |:-------------:|:--------:| 32 | |![AID](https://github.com/IvanZaycev0717/the_mystery_of_the_mansion/assets/111955306/138cf8d1-a6f8-4835-9e54-93a48df815d3)|[Ссылка](https://youtu.be/eud7mDUjJ3E)|Демонстрация работы приложения| 33 | |![habr](https://github.com/IvanZaycev0717/the_mystery_of_the_mansion/assets/111955306/772e1cac-b1e7-49c3-b87f-5f8fb2bdfbc8)|[Ссылка](https://habr.com/ru/articles/779624/)|Статья о создании приложения| 34 | 35 | ## :floppy_disk: EXE-версия приложения для Windows 36 | :boom: **Строго для операционной системы Windows. Не запускать в Linux с помощью wine** 37 | Скачать exe-версию можно по следующей ссылке: 38 | Сайт | Ссылка | 39 | | ------------- |:-------------:| 40 | | ![yandex](https://github.com/IvanZaycev0717/python_interview_assistant_rus/assets/111955306/df5ff781-7cdd-4be5-9359-113b3706324e)| [Download](https://disk.yandex.ru/d/h4-_4ZhoutI5-A)| 41 | 42 | ## :fax: Гибкая настройка приложения 43 | Алгоритмы приложения позволяют пользователю гибко настраивать процесс собеседования под себя. Среди основных возможностей: 44 | - Выбор одной или нескольких из 7 тем 45 | - Возможность задавать вопросы в случайном порядке 46 | - Возможность задавать вопросы в последовательно 47 | - Вопросы выбирает приложение 48 | - Вы сами выбираете вопросы 49 | - Регулировка громкости или отключение звука приложения 50 | 51 | # :bookmark_tabs: Скачать код приложения на локальный ПК 52 | Перед установкой у вас должен уже быть установлен Python версии 3.10+. 53 | 54 | 55 | :calling: **для Windows** 56 | 57 | - Скопируйте репозиторий к себе на компьютер по SSH-ключу git@github.com:IvanZaycev0717/python_interview_assistant_rus.git 58 | 59 | - Установите виртуальное окружение python -m venv venv 60 | 61 | - Активируйте виртуальное окружение source venv/Scripts/activate 62 | 63 | - Установите внешние библиотеки, выполнив: pip install -r requirements.txt 64 | 65 | - Запустите файл main.py 66 | 67 | :penguin: **Для Linux** (протестировано на Ubuntu 22.04) 68 | - Убедитесь, что у вас установлен Python. Для этого в терминале выполните команду: 69 | ```python3 --version``` 70 | Требуется версия не ниже 3.10 71 | - Необходимо обновить пакеты виртуального окружения Python 72 | ```sudo apt install python3.10-venv``` 73 | Если у вас более новая версия, вместо 3.10 укажите свою версию 74 | - Скачайте пакеты графического интерфейса через терминал 75 | ```sudo apt-get install python3-tk``` 76 | - Через терминал в выбранную вами директорию клонируйте код репозитория 77 | ```git clone git@github.com:IvanZaycev0717python_interview_assistant_rus.git``` 78 | - Перейдите в данную директорию и создайте виртуальное окружение командой 79 | ```python3.10 –m venv env``` 80 | Если у вас более новая версия, вместо 3.10 укажите свою версию 81 | - Активируйте виртуальное окружение 82 | ```. env/bin/activate``` 83 | - Выполните команду 84 | ```pip3 install -r requirements.txt``` 85 | - Запустите файл main.py 86 | 87 | ## :mage: Автор 88 | **Иван Зайцев ivzaycev0717@yandex.ru 89 | (c) 2023** 90 | 91 | 92 | -------------------------------------------------------------------------------- /colors.py: -------------------------------------------------------------------------------- 1 | # Frames colors 2 | YELLOW_BACKGROUND = '#fff1c8' 3 | PINK_BACKGROUND = '#ffcccc' 4 | GREEN_BACKGROUND = '#d7e4d1' 5 | SWAMP_FOREGROUND = '#e2f7b5' 6 | ORANGE_FOREGROUND = '#ffd38f' 7 | NEW_USER_WINDOW_FOREGROUND = '#d3e4ef' 8 | 9 | # Button colors 10 | PINK_FOREGROUND = '#d07979' 11 | CRIMSON_HOVER = '#92465f' 12 | BEGIN_BUTTON_FOREGROUND_COLOR = '#d1ff00' 13 | BEGIN_BUTTON_HOVER_COLOR = '#ffec00' 14 | SOUNDS_BUTTONS_FOREGROUND = '#333f65' 15 | SOUNDS_BUTTONS_HOVER = '#232e52' 16 | POSITIVE_BUTTON_FOREGROUND = '#578555' 17 | POSITIVE_BUTTON_HOVER = '#2d642a' 18 | NEGATIVE_BUTTON_FOREGROUND = '#ac1416' 19 | NEGATIVE_BUTTON_HOVER = '#ce6163' 20 | ANSWER_BUTTON_FOREGROUND = '#c1461e' 21 | ANSWER_BUTTON_HOVER = '#ff662a' 22 | 23 | # Checkboxes colors 24 | CHECKBOX_HOVER_COLOR = '#68a248' 25 | 26 | # Progress bars colors 27 | PROGRESS_FOREGROUND = '#e6ffda' 28 | PROGRESS_COLOR = '#55e400' 29 | 30 | # Question tree colors 31 | GREEN = '#b6d7a8' 32 | RED = '#fea5aa' 33 | WHITE = '#ffffff' 34 | 35 | # Error colors 36 | ERROR_COLOR = '#d3e4ef' 37 | SUCCESS_COLOR = '#9effa2' 38 | 39 | # Canvas colors 40 | PDF_OUTPUT_COLOR = '#ECE8F3' 41 | -------------------------------------------------------------------------------- /data.csv: -------------------------------------------------------------------------------- 1 | 8;0;Уровень языка Python;К какому уровню относится язык программирования Python;;1;0 2 | 9;1;Типизация Python;Какую типизацию имеет язык Python?;;1;0 3 | 10;2;Неявная типизация;Объясните, что представляет собой неявная типизация?;какие средства в Python существуют, чтобы обойти неявную типизацию, приведите примеры?;1;0 4 | 11;3;Динамическая типизация;Объясните, что представляет собой динамическая типизация?;Приведите пример динамической типизации?;1;0 5 | 12;4;"Символ ""=""";Что означает символ «=»?;Как проверить на какую ячейку памяти ссылается переменная?;1;0 6 | 13;5;Каскадное присваивание;Что такое каскадное присваивание?;;1;0 7 | 14;6;Тип данных;Как узнать какой тип данных, на которые ссылается переменная?;напишите несколько переменных, которые ссылаются на объекты разных типов и проверьте их типы?;1;0 8 | 15;7;Сильная типизация;Что такое сильная типизация?;вызовите ошибку из-за сильной типизации?;1;0 9 | 16;8;Изменение объекта в памяти;Объекты в ячейки памяти можно изменять?;;1;1 10 | 17;9;Изменяемые типы данных;Перечислите изменяемые типы данных Python?;напишите примеры изменяемых типов данных и присвойте им переменные;1;1 11 | 18;10;Хеш изменяемых типов данных;Какая особенность есть у изменяемых типов данных Python с точки зрения хеширования?;;1;1 12 | 19;11;Неизменяемые типы Python;Перечислите неизменяемые типы данных Python?;напишите примеры неизменяемых типов данных и присвойте им переменные;1;1 13 | 20;12;Возвращение элементов по одному;Как называются объекты, которые способны возвращать элементы по одному?;перечислите итерируемые объекты;1;1 14 | 21;13;Итерируемые типы данных;В какие типы данных можно передавать любой итерируемый объект?;;1;2 15 | 22;14;Разные типы данных и арифметика;Можно ли производить арифметические операции для разных типов данных в Python?;напишите примеры арифметических операций объектов одинаковых типов данных.;1;2 16 | 23;15;Арифметические операции Python;Перечислите все арифметические операции в Python?;приведите примеры всех арифметических операций в Python?;1;2 17 | 24;16;Другие операции Python;Какие операции бывают в Python помимо арифметических?;;1;2 18 | 25;17;Приоритет арифметических операций;Какие приоритеты выполнения ВСЕХ АРИФМЕТИЧЕСКИХ операций?;вычислите 27 ** 1/3;1;2 19 | 26;18;Приоритет логических операций;Какие приоритеты выполнения ЛОГИЧЕСКИХ операций?;;1;2 20 | 27;19;"Разница между ""="" и ""==""";Какая разница между «=» и «==»?;;1;2 21 | 28;20;Округление чисел;Как можно округлять числа в Python?;приведите примеры округления чисел;1;3 22 | 29;21;Целочисленный тип Python;Что такое целочисленный тип данных?;приведите примеры как можно упростить читаемость большого числа типа int;1;3 23 | 30;22;Бинарный вид целого числа;Что такое бинарный вид целого числа?;представите число 124 в бинарном виде?;1;3 24 | 31;23;Особенность бинарного вида;Для чего иногда нужно представлять целые числа в бинарном виде?;покажите любую операцию для бинарного вида целого числа?;1;3 25 | 32;24;Пустой тип данных Python;Какой тип данных соответствует ничему, пустому типу?;;1;3 26 | 33;25;Действительные числа Python;Какой тип данных соответствует действительному числу из математики?;;1;3 27 | 34;26;Список Python;Что такое списки в Python?;;1;3 28 | 35;27;Упорядоченная коллекция;Что значит упорядоченная коллекция?;;1;4 29 | 36;28;Изменение элемента в списке;Что происходит в ячейки памяти, если изменить элементы в списке?;;1;4 30 | 37;29;Разные типы данных в списке;Могут ли быть разные типы данных внутри списка?;напишите список, у которого элементы - разные типы данных;1;4 31 | 38;30;Ввод данных в Python;Какая есть функция в Python для ввода данных и какой тип данных она возвращает?;;1;4 32 | 39;31;Список из введенных данных;Как можно задать список, используя функцию для ввода данных?;;1;4 33 | 40;32;Замена значения элемента;Произведите замену значения элемента с индексом 1 на 666 в списке num = [1, 2, 4] ?;;1;4 34 | 41;33;Минимальное значение элемента списка;Дан список winter = [-10, -5, -20, -21, -4, -2]. Вывести его элемент, имеющий минимальное значение.;;1;4 35 | 42;34;Сумма всех элементов списка;Дан список summa = [7, 8, 10]. Найти сумму всех его элементов.;;1;5 36 | 43;35;Срез списка;Дан список lst = [1, 3, 4, 6]. Произвести срез элементов так, чтобы можно было вывести в консоль список [3, 4].;;1;5 37 | 44;36;Срез с заменой элементов списка;Дан список marks = [2, 3, 4, 5, 3]. С помощью среза заменить элемент со значением «4» на «хорошо», элемент со значением «5» на «отлично». Должно получиться [2, 3, 'хорошо', 'отлично', 3].;;1;5 38 | 45;37;Конкатенация списков;Что такое конкатенация для списков?;приведите пример конкатенация для списков list_1 = ['one', 'two', 'three'] и list_2 = ['one', 2, 3.0]?;1;5 39 | 46;38;Строка в список;Как можно преобразовать строку в список и что в этом случае получится?;;1;5 40 | 47;39;Добавление элемента в конец списка;Как добавить элемент в конец списка?;;1;6 41 | 48;40;Расширение списка;Как можно расширить список без конкатенации?;;1;6 42 | 49;41;Вставка элемента под конкретным индексом;Как добавить элемент в список в конкретное место под конкретным индексом?;;1;6 43 | 50;42;Удаление элемента списка;Как удалить элемент из списка с конкретным значением элемента?;;1;6 44 | 51;43;Нет элемента при удалении списка;Что будет если при удалении элемента его нет в списке?;;1;6 45 | 52;44;Удаление элемента списка по индексу;Как удалить элемент из списка по значению его индекса?;;1;6 46 | 53;45;Поиск и возврат элементов списка;Как произвести поиск и возврат индекса по значению элемента?;;1;7 47 | 54;46;Поиск и возврат одинаковых элементов списка;Как произвести поиск и возврат одинаковых элементов списка?;;1;7 48 | 55;47;Сортировка списка;Как производится сортировка элементов списка по возрастанию и убыванию?;;1;7 49 | 56;48;Инверсия списка;Как производится инверсия элементов списка?;;1;8 50 | 57;49;Копирование списка;Как скопировать список в другую ячейку памяти?;;1;8 51 | 58;50;Удаление элементов списка;Как удалить все элементы списка?;;1;8 52 | 59;51;Список в строку;Как из списка создать строку?;;1;8 53 | 60;52;Вызов элементов списка;Как сделать последовательный вызов всех элементов списка?;;1;9 54 | 61;53;Оператор *;Как с помощью оператора * можно объединить 2 списка?;;1;9 55 | 62;54;Вложенные списки;Что такое вложенные списки?;приведите пример вложенного списка:;1;9 56 | 63;55;Обращение к элементу вложенного списка;Как происходит обращение к элементу вложенного списка?;;1;9 57 | 64;56;Кортеж;Что такое кортеж?;дан список кортежей lt = [('Geeks', 2), ('For', 4), ('geek','6')]. Преобразовать его с список ['Geeks', 2, 'For', 4, 'geek', '6'];1;9 58 | 65;57;Отличие кортежа и списка;В чем отличие списка от кортежа?;;1;9 59 | 66;58;Запись кортежа из 1 элемента;Какая особенность записи кортежей, если мы хотим создать кортеж с одним элементом – числом 1?;;1;10 60 | 67;59;Объявление кортежа;Как объявляется кортеж?;;1;10 61 | 68;60;Распаковка кортежа;Как производится распаковка кортежа;;1;10 62 | 69;61;Число элементов кортежа;Как узнать число элементов в кортеже?;;1;10 63 | 70;62;Обращение к элементу кортежа;Как обратиться к элементу кортежа по индексу?;;1;10 64 | 71;63;Удаление элемента кортежа;Как удалить элемент кортежа?;;1;10 65 | 72;64;Объединение кортежей;Можно ли объединить 2 кортежа в 3ий?;;1;10 66 | 73;65;Разные типы данных в кортеж;Какие типы данных можно превратить в кортеж и как?;;1;11 67 | 74;66;Список внутри кортежа;Допустим, что в кортеже один из элементов – список. Можно ли в этот элемент кортежа добавить элемент?;;1;11 68 | 75;67;Подсчет количества определенных элементов кортежа;Как посчитать сколько элементов в кортеже с определенным значением;;1;11 69 | 76;68;Индекс элемента кортежа;Как найти индекс первого элемента со значением в кортеже?;;1;11 70 | 77;69;Определение range();Что такое range()?;;1;11 71 | 78;70;Список с помощью range();Как сделать список с помощью диапазона range()?;;1;11 72 | 79;71;Обратный ход range();Как задать последовательность диапазона range() в обратном порядке?;;1;12 73 | 80;72;Оператор * и range();Как использовать * (оператор упаковки и распаковки) для диапазона range?;;1;12 74 | 81;73;Строковый тип данных;Что такое строковый тип данных?;;1;12 75 | 82;74;f-строки;Что такое f-строки?;;1;12 76 | 83;75;Контакенанция строк;Что такое контакенанция строк?;;1;12 77 | 84;76;Дублирование строк;Как можно сделать дублирование строк?;;1;12 78 | 85;77;Число символов в строке;Как определить количество символов в строке?;;1;12 79 | 86;78;Подстрока в строке;Как определить, что одна строка входит в другую без использования алгоритмов (типа КМП)?;;1;13 80 | 87;79;Сравнение строк;Как производится сравнение строк?;;1;13 81 | 88;80;Строка и ASCII;Как узнать числовое значение символа по таблице ASCII?;;1;13 82 | 89;81;Срез строк;Как сделать срез строк?;;1;13 83 | 90;82;Срез с шагом в строке;Как в срезе задать шаг перебора символов?;;1;13 84 | 91;83;Инверсия строки;Как инвертировать строку с помощью среза?;;1;13 85 | 92;84;Строка и множество;Как обратить строку в множество set()?;;1;14 86 | 93;85;Обращение к символу строки;Как сделать обращение к элементу строки по индексу?;;1;14 87 | 94;86;Строка и список;Как произвести разделение строки на слова или другие фрагменты текста и сохранить полученное в список list?;;1;14 88 | 95;87;Список в строку;Как создать строки из списка?;;1;14 89 | 96;88;Нижний регистр строки;Как сделать перевод всех символов из ВЕРХНЕГО РЕГИСТРА в нижний регистр?;;1;15 90 | 97;89;Верхний регистр строки;Как сделать перевод всех символов из нижнего регистра в ВЕРХНИЙ РЕГИСТР?;;1;15 91 | 98;90;Строка в виде заголовка;Как сделать первый символ в верхнем регистре, а остальные в нижнем?;;1;15 92 | 99;91;Строка в виде имени человека;Как сделать, чтобы первый символ каждого нового слова переведен в верхний регистр, остальные в нижний?;;1;15 93 | 100;92;Замена слова в строке;Как произвести замену слова?;;1;15 94 | 101;93;Удаление элемента строки;Как удалить из строки ненужные элементы?;;1;16 95 | 102;94;Число повторений подстроки в строке;Как возвратить число повторений подстроки в строке?;;1;16 96 | 103;95;Анализ состава строки;Как проверить из чего состоит строка: только ли из букв или только ли из цифр?;;1;16 97 | 104;96;Специальные символы с строках;Перечислите специальные символы в строках?;;1;16 98 | 105;97;r-строки;Что такое r-строки?;;1;17 99 | 106;98;Понятия множества;Что такое множества?;;1;17 100 | 107;99;Изменение элемента множества;Можно ли изменить отдельный элемент в множестве?;;1;17 101 | 108;100;Тип данных во множестве;Что может быть элементами множества?;;1;17 102 | 109;101;Одинаковые элементы множества;Возможно ли наличие двух и более одинаковых элементов в множестве?;;1;17 103 | 110;102;Механизм работы множества при одинаковых элементах;Что будет, если одинаковые элементы попадают в множество?;;1;17 104 | 111;103;Список в множество;Как из списка можно получить множество?;;1;17 105 | 112;104;Добавление элемента в множество;Как можно добавить элемент в множество?;;1;18 106 | 113;105;Операции над множествами;Какие операции можно производить над множествами?;;1;18 107 | 114;106;Словари;Что такое словари в Python и из чего они состоят?;;1;18 108 | 115;107;Тип данных ключа словаря;Что может быть ключом в словаре?;;1;18 109 | 116;108;Тип данных значения словаря;Что может быть значением в словаре?;;1;18 110 | 117;109;Получение значения словаря;Как получить значение ключа словаря?;;1;18 111 | 118;110;Ключи словаря в список;Как можно преобразовать ключи словаря в список?;;1;18 112 | 119;111;Ключи словаря в множество;Как можно преобразовать ключи словаря во множество?;;1;19 113 | 120;112;Словарь из списка кортежей;Как можно создать словарь из списка кортежей?;;1;19 114 | 121;113;Создание словаря с присвоением;Как можно создать словарь с помощью непосредственного присвоения аргументов?;;1;19 115 | 122;114;Создание словаря встроенным методом;Назовите еще один способ создания словаря?;;1;19 116 | 123;115;Проверка наличия ключа в словаре;Как проверить есть ли ключ в словаре?;;1;19 117 | 124;116;Добавление нового элемента в словарь;Как добавить новый элемент в словарь?;;1;20 118 | 125;117;Объединение словарей;Как можно объединить 2 словаря?;;1;20 119 | 126;118;Удаление ключа словаря;Как удалить элемент из словаря?;;1;20 120 | 127;119;Возврат значения удаляемого элемента словаря;Как удалить элемент словаря и возвратить значение удаляемого элемента?;;1;20 121 | 128;120;Получение всех ключей и значений словаря;Как получить все ключи и все значения словаря?;;1;21 122 | 129;121;Перебор словаря;Как перебрать по ключам словарь и как по значениям?;;1;21 123 | 130;122;Перебор одновременно ключей и значений словаря;Как получить из словаря пары ключ-значение и как их перебрать?;;1;21 124 | 131;123;Удаление всех элементов словаря;Как удалить все элементы словаря?;;1;22 125 | 132;124;Копия словаря;Как сделать копию словаря?;;1;22 126 | 133;125;Возврат значения по ключу;Как возвратить значение по заданному ключу и если нет ключа он его создаст в словаре это ключ;;1;22 127 | 134;126;Тернарный условный оператор;Что такое тернарный условный оператор?;;1;22 128 | 135;127;Тернарный условный оператор для строки;Примените тернарный условный оператор для строк?;;1;22 129 | 136;128;Циклы в Python;Какие циклы бывают в Python?;продемонстрируйте на любых примерах работу этих циклов, в том числе и совместно, а также работу вложенных циклов;1;22 130 | 137;129;Оператор else в циклах while и for;Какую роль в цикле while играет ключевое слово else?;;1;23 131 | 138;130;Понятие итератора;Что такое итератор в Python. Приведите примеры?;;1;23 132 | 139;131;Назначение iter() и next();Для чего нужны iter() и next()?;;1;23 133 | 140;132;Перебор по индексу и значению одновременно;Как одновременно при переборе элементов списка, кортежа сразу брать индекс и значение элемента. Приведите пример?;;1;23 134 | 141;133;list comprehension;Как можно сгенерировать список?;;1;23 135 | 142;134;Тернарный оператор в list comprehension;Приведите пример использования тернарного оператора при генерации списка?;;1;23 136 | 143;135;dict comprehension;Покажите на любом примере генератор словаря?;;1;24 137 | 144;136;Ключ в значение, значение в ключ;Как сделать обратное преобразование ключей в значения, а значений в ключи?;;1;24 138 | 145;137;set comprehension;Как сделать генератор множеств?;;1;24 139 | 146;138;Функции в Python;Что такое функция в Python и из какой части программы можно вызвать функцию?;;1;24 140 | 147;139;Параметры функции и передаваемые аргументы;Какие типы аргументов могут быть переданы в параметры функции?;;1;24 141 | 148;140;Позиционные аргументы;Приведите примеры позиционных аргументов?;;1;24 142 | 149;141;Именованные аргументы;Приведите пример именованных аргументов?;;1;25 143 | 150;142;Параметры функции;Какие могут быть параметры у функции?;;1;25 144 | 151;143;Параметры по-умолчанию;Как задать в функции параметры по-умолчанию?;;1;25 145 | 152;144;Произвольное число параметров;Что делать, если при объявлении функции произвольное число фактических и формальных параметров?;;1;25 146 | 153;145;Рекурсия;Что такое рекурсия?;;1;25 147 | 154;146;Назначение рекурсии;Для чего нужна рекурсия?;;1;26 148 | 155;147;Условия правильной работы рекурсии;Что требуется для того, чтобы правильно работала рекурсия?;;1;26 149 | 156;148;Рекурсивная функция;Приведите пример рекурсивной функции?;;1;26 150 | 157;149;Рекурсия и цикл;Какое отличие рекурсии от цикла?;;1;26 151 | 158;150;Решение задач на рекурсию;Каков порядок решения задач на рекурсию?;;1;26 152 | 159;151;"Рекурсия ""под капотом""";Опишите процесс работы рекурсии в памяти компьютера. Что может произойти, когда не задан крайний случай?;;1;26 153 | 160;152;Глубина рекурсии;Что такое глубина рекурсии?;;1;27 154 | 161;153;Лямбда-функции;Что такое лямбда-функция. Приведите пример?;;1;27 155 | 162;154;Лямбда-функции и return;Нужен ли return в лямбда-функции?;;1;27 156 | 163;155;Тернарный оператор в лямбда-функции;Может ли быть в выражении лямбда-функции тернарный оператор?;;1;27 157 | 164;156;Отличие лямбда-функции от обычной;В чем отличие лямбда-функции от обычной функции?;;1;27 158 | 165;157;Ограничение лямбда-функции;Какие ограничения есть у лямбда-функций?;;1;28 159 | 166;158;Глобальные и локальные переменные;Что такое глобальные и локальные переменные, приведите примеры?;;1;28 160 | 167;159;Ключевое слово global;Объясните суть ключевого слова global. Приведите пример?;;1;28 161 | 168;160;Ключевое слово nonlocal;Объясните суть ключевого слова nonlocal?;;1;28 162 | 169;161;Замыкания в Python;Дайте определение замыканиям в Python?;;1;29 163 | 170;162;Механизм замыканий Python;Объясните механизм замыкания с примерами?;;1;29 164 | 171;163;Декораторы;Дайте определение декоратора в Python?;;1;30 165 | 172;164;Функция-декоратор;Напишите в общем виде функцию декоратор и как она работает?;;1;30 166 | 173;165;args и kwards во wrapper;Для чего нужны во wrapper args и kwargs?;;1;31 167 | 174;166;Декораторы с параметрами;Для чего в декораторах иногда используются параметры?;;1;31 168 | 175;167;Syntax sugar и декораторы;Каким символом может быть вызван декоратор?;;1;32 169 | 176;168;Понятие @wraps;Что такое @wraps и для чего он нужен?;;1;32 170 | 177;169;Конструкция if _ _ name _ _ == _ _ main _ _;Что такое конструкция if _ _ name _ _ == _ _ main _ _ и для чего она нужна?;;1;33 171 | 178;170;Пакеты в Python;Что такое пакеты в Python?;;1;34 172 | 179;171;Файлы в Python;Что такое файлы в Python?;;1;34 173 | 180;172;Функция для работы с файлами;Какая основная функция при работе с файлами?;;1;34 174 | 181;173;Файловая позиция;Расскажите о чтении информации из файла, о файловой позиции?;;1;34 175 | 182;174;Ошибка EOF;Что такое EOF?;;1;35 176 | 183;175;Управление файловой позицией;Как управлять файловой позицией?;;1;35 177 | 184;176;Отображение файловой позиции;Как отобразить текущую файловую позицию?;;1;35 178 | 185;177;Чтение строки файла;Как прочитать только строку?;;1;36 179 | 186;178;Перебор строк файла;Как сделать перебор строк файла?;;1;36 180 | 187;179;Список из строк в файле;Как из файла получить список из строк?;;1;36 181 | 188;180;Завершение работы с файлом;Что надо сделать с файлом, когда мы с ним перестали работать?;;1;36 182 | 189;181;Обработка исключений;Как в Python происходит обработка исключений?;;1;36 183 | 190;182;Менеджер контекста;Что такое ключевое слово with в Python и где используется?;;1;37 184 | 191;183;Создание файла;Как создать файл?;;1;37 185 | 192;184;Перезапись файла;Как полностью перезаписать файл?;;1;37 186 | 193;185;Новые строки в файле;Как записать несколько строчек с новой строки?;;1;37 187 | 194;186;Новая информация в файле;Как добавить в файл новую информация, не перезаписывая его?;;1;37 188 | 195;187;Бинарный режим работы с файлами;Что такое бинарный режим работы с файлом?;;1;38 189 | 196;188;Бинарные файлы;Как создать файл в бинарном режиме?;;1;38 190 | 197;189;Чтение бинарного файла;Как прочитать коллекцию из файла .bin?;;1;38 191 | 198;190;Несколько значений в бинарном файле;Как в бинарный файл сохранить сразу несколько значений?;;1;39 192 | 199;191;Чтение данных из бинарного файла;Как прочитать данные из бинарного файла?;;1;39 193 | 200;192;Генераторы;Что такое генераторы в Python?;;1;39 194 | 201;193;Задание генератора;Как задать объект генератора?;;1;40 195 | 202;194;Получение значения элемента генератора;Как получить конкретное значение из генератора?;;1;40 196 | 203;195;Генераторы и цикл for;Можно ли использовать цикл for для перебора элементов генератора?;;1;40 197 | 204;196;Количество циклов for в генераторе;Сколько раз можно использовать цикл for для генератора?;;1;40 198 | 205;197;Генераторы в другие типы данных;Можно ли генератор превратить в другой тип данных?;;1;40 199 | 206;198;Агрегирующие функции для генераторов;Можно ли использовать функции sum, max, min для генератора?;;1;40 200 | 207;199;Число элементов генератора;Можно ли использовать функцию len() для подсчета количества элементов в генераторе?;;1;40 201 | 208;200;Оператор yield;Что такое оператор yield и где он используется?;;1;40 202 | 209;201;Функция map;Что такое функция map и как она работает?;;1;41 203 | 210;202;Функция filter;Что такое функция filter и как она работает?;;1;41 204 | 211;203;Функция zip;Что такое функция zip и как она работает?;;1;42 205 | 212;204;Функция isinstance;Что такое функция isinstance и как она работает?;;1;42 206 | 213;205;Функция all;Что такое функция all() и как она работает?;;1;42 207 | 214;206;Функция any;Что такое функция any() и как она работает?;;1;43 208 | 215;207;Операторы match-case;Что такое конструкция match-case и где используется?;;1;43 209 | 216;208;Мануальное тестирование;Что такое мануальное тестирование программы Python?;;1;44 210 | 217;209;Оператор assert;Что такое assert и когда используется?;;1;44 211 | 218;210;Автотесты в Python;Какие библиотеки в Python существуют для написания автотестов кода?;;1;45 212 | 219;211;Понятие TDD;Что такое TDD?;;1;45 213 | 220;212;Паттерн ААА;Что такое паттерн ААА при написании автотестов?;;1;45 214 | 221;213;Понятие фикстуры;Что такое фикстуры при тестировании?;;1;45 215 | 222;214;Работа с временем, датами в Python;Для чего используется стандартные библиотеки time и datetime. В чем их отличие?;;1;45 216 | 223;215;Работа с псевдослучайными числами;Для чего используется стандартная библиотека random?;;1;45 217 | 224;216;Функция reduce;Что такое функция reduce и для чего она используется?;;1;45 218 | 225;0;Программы в ООП;Какая последовательность реализации программы в парадигме ООП?;;2;0 219 | 226;1;Понятие класса;Что такое класс?;напишите класс;2;0 220 | 227;2;Атрибуты класса;Какие бывают атрибуты в классе и где они прописываются?;напишите класс и все атрибуты в нем;2;0 221 | 228;3;Методы класса;Что такое метод класса?;напишите класс и объявите в нем методы;2;0 222 | 229;4;Понятие интерфейса;Что такое интерфейс?;напишите класс и создайте к нему интерфейс;2;0 223 | 230;5;Экземпляр класса;Что такое объект (экземпляр) класса?;напишите класс и создайте его экземпляр;2;0 224 | 231;6;Отсутствие атрибута в классе;Что будут, если в экземпляре класса не существует атрибут?;напишите класс, создайте экземпляр, вызовите несуществующее свойство экземпляра, но существующее у самого класса;2;0 225 | 232;7;Понятие SOLID;Что такое SOLID?;;2;1 226 | 233;8;Назначение SOLID;Для чего нужно соблюдать SOLID?;;2;1 227 | 234;9;Характеристика SOLID;Охарактеризуйте каждый из принципов SOLID?;;2;1 228 | 235;10;Понятие инкапсуляции;Что такое инкапсуляция?;;2;1 229 | 236;11;Понятие наследования;Что такое наследование?;;2;2 230 | 237;12;Понятие полиморфизма;Что такое полиморфизм?;;2;2 231 | 238;13;Скобки после названия класса;После объявления класса ставятся скобки?;;2;2 232 | 239;14;Что внутри класса;как можно посмотреть содержимое класса?;создайте класс, опишите методы, создайте экземпляр, посмотрите содержимое класса и экземпляра;2;2 233 | 240;15;Определение типа объекта;Как определить к какому классу относятся объекты, которые мы создали?;создайте класс, опишите методы, создайте экземпляр, определите к какому классу он относится.;2;2 234 | 241;16;Определение принадлежности к классу;Как проверить к относится ли объект к данному классу или нет?;;2;2 235 | 242;17;Новый атрибут в классе;Как в классе создать новый атрибут?;;2;2 236 | 243;18;Добавление нового атрибута;Какую функцию можно использовать для добавления нового свойства в класс?;добавьте новое свойство в класс;2;2 237 | 244;19;Удаление атрибута;Как удалить атрибут из класса?;;2;2 238 | 245;20;Наличие атрибута в классе;Как проверить, есть ли атрибут в классе?;;2;3 239 | 246;21;Поиск в атрибуте класса;Как происходит поиск в атрибуте объекта класса?;;2;3 240 | 247;22;Понятие self;Что такое self?;;2;3 241 | 248;23;self в методах класса;Для чего нужно у метода класса указывать параметр self?;;2;3 242 | 249;24;Понятие инициализатора (конструктора) класса;Что такое _ _ init _ _?;;2;3 243 | 250;25;Порядок вызова инициализатора;_ _ init _ _ вызывается самый первый при создании экземпляра класса?;;2;3 244 | 251;26;Метод _ _ del _ _;Что такое _ _ del _ _ и прописывается ли он?;;2;4 245 | 252;27;Удаление объектов в Python;Когда и в какой момент происходит удаление объектов?;;2;4 246 | 253;28;"Удаление ""под капотом""";Что значит удаление объекта?;;2;4 247 | 254;29;Порядок вызова метода _ _ new _ _;Когда вызывается метод _ _ new _ _ ?;;2;4 248 | 255;30;Назначение метода _ _ new _ _;Зачем нужен метод _ _ new _ _ ?;;2;4 249 | 256;31;Методы _ _ new _ _ и _ _ init _ _;Зачем было создавать 2 разных метода new и init, которые вызываются при создании объектов класса?;;2;4 250 | 257;32;Отличие cls от self;Чем отличается cls от self?;;2;4 251 | 258;33;Неизвестное число аргументов в _ _ new _ _;При вызове метода _ _ new _ _ используется args и kwargs. Для чего?;;2;4 252 | 259;34;Метод _ _ new _ _ для своего класса;Напишите метод _ _ new _ _ для своего класса?;;2;4 253 | 260;35;Паттерн Singleton;Что такое паттерн Singleton?;реализуйте паттерн Singleton;2;5 254 | 261;36;Обозначение методов класса;Что такое методы класса и как они обозначаются?;реализуйте метод класса;2;6 255 | 262;37;Особенности @classmethod;Какая особенность у метода класса @classmethod?;;2;7 256 | 263;38; @classmethod и экземпляр класса;Имеет ли метод класса @classmethod доступ к экземпляру класса?;;2;7 257 | 264;39;Статический метод класса @staticmethod;Что такое статический метод класса @staticmethod?;реализуйте статический метод класса;2;7 258 | 265;40;Реализация инкапсуляции в Python;Как осуществляется инкапсуляция в Python?;;2;8 259 | 266;41;Публичный режим доступа;Что делает публичный режим доступа?;реализуйте публичный режим доступа;2;8 260 | 267;42;Защищенный режим доступа;Что делает защищенный режим доступа и какая у него особенность?;реализуйте защищенный режим доступа;2;9 261 | 268;43;Приватный режим доступа;Что делает приватный режим доступа и какая у него особенность?;реализуйте приватный режим;2;9 262 | 269;44;Работа с защищенными атрибутами;Как можно работать с защищенными локальными атрибутами?;;2;10 263 | 270;45;Назначение приватных атрибутов;Зачем в классе создавать приватные атрибуты и еще дополнительно работать с ними из вне?;;2;10 264 | 271;46;Обращение к приватным атрибутам;Как узнать какие приватные свойства есть у экземпляра класса?;;2;11 265 | 272;47;Запрет обращения к методам класса;Как надежно запретить обращение к методам класса?;;2;11 266 | 273;48;Работа с приватными атрибутами;Какой самый простой способ для работы с приватными атрибутами класса?;;2;11 267 | 274;49;Функция property;Для чего служит функция property?;;2;11 268 | 275;50;Понятие объектов-свойств;Что такое объекты-свойства и как они работают?;реализуйте объекты-свойства и обратитесь и изменяйте приватный атрибут экземпляра класса;2;11 269 | 276;51;Декоратор @property;В каких случаях целесообразно использовать объекты-свойства @property?;;2;12 270 | 277;52;Дескрипторы;Что применяют, если требуется много объектов-свойств в классе?;напишите дескриптор;2;12 271 | 278;53;Метод для считывания атрибута при присвоении значения;Какой метод вызывается, когда происходит считывание атрибута через экземпляр класса?;покажите практическое использование метода getattribute;2;13 272 | 279;54;Процесс присвоения значения атрибуту;Какой метод вызывается, когда идет присвоение какому-нибудь атрибуту какого-либо значения?;покажите практическое применение setattr;2;14 273 | 280;55;Обращение к несуществующему атрибуту;Какой метод вызывается, когда идет обращение к несуществующему экземпляру класса?;покажите практическое применение getattr;2;15 274 | 281;56;Метод при удалении атрибута;Какой метод вызывается, когда удаляется определенный атрибут из экземпляра класса?;покажите практическое применение delattr;2;15 275 | 282;57;Круглые скобки у экземпляра класса;Допустим экземпляр класса создается следующим образом p = Counter(). Что означают круглые скобки?;;2;16 276 | 283;58;Понятие функтора;Что такое функторы?;Напишите программу, в которой можно было использовать экземпляры класса следующим образом с(), с(20);2;16 277 | 284;59;Замыкания и классы;Как можно с помощью класса реализовать замену замыканий функций?;;2;19 278 | 285;60;Декоратор с помощью класса;Как создать декоратор с помощью класса?;;2;19 279 | 286;61;Вывод в консоль информации о классе;Как в консоль вывести экземпляр класса с помощью строки? Чтобы не было отображение ячейки памяти?;реализуйте метод для отображения информации в консоль;2;20 280 | 287;62;Функция len и экземпляр класса;Как можно к экземпляру класса применить функцию len?;;2;20 281 | 288;63;Функция abs и экземпляр класса;Как можно к экземпляру класса применить функцию abs?;;2;21 282 | 289;64;Сложение экземпляров класса;Как можно сложить экземпляры классов?;напишите программу, которая складывает, вычитает, умножает и инкрементирует экземпляры класса, а также сложение с числом.;2;23 283 | 290;65;Сравнение экземпляров класса;Можно ли сравнивать экземпляры классов?;реализуйте сравнение экземпляров класса;2;24 284 | 291;66;Хеширование;Что такое hash в Python?;;2;25 285 | 292;67;Хешируемые объекты;Что в Python является хешируемыми объектами?;;2;25 286 | 293;68;Назначение хешей;Для чего используется hash?;;2;25 287 | 294;69;Хешируемость экземпляров класса;Экземпляры классы являются хешируемыми объектами?;реализуйте выполнение hash для экземпляра класса;2;25 288 | 295;70;Функция bool в экземплярах класса;Как правильно можно использовать функцию bool и для чего её надо использовать?;реализуйте функцию bool для экземпляра класса;2;26 289 | 296;71;Работа с последовательностями в ООП;Какие методы существуют для работы с последовательностями?;реализуйте методы для работы с последовательностями?;2;26 290 | 297;72;Экземпляр класса как итерируемый объект;Как сделать экземпляр класса итерируемым?;;2;27 291 | 298;73;Наследование в ООП;Как обозначается наследование в Python?;;2;27 292 | 299;74;Поиск атрибутов и методов в дочернем классе;Какой порядок поиска атрибутов и методов в дочернем классе?;;2;28 293 | 300;75;Наследование и принцип DRY;Как наследование соотносится с принципом DRY?;;2;28 294 | 301;76;self в родительском классе;Какие особенности у self в родительском классе?;;2;28 295 | 302;77;Наличие методов у базового класса;Откуда берутся все магические методы у базового класса в программе?;;2;28 296 | 303;78;Проверка наследования;Как проверить наследование?;;2;28 297 | 304;79;Расширение дочернего класса;Что такое расширение дочернего класса?;;2;28 298 | 305;80;Переопределение дочернего класса;Что такое переопределение дочернего класса?;;2;28 299 | 306;81;Обращение к базовому классу из дочернего;Как обратиться к базовому классу из дочернего?;;2;28 300 | 307;82;Callable();Что такое Callable()?;;2;29 301 | 308;83;Дочерний класс и приватные свойства;Можно ли из дочернего класса вызвать приватные свойства базового класса?;;2;29 302 | 309;84;Нижние подчеркивания;Если метод начинается и заканчивается двойными подчеркиваниями - он приватный или публичный?;;2;29 303 | 310;85;Режим доступа к магическим методам;Магические методы являются приватными или публичными?;;2;29 304 | 311;86;Понятие полиморфизма в ООП;Что такое полиморфизм?;;2;29 305 | 312;87;Реализация полиморфизма;Что нужно для реализации полиморфизма?;;2;29 306 | 313;88;Абстрактные методы;Что такое абстрактные методы?;реализуйте полиморфизм с абстрактными методами на любом примере;2;29 307 | 314;89;Полиморфизм и наследование;Нужно ли реализовывать полиморфизм в Python через механизм наследования?;;2;29 308 | 315;90;Библиотека Python для абстрактным методов;Какая существует библиотека в Python для реализации абстрактных методов?;;2;30 309 | 316;91;Множественное наследование;Что такое множественное наследование?;;2;30 310 | 317;92;Применение множественного наследования;Где используется множественное наследование?;;2;30 311 | 318;93;Последовательность множественного наследования;Важна ли последовательность базовых классов при множественном наследовании?;;2;30 312 | 319;94;Понятие MRO;Что такое MRO?;;2;30 313 | 320;95;Коллекция _ _ slots _ _;Что такое коллекция _ _ slots _ _ и как она работает?;;2;30 314 | 321;96;Особенность коллекции _ _ slots _ _; Какая особенность коллекции _ _ slots _ _ при наследовании?;;2;30 315 | 322;97;Вложенные классы;Что такое вложенные классы и для чего они нужны?;;2;30 316 | 323;98;Пример вложенного класса;Приведите устно примеры вложенных классов?;;2;31 317 | 324;99;Упрощение написания инициализатора;Как можно упростить написание инициализаторов класса?;;2;31 318 | 325;100;Пример аннотации класса;Приведите пример аннотации класса?;;2;31 319 | 326;101;Инструмент для аннотации класса;Какой инструмент используется для аннотации типов внутри класса:;;2;31 320 | 327;102;Обработка исключений except;В обработке исключений что самое главное при написании excpect?;;2;32 321 | 328;103;Порядок записи исключений;Какие существуют порядки записи исключений?;;2;32 322 | 329;104;Обработка исключений else;Как работает else при обработке исключений?;;2;32 323 | 330;105;Обработка исключений finally;Как работает finally при обработке исключений?;;2;32 324 | 331;106;Использование finally;В каких ситуациях чаще всего используется блок finally?;;2;32 325 | 332;107;Особенность finally; Какая особенность finally в функциях?;;2;32 326 | 333;108;Понятие распространения исключений;Что такое распространение исключений?;;2;32 327 | 334;109;Оператор raise;Что такое оператор raise?;;2;32 328 | 335;110;Оператор with;Что делает оператор with?;;2;33 329 | 336;0;Документы с требованиями к коду;Какие документы устанавливают основные требования к коду Python?;;3;0 330 | 337;1;Константы в Python;Как именуются константы в Python и можно ли их изменять?;;3;0 331 | 338;2;Обозначение переменных;Как именуются переменные в Python?;;3;0 332 | 339;3;Обозначение функций;Как именуются функции в Python?;;3;0 333 | 340;4;Обозначения классов;Как именуются классы в Python?;;3;0 334 | 341;5;Максимальная длина строки;Какая максимальная длина строки должна быть согласно PEP8?;;3;0 335 | 342;6;Порядок импортов и сортировка;Порядок импортов и их сортировка?;;3;0 336 | 343;7;Перенос строки;Можно ли применять бексплеши для переноса?;;3;0 337 | 344;8;Кавычки;Какие кавычки должны быть в программе?;;3;0 338 | 345;9;Слово else;Стоит ли всегда использовать ключевое слово else в условной конструкции?;;3;0 339 | 346;10;Точка входа;Какой конструкцией должен быть закрыт код в исполняемом py-файле?;;3;1 340 | 347;11;Списки или кортежи;Если в программе возможен выбор между списками и кортежами, что предпочтительнее выбирать?;;3;1 341 | 348;12;Использование f-строк;Какая специфика использования f-строк?;;3;1 342 | 349;13;Переменные;Как должны быть названы переменные?;;3;1 343 | 350;14;Документ РЕР257;О чем документ РЕР257?;;3;1 344 | 351;15;Документированная строка;Что такое документированная строка?;;3;1 345 | 352;16;Docstrings;Для каких объектов применяется Docstrings?;;3;1 346 | 353;17;Правила документации;Расскажите основные правила документирования?;;3;1 347 | 354;18;Документирование каждой строчки кода;Стоит ли документировать каждую строчку кода?;;3;1 348 | 355;19;Инструменты для проверки кода;Какие существуют инструменты для Python для проверки кода на соответствие стандартам?;;3;2 349 | 356;20;Принципы YAGNI, KISS, DRY;В чем суть принципов YAGNI, KISS, DRY?;;3;2 350 | 357;21;Аннотация типов;Что такое аннотация типов и для чего она нужна?;;3;2 351 | 358;22;Библиотека для аннотации типов;Какая библиотека существует для аннотации типов и как она работает?;;3;2 352 | 359;23;Аннотация составных типов данных;Что аннотирует типы данных, которые могут содержать 2 типа данных?;;3;2 353 | 360;24;Данные нескольких типов;Что используется, если переменная должна принимать данные нескольких разных типов?;;3;2 354 | 361;25;Ограничение типов переменной;Что применять, когда не нужно ограничивать возможные типы переменной?;;3;3 355 | 362;26;Аннотация коллекций;Как производится аннотация коллекций?;;3;3 356 | 363;27;Аннотация функции в качестве параметра;Что использовать если функция передаётся в качестве аргумента в другую функцию или в метод и там вызывается?;;3;3 357 | 364;0;Скорость работы алгоритмов;Как оценивать и сравнивать между собой скорость работы алгоритмов?;;4;0 358 | 365;1;Понятие Big O;Что такое Big O?;;4;0 359 | 366;2;Выполнение алгоритма;Какие ресурсы компьютера требуются для выполнения алгоритма?;;4;0 360 | 367;3;Запись Big O;Как записывается Big O?;;4;0 361 | 368;4;Асимптотики сложности алгоритмов;Перечислите классические асимптотики для алгоритмов?;;4;0 362 | 369;5;О(Const) или О(1);Расскажите про сложность алгоритма О(Const) или О(1)?;;4;1 363 | 370;6;O(n);Расскажите про сложность алгоритма O(n)?;;4;1 364 | 371;7;Цикл и сложность алгоритма;Какая сложность Big(O) если у нас есть несколько циклов от одного и того же массива?;;4;1 365 | 372;8;Несколько циклов и сложность алгоритма;Какая сложность Big(O) если у нас есть несколько циклов от двух разных массивов?;;4;2 366 | 373;9;O(n^2);Расскажите про сложность алгоритма O(n^2)?;;4;2 367 | 374;10;O(log n);Расскажите про сложность алгоритма O(log n)?;;4;2 368 | 375;11;O(n!);Расскажите про сложность алгоритма O(n!)?;;4;2 369 | 376;12;Понятие массива;Что такое массив?;;4;2 370 | 377;13;Понятие статического массива;Что такое статический массив?;;4;2 371 | 378;14;Признаки статического массива;Перечислите отличительные признаки статического массива:;;4;3 372 | 379;15;Кортеж и статический массив;Является ли кортеж Python (tuple) статическим массивом?;;4;3 373 | 380;16;Реализация статического массива;Как в Python можно реализовать статический массив?;;4;3 374 | 381;17;Преимущества статического массива;Преимущества статических массивов?;;4;3 375 | 382;18;Недостатки статического массива;Недостатки статических массивов?;;4;3 376 | 383;19;Понятие динамического массива;Что такое динамический массив?;;4;4 377 | 384;20;Отличия статического и динамического массивов;Отличие динамического массива от статического?;;4;4 378 | 385;21;Увеличение размера массива;Объясните механизм увеличения размера массива?;;4;4 379 | 386;22;Особенность увеличения размера массива;Зачем под новый массив выделять именно в 2 раза больше элементов, чем в прежний, а не увеличить его на 1-2 элемента?;;4;4 380 | 387;23;Списки и динамический массив;Можно ли считать список Python (list) динамическим массивом?;;4;4 381 | 388;24;Методы списков в Python и их Big O;Перечислите несколько методов списков Python и назовите их сложность Big O?;;4;5 382 | 389;25;Динамический массив при срезе списка;Что происходит при создании среза списка Python?;;4;5 383 | 390;26;Недостаток динамического массива;Какой главный недостаток динамического массива и как его можно разрешить с помощью другой структуры данных?;;4;5 384 | 391;27;Понятие односвязного списка;Что такое односвязный список?;;4;6 385 | 392;28;Добавление элемента в конец односвязного списка;Расскажите механизм добавления нового элемента в конец односвязного списка?;;4;6 386 | 393;29;Операции с односвязным списком;Перечислите все операции с односвязным списком и назовите их сложность?;;4;7 387 | 394;30;Реализация односвязного списка;Как реализовать односвязный список в Python с помощью ООП?;;4;7 388 | 395;31;Понятие двусвязного списка;Что такое двусвязный список7;;4;8 389 | 396;32;Операции над двусвязным списком;Перечислите все возможные операции на двусвязном списке и их сложность Big O?;;4;8 390 | 397;33;Использование двусвязных списков;Назовите несколько примеров, где используется двусвязный список?;;4;8 391 | 398;34;Реализация двусвязного списка;Реализуйте двусвязный список на Python с помощью ООП?;;4;9 392 | 399;35;Понятие абстрактных структур данных;что такое абстрактные структуры данных?;;4;11 393 | 400;36;Все абстрактные структуры данных;Перечислите все абстрактные структуры данных?;;4;11 394 | 401;37;Понятие очереди;Что такое очередь?;;4;11 395 | 402;38;Использование очереди;Где используется очередь, приведите несколько примеров?;;4;11 396 | 403;39;Реализация очереди;С помощью каких структур данных можно реализовать очередь?;;4;11 397 | 404;40;deque;Что такое deque?;;4;11 398 | 405;41;Очередь в Python;Расскажите о реализации очереди в Python с помощью стандартной библиотеки collections?;;4;12 399 | 406;42;Понятие стека;Что такое стек?;;4;12 400 | 407;43;Назначение стека;Зачем стек нужен как отдельная структура данных?;;4;12 401 | 408;44;Применение стека;Приведите практические примеры использования стека?;;4;13 402 | 409;45;Операции стека;Перечислите все операции, которые можно выполнять для стека?;;4;13 403 | 410;46;Реализация стека;Как реализуется стек в Python с использованием стандартной библиотеки collections?;;4;13 404 | 411;47;Понятие дерева;что такое дерево в программировании?;;4;14 405 | 412;48;Понятие бинарного дерева;Что такое бинарное дерево?;;4;14 406 | 413;49;Элементы бинарного дерева;Из чего состоит бинарное дерево?;;4;14 407 | 414;50;Структура бинарного дерева;Расскажите структуру бинарного дерева;;4;14 408 | 415;51;Указатель root в бинарном дереве;Что делается через указатель root в бинарном дереве? ;;4;15 409 | 416;52;Уровни бинарного дерева;Что такое уровни бинарного дерева?;;4;15 410 | 417;53;Правила формирования бинарного дерева;Расскажите о правилах формирования бинарного дерева?;;4;16 411 | 418;54;Поиск значений в бинарном дереве;Расскажите о поиске значений в бинарном дереве и сложность Big O?;;4;17 412 | 419;55;Сбалансированные деревья;Что такое сбалансированное бинарное дерево?;;4;17 413 | 420;56;Несбалансированные деревья;Что такое несбалансированное бинарное дерево?;;4;17 414 | 421;57;Методы балансировки дерева;Перечислите методы балансировки деревьев?;;4;18 415 | 422;58;Способы обхода бинарного дерева;Перечислите способы обхода бинарного дерева?;;4;18 416 | 423;59;Обход в ширину бинарного дерева;Опишите обход вершин бинарного дерева в ширину?;;4;18 417 | 424;60;Обход в глубину бинарного дерева;Опишите обход вершин бинарного дерева в глубину?;;4;20 418 | 425;61;Понятие ассоциативных массивов;Что такое ассоциативные массивы?;;4;21 419 | 426;62;Представление ассоциативных массивов;Что используется, чтобы представить ассоциативные массивы в памяти компьютера?;;4;21 420 | 427;63;Понятие хеш-таблицы;Что такое хеш-таблица?;;4;22 421 | 428;64;Хеш-таблицы и Big O;Какие Big O для операций в хеш-таблицах?;;4;22 422 | 429;65;Хеширование;Что такое хеш-функция и хеширование?;;4;22 423 | 430;66;Понятие хеш-функции;Что такое хеш-функция?;;4;22 424 | 431;67;Коллизии в хеш-таблицах;Что такое коллизии в хеш-таблицах?;;4;22 425 | 432;68;Поиск ключей в хеш-таблице;Как происходит поиск ключей в хеш-таблице?;;4;23 426 | 433;69;Хеш-таблицы и Python;Какие типы данных Python используют хеш-таблицы?;;4;23 427 | 434;0;Алгоритм замены переменных;Даны переменные a и b. Как сделать так, чтобы переменная a ссылалась на переменную b, а переменная b на переменную a?;Переменная a ссылается на объект “мама”, переменная b ссылается на объект “мыла”, переменная c ссылается на объект “раму”. Сделать так, чтобы переменная a ссылалась на переменную c, а переменная c на переменную a. Вывести print(f’{a} {b} {c});5;0 428 | 435;1;Алгоритм перевода числа в другую систему счисления;Напишите алгоритм для перевода любого десятичного числа в любую систему счисления, реализованный с помощью схемы Горнера?;перевести число 562715273651 в двоичную систему счисления и сравнить полученный результат с функцией Python – bin();5;0 429 | 436;2;Алгоритм определения простых чисел;Напишите алгоритм, который определяет является ли число простым (простое – это число, которое делится только на 1 и на само себя без остатка, на все остальные числа делится с остатком);даны шесть чисел: число 160, число 161, число 162, число 163, число 164, число 165. Определить какое из этих чисел является простым:;5;0 430 | 437;3;Алгоритм разбиения числа на простые множители;Напишите алгоритм разбиения числа на множители?;разбейте число 8721 на множители;5;1 431 | 438;4;Алгоритм линейного поиска в массиве;Напишите алгоритм линейного поиска в массиве. То есть дан массив чисел и необходимо найти индекс этого числа в массиве. Функцию index() не использовать;;5;1 432 | 439;5;Алгоритм инверсии массива;Напишите алгоритм инверсии массива. То есть дан массив чисел, надо вывести тот же самый массив, только прочитанный справа налево. Функцию reverse() не использовать;;5;2 433 | 440;6;Алгоритм циклического сдвига в массиве;Напишите алгоритм для циклического сдвига в массиве?;;5;2 434 | 441;7;Сортировка вставками;Опишите и реализуйте алгоритм сортировки вставками? Какова сложность алгоритма?;;5;2 435 | 442;8;Сортировка выбором;Опишите и реализуйте алгоритм сортировки выбором? Какова сложность алгоритма?;;5;3 436 | 443;9;Сортировка пузырьком;Опишите и реализуйте алгоритм сортировки пузырьком? Какова сложность алгоритма?;;5;3 437 | 444;10;Сортировка подсчетом;Опишите и реализуйте алгоритм сортировки подсчётом? Какова сложность алгоритма?;;5;3 438 | 445;11;Алгоритм нахождения НОД;Напишите алгоритм для нахождения наибольшего общего делителя двух целых чисел (Алгоритм Евклида);даны два числа: число 84 и число 90. Найти НОД этих чисел:;5;4 439 | 446;12;Алгоритм возведения числа в степень;Напишите алгоритм возведения числа в степень с использованием рекурсии?;;5;4 440 | 447;13;Перестановки;Что такое перестановки в программировании? Приведите любой пример;;5;5 441 | 448;14;Алгоритм генерации перестановок;Напишите алгоритм генерации перестановок элементов в массиве? Библиотекой itertools не пользоваться;;5;5 442 | 449;15;Генерация перестановок с помощью itertools;Реализуйте алгоритм перестановок с помощью библиотеки itertools для Python?;;5;6 443 | 450;16;Быстрая сортировка;Напишите алгоритм быстрой сортировки (сортировка Хоара)? Какая сложность алгоритма?;дан список [7, 4, 7, 3, 0, 2, 1]. Произвести сортировку чисел списка алгоритмом Хоара;5;6 444 | 451;17;Сортировка слиянием;Напишите алгоритм сортировки слиянием? Какая сложность алгоритма?;;5;7 445 | 452;18;Алгоритм определения отсортированности;Напишите алгоритм отсортированности? То есть дать ответ: является ли массив отсортированным или нет?;;5;8 446 | 453;19;Алгоритм бинарного поиска в массиве;Напишите алгоритм бинарного поиска? Что самое главное при реализации бинарного поиска?;;5;9 447 | 454;20;Числа Фибоначчи и их применение;Что такое числа Фибоначчи и для чего они могут быть использованы?;;5;10 448 | 455;21;Динамическое программирование на основе чисел Фибоначчи;Напишите алгоритм для создания массива из чисел Фибоначчи заданной длины?;;5;10 449 | 456;22;Задача «Ханойские башни»;Задача «Ханойские башни». Головоломка “Ханойские башни” состоит из трех стержней, пронумерованных числами 1, 2, 3. На стержень 1 надета пирамидка из n дисков различного диаметра в порядке возрастания диаметра. Диски можно перекладывать с одного стержня на другой по одному, при этом диск нельзя класть на диск меньшего диаметра. Необходимо переложить всю пирамидку со стержня 1 на стержень 3 за минимальное число перекладываний.;;5;11 450 | 457;23;Задача «Кузнечик»;Задача «Кузнечик». На числовой прямой сидит кузнечик, который может прыгать вправо на одну или на две единицы. Первоначально кузнечик находится в точке с координатой 0. Определите количество различных маршрутов кузнечика, приводящих его в точку с координатой n.;;5;11 451 | 458;24;Алгоритм нахождения наибольшей общей подпоследовательности;Напишите алгоритм решения задачи нахождения наибольшей общей подпоследовательности.;;5;11 452 | 459;25;Алгоритм наибольшей возрастающей подпоследовательности;Напишите алгоритм наибольшей возрастающей подпоследовательности?;;5;11 453 | 460;26;Задача о рюкзаке;Задача о рюкзаке (задача о ранце). Из заданного множества предметов со свойствами «стоимость» и «вес» требуется отобрать подмножество с максимальной полной стоимостью, соблюдая при этом ограничение на суммарный вес. ;;5;12 454 | 461;27;Алгоритм Левенштейна;Напишите алгоритм определения редакционного расстояния между строками (алгоритм Левенштейна). Какова сложность алгоритма?;Сколько типографических опечаток (перепутали символ, вставили лишний символ, потеряли нужный символ) в слове «колокол» надо совершить, чтобы получилось слово «молоко».;5;12 455 | 462;28;Алгоритм Кнута-Морриса-Пратта (КМП);Написать алгоритм Кнута-Морриса-Пратта (КМП) для поиска подстроки в строке.;сколько раз в строке 'с новым годом' встречается подстрока 'годам'. Не использовать оператор in. Пользоваться алгоритмом КМП;5;13 456 | 463;29;Задача на правильную скобочную последовательность;Задача на правильную скобочную последовательность. Задана строка, в которой могут быть встречены 3 типа скобок: фигурные, квадратные и круглые. Помимо скобок в строке встречаются и другие последовательности символов. Вложенность скобок может быть произвольной. Необходимо проверить корректность скобочной записи: каждой открывающей скобке должна соответствовать следующая за ней закрывающая скобка того же типа на том же уровне вложенности, не должно быть открывающей или закрывающей скобки без пары.;;5;14 457 | 464;30;Задача «Обратная польская запись»;Задача «Обратная польская запись». В единственной строке записано выражение в постфиксной записи, содержащее однозначные числа и операции +, –, *, /. Строка содержит не более 100 чисел и операций. Числа и операции отделяются друг от друга ровно одним пробелом.;;5;15 458 | 465;31;Сортировка кучей;Написать алгоритм пирамидальной сортировки (сортировки кучей)? Какова сложность данного алгоритма?;;5;17 459 | 466;32;Матрица смежности;Как с помощью клавиатуры создать матрицу смежности?;;5;18 460 | 467;33;Список смежности;Как с помощью клавиатуры создать список смежности?;;5;19 461 | 468;34;Алгоритм обхода графа в глубину DFS;Напишите алгоритм обхода графа в глубину DFS (deep-first search);;5;19 462 | 469;35;Алгоритм Косарайю;Для чего используется алгоритм Косарайю?;;5;20 463 | 470;36;Алгоритм Тарьяна;Для чего используется алгоритм Тарьяна?;;5;20 464 | 471;37;Алгоритм обхода графа в ширину BFS;Напишите алгоритм обхода графа в ширину BFS? Для чего используется данный алгоритм? Какова его сложность?;;5;20 465 | 472;38;Алгоритм Дейкстры;Напишите алгоритм поиска кратчайшего пути (алгоритм Дейкстры). ;;5;21 466 | 473;39;Задача на создание треугольников;Задача на создание треугольников. Для того чтобы составить треугольники из данных точек и посчитать их периметры, нужно перебрать все возможные комбинации из трех точек и для каждой комбинации проверить, являются ли эти точки вершинами треугольника. Если да, то можно вычислить периметр этого треугольника. Реализовать с помощью Python;;5;24 467 | 474;0;Назначение командной строки;Почему прибегают к использованию командной строки?;;6;0 468 | 475;1;Управление командной строкой;Чем позволяет управлять командная строка?;;6;0 469 | 476;2;Действия с командной строкой;Что можно делать из командной строки?;;6;0 470 | 477;3;Понятие Git;Что такое Git?;;6;0 471 | 478;4;Понятие репозитория;Что такое репозиторий?;;6;0 472 | 479;5;Хранение репозитория;Где хранятся репозитории?;;6;0 473 | 480;6;ОС Windows и Git;Что лучше использовать в Windows для работы Git?;;6;0 474 | 481;7;GitBash директории;Как в GitBash посмотреть где я сейчас нахожусь?;;6;0 475 | 482;8;Содержимое директории;Как узнать, что в директории содержится?;;6;0 476 | 483;9;Управление файлами;Как можно через Git Bush управлять файлами?;;6;0 477 | 484;10;Перемещение по директориям;Как перемещаться по директории?;;6;1 478 | 485;11;Возврат в предыдущую директорию;Как вернуться в предыдущую директорию?;;6;1 479 | 486;12;Переход в корень диска;Как перейти к корень диска, например D:?;;6;1 480 | 487;13;Создание папки в директории;Как создать папку в директории?;;6;1 481 | 488;14;Создание файла в директории;Как создать файл в директории?;;6;1 482 | 489;15;Запуск Python-файла;Как запустить эту программу proba.py?;;6;1 483 | 490;16;Удаление директории или файла;Как удалять папки и файлы?;;6;1 484 | 491;17;Упрощенный ввод названия директорий;Как проще вводит имена папок и файлов, чтобы Git Bush как бы нам подсказывал?;;6;1 485 | 492;18;Виртуальное окружение;Что такое виртуальное окружение Python?;;6;1 486 | 493;19;Создание виртуального окружения;Как создать виртуальное окружение?;;6;2 487 | 494;20;Поделиться виртуальным окружением;Что надо сделать, чтобы поделиться своим виртуальным окружением?;;6;2 488 | 495;21;Файл requirements.txt;Что такое файл requirements.txt?;;6;2 489 | 496;22;Удаление виртуального окружения;Как удалить виртуальное окружение?;;6;2 490 | 497;23;GitHub;Что такое GitHub и для чего он нужен?;;6;2 491 | 498;24;Разница между Git и GitHub;В чем разница между Git и GitHub?;;6;2 492 | 499;25;Связь Git и GitHub;Что делает Git для GitHub?;;6;3 493 | 500;26;Аутентификация на GitHub;Какие способы аутентификации существуют на GitHub?;;6;3 494 | 501;27;Понятие SSH;Что такое SSH?;;6;3 495 | 502;28;Public/Private у репозитория;Что означают параметры Public/Private у репозитория?;;6;3 496 | 503;29;README файл в репозитории;Что такое README файл в репозитории?;;6;3 497 | 504;30;Файл .gitignore в репозитории;Что такое файл .gitignore в репозитории?;;6;3 498 | 505;31;Лицензия в репозитории;Что такое лицензия в репозитории?;;6;3 499 | 506;32;Содержание README;Из чего состоит файл README?;;6;3 500 | 507;33;Клонирование репозитория;Как клонировать репозиторий на локальный компьютер?;;6;4 501 | 508;34;Статусы файлов в репозитории;Перечислите статусы файлов Git?;;6;4 502 | 509;35;Определение статуса файла;Как отследить статус файлов в репозитории?;;6;4 503 | 510;36;Изменение файлов;Если файлы были изменены как добавить к локальный репозиторий индекс?;;6;4 504 | 511;37;Не отслеживаемые файлы;"Как файл с индексом можно перевести в состояние ""не отслеживаемый»?";;6;5 505 | 512;38;Коммит;"Как зафиксировать изменения в файле, ""бросить его в бой"" – т.е. сделать коммит?";;6;5 506 | 513;39;Отправка файлов на сервер;Как отправить файлы на сервер?;;6;5 507 | 514;40;Лишние файлы и папки;Что делать, если в удаленный репозиторий попали лишние файлы и папки?;;6;5 508 | 515;41;Все коммиты;Как посмотреть все коммиты, которые были сделаны?;;6;5 509 | 516;42;Откат к определенному коммиту;Как сделать откат к определенному коммиту?;;6;5 510 | 517;43;Техлид;Кто такой техлид проекта?;;6;5 511 | 518;44;Тимлид;Кто такой тимлид?;;6;5 512 | 519;45;Таск-трекер;Что такое таск-трекеры?;;6;5 513 | 520;46;Встреча daily;Что такое встреча daily?;;6;6 514 | 521;47;Встреча демо;Что такое встреча демо?;;6;6 515 | 522;48;Встреча ретро;Что такое встреча ретро?;;6;6 516 | 523;49;Совместная разработка;Что в первую очередь должен сделать тимлид при совместной разработке?;;6;6 517 | 524;50;Ветка при совместной разработке;Что такое ветка при командной разработки?;;6;6 518 | 525;51;Репозиторий при совместной разработке;Из чего состоит репозиторий при командной разработке?;;6;6 519 | 526;52;Создание своей ветки;Как можно создать свою ветку в проекте?;;6;7 520 | 527;53;Новая часть проекта;Что надо сделать прежде чем написать новую часть проекта?;;6;7 521 | 528;54;Переключение между ветками;Как переключаться между ветками в проекте?;;6;7 522 | 529;55;Название ветки;Какое название подойдет ветки, если там исправление багов?;;6;7 523 | 530;56;Видимость ветки для других разработчиков;Что надо сделать чтобы ветка стала видна и доступна для других разработчиков ?;;6;7 524 | 531;57;Актуальное состояние репозитория;Что делать другим разработчикам, чтобы иметь актуальное состояние репозитория?;;6;7 525 | 532;58;Слияние веток;Как производится промежуточное код-ревью перед слиянием веток?;;6;7 526 | 533;59;Pull request;Что такое Pull request?;;6;8 527 | 534;60;Участники Pull request;Кто участвует в Pull request?;;6;8 528 | 535;61;Замечания на Pull request;Что делать с замечаниями в Pull request?;;6;8 529 | 536;62;Прохождение Pull request;Что надо делать прохождения Pull request?;;6;8 530 | 537;63;Merge;Какая команда делает мердж?;;6;8 531 | 538;64;Ошибки при Merge;Что может возникнуть при попытки смержить файлы?;;6;8 532 | 539;0;Определение базы данных;Что такое база данных?;;7;0 533 | 540;1;Виды баз данных;Какие виды баз данных бывают?;;7;0 534 | 541;2;СУБД;Что такое СУБД?;;7;0 535 | 542;3;Функции СУБД;Какие функции выполняет СУБД?;;7;0 536 | 543;4;Определение SQL;Что такое SQL?;;7;0 537 | 544;5;Элементы таблицы в БД;Расскажите из чего состоит таблица в базе данных на любом примере?;;7;0 538 | 545;6;Содержимое таблицы;Какой оператор позволяет вывести все содержимое таблицы?;Дана таблица Customers, вывести все её содержимое;7;1 539 | 546;7;Определенное поле таблицы;Что надо указать, чтобы вывести определенные поля из данной таблицы?;в таблице Products вывести поля ProductName, UnitPrice;7;1 540 | 547;8;Фильтрация строк;Какой оператор в SQL служит для фильтрации строк?;Какой оператор в SQL служит для фильтрации строк?;7;1 541 | 548;9;Знаки сравнения;Перечислите все знаки сравнения в SQL?;;7;1 542 | 549;10;Фильтрация строк по нескольким значениям;Как производится фильтрация строк по нескольким значениям одного и того же поля?;в таблице Products выполнить фильтрацию строк по полю ProductName, значения которого могут принимать значения 'Tofu', 'Chang', 'Konbu';7;2 543 | 550;11;Фильтрация строк без NULL;Как произвести фильтрацию строк, если надо выбрать все значения, которые не принимают значение NULL?;из таблицы Customers вывести все регионы (поле Region) с ненулевым значением;7;2 544 | 551;12;Шаблонные выражения;Что такое шаблонные выражения в SQL?;в таблице Customers вывести все строки из полей ContactName, ContactTitle при условии, что поле ContactTitle содерджит слово ‘Sales’;7;2 545 | 552;13;Логические операторы;Перечислите все логические операторы в SQL?;AND, OR, BETWEEN, NOT, IS;7;2 546 | 553;14;Сортировка;Что такое сортировка в SQL и с помощью какого слова она реализуется?;В таблице Products вывести поля ProductName, UnitPrice, CategoryID и отсортировать их по возрастанию цены UnitPrice;7;2 547 | 554;15;Сортировка от большего к меньшему;Что нужно указать при сортировке, чтобы она происходила от большего к меньшему для числе или дат, а также в обратном алфавитном порядке?;;7;3 548 | 555;16;Сортировка по нескольким полям;Сортировка производится по нескольким полям или только по одному?;;7;3 549 | 556;17;Понятие агрегирующих функции;Что такое агрегирующие функции в SQL?;;7;3 550 | 557;18;Виды агрегирующих функций;Перечислите все агрегирующие функции SQL?;вывести в таблице Products число строк, минимальное значение поля UnitPrice, максимальное значение поля UnitPrice, среднее значение поля UnitPrice, сумму всех значений поля UnitPrice;7;3 551 | 558;19;Алиасы;Что такое алиасы в SQL и зачем они нужны?;Если столбец назван по-новому (то есть через ключевое слово AS) - то его называют Алиасом;7;4 552 | 559;20;Уникальные значения;Что нужно сделать, чтобы отображались только уникальные значения, не используя группировку?;в таблице Customers вывести все уникальные значения поля Country;7;4 553 | 560;21;Округление числа;Какая функция существует, чтобы округлять данное число до указанного количества знаков?;;7;4 554 | 561;22;Группировка;Что такое группировка в SQL?;;7;5 555 | 562;23;Синтаксис группировки;Что помимо поля для группировки должно быть указано?;дана таблица Customers, сгруппировать по полю Country, и посчитать количество клиентов в этих странах с алиасом clients_number;7;5 556 | 563;24;Фильтрация группировки;Как происходит фильтрация сгруппированных строк?;;7;5 557 | 564;25;Алиас и группировки;Можно ли производить фильтрацию сгруппированных строк по алиасу в ЧИСТОМ SQL?;;7;5 558 | 565;26;Группировка и WHERE;Можно ли использовать при группировках ключевое слово WHERE?;;7;5 559 | 566;27;Группировка и сортировка;Можно ли сгруппированную таблицу отсортировать по полям?;дана таблица Customers, надо сгруппировать её по полю Country, и посчитать количество клиентов в этих странах с алиасом clients_number, в которых число клиентов больше 5, отсортировать по количеству строк от меньше к большему;7;5 560 | 567;28;Группировка по нескольким полям;Можно ли сделать группировку по нескольким полям?;;7;6 561 | 568;29;Арифметические операции в SQL;Перечислите все арифметические операции и константы в SQL?;;7;6 562 | 569;30;Типы связей между таблицами;Перечислите все типы связей между таблицами базы данных в SQL?;;7;6 563 | 570;31;Связь one-to-one;Для чего на практике нужна связь one-to-one?;;7;7 564 | 571;32;Связь many-to-many;Для чего в связи many-to-many нужна промежуточная таблица?;;7;7 565 | 572;33;PK;Что такое PK в таблице?;;7;7 566 | 573;34;FK;Что такое FK в таблице?;;7;7 567 | 574;35;Графическое изображение базы данных;Как графически можно представить связь между таблицами в базе данных?;;7;7 568 | 575;36;Объединение таблиц;Как можно объединить 2 и более таблиц по общему ключу при этом в соединенной таблице не будут строки со значением NULL?;Даны таблицы Products и Categories, их можно соединить по полю CategoryID. Реализовать соединение таблиц.;7;7 569 | 576;37;Объединение таблиц с NULL;Что делать если в результирующей таблице при соединении должны отображаться поля со значениями NULL?;;7;8 570 | 577;38;Объединение списков;Как объединить 2 списка? В результирующем должны быть только уникальные значения:;;7;8 571 | 578;39;Объединение записей;Как вывести записи, которые есть в первой таблице, но отсутствуют во второй?;;7;8 572 | 579;40;Сопоставление записей;Как вывести записи, которые совпадают в первой и во второй таблице?;;7;8 573 | 580;41;Вложенные запросы;Что такое вложенные запросы в SQL?;;7;8 574 | 581;42;Алиасы и вложенные запросы;Нужно ли использовать алиасы во вложенном запросе?;;7;8 575 | 582;43;Принципы реляционныз БД;Сформулируйте основные принципы реляционных баз данных?;;7;8 576 | 583;44;Основные типы данных SQL;Перечислите основные типы данных в SQL?;;7;9 577 | 584;45;Создание новой таблицы;Какой оператор в SQL служит для создания новой таблицы?;создайте таблицу с названием genre, где имеются следующие поля их типы genre_id INT PRIMARY KEY AUTO_INCREMENT, name_genre VARCHAR(30);7;9 578 | 585;46;Создание таблицы из ранее созданной;Можно ли из ранее созданной таблицы создать новую таблицу?;;7;10 579 | 586;47;Создание новой таблицы из нескольких связанных таблиц;Можно ли создать новую таблицу из нескольких связанных таблиц?;;7;10 580 | 587;48;Вставка в таблицу;Как вставить в таблицу некоторые пользовательские значения?;;7;10 581 | 588;49;Условный оператор в SQL;Можно ли в SQL реализовать конструкцию, аналогичную if-else в Python?;;7;10 582 | 589;50;Добавление записи из другой таблицы;Как можно добавить записи из другой таблицы?;;7;11 583 | 590;51;Обновление данных в таблице;Как произвести обновление данных в таблице?;;7;11 584 | 591;52;Удаление строки в таблице;Как удалить определенные строки из таблицы?;;7;11 585 | 592;53;Создание FK; Как создать внешний ключ при создании таблицы?;;7;11 586 | 593;54;Удаление ключа в таблице;Расскажите какие все возможные последствия могут быть при удалении ключа в таблице?;;7;12 587 | 594;55;Связь таблиц в кольцо;Какая есть особенность, когда мы связываем таблицы и замыкаем их в кольцо?;;7;12 588 | 595;56;Удаление таблицы;Как удалить существующую таблицу в базе данных;;7;12 589 | 596;57;Удаление данных таблицы;Как удалить все данные внутри самой таблицы за один запрос?;;7;13 590 | 597;58;Изменение структуры таблицы;Расскажите изменении структуры таблицы?;;7;13 591 | -------------------------------------------------------------------------------- /images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/images/add.png -------------------------------------------------------------------------------- /images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/images/delete.png -------------------------------------------------------------------------------- /images/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/images/icon.ico -------------------------------------------------------------------------------- /images/sound_OFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/images/sound_OFF.png -------------------------------------------------------------------------------- /images/sound_ON.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/images/sound_ON.png -------------------------------------------------------------------------------- /images/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/images/start.png -------------------------------------------------------------------------------- /images/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/images/stop.png -------------------------------------------------------------------------------- /knowledge/1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/knowledge/1.pdf -------------------------------------------------------------------------------- /knowledge/2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/knowledge/2.pdf -------------------------------------------------------------------------------- /knowledge/3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/knowledge/3.pdf -------------------------------------------------------------------------------- /knowledge/4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/knowledge/4.pdf -------------------------------------------------------------------------------- /knowledge/5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/knowledge/5.pdf -------------------------------------------------------------------------------- /knowledge/6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/knowledge/6.pdf -------------------------------------------------------------------------------- /knowledge/7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanZaycev0717/python_interview_assistant_rus/e7e65892132c5c3ac81c4a8dda2dd6ead01702fb/knowledge/7.pdf -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from collections import deque 3 | import random 4 | import platform 5 | import csv 6 | import threading 7 | import tkinter as tk 8 | from tkinter import ttk 9 | from tkinter import PhotoImage 10 | from typing import Optional 11 | import sys 12 | 13 | from PIL import Image 14 | from CTkMessagebox import CTkMessagebox 15 | import customtkinter as ctk 16 | import fitz 17 | if platform.system() == 'Windows': 18 | import pyttsx3 19 | 20 | from colors import (YELLOW_BACKGROUND, PINK_BACKGROUND, 21 | GREEN_BACKGROUND, SWAMP_FOREGROUND, 22 | ORANGE_FOREGROUND, NEW_USER_WINDOW_FOREGROUND, 23 | PINK_FOREGROUND, CRIMSON_HOVER, 24 | BEGIN_BUTTON_FOREGROUND_COLOR, BEGIN_BUTTON_HOVER_COLOR, 25 | SOUNDS_BUTTONS_FOREGROUND, SOUNDS_BUTTONS_HOVER, 26 | POSITIVE_BUTTON_FOREGROUND, POSITIVE_BUTTON_HOVER, 27 | NEGATIVE_BUTTON_FOREGROUND, NEGATIVE_BUTTON_HOVER, 28 | ANSWER_BUTTON_FOREGROUND, ANSWER_BUTTON_HOVER, 29 | CHECKBOX_HOVER_COLOR, PROGRESS_FOREGROUND, 30 | PROGRESS_COLOR, GREEN, 31 | RED, WHITE, ERROR_COLOR, PDF_OUTPUT_COLOR) 32 | from settings import (CREATE_USER_WINDOW, HINT_WINDOW_TITLE, 33 | Theme, QuestionThreshold as qt, 34 | ValidResponse, 35 | APP_NAME, APP_RESOLUTION) 36 | from models import create_db 37 | from manage_db import (create_new_user, get_user_names, 38 | get_user_interview_duration, 39 | get_user_progress, get_last_enter_date, 40 | update_interview_duration, update_last_enter_date, 41 | update_user_progress, delete_this_user) 42 | from user_statistics import (convert_seconds_to_hours, 43 | count_interview_duration, 44 | get_right_answers_amount, 45 | get_last_enter_message) 46 | from validator import (is_name_empty, is_name_too_short, 47 | has_name_first_wrong_symbol, has_name_wrong_symbols, 48 | is_name_too_long, is_user_already_exists) 49 | from my_timers import CommandTimer, MessageTimer 50 | 51 | 52 | class Main(ctk.CTk): 53 | """Main class for the App governing vars between other classes.""" 54 | def __init__(self, title: str, size: tuple[int, int]) -> None: 55 | super().__init__() 56 | # Setup 57 | self.title(title) 58 | self.geometry(f'{size[0]}x{size[1]}') 59 | self.resizable(False, False) 60 | if platform.system() == 'Windows': 61 | self.iconbitmap(default='images/icon.ico') 62 | 63 | # Instance vars 64 | self.current_user: str = '' 65 | self.volume: float = 0.5 66 | self.user_progress: dict = {} 67 | self.create_user_window: Optional[CreateNewUser] = None 68 | self.hint_window: Optional[HintWindow] = None 69 | 70 | # Load questions and create question bank 71 | self.question_bank = self.load_csv() 72 | 73 | # Themes dictionary 74 | self.themes: dict[int, Theme] = { 75 | 0: Theme.BASICS, 76 | 1: Theme.OOP, 77 | 2: Theme.PEP8, 78 | 3: Theme.STRUCTURES, 79 | 4: Theme.ALGHORITMS, 80 | 5: Theme.GIT, 81 | 6: Theme.SQL 82 | } 83 | 84 | # Interview mode dictionary 85 | self.interview_mode: dict[Theme | str, int] = { 86 | Theme.BASICS: 1, 87 | Theme.OOP: 0, 88 | Theme.PEP8: 0, 89 | Theme.STRUCTURES: 0, 90 | Theme.ALGHORITMS: 0, 91 | Theme.GIT: 0, 92 | Theme.SQL: 0, 93 | 'Freemode': 0, 94 | 'Random': 0 95 | } 96 | 97 | # Notebook 98 | self.notebook = ctk.CTkTabview( 99 | self, 100 | segmented_button_fg_color='black', 101 | segmented_button_selected_color='green', 102 | segmented_button_selected_hover_color='green', 103 | text_color='white', 104 | segmented_button_unselected_color='black', 105 | segmented_button_unselected_hover_color='black') 106 | self.notebook.pack( 107 | padx=20, 108 | pady=20, 109 | side='left', 110 | fill='both', 111 | expand=True 112 | ) 113 | 114 | self.notebook.add(name='Профиль пользователей') 115 | self.notebook.add(name='Настройки собеседования') 116 | self.notebook.add(name='Пройти собеседование') 117 | self.notebook.set('Профиль пользователей') 118 | 119 | # Tabs of notebook 120 | self.userstats = UserStatisticsTab( 121 | parent=self.notebook.tab('Профиль пользователей'), 122 | create_new_user=self.create_new_user, 123 | set_current_user=self.set_current_user, 124 | set_user_progress=self.set_user_progress, 125 | set_color_for_user_progress=self.set_color_for_user_progress 126 | ) 127 | 128 | self.interview_settings = InterviewSettingsTab( 129 | parent=self.notebook.tab('Настройки собеседования'), 130 | set_interview_mode=self.set_interview_mode, 131 | get_volume=self.get_volume, 132 | set_volume=self.set_volume 133 | ) 134 | 135 | self.interview_pass = InterviewPassTab( 136 | parent=self.notebook.tab('Пройти собеседование'), 137 | themes=self.themes, 138 | database=self.question_bank, 139 | show_hint_window=self.show_hint_window, 140 | get_volume=self.get_volume, 141 | set_volume=self.set_volume, 142 | get_current_user=self.get_current_user, 143 | set_notebook_status=self.set_notebook_status, 144 | get_interview_mode=self.get_interview_mode, 145 | get_user_progress=self.get_user_progress, 146 | update_progress=self.update_progress, 147 | ) 148 | 149 | def load_csv(self) -> list[tuple[int | str]]: 150 | """Converts data.csv to list of tuples.""" 151 | with open('data.csv', encoding='utf-8', mode='r') as f: 152 | reader = csv.reader(f, delimiter=';') 153 | data = tuple(reader) 154 | return [ 155 | tuple( 156 | [int(item) if item.isdigit() else item for item in row] 157 | ) for row in data] 158 | 159 | def create_new_user(self) -> None: 160 | """Makes a new window to create a new user.""" 161 | if self.create_user_window is None or not self.create_user_window.winfo_exists(): 162 | self.create_user_window = CreateNewUser( 163 | title=CREATE_USER_WINDOW, 164 | update_combobox=self.update_combobox 165 | ) 166 | self.focus() 167 | self.create_user_window.focus() 168 | else: 169 | self.create_user_window.lift() 170 | self.create_user_window.focus() 171 | 172 | def show_hint_window(self, filepath: str, page_number: int) -> None: 173 | """Makes a new window to show the answer to the question.""" 174 | if self.hint_window is None or not self.hint_window.winfo_exists(): 175 | self.hint_window = HintWindow( 176 | HINT_WINDOW_TITLE, 177 | filepath, 178 | page_number 179 | ) 180 | self.focus() 181 | self.hint_window.focus() 182 | else: 183 | self.hint_window.lift() 184 | self.hint_window.focus() 185 | 186 | def get_volume(self) -> float: 187 | """Returns current a volume value.""" 188 | return self.volume 189 | 190 | def set_volume(self, volume: float) -> None: 191 | """Sets transferred value of volume.""" 192 | self.volume = volume 193 | if not self.volume: 194 | self.interview_pass.mute_button.configure( 195 | image=self.interview_pass.mute_button_img_OFF 196 | ) 197 | else: 198 | self.interview_pass.mute_button.configure( 199 | image=self.interview_pass.mute_button_img_ON 200 | ) 201 | hundred_volume = 100 * self.volume 202 | self.interview_settings.sound_volume.set(hundred_volume) 203 | self.interview_settings.sound_text.set( 204 | f'Громкость: {int(hundred_volume)}%' 205 | ) 206 | 207 | def update_combobox(self) -> None: 208 | """Updates user list at the user statistics tab.""" 209 | self.userstats.update_user_list() 210 | 211 | def update_progress(self) -> None: 212 | """Updates users progress bars at the user statistics tab.""" 213 | self.userstats.update_user_progress() 214 | 215 | def set_interview_mode(self, 216 | interview_mode: dict[Theme | str, int]) -> None: 217 | """Sets parametres of interview according selection 218 | at user settings tab. 219 | """ 220 | self.interview_mode = interview_mode 221 | 222 | def set_current_user(self, current_user: str) -> None: 223 | """Sets a name of user from another tabs.""" 224 | self.current_user = current_user 225 | 226 | def get_current_user(self) -> str: 227 | """Returns current user name.""" 228 | return self.current_user 229 | 230 | def set_notebook_status(self, status: str) -> None: 231 | """Sets notebook state according transferred value.""" 232 | self.notebook.configure(state=status) 233 | 234 | def get_interview_mode(self) -> dict[Theme | str, int]: 235 | """Returns the interview mode uncluding: 236 | - chosen themes 237 | - is chosen a Random mode 238 | - is chosen a Free mode. 239 | """ 240 | return self.interview_mode 241 | 242 | def set_user_progress(self, user_progress: dict) -> None: 243 | """Sets user progress according value.""" 244 | self.user_progress = user_progress 245 | 246 | def get_user_progress(self) -> None: 247 | """Returns current user progress.""" 248 | return self.user_progress 249 | 250 | def set_color_for_user_progress(self) -> None: 251 | """Updates question strings' color 252 | at the user intervew tab, 253 | according current user progress. 254 | """ 255 | self.interview_pass.set_color_for_user_progress() 256 | 257 | 258 | class UserStatisticsTab(ctk.CTkFrame): 259 | """Class for showing user statistics tab.""" 260 | def __init__(self, parent, create_new_user, 261 | set_current_user, set_user_progress, 262 | set_color_for_user_progress): 263 | # Setup 264 | super().__init__(parent) 265 | self.width = 1000 266 | self.place(x=0, y=0) 267 | self.columnconfigure((0, 1), weight=1) 268 | self.rowconfigure((0, 1), weight=1) 269 | 270 | # Users vars 271 | self.create_new_user = create_new_user 272 | self.users = get_user_names() 273 | self.current_user = set_current_user 274 | self.set_user_progress = set_user_progress 275 | self.chosen_user = None 276 | self.set_color_for_user_progress = set_color_for_user_progress 277 | 278 | # MESSAGE VARS 279 | # pink screen 280 | self.user_var = tk.StringVar(value='Выберите пользователя...') 281 | 282 | # yellow screen 283 | self.last_enter_message = tk.StringVar() 284 | self.interview_duration_message = tk.StringVar() 285 | self.rigth_answer_message = tk.StringVar() 286 | self.percentage_completion_message = tk.StringVar() 287 | 288 | self.create_widgets() 289 | self.author_note() 290 | self.set_to_zero_progress_bars() 291 | 292 | # EVENTS 293 | self.combobox1.bind("<>", self.choose_user) 294 | 295 | def update_user_list(self) -> None: 296 | """Updates the list of users in Combobox.""" 297 | self.combobox1['values'] = get_user_names() 298 | 299 | def reset_settings(self) -> None: 300 | """Turns to zero any in statistics.""" 301 | self.chosen_user = None 302 | self.current_user(self.chosen_user) 303 | self.user_var.set('Выберите пользователя...') 304 | self.last_enter_message.set('') 305 | self.interview_duration_message.set('') 306 | self.rigth_answer_message.set('') 307 | self.percentage_completion_message.set('') 308 | 309 | def update_user_progress(self) -> None: 310 | """Updates everything in current user statistics.""" 311 | progress = get_right_answers_amount( 312 | get_user_progress(self.chosen_user) 313 | ) 314 | self.last_enter_message.set( 315 | get_last_enter_message( 316 | get_last_enter_date(self.chosen_user) 317 | ) 318 | ) 319 | self.interview_duration_message.set( 320 | str(convert_seconds_to_hours( 321 | get_user_interview_duration(self.chosen_user))) + ' ч.' 322 | ) 323 | self.rigth_answer_message.set(progress['right_answers_amount']) 324 | self.percentage_completion_message.set( 325 | progress['percentage_completion'] 326 | ) 327 | self.basic_progress_bar.set(progress['basic_progress']) 328 | self.oop_progress_bar.set(progress['oop_progress']) 329 | self.pep_progress_bar.set(progress['pep_progress']) 330 | self.structures_progress_bar.set(progress['structures_progress']) 331 | self.alghoritms_progress_bar.set(progress['alghorimts_progress']) 332 | self.git_progress_bar.set(progress['git_progress']) 333 | self.sql_progress_bar.set(progress['sql_progress']) 334 | 335 | def set_to_zero_progress_bars(self) -> None: 336 | """Turns to zero every progress bar.""" 337 | self.basic_progress_bar.set(0) 338 | self.oop_progress_bar.set(0) 339 | self.pep_progress_bar.set(0) 340 | self.structures_progress_bar.set(0) 341 | self.alghoritms_progress_bar.set(0) 342 | self.git_progress_bar.set(0) 343 | self.sql_progress_bar.set(0) 344 | 345 | def delete_user(self) -> None: 346 | """Deletes the chosen user from DB and screen.""" 347 | delete_this_user(self.chosen_user) 348 | self.reset_settings() 349 | self.update_user_list() 350 | self.set_to_zero_progress_bars() 351 | 352 | def choose_user(self, event) -> None: 353 | """Processes event choosing a user. 354 | It updates everything at the tab. 355 | """ 356 | self.chosen_user = self.user_var.get() 357 | self.current_user(self.chosen_user) 358 | self.set_user_progress(get_user_progress(self.chosen_user)) 359 | self.get_current_user_statistics() 360 | self.update_user_progress() 361 | self.set_color_for_user_progress() 362 | 363 | def get_current_user_statistics(self) -> None: 364 | """Gets current user statistics.""" 365 | self.last_enter_message.set( 366 | get_last_enter_message(get_last_enter_date(self.chosen_user)) 367 | ) 368 | 369 | self.interview_duration_message.set( 370 | str(convert_seconds_to_hours( 371 | get_user_interview_duration(self.chosen_user))) + ' ч.' 372 | ) 373 | 374 | messages_data = get_right_answers_amount( 375 | get_user_progress(self.chosen_user) 376 | ) 377 | self.rigth_answer_message.set(messages_data['right_answers_amount']) 378 | self.percentage_completion_message.set( 379 | messages_data['percentage_completion'] 380 | ) 381 | 382 | def author_note(self) -> None: 383 | """Shows a title of author.""" 384 | self.author_label = ctk.CTkLabel( 385 | master=self, 386 | text='github.com/IvanZaycev0717\n\nTelegram: @ivanzaycev0717' 387 | ) 388 | self.author_label.place(x=20, y=560) 389 | 390 | def create_widgets(self) -> None: 391 | """Creates widgets at the user statistics tab.""" 392 | # PINK SCREEN 393 | self.choose_user_frame = ctk.CTkFrame( 394 | self, 395 | fg_color=PINK_BACKGROUND, 396 | width=600, 397 | height=300 398 | ) 399 | self.choose_user_frame.grid( 400 | row=0, 401 | column=0, 402 | sticky='n', 403 | padx=20, 404 | pady=20 405 | ) 406 | 407 | # Static labels 408 | self.user_manage_label = ctk.CTkLabel( 409 | self.choose_user_frame, 410 | text='Управление пользователями', 411 | font=('Calibri', 25) 412 | ) 413 | self.user_manage_label.place(x=30, y=10) 414 | self.choose_user_label = ctk.CTkLabel( 415 | self.choose_user_frame, 416 | text='Выберите пользователя', 417 | font=('Calibri', 18)) 418 | self.choose_user_label.place(x=30, y=50) 419 | self.create_new_user_label = ctk.CTkLabel( 420 | self.choose_user_frame, 421 | text='Вы можете создать нового пользователя', 422 | font=('Calibri', 18) 423 | ) 424 | self.create_new_user_label.place(x=30, y=200) 425 | 426 | # Combobox 427 | self.combobox1 = ttk.Combobox( 428 | self.choose_user_frame, 429 | textvariable=self.user_var, 430 | state="readonly") 431 | self.combobox1.configure(values=self.users) 432 | self.combobox1.place(x=30, y=80, width=250, height=35) 433 | 434 | # Images at the buttons 435 | self.create_user_button_img = ctk.CTkImage( 436 | light_image=Image.open('images/add.png').resize((30, 30)), 437 | dark_image=Image.open('images/add.png').resize((30, 30)) 438 | ) 439 | self.delete_user_button_img = ctk.CTkImage( 440 | light_image=Image.open('images/delete.png').resize((30, 30)), 441 | dark_image=Image.open('images/delete.png').resize((30, 30)) 442 | ) 443 | 444 | # Buttons 445 | self.create_user_button = ctk.CTkButton( 446 | self.choose_user_frame, 447 | width=250, 448 | height=35, 449 | fg_color=PINK_FOREGROUND, 450 | hover_color=CRIMSON_HOVER, 451 | text='Создать пользователя', 452 | image=self.create_user_button_img, 453 | text_color='black', 454 | command=self.create_new_user) 455 | self.create_user_button.place(x=30, y=240) 456 | 457 | self.delete_user_button = ctk.CTkButton( 458 | self.choose_user_frame, 459 | width=200, 460 | height=35, 461 | fg_color=PINK_FOREGROUND, 462 | hover_color=CRIMSON_HOVER, 463 | text='Удалить пользователя', 464 | image=self.delete_user_button_img, 465 | text_color='black', 466 | command=self.delete_user) 467 | self.delete_user_button.place(x=320, y=80) 468 | 469 | # YELLOW SCREEN 470 | # frame 471 | self.global_stats_frame = ctk.CTkFrame( 472 | self, 473 | fg_color=YELLOW_BACKGROUND, 474 | width=400, 475 | height=250 476 | ) 477 | self.global_stats_frame.grid( 478 | row=1, 479 | column=0, 480 | sticky='e', 481 | padx=20, 482 | pady=20 483 | ) 484 | 485 | # Static labels 486 | self.global_stat_label = ctk.CTkLabel( 487 | self.global_stats_frame, 488 | text='Глобальная статистика', 489 | font=('Calibri', 25) 490 | ) 491 | self.global_stat_label.place(x=30, y=10) 492 | self.last_enter_label = ctk.CTkLabel( 493 | self.global_stats_frame, 494 | text='Собеседование было:', 495 | font=('Calibri', 18) 496 | ) 497 | self.last_enter_label.place(x=30, y=60) 498 | self.interview_duration_label = ctk.CTkLabel( 499 | self.global_stats_frame, 500 | text='Время собеседований:', 501 | font=('Calibri', 18) 502 | ) 503 | self.interview_duration_label.place(x=30, y=110) 504 | self.right_answer_amount_label = ctk.CTkLabel( 505 | self.global_stats_frame, 506 | text='Правильных ответов:', 507 | font=('Calibri', 18) 508 | ) 509 | self.right_answer_amount_label.place(x=30, y=160) 510 | self.percentage_completion_label = ctk.CTkLabel( 511 | self.global_stats_frame, 512 | text='Процент завершения:', 513 | font=('Calibri', 18) 514 | ) 515 | self.percentage_completion_label.place(x=30, y=210) 516 | 517 | # Dynamic messages of user's staticstics 518 | self.last_enter_message_label = ttk.Label( 519 | master=self.global_stats_frame, 520 | textvariable=self.last_enter_message, 521 | font=('Calibri', 16), 522 | background=YELLOW_BACKGROUND 523 | ) 524 | self.last_enter_message_label.place(x=215, y=58) 525 | 526 | self.interview_duration_message_label = tk.Label( 527 | master=self.global_stats_frame, 528 | textvariable=self.interview_duration_message, 529 | font=('Calibri', 16), 530 | background=YELLOW_BACKGROUND 531 | ) 532 | self.interview_duration_message_label.place(x=220, y=107) 533 | 534 | self.rigth_answer_message_label = ttk.Label( 535 | master=self.global_stats_frame, 536 | textvariable=self.rigth_answer_message, 537 | font=('Calibri', 16), 538 | background=YELLOW_BACKGROUND 539 | ) 540 | self.rigth_answer_message_label.place(x=205, y=157) 541 | 542 | self.percentage_completion_message_label = ttk.Label( 543 | master=self.global_stats_frame, 544 | textvariable=self.percentage_completion_message, 545 | font=('Calibri', 16), 546 | background=YELLOW_BACKGROUND 547 | ) 548 | self.percentage_completion_message_label.place(x=205, y=208) 549 | 550 | # GREEN SCREEN 551 | # Frame 552 | self.particular_stats_frame = ctk.CTkFrame( 553 | self, 554 | fg_color=GREEN_BACKGROUND, 555 | width=550 556 | ) 557 | self.particular_stats_frame.grid( 558 | row=0, 559 | column=1, 560 | rowspan=2, 561 | sticky='nsew', 562 | padx=20, 563 | pady=20 564 | ) 565 | 566 | self.detail_progress_title = ctk.CTkLabel( 567 | self.particular_stats_frame, 568 | text='Детальный прогресс по собеседованиям', 569 | font=('Calibri', 25) 570 | ) 571 | self.detail_progress_title.place(x=30, y=10) 572 | 573 | # Basic syntax progress 574 | self.basic_progress_label = ctk.CTkLabel( 575 | self.particular_stats_frame, 576 | text=Theme.BASICS.value, 577 | font=('Calibri', 18) 578 | ) 579 | self.basic_progress_label.place(x=30, y=60) 580 | 581 | self.basic_progress_bar = ctk.CTkProgressBar( 582 | self.particular_stats_frame, 583 | width=480, 584 | height=30, 585 | fg_color=PROGRESS_FOREGROUND, 586 | progress_color=PROGRESS_COLOR) 587 | self.basic_progress_bar.place(x=30, y=90) 588 | 589 | # OOP progress 590 | self.oop_progress_label = ctk.CTkLabel( 591 | self.particular_stats_frame, 592 | text=Theme.OOP.value, 593 | font=('Calibri', 18) 594 | ) 595 | self.oop_progress_label.place(x=30, y=130) 596 | 597 | self.oop_progress_bar = ctk.CTkProgressBar( 598 | self.particular_stats_frame, 599 | width=480, 600 | height=30, 601 | fg_color=PROGRESS_FOREGROUND, 602 | progress_color=PROGRESS_COLOR) 603 | self.oop_progress_bar.place(x=30, y=160) 604 | 605 | # PEP progress 606 | self.pep_progress_label = ctk.CTkLabel( 607 | self.particular_stats_frame, 608 | text='Правила оформления кода (PEP8, PEP257)', 609 | font=('Calibri', 18) 610 | ) 611 | self.pep_progress_label.place(x=30, y=200) 612 | 613 | self.pep_progress_bar = ctk.CTkProgressBar( 614 | self.particular_stats_frame, 615 | width=480, 616 | height=30, 617 | fg_color=PROGRESS_FOREGROUND, 618 | progress_color=PROGRESS_COLOR) 619 | self.pep_progress_bar.place(x=30, y=230) 620 | 621 | # Structures progress 622 | self.structures_progress_label = ctk.CTkLabel( 623 | self.particular_stats_frame, 624 | text=Theme.STRUCTURES.value, 625 | font=('Calibri', 18) 626 | ) 627 | self.structures_progress_label.place(x=30, y=270) 628 | 629 | self.structures_progress_bar = ctk.CTkProgressBar( 630 | self.particular_stats_frame, 631 | width=480, 632 | height=30, 633 | fg_color=PROGRESS_FOREGROUND, 634 | progress_color=PROGRESS_COLOR) 635 | self.structures_progress_bar.place(x=30, y=300) 636 | 637 | # Alghoritms progress 638 | self.alghoritms_progress_label = ctk.CTkLabel( 639 | self.particular_stats_frame, 640 | text=Theme.ALGHORITMS.value, 641 | font=('Calibri', 18) 642 | ) 643 | self.alghoritms_progress_label.place(x=30, y=340) 644 | 645 | self.alghoritms_progress_bar = ctk.CTkProgressBar( 646 | self.particular_stats_frame, 647 | width=480, 648 | height=30, 649 | fg_color=PROGRESS_FOREGROUND, 650 | progress_color=PROGRESS_COLOR) 651 | self.alghoritms_progress_bar.place(x=30, y=370) 652 | 653 | # GIT progress 654 | self.git_progress_label = ctk.CTkLabel( 655 | self.particular_stats_frame, 656 | text=Theme.GIT.value, 657 | font=('Calibri', 18) 658 | ) 659 | self.git_progress_label.place(x=30, y=410) 660 | 661 | self.git_progress_bar = ctk.CTkProgressBar( 662 | self.particular_stats_frame, 663 | width=480, 664 | height=30, 665 | fg_color=PROGRESS_FOREGROUND, 666 | progress_color=PROGRESS_COLOR) 667 | self.git_progress_bar.place(x=30, y=440) 668 | 669 | # SQL progress 670 | self.sql_progress_label = ctk.CTkLabel( 671 | self.particular_stats_frame, 672 | text=Theme.SQL.value, 673 | font=('Calibri', 18) 674 | ) 675 | self.sql_progress_label.place(x=30, y=480) 676 | 677 | self.sql_progress_bar = ctk.CTkProgressBar( 678 | self.particular_stats_frame, 679 | width=480, 680 | height=30, 681 | fg_color=PROGRESS_FOREGROUND, 682 | progress_color=PROGRESS_COLOR) 683 | self.sql_progress_bar.place(x=30, y=510) 684 | 685 | 686 | class InterviewSettingsTab(ctk.CTkFrame): 687 | """Class for choosing interview's settings.""" 688 | def __init__(self, parent, set_interview_mode, get_volume, set_volume): 689 | super().__init__(parent) 690 | self.width = 1200 691 | self.place(x=0, y=0) 692 | self.columnconfigure((0, ), weight=1) 693 | self.rowconfigure((0, 1, 2, 3), weight=1) 694 | self.set_interview_mode = set_interview_mode 695 | 696 | # Flags in Checkboxes 697 | self.basics_chosen = ctk.IntVar(value=1) 698 | self.oop_chosen = ctk.IntVar(value=0) 699 | self.pep_chosen = ctk.IntVar(value=0) 700 | self.structures_chosen = ctk.IntVar(value=0) 701 | self.alghoritms_chosen = ctk.IntVar(value=0) 702 | self.git_chosen = ctk.IntVar(value=0) 703 | self.sql_chosen = ctk.IntVar(value=0) 704 | 705 | # Flags in sequence mode 706 | self.are_random_questions = ctk.IntVar(value=0) 707 | 708 | # Flag in free mode 709 | self.freemode_var = ctk.IntVar() 710 | self.get_volume = get_volume 711 | self.set_volume = set_volume 712 | self.sound_volume = ctk.IntVar(value=int(100 * self.get_volume())) 713 | self.sound_text = ctk.StringVar( 714 | value=f'Громкость: {self.sound_volume.get()}%' 715 | ) 716 | 717 | self.choose_interview_mode_tab() 718 | self.choose_random_interview() 719 | self.choose_free_mode() 720 | self.toggle_sounds() 721 | 722 | def draw_line(self, frame) -> None: 723 | """Draws a line near a setting title.""" 724 | self.tab_line = ctk.CTkCanvas( 725 | frame, 726 | width=5, 727 | height=80, 728 | bd=0, 729 | highlightthickness=0 730 | ) 731 | self.tab_line.place(x=400, y=10) 732 | self.tab_line.create_line(0, 0, 0, 80, width=10) 733 | 734 | def draw_label(self, frame, text) -> None: 735 | """Draws a label of setting with custom text.""" 736 | ctk.CTkLabel(frame, text=text, font=('Calibri', 20)).place(x=20, y=35) 737 | 738 | def choose_interview_mode_tab(self) -> None: 739 | """Creates a choice of themes for interview.""" 740 | self.choose_interview_mode_frame = ctk.CTkFrame( 741 | self, fg_color=SWAMP_FOREGROUND, 742 | width=1185, 743 | height=100 744 | ) 745 | self.choose_interview_mode_frame.grid( 746 | row=0, 747 | column=0, 748 | sticky='n', 749 | padx=20, 750 | pady=20 751 | ) 752 | self.draw_label( 753 | self.choose_interview_mode_frame, 754 | 'Выбор тем собеседования' 755 | ) 756 | self.draw_line(self.choose_interview_mode_frame) 757 | 758 | self.basics = ctk.CTkCheckBox( 759 | master=self.choose_interview_mode_frame, 760 | text=Theme.BASICS.value, 761 | hover_color=CHECKBOX_HOVER_COLOR, 762 | fg_color=CHECKBOX_HOVER_COLOR, 763 | variable=self.basics_chosen, 764 | command=self.add_chosen_theme) 765 | self.basics.place(x=420, y=15) 766 | 767 | self.oop = ctk.CTkCheckBox( 768 | master=self.choose_interview_mode_frame, 769 | text='ООП Python', 770 | hover_color=CHECKBOX_HOVER_COLOR, 771 | fg_color=CHECKBOX_HOVER_COLOR, 772 | variable=self.oop_chosen, 773 | command=self.add_chosen_theme) 774 | self.oop.place(x=420, y=55) 775 | 776 | self.pep = ctk.CTkCheckBox( 777 | master=self.choose_interview_mode_frame, 778 | text='PEP8, PEP257', 779 | hover_color=CHECKBOX_HOVER_COLOR, 780 | fg_color=CHECKBOX_HOVER_COLOR, 781 | variable=self.pep_chosen, 782 | command=self.add_chosen_theme) 783 | self.pep.place(x=650, y=15) 784 | 785 | self.structures = ctk.CTkCheckBox( 786 | master=self.choose_interview_mode_frame, 787 | text=Theme.STRUCTURES.value, 788 | hover_color=CHECKBOX_HOVER_COLOR, 789 | fg_color=CHECKBOX_HOVER_COLOR, 790 | variable=self.structures_chosen, 791 | command=self.add_chosen_theme) 792 | self.structures.place(x=650, y=55) 793 | 794 | self.alghoritms = ctk.CTkCheckBox( 795 | master=self.choose_interview_mode_frame, 796 | text=Theme.ALGHORITMS.value, 797 | hover_color=CHECKBOX_HOVER_COLOR, 798 | fg_color=CHECKBOX_HOVER_COLOR, 799 | variable=self.alghoritms_chosen, 800 | command=self.add_chosen_theme) 801 | self.alghoritms.place(x=870, y=15) 802 | 803 | self.sql = ctk.CTkCheckBox( 804 | master=self.choose_interview_mode_frame, 805 | text=Theme.SQL.value, 806 | hover_color=CHECKBOX_HOVER_COLOR, 807 | fg_color=CHECKBOX_HOVER_COLOR, 808 | variable=self.sql_chosen, 809 | command=self.add_chosen_theme) 810 | self.sql.place(x=870, y=55) 811 | 812 | self.git = ctk.CTkCheckBox( 813 | master=self.choose_interview_mode_frame, 814 | text=Theme.GIT.value, 815 | hover_color=CHECKBOX_HOVER_COLOR, 816 | fg_color=CHECKBOX_HOVER_COLOR, 817 | variable=self.git_chosen, 818 | command=self.add_chosen_theme) 819 | self.git.place(x=1100, y=15) 820 | 821 | def add_chosen_theme(self) -> None: 822 | """Transfers chosen settings to the main class.""" 823 | # Freemode special behavior 824 | if self.freemode_var.get(): 825 | self.basics_chosen.set(1) 826 | self.oop_chosen.set(1) 827 | self.pep_chosen.set(1) 828 | self.structures_chosen.set(1) 829 | self.alghoritms_chosen.set(1) 830 | self.git_chosen.set(1) 831 | self.sql_chosen.set(1) 832 | self.basics.configure(state=tk.DISABLED) 833 | self.oop.configure(state=tk.DISABLED) 834 | self.pep.configure(state=tk.DISABLED) 835 | self.structures.configure(state=tk.DISABLED) 836 | self.alghoritms.configure(state=tk.DISABLED) 837 | self.git.configure(state=tk.DISABLED) 838 | self.sql.configure(state=tk.DISABLED) 839 | else: 840 | self.basics.configure(state=tk.NORMAL) 841 | self.oop.configure(state=tk.NORMAL) 842 | self.pep.configure(state=tk.NORMAL) 843 | self.structures.configure(state=tk.NORMAL) 844 | self.alghoritms.configure(state=tk.NORMAL) 845 | self.git.configure(state=tk.NORMAL) 846 | self.sql.configure(state=tk.NORMAL) 847 | 848 | self.interview_mode = { 849 | Theme.BASICS: self.basics_chosen.get(), 850 | Theme.OOP: self.oop_chosen.get(), 851 | Theme.PEP8: self.pep_chosen.get(), 852 | Theme.STRUCTURES: self.structures_chosen.get(), 853 | Theme.ALGHORITMS: self.alghoritms_chosen.get(), 854 | Theme.GIT: self.git_chosen.get(), 855 | Theme.SQL: self.sql_chosen.get(), 856 | 'Freemode': self.freemode_var.get(), 857 | 'Random': self.are_random_questions.get() 858 | } 859 | self.set_interview_mode(self.interview_mode) 860 | 861 | def choose_random_interview(self) -> None: 862 | """Creates a panel for random interview setting.""" 863 | self.choose_random_interview_frame = ctk.CTkFrame( 864 | self, fg_color=SWAMP_FOREGROUND, 865 | width=1185, 866 | height=100 867 | ) 868 | self.choose_random_interview_frame.grid( 869 | row=1, 870 | column=0, 871 | sticky='n', 872 | padx=20, 873 | pady=20 874 | ) 875 | 876 | self.draw_label( 877 | self.choose_random_interview_frame, 878 | 'Последовательность вопросов' 879 | ) 880 | self.draw_line(self.choose_random_interview_frame) 881 | 882 | self.random_button_off = ctk.CTkRadioButton( 883 | self.choose_random_interview_frame, 884 | value=0, 885 | text='Вопросы задают последовательно', 886 | variable=self.are_random_questions, 887 | fg_color=CHECKBOX_HOVER_COLOR, 888 | hover_color=CHECKBOX_HOVER_COLOR, 889 | command=self.add_chosen_theme) 890 | self.random_button_off.place(x=420, y=40) 891 | 892 | self.random_button_on = ctk.CTkRadioButton( 893 | self.choose_random_interview_frame, 894 | value=1, 895 | text='Вопросы задают случайно', 896 | variable=self.are_random_questions, 897 | fg_color=CHECKBOX_HOVER_COLOR, 898 | hover_color=CHECKBOX_HOVER_COLOR, 899 | command=self.add_chosen_theme) 900 | self.random_button_on.place(x=700, y=40) 901 | 902 | def choose_free_mode(self) -> None: 903 | """Creates a freemode setting.""" 904 | self.choose_free_mode_frame = ctk.CTkFrame( 905 | self, 906 | fg_color=SWAMP_FOREGROUND, 907 | width=1185, 908 | height=100 909 | ) 910 | self.choose_free_mode_frame.grid( 911 | row=2, 912 | column=0, 913 | sticky='n', 914 | padx=20, 915 | pady=20 916 | ) 917 | 918 | self.draw_label( 919 | self.choose_free_mode_frame, 920 | 'Свободное перемещение по вопросам' 921 | ) 922 | self.draw_line(self.choose_free_mode_frame) 923 | 924 | self.freemode_button_on = ctk.CTkRadioButton( 925 | self.choose_free_mode_frame, 926 | value=1, 927 | text='Включить свободный выбор вопросов', 928 | variable=self.freemode_var, 929 | fg_color=CHECKBOX_HOVER_COLOR, 930 | hover_color=CHECKBOX_HOVER_COLOR, 931 | command=self.add_chosen_theme) 932 | self.freemode_button_on.place(x=420, y=40) 933 | 934 | self.freemode_button_off = ctk.CTkRadioButton( 935 | self.choose_free_mode_frame, 936 | value=0, 937 | text='Отключить свободный выбор вопросов', 938 | variable=self.freemode_var, 939 | fg_color=CHECKBOX_HOVER_COLOR, 940 | hover_color=CHECKBOX_HOVER_COLOR, 941 | command=self.add_chosen_theme) 942 | self.freemode_button_off.place(x=700, y=40) 943 | 944 | def toggle_sounds(self) -> None: 945 | """Creates a panel to manage a sound volume.""" 946 | self.toggle_sounds_frame = ctk.CTkFrame( 947 | self, 948 | fg_color=SWAMP_FOREGROUND, 949 | width=1185, 950 | height=100 951 | ) 952 | self.toggle_sounds_frame.grid( 953 | row=3, 954 | column=0, 955 | sticky='n', 956 | padx=20, 957 | pady=20 958 | ) 959 | self.draw_label( 960 | self.toggle_sounds_frame, 961 | 'Управление громкостью собеседования' 962 | ) 963 | self.draw_line(self.toggle_sounds_frame) 964 | 965 | self.sound_scale = ctk.CTkSlider( 966 | self.toggle_sounds_frame, 967 | orientation='horizontal', 968 | from_=0, 969 | to=100, 970 | variable=self.sound_volume, 971 | width=280, 972 | command=self.transfer_volume_number, 973 | button_color=CHECKBOX_HOVER_COLOR, 974 | button_hover_color=CHECKBOX_HOVER_COLOR, 975 | progress_color=CHECKBOX_HOVER_COLOR) 976 | self.sound_scale.place(x=420, y=40) 977 | self.sound_label = ctk.CTkLabel( 978 | self.toggle_sounds_frame, 979 | textvariable=self.sound_text 980 | ) 981 | self.sound_label.place(x=710, y=32) 982 | 983 | def transfer_volume_number(self, value) -> None: 984 | """Transferring a current volume to the main class.""" 985 | self.sound_text.set(f'Громкость: {self.sound_volume.get()}%') 986 | self.set_volume(round(int(self.sound_volume.get()) / 100, 1)) 987 | 988 | 989 | class InterviewPassTab(ctk.CTkFrame): 990 | """Class for interview passing.""" 991 | def __init__(self, parent, themes, 992 | database, show_hint_window, 993 | get_volume, set_volume, 994 | get_current_user, set_notebook_status, 995 | get_interview_mode, get_user_progress, 996 | update_progress): 997 | # Setup 998 | super().__init__(parent) 999 | self.width = 1200 1000 | self.place(x=0, y=0) 1001 | self.columnconfigure((0, 1), weight=1) 1002 | self.rowconfigure((0, 1), weight=1) 1003 | 1004 | # The outer functions 1005 | self.question_bank = database 1006 | self.themes = themes 1007 | self.show_hint_window = show_hint_window 1008 | self.get_volume = get_volume 1009 | self.set_volume = set_volume 1010 | self.get_current_user = get_current_user 1011 | self.set_notebook_status = set_notebook_status 1012 | self.get_interview_mode = get_interview_mode 1013 | self.get_user_progress = get_user_progress 1014 | self.update_progress = update_progress 1015 | 1016 | # Instance vars 1017 | self.current_user = None 1018 | self.interview_mode = {} 1019 | self.question_list = [] 1020 | self.user_progress = {} 1021 | self.questions_while_interviewing = deque() 1022 | self.pointer = 0 1023 | self.start_interview_time = datetime.datetime 1024 | self.stop_interview_time = datetime.datetime 1025 | self.button_text = ctk.StringVar(value='Начать собеседование') 1026 | self.question_key = None 1027 | 1028 | # Flags 1029 | self.is_interview_in_progress = False 1030 | 1031 | # Context menu for copying text from textbox 1032 | self.context_menu = tk.Menu(self, tearoff=0) 1033 | self.context_menu.add_command( 1034 | label="Копировать", 1035 | command=lambda: self.focus_get().event_generate("<>") 1036 | ) 1037 | 1038 | # Widgets creating 1039 | self.create_interview_frame() 1040 | self.create_control_frame() 1041 | self.create_treeview_frame() 1042 | 1043 | # Events 1044 | self.context_menu_event_loop(self.theory_textbox) 1045 | self.context_menu_event_loop(self.coding_textbox) 1046 | self.treeview_events() 1047 | 1048 | def create_interview_frame(self): 1049 | """Creates a panel with: 1050 | - start/stop interview button 1051 | - theory/livecoding textboxes 1052 | - buttons for managing sounds. 1053 | """ 1054 | self.interview_frame = ctk.CTkFrame( 1055 | self, 1056 | fg_color=ORANGE_FOREGROUND, 1057 | width=620, 1058 | height=400 1059 | ) 1060 | self.interview_frame.grid(row=0, column=0, padx=30, pady=10) 1061 | 1062 | # first row 1063 | self.begin_button_start = ctk.CTkImage( 1064 | light_image=Image.open('images/start.png').resize((30, 30)), 1065 | dark_image=Image.open('images/start.png').resize((30, 30)) 1066 | ) 1067 | self.begin_button_stop = ctk.CTkImage( 1068 | light_image=Image.open('images/stop.png').resize((30, 30)), 1069 | dark_image=Image.open('images/stop.png').resize((30, 30)) 1070 | ) 1071 | 1072 | self.begin_button = ctk.CTkButton( 1073 | master=self.interview_frame, 1074 | width=300, 1075 | height=40, 1076 | textvariable=self.button_text, 1077 | fg_color=BEGIN_BUTTON_FOREGROUND_COLOR, 1078 | text_color='black', 1079 | hover_color=BEGIN_BUTTON_HOVER_COLOR, 1080 | command=self.begin_interview, 1081 | image=self.begin_button_start) 1082 | self.begin_button.place(x=20, y=20) 1083 | 1084 | self.replay_button = ctk.CTkButton( 1085 | master=self.interview_frame, 1086 | width=150, 1087 | height=40, 1088 | text='Проиграть вопрос', 1089 | fg_color=SOUNDS_BUTTONS_FOREGROUND, 1090 | hover_color=SOUNDS_BUTTONS_HOVER, 1091 | command=self.speak_theory_question 1092 | ) 1093 | self.replay_button.place(x=400, y=20) 1094 | 1095 | self.mute_button_img_ON = ctk.CTkImage( 1096 | light_image=Image.open('images/sound_ON.png').resize((30, 30)), 1097 | dark_image=Image.open('images/sound_ON.png').resize((30, 30)) 1098 | ) 1099 | self.mute_button_img_OFF = ctk.CTkImage( 1100 | light_image=Image.open('images/sound_OFF.png').resize((30, 30)), 1101 | dark_image=Image.open('images/sound_OFF.png').resize((30, 30)) 1102 | ) 1103 | 1104 | self.mute_button = ctk.CTkButton( 1105 | master=self.interview_frame, 1106 | width=40, 1107 | height=40, 1108 | text='', 1109 | fg_color=SOUNDS_BUTTONS_FOREGROUND, 1110 | hover_color=SOUNDS_BUTTONS_HOVER, 1111 | image=self.mute_button_img_ON, 1112 | command=self.mute_sound) 1113 | self.mute_button.place(x=560, y=20) 1114 | 1115 | ctk.CTkLabel( 1116 | master=self.interview_frame, 1117 | text='Теоретический вопрос', 1118 | font=('Calibri', 25)).place(x=20, y=75) 1119 | 1120 | self.theory_textbox = ctk.CTkTextbox( 1121 | master=self.interview_frame, 1122 | width=580, 1123 | height=100, 1124 | font=('Calibri', 18) 1125 | ) 1126 | self.theory_textbox.place(x=20, y=120) 1127 | 1128 | ctk.CTkLabel( 1129 | master=self.interview_frame, 1130 | text='Live coding', 1131 | font=('Calibri', 25)).place(x=20, y=240) 1132 | 1133 | self.coding_button = ctk.CTkButton( 1134 | master=self.interview_frame, 1135 | width=150, 1136 | height=28, 1137 | text='Проиграть вопрос Live coding', 1138 | fg_color=SOUNDS_BUTTONS_FOREGROUND, 1139 | hover_color=SOUNDS_BUTTONS_HOVER, 1140 | command=self.speak_livecoding,) 1141 | self.coding_button.place(x=160, y=242) 1142 | 1143 | self.coding_textbox = ctk.CTkTextbox( 1144 | master=self.interview_frame, 1145 | width=580, 1146 | height=100, 1147 | font=('Calibri', 18) 1148 | ) 1149 | self.coding_textbox.place(x=20, y=280) 1150 | 1151 | def create_control_frame(self): 1152 | """Creates a panel with: 1153 | - correct answer button 1154 | - wrong answer button 1155 | - show the answer button. 1156 | """ 1157 | self.control_frame = ctk.CTkFrame( 1158 | self, 1159 | fg_color=ORANGE_FOREGROUND, 1160 | width=620, 1161 | height=200 1162 | ) 1163 | self.control_frame.grid(row=1, column=0) 1164 | 1165 | self.positive_button = ctk.CTkButton( 1166 | master=self.control_frame, 1167 | width=260, 1168 | height=70, 1169 | text='Я правильно ответил на вопрос', 1170 | fg_color=POSITIVE_BUTTON_FOREGROUND, 1171 | hover_color=POSITIVE_BUTTON_HOVER, 1172 | command=self.answer_correctly, 1173 | ) 1174 | self.positive_button.place(x=20, y=20) 1175 | 1176 | self.negative_button = ctk.CTkButton( 1177 | master=self.control_frame, 1178 | width=260, 1179 | height=70, 1180 | text='Я не знаю, следующий вопрос', 1181 | fg_color=NEGATIVE_BUTTON_FOREGROUND, 1182 | hover_color=NEGATIVE_BUTTON_HOVER, 1183 | command=self.answer_wrong, 1184 | ) 1185 | self.negative_button.place(x=340, y=20) 1186 | 1187 | self.answer_button = ctk.CTkButton( 1188 | master=self.control_frame, 1189 | width=580, 1190 | height=70, 1191 | text='Посмотреть ответ на вопрос', 1192 | fg_color=ANSWER_BUTTON_FOREGROUND, 1193 | hover_color=ANSWER_BUTTON_HOVER, 1194 | command=self.push_hint_button 1195 | ).place(x=20, y=110) 1196 | 1197 | def create_treeview_frame(self): 1198 | """Creates a question treeview.""" 1199 | # The frame which has all the widgets 1200 | self.control_frame = ctk.CTkFrame( 1201 | self, 1202 | fg_color=ORANGE_FOREGROUND, 1203 | width=530, 1204 | height=615 1205 | ) 1206 | self.control_frame.grid(row=0, column=1, rowspan=2, pady=10) 1207 | 1208 | # Question tree 1209 | self.question_tree = ttk.Treeview( 1210 | master=self.control_frame, 1211 | selectmode='none' 1212 | ) 1213 | 1214 | self.question_tree.heading( 1215 | '#0', 1216 | text='Темы и вопросы собеседования', 1217 | anchor=tk.W 1218 | ) 1219 | 1220 | # adding data 1221 | for theme_id, theme_title in self.themes.items(): 1222 | self.question_tree.insert( 1223 | '', 1224 | tk.END, 1225 | text=theme_title, 1226 | iid=theme_id, 1227 | open=False 1228 | ) 1229 | 1230 | # adding children of first node 1231 | for data in self.question_bank: 1232 | self.question_tree.insert( 1233 | '', 1234 | tk.END, 1235 | text=f'Вопрос {data[0] - 7}. {data[2]}', 1236 | iid=data[0], 1237 | open=False 1238 | ) 1239 | match data[0]: 1240 | case num if qt.BASIC_FIRST_QUESTION <= num <= qt.BASIC_LAST_QUESTION: 1241 | self.question_tree.move(data[0], 0, data[1]) 1242 | case num if qt.OOP_FIRST_QUESTION <= num <= qt.OOP_LAST_QUESTION: 1243 | self.question_tree.move(data[0], 1, data[1]) 1244 | case num if qt.PEP8_FIRST_QUESTION <= num <= qt.PEP8_LAST_QUESTION: 1245 | self.question_tree.move(data[0], 2, data[1]) 1246 | case num if qt.STRUCTURES_FIRST_QUESTION <= num <= qt.STRUCTURES_LAST_QUESTION: 1247 | self.question_tree.move(data[0], 3, data[1]) 1248 | case num if qt.ALGHORITMS_FIRST_QUESTION <= num <= qt.ALGHORITMS_LAST_QUESTION: 1249 | self.question_tree.move(data[0], 4, data[1]) 1250 | case num if qt.GIT_FIRST_QUESTION <= num <= qt.GIT_LAST_QUESTION: 1251 | self.question_tree.move(data[0], 5, data[1]) 1252 | case num if qt.SQL_FIRST_QUESTION <= num <= qt.SQL_LAST_QUESTION: 1253 | self.question_tree.move(data[0], 6, data[1]) 1254 | 1255 | self.question_tree.place(x=20, y=20, width=490, height=580) 1256 | 1257 | self.scroll_question_tree = ctk.CTkScrollbar( 1258 | master=self.control_frame, 1259 | orientation='vertical', 1260 | command=self.question_tree.yview) 1261 | self.question_tree.configure( 1262 | yscrollcommand=self.scroll_question_tree.set 1263 | ) 1264 | self.scroll_question_tree.place(x=500, y=20, relheight=0.945) 1265 | 1266 | self.style = ttk.Style() 1267 | self.style.configure('Treeview.Heading', font=('Calibri', 18)) 1268 | self.style.configure('Treeview', font=('Calibri', 12)) 1269 | 1270 | def begin_interview(self): 1271 | """Manages interview passing. 1272 | - Loads essential information 1273 | - Check user's existance 1274 | - Updates DB data. 1275 | """ 1276 | # Turn ON answers buttons 1277 | self.positive_button.configure(state='normal') 1278 | self.negative_button.configure(state='normal') 1279 | 1280 | # Loading data 1281 | self.current_user = self.get_current_user() 1282 | self.interview_mode = self.get_interview_mode() 1283 | 1284 | if not self.is_interview_in_progress: 1285 | self.start_interview_time = self.start_interview_time.today() 1286 | update_last_enter_date( 1287 | self.current_user, 1288 | self.start_interview_time 1289 | ) 1290 | self.begin_button.configure( 1291 | image=self.begin_button_stop 1292 | ) 1293 | self.button_text.set('Закончить собеседование') 1294 | self.is_interview_in_progress = True 1295 | self.set_notebook_status('disabled') 1296 | if not self.current_user: 1297 | CTkMessagebox( 1298 | title='Предупреждение', 1299 | message='Вы не выбрали пользователя. Статистика не ведется' 1300 | ) 1301 | self.question_tree.configure(selectmode='browse') 1302 | self.open_chosen_themes() 1303 | self.set_pointer_at_first_question() 1304 | else: 1305 | if self.interview_mode['Freemode']: 1306 | self.question_tree.configure(selectmode='browse') 1307 | else: 1308 | self.question_tree.configure(selectmode='none') 1309 | self.open_chosen_themes() 1310 | self.set_pointer_at_first_question() 1311 | else: 1312 | self.stop_interview() 1313 | 1314 | def stop_interview(self): 1315 | """Stops the interview updating user progress.""" 1316 | self.stop_interview_time = self.stop_interview_time.today() 1317 | self.update_interview_duration() 1318 | self.is_interview_in_progress = False 1319 | self.set_notebook_status('normal') 1320 | self.begin_button.configure(image=self.begin_button_start) 1321 | self.button_text.set('Начать собеседование') 1322 | self.question_tree.configure(selectmode='none') 1323 | self.question_list.clear() 1324 | self.questions_while_interviewing.clear() 1325 | self.positive_button.configure(state='disabled') 1326 | self.negative_button.configure(state='disabled') 1327 | if self.current_user: 1328 | self.update_progress() 1329 | 1330 | def update_interview_duration(self): 1331 | """Updates an interview duration on DB.""" 1332 | if self.current_user: 1333 | initial_duration = get_user_interview_duration(self.current_user) 1334 | seconds_left = count_interview_duration( 1335 | self.start_interview_time, 1336 | self.stop_interview_time 1337 | ) 1338 | result_duration = initial_duration + seconds_left 1339 | update_interview_duration(self.current_user, result_duration) 1340 | 1341 | def open_chosen_themes(self): 1342 | """Opens the themes in the question tree which were chosen.""" 1343 | for theme in self.question_tree.get_children(): 1344 | self.question_tree.item(theme, open=False) 1345 | themes_status = tuple(self.interview_mode.values())[:7] 1346 | open_themes = [ 1347 | theme_index 1348 | for theme_index, is_chosen in enumerate(themes_status) if is_chosen 1349 | ] 1350 | if not open_themes: 1351 | CTkMessagebox( 1352 | title='Ошибка', 1353 | message="Вы не выбрали ни одной темы", 1354 | icon="cancel", 1355 | option_1="Отлично" 1356 | ) 1357 | self.stop_interview() 1358 | for theme in open_themes: 1359 | self.question_tree.item(theme, open=True) 1360 | self.generate_question_list(open_themes) 1361 | 1362 | def generate_question_list(self, open_themes): 1363 | """Generares a question list for this session.""" 1364 | self.user_progress = self.get_user_progress() 1365 | user_right_answer = { 1366 | question_number 1367 | for question_number, is_right 1368 | in self.user_progress.items() if is_right 1369 | } 1370 | 1371 | for theme in open_themes: 1372 | match theme: 1373 | case 0: 1374 | self.question_list += [ 1375 | question_number 1376 | for question_number 1377 | in range(qt.BASIC_FIRST_QUESTION, 1378 | qt.BASIC_LAST_QUESTION + 1) 1379 | ] 1380 | case 1: 1381 | self.question_list += [ 1382 | question_number 1383 | for question_number 1384 | in range(qt.OOP_FIRST_QUESTION, 1385 | qt.OOP_LAST_QUESTION + 1) 1386 | ] 1387 | case 2: 1388 | self.question_list += [ 1389 | question_number 1390 | for question_number 1391 | in range(qt.PEP8_FIRST_QUESTION, 1392 | qt.PEP8_LAST_QUESTION + 1) 1393 | ] 1394 | case 3: 1395 | self.question_list += [ 1396 | question_number 1397 | for question_number 1398 | in range(qt.STRUCTURES_FIRST_QUESTION, 1399 | qt.STRUCTURES_LAST_QUESTION + 1) 1400 | ] 1401 | case 4: 1402 | self.question_list += [ 1403 | question_number 1404 | for question_number 1405 | in range(qt.ALGHORITMS_FIRST_QUESTION, 1406 | qt.ALGHORITMS_LAST_QUESTION + 1) 1407 | ] 1408 | case 5: 1409 | self.question_list += [ 1410 | question_number 1411 | for question_number 1412 | in range(qt.GIT_FIRST_QUESTION, 1413 | qt.GIT_LAST_QUESTION + 1) 1414 | ] 1415 | case 6: 1416 | self.question_list += [ 1417 | question_number 1418 | for question_number 1419 | in range(qt.SQL_FIRST_QUESTION, 1420 | qt.SQL_LAST_QUESTION + 1) 1421 | ] 1422 | self.question_list = [ 1423 | question_number 1424 | for question_number 1425 | in self.question_list if question_number not in user_right_answer 1426 | ] 1427 | if self.interview_mode['Random']: 1428 | random.shuffle(self.question_list) 1429 | 1430 | # CORRECT OR WRONG ANSWER SECTION 1431 | def answer_correctly(self): 1432 | """Manages user progress when 1433 | user has answered correctrly. 1434 | """ 1435 | try: 1436 | if not self.interview_mode['Freemode']: 1437 | self.turn_to_green() 1438 | index = self.questions_while_interviewing.popleft() 1439 | self.question_tree.selection_set( 1440 | (str(self.questions_while_interviewing[0]), ) 1441 | ) 1442 | self.question_tree.see( 1443 | (str(self.questions_while_interviewing[0]), ) 1444 | ) 1445 | self.speak_theory_question() 1446 | self.user_progress[index] = True 1447 | update_user_progress(self.current_user, self.user_progress) 1448 | else: 1449 | self.turn_to_green() 1450 | self.user_progress[self.question_key + 8] = True 1451 | update_user_progress(self.current_user, self.user_progress) 1452 | except IndexError: 1453 | self.stop_interview() 1454 | CTkMessagebox( 1455 | title='Вы ответили на все вопросы', 1456 | message="Поздравляем! Вы ответили на все вопросы данной темы", 1457 | icon="check", 1458 | option_1="Отлично" 1459 | ) 1460 | 1461 | def answer_wrong(self): 1462 | """Manages user progress when 1463 | user has answered wrong. 1464 | """ 1465 | if not self.interview_mode['Freemode']: 1466 | self.turn_to_red() 1467 | self.questions_while_interviewing.rotate(-1) 1468 | self.question_tree.selection_set( 1469 | (str(self.questions_while_interviewing[0]), ) 1470 | ) 1471 | self.question_tree.see( 1472 | (str(self.questions_while_interviewing[0]), ) 1473 | ) 1474 | self.speak_theory_question() 1475 | else: 1476 | self.turn_to_red() 1477 | 1478 | def set_pointer_at_first_question(self): 1479 | """Shows the first question when interview has started.""" 1480 | for question_number in self.question_list: 1481 | self.questions_while_interviewing.append(question_number) 1482 | try: 1483 | self.question_tree.selection_set( 1484 | (str(self.questions_while_interviewing[0]), ) 1485 | ) 1486 | self.question_tree.see( 1487 | (str(self.questions_while_interviewing[0]), ) 1488 | ) 1489 | self.speak_theory_question() 1490 | except IndexError: 1491 | pass 1492 | 1493 | def set_color_for_user_progress(self): 1494 | """Turns to green or red user's answer.""" 1495 | # Set zero (white) color in everywhere 1496 | for question_number in range(qt.BASIC_FIRST_QUESTION, 1497 | qt.SQL_LAST_QUESTION + 1): 1498 | self.question_tree.item( 1499 | question_number, 1500 | tags=(WHITE, ), 1501 | values=(WHITE, ) 1502 | ) 1503 | self.question_tree.tag_configure(WHITE, background=WHITE) 1504 | 1505 | # Get user progress 1506 | self.user_progress = self.get_user_progress() 1507 | 1508 | # Get color of questions according user progress 1509 | for question_number, is_right in self.user_progress.items(): 1510 | if is_right: 1511 | self.question_tree.item( 1512 | question_number, 1513 | tags=(GREEN, ), values=(GREEN, ) 1514 | ) 1515 | self.question_tree.tag_configure(GREEN, background=GREEN) 1516 | 1517 | def turn_to_green(self): 1518 | """Turns user's answer to green.""" 1519 | if isinstance(self.question_key, int): 1520 | self.question_tree.item( 1521 | self.question_key + 8, 1522 | tags=(GREEN, ), 1523 | values=(GREEN, ) 1524 | ) 1525 | self.question_tree.tag_configure(GREEN, background=GREEN) 1526 | 1527 | def turn_to_red(self): 1528 | """Turns user's answer to red.""" 1529 | if isinstance(self.question_key, int): 1530 | self.question_tree.item( 1531 | self.question_key + 8, tags=(RED, ), values=(RED, ) 1532 | ) 1533 | self.question_tree.tag_configure(RED, background=RED) 1534 | 1535 | def push_hint_button(self): 1536 | """Shows the PDF-file with correct answer.""" 1537 | if isinstance(self.question_key, int): 1538 | self.show_hint_window( 1539 | filepath=( 1540 | f'knowledge/{self.question_bank[self.question_key][5]}.pdf' 1541 | ), 1542 | page_number=self.question_bank[self.question_key][6] 1543 | ) 1544 | 1545 | # SOUNDS AND VOLUME SECTION 1546 | def mute_sound(self): 1547 | """Mutes every sounds in the app.""" 1548 | if self.get_volume(): 1549 | self.set_volume(0) 1550 | else: 1551 | self.set_volume(0.5) 1552 | 1553 | def prepare_livecoding(self): 1554 | """Prepares the text from livecoding textbox to speech (TTS).""" 1555 | try: 1556 | if self.get_volume(): 1557 | engine = pyttsx3.init() 1558 | engine.setProperty('volume', self.get_volume()) 1559 | engine.say(self.question_bank[ 1560 | self.questions_while_interviewing[0] - 8][4]) 1561 | engine.runAndWait() 1562 | except RuntimeError: 1563 | pass 1564 | 1565 | def prepare_theory_question(self): 1566 | """Prepares the text from theory textbox to speech (TTS).""" 1567 | try: 1568 | if self.get_volume(): 1569 | engine = pyttsx3.init() 1570 | engine.setProperty('volume', self.get_volume()) 1571 | engine.say(self.question_bank[ 1572 | self.questions_while_interviewing[0] - 8][3]) 1573 | engine.runAndWait() 1574 | except RuntimeError: 1575 | pass 1576 | 1577 | def speak_theory_question(self): 1578 | """Plays theory question.""" 1579 | if platform.system() == 'Windows': 1580 | threading.Thread(target=self.prepare_theory_question).start() 1581 | 1582 | def speak_livecoding(self): 1583 | """Plays livecoding question.""" 1584 | if platform.system() == 'Windows': 1585 | threading.Thread(target=self.prepare_livecoding).start() 1586 | 1587 | # EVENTS SECTION 1588 | def context_menu_event_loop(self, text_box): 1589 | """Allows to use CTRL-C or RMB to copy the text from a textbox.""" 1590 | text_box.bind( 1591 | "", 1592 | lambda event: self.context_menu.post(event.x_root, event.y_root) 1593 | ) 1594 | text_box.bind( 1595 | "", 1596 | lambda event: self.copy_text 1597 | ) 1598 | 1599 | def treeview_events(self): 1600 | """Allows to select items for question treeview.""" 1601 | self.question_tree.bind('<>', self.item_select) 1602 | 1603 | def insert_question_in_textfield(self, question_key): 1604 | """Inserts the questions to the textboxes.""" 1605 | if question_key is not None: 1606 | self.theory_textbox.delete('1.0', 'end') 1607 | self.coding_textbox.delete('1.0', 'end') 1608 | self.theory_textbox.insert('1.0', 1609 | self.question_bank[question_key][3]) 1610 | self.coding_textbox.insert('1.0', 1611 | self.question_bank[question_key][4]) 1612 | else: 1613 | self.theory_textbox.delete('1.0', 'end') 1614 | self.coding_textbox.delete('1.0', 'end') 1615 | 1616 | def item_select(self, event): 1617 | """Allows to select items from question tree.""" 1618 | for i in self.question_tree.selection(): 1619 | self.question_key = ( 1620 | self.question_tree.item(i)['text'].split( 1621 | '. ')[0].strip('Вопрос ') 1622 | ) 1623 | self.question_key = ( 1624 | int(self.question_key) - 1 1625 | if self.question_key.isdigit() else None 1626 | ) 1627 | self.insert_question_in_textfield(self.question_key) 1628 | 1629 | def copy_text(event): 1630 | """Allows to copy a text from textboxes.""" 1631 | widget = event.widget 1632 | selected_text = widget.clipboard_get() 1633 | if widget.tag_ranges("sel"): 1634 | selected_text = widget.get("sel.first", "sel.last") 1635 | widget.clipboard_clear() 1636 | widget.clipboard_append(selected_text) 1637 | 1638 | 1639 | class CreateNewUser(ctk.CTkToplevel): 1640 | """Class for a window creating a new user.""" 1641 | def __init__(self, title, update_combobox): 1642 | # Setup 1643 | super().__init__() 1644 | self.title(title) 1645 | self.geometry('390x160') 1646 | self.resizable(False, False) 1647 | if platform.system() == 'Windows' and sys.platform.startswith("win"): 1648 | self.iconbitmap(default='images/icon.ico') 1649 | self.after(200, lambda: self.iconbitmap("images/icon.ico")) 1650 | self.update_combobox = update_combobox 1651 | 1652 | # Vars 1653 | self.user_name = ctk.StringVar() 1654 | self.error_message = ctk.StringVar(value='') 1655 | 1656 | # Widgets 1657 | self.frame = ctk.CTkFrame( 1658 | self, 1659 | width=350, 1660 | height=110, 1661 | fg_color=NEW_USER_WINDOW_FOREGROUND 1662 | ) 1663 | self.frame.pack(side='top', expand=True, fill='both', padx=10, pady=10) 1664 | self.frame.rowconfigure((0, 1, 2, 3), weight=1) 1665 | self.frame.columnconfigure((0, 1), weight=1) 1666 | 1667 | self.label = ctk.CTkLabel( 1668 | self.frame, 1669 | text='Создайте имя пользователя:' 1670 | ) 1671 | self.label.grid(row=0, column=0, sticky='ws', padx=10) 1672 | self.enter = ctk.CTkEntry( 1673 | self.frame, 1674 | width=350, 1675 | textvariable=self.user_name 1676 | ) 1677 | self.enter.grid(row=1, column=0, sticky='wn', padx=10, columnspan=2) 1678 | self.error_label = ttk.Label( 1679 | self.frame, 1680 | textvariable=self.error_message, 1681 | background=ERROR_COLOR 1682 | ) 1683 | self.error_label.grid( 1684 | row=2, 1685 | column=0, 1686 | columnspan=2, 1687 | sticky='wn', 1688 | padx=10 1689 | ) 1690 | self.save_button = ctk.CTkButton( 1691 | self.frame, 1692 | text='Создать', 1693 | command=self.add_to_db 1694 | ) 1695 | self.save_button.grid(row=3, column=0, sticky='wn', padx=10) 1696 | self.cancel_button = ctk.CTkButton( 1697 | self.frame, 1698 | text='Отмена', 1699 | command=self.close_the_window 1700 | ) 1701 | self.cancel_button.grid(row=3, column=1, sticky='en', padx=10) 1702 | 1703 | def close_the_window(self): 1704 | """Destroys the window.""" 1705 | self.destroy() 1706 | 1707 | def add_to_db(self): 1708 | """Adds a new user to DB provided he passes a validation.""" 1709 | current_user = self.user_name.get() 1710 | if is_name_empty(current_user): 1711 | self.error_label.config(background='red') 1712 | self.error_message.set(ValidResponse.EMPTY_NAME) 1713 | self.set_timer(3) 1714 | elif is_name_too_short(current_user): 1715 | self.error_label.config(background='red') 1716 | self.error_message.set(ValidResponse.SHORT_NAME) 1717 | self.set_timer(3) 1718 | elif is_name_too_long(current_user): 1719 | self.error_label.config(background='red') 1720 | self.error_message.set(ValidResponse.NAME_TOO_LONG) 1721 | self.set_timer(3) 1722 | elif has_name_first_wrong_symbol(current_user): 1723 | self.error_label.config(background='red') 1724 | self.error_message.set(ValidResponse.WRONG_FIRST_SYMBOL) 1725 | self.set_timer(3) 1726 | elif has_name_wrong_symbols(current_user): 1727 | self.error_label.config(background='red') 1728 | self.error_message.set(ValidResponse.WRONG_SYMBOLS) 1729 | self.set_timer(3) 1730 | elif is_user_already_exists(current_user): 1731 | self.error_label.config(background='red') 1732 | self.error_message.set(ValidResponse.USER_ALREADY_EXISTS) 1733 | self.set_timer(3) 1734 | else: 1735 | create_new_user(self.user_name.get()) 1736 | self.update_combobox() 1737 | CommandTimer(1, self.destroy, self.error_label, self.error_message) 1738 | 1739 | def set_timer(self, delay): 1740 | """Creates a delay for showing message.""" 1741 | MessageTimer(delay, self.error_message, self.error_label) 1742 | 1743 | 1744 | class HintWindow(ctk.CTkToplevel): 1745 | """Class for a new window showing a right answer.""" 1746 | def __init__(self, title, filepath, current_page): 1747 | # Setup 1748 | super().__init__() 1749 | self.title(title) 1750 | self.geometry('900x800+440+180') 1751 | self.resizable(False, False) 1752 | if platform.system() == 'Windows' and sys.platform.startswith("win"): 1753 | self.iconbitmap('images/icon.ico') 1754 | self.after(200, lambda: self.iconbitmap("images/icon.ico")) 1755 | self.rowconfigure((0, 1), weight=1) 1756 | self.columnconfigure((0, 1), weight=1) 1757 | 1758 | # The outer functions 1759 | self.file = filepath 1760 | self.current_page = current_page 1761 | 1762 | # Vars 1763 | self.numPages = None 1764 | self.pages_amount = ctk.StringVar() 1765 | 1766 | # Top Frame 1767 | self.top_frame = ctk.CTkFrame(self, width=850, height=700) 1768 | self.top_frame.grid(row=0, column=0) 1769 | 1770 | # Bottom Frame 1771 | self.bottom_frame = ctk.CTkFrame( 1772 | master=self, 1773 | width=580, 1774 | height=50, 1775 | fg_color='transparent' 1776 | ) 1777 | self.bottom_frame.grid(row=1, column=0) 1778 | self.bottom_frame.rowconfigure((0,), weight=1) 1779 | self.bottom_frame.columnconfigure((0, 1, 2), weight=1) 1780 | 1781 | # Vertical Scrolbar 1782 | self.scrolly = ctk.CTkScrollbar(self.top_frame, orientation='vertical') 1783 | self.scrolly.grid(row=0, column=1, sticky='ns') 1784 | 1785 | # Show PDF 1786 | self.output = ctk.CTkCanvas( 1787 | self.top_frame, 1788 | bg=PDF_OUTPUT_COLOR, 1789 | width=880, 1790 | height=700 1791 | ) 1792 | self.output.configure(yscrollcommand=self.scrolly.set) 1793 | self.output.grid(row=0, column=0) 1794 | self.scrolly.configure(command=self.output.yview) 1795 | self.output.bind( 1796 | '', lambda event: self.output.yview_scroll( 1797 | -1*(event.delta//120), "units") 1798 | ) 1799 | 1800 | # Buttons and page label 1801 | self.upbutton = ctk.CTkButton( 1802 | master=self.bottom_frame, 1803 | text='Предыдущая страница', 1804 | command=self.previous_page) 1805 | self.upbutton.grid(row=0, column=0, padx=5, pady=5) 1806 | self.downbutton = ctk.CTkButton( 1807 | master=self.bottom_frame, 1808 | text='Следующая страница', 1809 | command=self.next_page 1810 | ) 1811 | self.downbutton.grid(row=0, column=1, pady=5) 1812 | self.page_label = ctk.CTkLabel( 1813 | master=self.bottom_frame, 1814 | textvariable=self.pages_amount 1815 | ) 1816 | self.page_label.grid(row=0, column=2, padx=5) 1817 | 1818 | if self.file: 1819 | self.miner = PDFMiner(self.file) 1820 | data, numPages = self.miner.get_metadata() 1821 | if numPages: 1822 | self.numPages = numPages 1823 | self.display_page() 1824 | 1825 | def display_page(self): 1826 | """Shows a particular page.""" 1827 | if 0 <= self.current_page < self.numPages: 1828 | self.img_file = self.miner.get_page(self.current_page) 1829 | self.output.create_image(0, 0, anchor='nw', image=self.img_file) 1830 | self.stringified_current_page = self.current_page + 1 1831 | self.pages_amount.set( 1832 | f'Страница: {self.stringified_current_page} из {self.numPages}' 1833 | ) 1834 | region = self.output.bbox(tk.ALL) 1835 | self.output.configure(scrollregion=region) 1836 | 1837 | def next_page(self): 1838 | """Turns to the next page.""" 1839 | if self.current_page <= self.numPages - 1: 1840 | self.current_page += 1 1841 | self.display_page() 1842 | 1843 | def previous_page(self): 1844 | """Turns to the previous page.""" 1845 | if self.current_page > 0: 1846 | self.current_page -= 1 1847 | self.display_page() 1848 | 1849 | 1850 | class PDFMiner: 1851 | """Class for rendering PDF-files.""" 1852 | def __init__(self, filepath): 1853 | self.filepath = filepath 1854 | self.pdf = fitz.open(self.filepath) 1855 | self.first_page = self.pdf.load_page(0) 1856 | self.width, self.height = ( 1857 | self.first_page.rect.width, 1858 | self.first_page.rect.height 1859 | ) 1860 | self.zoom = 1.5 1861 | 1862 | def get_metadata(self): 1863 | metadata = self.pdf.metadata 1864 | numPages = self.pdf.page_count 1865 | return metadata, numPages 1866 | 1867 | def get_page(self, page_num): 1868 | page = self.pdf.load_page(page_num) 1869 | if self.zoom: 1870 | mat = fitz.Matrix(self.zoom, self.zoom) 1871 | pix = page.get_pixmap(matrix=mat) 1872 | else: 1873 | pix = page.get_pixmap() 1874 | px1 = fitz.Pixmap(pix, 0) if pix.alpha else pix 1875 | imgdata = px1.tobytes("ppm") 1876 | return PhotoImage(data=imgdata) 1877 | 1878 | def get_text(self, page_num): 1879 | page = self.pdf.load_page(page_num) 1880 | text = page.getText('text') 1881 | return text 1882 | 1883 | 1884 | if __name__ == '__main__': 1885 | create_db() 1886 | ctk.set_appearance_mode("Light") 1887 | main_window = Main(APP_NAME, APP_RESOLUTION) 1888 | main_window.mainloop() 1889 | -------------------------------------------------------------------------------- /manage_db.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import json 3 | 4 | from sqlalchemy import delete, insert, select, update 5 | 6 | from models import engine, User 7 | from settings import QuestionThreshold as qt 8 | 9 | 10 | # user_name column 11 | def create_new_user(user_name: str) -> None: 12 | with engine.connect() as conn: 13 | conn.execute(insert(User).values( 14 | user_name=user_name, 15 | interviews_duration=0, 16 | progress=_get_zero_progress() 17 | ) 18 | ) 19 | conn.commit() 20 | 21 | 22 | def get_user_names() -> list[str]: 23 | names = get_users_list() 24 | return [person for name in names for person in name] 25 | 26 | 27 | def get_users_list() -> list[tuple[str]]: 28 | with engine.connect() as conn: 29 | result = conn.execute(select(User.user_name)) 30 | conn.commit() 31 | return result.all() 32 | 33 | 34 | def delete_this_user(user_name: str) -> None: 35 | with engine.connect() as conn: 36 | conn.execute(delete(User).where(User.user_name == user_name)) 37 | conn.commit() 38 | 39 | 40 | # last_enter_date Column 41 | def get_last_enter_date(user_name: str) -> datetime.datetime: 42 | with engine.connect() as conn: 43 | result = conn.execute( 44 | select(User.last_enter_date).where( 45 | User.user_name == user_name)).first() 46 | conn.commit() 47 | return result[0] 48 | 49 | 50 | def update_last_enter_date(user_name: str, date) -> None: 51 | with engine.connect() as conn: 52 | conn.execute( 53 | update(User).where( 54 | User.user_name == user_name).values(last_enter_date=date)) 55 | conn.commit() 56 | 57 | 58 | # interview_duration Column 59 | def get_user_interview_duration(user_name: str) -> int: 60 | with engine.connect() as conn: 61 | interview_duration = conn.execute( 62 | select(User.interviews_duration 63 | ).where(User.user_name == user_name) 64 | ).first() 65 | conn.commit() 66 | return int(interview_duration[0]) 67 | 68 | 69 | def update_interview_duration(user_name: str, duration) -> None: 70 | with engine.connect() as conn: 71 | conn.execute( 72 | update(User).where( 73 | User.user_name == user_name).values( 74 | interviews_duration=duration 75 | ) 76 | ) 77 | conn.commit() 78 | 79 | 80 | # progress Column 81 | def get_user_progress( 82 | user_name: str) -> dict[int, bool]: 83 | progress = json.loads(load_user_progress(user_name)) 84 | return { 85 | int(question_number): is_rigth 86 | for question_number, is_rigth in progress.items() 87 | } 88 | 89 | 90 | def load_user_progress(user_name: str) -> str: 91 | with engine.connect() as conn: 92 | result = conn.execute(select(User.progress).where( 93 | User.user_name == user_name)) 94 | conn.commit() 95 | return result.all()[0][0] 96 | 97 | 98 | def update_user_progress(user_name: str, progress: dict) -> None: 99 | with engine.connect() as conn: 100 | conn.execute( 101 | update(User).where( 102 | User.user_name == user_name).values( 103 | progress=json.dumps(progress))) 104 | conn.commit() 105 | 106 | 107 | # Support functions 108 | def _get_zero_progress() -> str: 109 | return json.dumps(_create_zero_progress()) 110 | 111 | 112 | def _create_zero_progress() -> dict[int, bool]: 113 | return { 114 | question_number: False for question_number 115 | in range(qt.BASIC_FIRST_QUESTION, qt.SQL_LAST_QUESTION + 1) 116 | } 117 | -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Type 3 | 4 | from sqlalchemy import create_engine 5 | from sqlalchemy import DateTime, Integer, JSON, String 6 | from sqlalchemy import MetaData 7 | from sqlalchemy.orm import DeclarativeBase 8 | from sqlalchemy.orm import Mapped 9 | from sqlalchemy.orm import mapped_column 10 | 11 | from settings import DATABASE_NAME 12 | 13 | engine = create_engine(f'sqlite:///{DATABASE_NAME}', echo=False) 14 | metadata = MetaData() 15 | 16 | 17 | class Base(DeclarativeBase): 18 | pass 19 | 20 | 21 | class User(Base): 22 | """A class representing user's data in the app. 23 | 24 | Attributes: 25 | __tablename__ (str): The name of the table in the database. 26 | id: The unique identifier of the user. 27 | user_name: The name of the user. 28 | last_enter_date: The date and time of the user's last login. 29 | interviews_duration: The total duration of interviews 30 | for the user in seconds. 31 | progress: The user's progress data stored in JSON format. 32 | """ 33 | __tablename__ = 'users' 34 | 35 | id: Mapped[int] = mapped_column(primary_key=True) 36 | user_name: Mapped[str] = mapped_column(String(25)) 37 | last_enter_date: Mapped[Type] = mapped_column(DateTime, nullable=True) 38 | interviews_duration: Mapped[int] = mapped_column(Integer) 39 | progress: Mapped[Type] = mapped_column(JSON) 40 | 41 | 42 | def create_db() -> None: 43 | """Creates database as a SQLite-file""" 44 | if not _is_db_created(): 45 | Base.metadata.create_all(engine) 46 | 47 | 48 | def _is_db_created() -> bool: 49 | """Checks DB existence in the root folder.""" 50 | return os.path.exists(f'./{DATABASE_NAME}') 51 | -------------------------------------------------------------------------------- /my_timers.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from threading import Timer 3 | from colors import ERROR_COLOR, SUCCESS_COLOR 4 | 5 | from settings import ValidResponse 6 | 7 | 8 | class MyTimerInterface(ABC): 9 | """Abstract class for custom timers.""" 10 | 11 | @abstractmethod 12 | def timeout(self): 13 | """Performs user's commands when time has ended.""" 14 | pass 15 | 16 | 17 | class MessageTimer(MyTimerInterface): 18 | """Class for changing an error message when time has ended.""" 19 | 20 | def __init__(self, delay, condition, label): 21 | self.delay = Timer(delay, self.timeout) 22 | self.condition = condition 23 | self.label = label 24 | self.delay.start() 25 | 26 | def timeout(self): 27 | self.condition.set('') 28 | self.label.config(background=ERROR_COLOR) 29 | 30 | 31 | class CommandTimer(MyTimerInterface): 32 | """Class for performing of command when time has ended.""" 33 | 34 | def __init__(self, delay, command, label, message): 35 | self.delay = Timer(delay, self.timeout) 36 | self.command = command 37 | self.label = label 38 | self.message = message 39 | self.delay.start() 40 | self.message.set(ValidResponse.SUCCESS) 41 | self.label.config(background=SUCCESS_COLOR) 42 | 43 | def timeout(self): 44 | self.command() 45 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.17.4 2 | comtypes==1.2.0 3 | CTkMessagebox==2.5 4 | customtkinter==5.2.1 5 | darkdetect==0.8.0 6 | greenlet==3.0.1 7 | packaging==23.2 8 | pefile==2023.2.7 9 | Pillow==10.1.0 10 | pyinstaller==6.2.0 11 | pyinstaller-hooks-contrib==2023.10 12 | PyMuPDF==1.23.6 13 | PyMuPDFb==1.23.6 14 | pypiwin32==223; sys_platform == 'win32' 15 | pyttsx3==2.90; sys_platform == 'win32' 16 | pywin32==306; sys_platform == 'win32' 17 | pywin32-ctypes==0.2.2 18 | setuptools==69.0.2 19 | SQLAlchemy==2.0.23 20 | typing_extensions==4.8.0 21 | 22 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | # App setup 4 | APP_NAME = 'Python Interview Assistant' 5 | APP_RESOLUTION = (1280, 720) 6 | CREATE_USER_WINDOW = 'Добавить пользователя' 7 | HINT_WINDOW_TITLE = 'Подсказка' 8 | 9 | # Validator section 10 | WRONG_SYMBOLS = ( 11 | '#', '@', '!', '?', '<', '>', '/', 12 | '|', '$', '^', '*', '(', ')', '+', '-', '=' 13 | ) 14 | 15 | # Database name 16 | DATABASE_NAME = 'users.db' 17 | 18 | class ValidResponse(str, Enum): 19 | SUCCESS = '*Пользователь успешно создан' 20 | EMPTY_NAME = '*Имя пользователя не может быть пустой строкой' 21 | SHORT_NAME = '*Имя должно состоять минимум из двух символов' 22 | WRONG_FIRST_SYMBOL = '*Имя должно начинаться с буквы' 23 | WRONG_SYMBOLS = '*Имя содержит недопустимые символы' 24 | NAME_TOO_LONG = '*Имя должно содержать не более 25 символов' 25 | USER_ALREADY_EXISTS = '*Пользователь с таким именем уже существует' 26 | 27 | 28 | class Theme(str, Enum): 29 | BASICS = 'Базовый синтаксис Python' 30 | OOP = 'Объекто-ориентированное программирование (ООП)' 31 | PEP8 = 'Правила оформления кода (PEP8, PEP257)' 32 | STRUCTURES = 'Структуры данных на Python' 33 | ALGHORITMS = 'Алгоритмы на Python' 34 | GIT = 'Git' 35 | SQL = 'Базы данных и SQL запросы' 36 | 37 | 38 | class QuestionThreshold(int, Enum): 39 | BASIC_FIRST_QUESTION = 8 40 | BASIC_LAST_QUESTION = 224 41 | 42 | OOP_FIRST_QUESTION = 225 43 | OOP_LAST_QUESTION = 335 44 | 45 | PEP8_FIRST_QUESTION = 336 46 | PEP8_LAST_QUESTION = 363 47 | 48 | STRUCTURES_FIRST_QUESTION = 364 49 | STRUCTURES_LAST_QUESTION = 433 50 | 51 | ALGHORITMS_FIRST_QUESTION = 434 52 | ALGHORITMS_LAST_QUESTION = 473 53 | 54 | GIT_FIRST_QUESTION = 474 55 | GIT_LAST_QUESTION = 538 56 | 57 | SQL_FIRST_QUESTION = 539 58 | SQL_LAST_QUESTION = 597 59 | -------------------------------------------------------------------------------- /user_statistics.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from typing import TypedDict 3 | 4 | from settings import QuestionThreshold as qt 5 | 6 | 7 | class StatInformation(TypedDict): 8 | right_answers_amount: str 9 | percentage_completion: str 10 | basic_progress: float 11 | oop_progress: float 12 | pep_progress: float 13 | structures_progress: float 14 | alghorimts_progress: float 15 | git_progress: float 16 | sql_progress: float 17 | 18 | 19 | def get_right_answers_amount(progress: dict) -> StatInformation: 20 | # Summary progress 21 | right_answers_amount = len([right for right in progress.values() if right]) 22 | amount_of_answers = max(progress) - 7 23 | percentage_completion = ( 24 | f'{round(100 * right_answers_amount / amount_of_answers, 1)}%' 25 | ) 26 | 27 | # Patricular progress 28 | basic_amount = qt.BASIC_LAST_QUESTION - qt.BASIC_FIRST_QUESTION 29 | oop_amount = qt.OOP_LAST_QUESTION - qt.OOP_FIRST_QUESTION 30 | pep_amount = qt.PEP8_LAST_QUESTION - qt.PEP8_FIRST_QUESTION 31 | structures_amount = ( 32 | qt.STRUCTURES_LAST_QUESTION - qt.STRUCTURES_FIRST_QUESTION 33 | ) 34 | alghorimts_amount = ( 35 | qt.ALGHORITMS_LAST_QUESTION - qt.ALGHORITMS_FIRST_QUESTION 36 | ) 37 | git_amount = qt.GIT_LAST_QUESTION - qt.GIT_FIRST_QUESTION 38 | sql_amount = qt.SQL_LAST_QUESTION - qt.SQL_FIRST_QUESTION 39 | 40 | basic_progress = get_paticular_progress( 41 | progress, basic_amount, qt.BASIC_FIRST_QUESTION, qt.BASIC_LAST_QUESTION 42 | ) 43 | oop_progress = get_paticular_progress( 44 | progress, oop_amount, qt.OOP_FIRST_QUESTION, qt.OOP_LAST_QUESTION 45 | ) 46 | pep_progress = get_paticular_progress( 47 | progress, pep_amount, qt.PEP8_FIRST_QUESTION, qt.PEP8_LAST_QUESTION 48 | ) 49 | structures_progress = get_paticular_progress( 50 | progress, structures_amount, 51 | qt.STRUCTURES_FIRST_QUESTION, 52 | qt.STRUCTURES_LAST_QUESTION 53 | ) 54 | alghorimts_progress = get_paticular_progress( 55 | progress, 56 | alghorimts_amount, 57 | qt.ALGHORITMS_FIRST_QUESTION, 58 | qt.ALGHORITMS_LAST_QUESTION 59 | ) 60 | git_progress = get_paticular_progress( 61 | progress, git_amount, qt.GIT_FIRST_QUESTION, qt.GIT_LAST_QUESTION 62 | ) 63 | sql_progress = get_paticular_progress( 64 | progress, sql_amount, qt.SQL_FIRST_QUESTION, qt.SQL_LAST_QUESTION 65 | ) 66 | 67 | return StatInformation( 68 | right_answers_amount=f'{right_answers_amount} из {amount_of_answers}', 69 | percentage_completion=percentage_completion, 70 | basic_progress=basic_progress, 71 | oop_progress=oop_progress, 72 | pep_progress=pep_progress, 73 | structures_progress=structures_progress, 74 | alghorimts_progress=alghorimts_progress, 75 | git_progress=git_progress, 76 | sql_progress=sql_progress, 77 | ) 78 | 79 | 80 | def get_last_enter_message(date) -> str: 81 | return ( 82 | f'{date.day}.{date.month}.{date.year}' 83 | if type(date) is datetime.datetime 84 | else 'не было' 85 | ) 86 | 87 | 88 | def get_paticular_progress( 89 | user_progress: dict, amount_kind: int, from_: int, to_: int) -> float: 90 | return round( 91 | len( 92 | [is_right for question_number, is_right in user_progress.items() 93 | if question_number 94 | in range(from_, to_) and is_right]) / amount_kind, 1 95 | ) 96 | 97 | 98 | def count_interview_duration(start_date, stop_date) -> int: 99 | time_difference = stop_date - start_date 100 | difference_in_seconds = time_difference.total_seconds() 101 | return int(difference_in_seconds) 102 | 103 | 104 | def convert_seconds_to_hours(seconds: int) -> float: 105 | hours = seconds / 3600 106 | hours_decimal = round(hours, 1) 107 | return hours_decimal 108 | -------------------------------------------------------------------------------- /validator.py: -------------------------------------------------------------------------------- 1 | from manage_db import get_user_names 2 | from settings import WRONG_SYMBOLS 3 | 4 | 5 | def is_name_empty(user_name: str) -> bool: 6 | return len(user_name) == 0 7 | 8 | 9 | def is_name_too_short(user_name: str) -> bool: 10 | return len(user_name) < 2 11 | 12 | 13 | def has_name_first_wrong_symbol(user_name: str) -> bool: 14 | return user_name[0] in (' ', *WRONG_SYMBOLS, *map(str, range(10))) 15 | 16 | 17 | def has_name_wrong_symbols(user_name: str) -> bool: 18 | return any({symbol in WRONG_SYMBOLS for symbol in user_name}) 19 | 20 | 21 | def is_name_too_long(user_name: str) -> bool: 22 | return len(user_name) > 25 23 | 24 | 25 | def is_user_already_exists(user_name: str) -> bool: 26 | return user_name in get_user_names() 27 | --------------------------------------------------------------------------------