diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 164630d..f58e14e 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -5,4 +5,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/ruff-action@v3 + - name: Run Ruff Linter + uses: astral-sh/ruff-action@v3 + with: + args: "check --output-format=github" + - name: Run Ruff Formatter + uses: astral-sh/ruff-action@v3 + with: + args: "format --check --diff" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d09842c..43d547d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,12 +12,12 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.13" + python-version: "3.12.3" - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pytest + pip install -r requirements.txt - name: Run Pytest tests run: | diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..330f0e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,210 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,linux \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..025ab70 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Michael Vasiliev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index e93444f..1068581 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,4 @@ Темы: Git и GitHub, стандарты оформления кода, хорошие практики, тестирование. -## Этапы выполнения работы - -- Сделать **fork** этого репозитория - - Добавить недостающие файлы для "хорошо оформленного" репозитория -- Исправить ошибки, из-за которых не проходит CI -- Найти и исправить баг(и), которые есть в `src/checksum.py` и `src/bin_search.py` - - Добавить исключения в соответствующих случаях (например, когда когда поиск) - - Исправить и/или дополнить тесты так, чтобы они покрывали найденные ошибки - - Для каждого бага должен быть отдельный **commit** с его исправлением, в его описании должно быть пояснение (можно на русском) решённой проблемы -- На основе [ISBN10](https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D0%B4%D1%83%D0%BD%D0%B0%D1%80%D0%BE%D0%B4%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BD%D0%B8%D0%B6%D0%BD%D1%8B%D0%B9_%D0%BD%D0%BE%D0%BC%D0%B5%D1%80) реализовать простую консольную утилиту - - Пользователь вводит в консоль номер ISBN $\to$ ему выводится "correct" или "incorrect" в зависимости от правильности номера - - Выход из утилиты происходит по вводу пользователем `-1` - - Ошибки не должны прерывать прерывать работу утилиты, но пользователь должен быть уведомлен о том, что он сделал что-то не так - - Реализация утилиты должна быть в модуле `src/checksum.py` -- Сделать **pull request** (один на обе задачи) с решением в main ветку этого репозитория - - **pull request** должен содержать описание проделанной работы, ваше ФИО и номер группы - - Если не получилось/забыли сделать адекватное описание исправленных ошибок в описании к **commit**, то нужно сделать это хотя бы в комментариях к **pull request** +Выполнил Васильев Михаил, группа 25.Б42-мм diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b5d47ed --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +# Python 3.12.3 + +# For tests +pytest==8.4.2 + +# For linting and formatting +ruff==0.14.2 diff --git a/src/checksum.py b/src/checksum.py index 74b4fb0..c790159 100644 --- a/src/checksum.py +++ b/src/checksum.py @@ -1,14 +1,24 @@ -def modulo11Checksum(ISBNNumber: str): +def modulo_11_checksum(isbn_number: str) -> bool: + digits_str = "".join(isbn_number.replace("-", " ").split()) - digits = [int(char) for char in ISBNNumber if char.isdigit()] + if len(digits_str) != 10: + raise Exception( + "Количество значащих символов, то есть не считая пробелов и тире, в ISBN номере должно равняться 10." + ) + if not digits_str[:9].isdigit() or digits_str[9] not in "0123456789Xx": + raise Exception( + "ISBN может содержать только арабские цифры, а также римскую цифру X для контрольной цифры." + ) - checkDigit = digits[-1] + digits = [int(char) for char in digits_str[:9]] + + check_digit = 10 if digits_str[-1] in "Xx" else int(digits_str[-1]) total = 0 - for i in range(len(digits) - 1): - weight = 10 + weight = 10 + for i in range(len(digits_str) - 1): digit = digits[i] total += digit * weight - - checksum = total + checkDigit + weight -= 1 + checksum = total + (check_digit * weight) return checksum % 11 == 0 diff --git a/test/test_checksum.py b/test/test_checksum.py index f64a64c..49de6e0 100644 --- a/test/test_checksum.py +++ b/test/test_checksum.py @@ -1,9 +1,35 @@ -from src.checksum import modulo11Checksum +import pytest + +from src.checksum import modulo_11_checksum def test_good(): - assert modulo11Checksum("2-266-11156-8") + assert modulo_11_checksum("2-266-11156-8") def test_bad(): - assert not modulo11Checksum("2-266-11156-3") + assert not modulo_11_checksum("2-266-11156-3") + + +def test_less_chars(): + with pytest.raises( + Exception, + match="Количество значащих символов, то есть не считая пробелов и тире, в ISBN номере должно равняться 10.", + ): + modulo_11_checksum("2 35") + + +def test_more_chars(): + with pytest.raises( + Exception, + match="Количество значащих символов, то есть не считая пробелов и тире, в ISBN номере должно равняться 10.", + ): + modulo_11_checksum("123456789011") + + +def test_inappropriate_chars(): + with pytest.raises( + Exception, + match="ISBN может содержать только арабские цифры, а также римскую цифру X для контрольной цифры.", + ): + modulo_11_checksum("dfgdfg")