diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..bb8ff1d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# CODEOWNERS: set the repository owners who should be requested for review +* @YOUR_GITHUB_USERNAME diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..fcd889f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,15 @@ +--- +name: Bug report +about: Create a report to help us improve +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. ... +2. ... + +**Expected behavior** +A clear and concise description of what you expected to happen. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..57e801a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,10 @@ +--- +name: Feature request +about: Suggest an idea for this project +--- + +**Is your feature request related to a problem? Please describe.** + +**Describe the solution you'd like** + +**Describe alternatives you've considered** diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6a7695c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..89e062d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ +## Summary + +Describe the change and why it was made. + +## Checklist + +- [ ] Tests added or updated +- [ ] Documentation updated as needed diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml new file mode 100644 index 0000000..58e7564 --- /dev/null +++ b/.github/workflows/python-ci.yml @@ -0,0 +1,30 @@ +name: Python CI + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8, 3.9, 3.10, 3.11] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run tests + run: | + pytest -q diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..47184b9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,20 @@ +name: Create Release + +on: + push: + tags: + - 'v*.*.*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Create Release + uses: actions/create-release@v1 + with: + tag_name: ${{ github.ref_name }} + release_name: Release ${{ github.ref_name }} + body: "Automated release for ${{ github.ref_name }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32ffdbf --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# 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/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ + +# IDEs and system files +.vscode/ +.idea/ +.DS_Store diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b206992 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +repos: + - repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black + language_version: python3 + + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + + - repo: https://github.com/pycqa/flake8 + rev: 6.1.0 + hooks: + - id: flake8 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7688a37 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## Unreleased + +- Initial repository cleanup: refactor scripts, add tests, CI, license. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..9b51473 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,7 @@ +# Contributor Covenant Code of Conduct + +This project adheres to the Contributor Covenant code of conduct. By +participating, you are expected to uphold this code. Please report +unacceptable behavior to the maintainers. + +For the full text see: https://www.contributor-covenant.org/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ac1838f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +Thank you for considering contributing to this repository. + +Guidelines +---------- + +- Open an issue to discuss larger changes before implementing them. +- Fork the repository and create a feature branch for your change. +- Keep changes small and focused; include tests for behavior changes. +- Run the test suite and ensure all checks pass before submitting a PR. + +Running tests locally +--------------------- + +```bash +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +pytest +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a039926 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Your Name + +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 76e3148..23adbea 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,56 @@ # basic-python-project -# hi frined,these project that i did it my self in python -# thanks for coming by! + +Small collection of beginner Python utility scripts and exercises. + +Badges +------ + +[![Python CI](https://github.com/ali-ezz/basic-python-project/actions/workflows/python-ci.yml/badge.svg)](https://github.com/ali-ezz/basic-python-project/actions) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +Overview +-------- + +This repository contains a few small utilities: + +- `from_roman_to_english.py` - convert Roman numerals to integers (`roman_to_int`). +- `max_number.py` - simple `find_max` helper. +- `number_to_words.py` - convert digits to spoken words. +- `roll_dice.py` - small `Dice` class that rolls six-sided dice. +- `sigma.py` - tiny example returning the string "sigma". +- `symbol_to_emoji.py` - map text emoticons to emoji characters. + +Getting started +--------------- + +1. Create a virtual environment and activate it: + +```bash +python -m venv .venv +source .venv/bin/activate +``` + +2. Install test dependencies: + +```bash +pip install -r requirements.txt +``` + +Running tests +------------- + +Run the test suite with: + +```bash +pytest -q +``` + +Contributing +------------ + +See `CONTRIBUTING.md` for guidelines on submitting issues and pull requests. + +License +------- + +This project is licensed under the MIT License - see the `LICENSE` file for details. diff --git a/from_roman_to_english.py b/from_roman_to_english.py index 9e1a7de..3f4af12 100644 --- a/from_roman_to_english.py +++ b/from_roman_to_english.py @@ -1,41 +1,33 @@ -count=0 -i=0 -s='XII' -while i<=len(s): - if 'CM' in s: - count+=900 - s=s.replace("CM","") - elif 'IV' in s: - count+=4 - s=s.replace("IV","") - elif 'IX' in s: - count+=9 - s=s.replace("IX","") - elif 'XL' in s: - count+=40 - s=s.replace("XL","") - elif 'XC' in s: - count+=90 - s=s.replace("XC","") - elif 'CD' in s: - count+=400 - s=s.replace("CD","") - i+=1 -for item in s: - if item =='I': - count+=1 - elif item =='V': - count+=5 - elif item =='X': - count+=10 - elif item =='L': - count+=50 - elif item =='C': - count+=100 - elif item =='D': - count+=500 - elif item =='CM': - count+=400 - elif item =='M': - count+=1000 -print(count) \ No newline at end of file +"""Utilities for converting Roman numerals to integers. + +This module exposes a single function `roman_to_int` and a small +command-line entrypoint for quick manual use. +""" + +from __future__ import annotations + + +def roman_to_int(s: str) -> int: + """Convert a Roman numeral string to an integer. + + Handles standard subtractive notation (IV, IX, XL, XC, CD, CM). + Non-Roman characters are ignored (treated as 0). + """ + values = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} + total = 0 + prev = 0 + for ch in reversed(s.strip().upper()): + val = values.get(ch, 0) + if val < prev: + total -= val + else: + total += val + prev = val + return total + + +if __name__ == "__main__": + import sys + + arg = sys.argv[1] if len(sys.argv) > 1 else "XII" + print(roman_to_int(arg)) diff --git a/max_number.py b/max_number.py index de81059..c7a6d1f 100644 --- a/max_number.py +++ b/max_number.py @@ -1,6 +1,21 @@ -def find_max(numbers): - max=numbers[0] +"""Utilities for finding the maximum value in a list.""" + +from __future__ import annotations + + +def find_max(numbers: list) -> int: + """Return the maximum value from a non-empty list of numbers. + + Raises `ValueError` when `numbers` is empty. + """ + if not numbers: + raise ValueError("numbers must not be empty") + max_val = numbers[0] for num in numbers: - if max <= num: - max =num - print(f"the ma x number in the list is = {max}") \ No newline at end of file + if num > max_val: + max_val = num + return max_val + + +if __name__ == "__main__": + print(find_max([3, 1, 4, 2])) diff --git a/number_to_words.py b/number_to_words.py new file mode 100644 index 0000000..56705d0 --- /dev/null +++ b/number_to_words.py @@ -0,0 +1,33 @@ +"""Convert numeric strings into spoken word sequences. + +Example: + number_to_words('123') -> 'one two three' +""" + +DIGIT_WORDS = { + "0": "zero", + "1": "one", + "2": "two", + "3": "three", + "4": "four", + "5": "five", + "6": "six", + "7": "seven", + "8": "eight", + "9": "nine", +} + + +def number_to_words(num_str: str) -> str: + """Return the spoken words for each digit in `num_str`. + + Non-digit characters are represented by '!' in the output. + """ + return " ".join(DIGIT_WORDS.get(ch, "!") for ch in str(num_str).strip()) + + +if __name__ == "__main__": + import sys + + s = sys.argv[1] if len(sys.argv) > 1 else "123" + print(number_to_words(s)) diff --git a/numbr to ch.py b/numbr to ch.py deleted file mode 100644 index 35f0789..0000000 --- a/numbr to ch.py +++ /dev/null @@ -1,45 +0,0 @@ - -num =str(input('phone: ')) -for item in num: - if item=='1': - print('one') - elif item=='2': - print('two') - elif item=='3': - print('three') - elif item=='4': - print('four') - elif item=='5': - print('five') - elif item=='6': - print('six') - elif item=='7': - print('seven') - elif item=='8': - print('ehgt') - elif item=='9': - print('nune') - elif item=='0': - print('zero') - else: - print('that numt a number\n') - #or -num =input('phone: ') -diget_dic = { - "0":"zero", - "1":"one", - "2":"two", - "3":"three", - "4":"four", - "5":"five", - "6":"six", - "7":"seven", - "8":"eight", - "9":"nine" -} -num_ch=' ' -for item in num: - num_ch=num_ch+diget_dic.get(item,"!")+' ' -print(num_ch) - - \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f721cf1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[project] +name = "basic-python-project" +version = "0.1.0" +description = "Small collection of Python utility scripts" +readme = "README.md" +requires-python = ">=3.8" +license = { text = "MIT" } +authors = [ { name = "Your Name", email = "you@example.com" } ] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -q" +testpaths = ["tests"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b197d32 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pytest>=7.0 diff --git a/roll_dice.py b/roll_dice.py new file mode 100644 index 0000000..bedd003 --- /dev/null +++ b/roll_dice.py @@ -0,0 +1,21 @@ +"""Dice rolling utility. + +Provides a small `Dice` class that can roll one or more six-sided dice. +""" + +from __future__ import annotations + +import random +from typing import Tuple + + +class Dice: + """Simple six-sided dice roller.""" + + def roll(self, count: int = 2) -> Tuple[int, ...]: + """Roll `count` six-sided dice and return a tuple of results.""" + return tuple(random.randint(1, 6) for _ in range(count)) + + +if __name__ == "__main__": + print(Dice().roll()) diff --git a/roll_dise.py b/roll_dise.py deleted file mode 100644 index e081f71..0000000 --- a/roll_dise.py +++ /dev/null @@ -1,9 +0,0 @@ -import random -class dise: - def roll(self): - numbers = (1,2,3,4,5,6) - first= random.choice(numbers) - secand= random.choice(numbers) - return first,secand -s=dise() -print(s.roll()) \ No newline at end of file diff --git a/sigma.py b/sigma.py index 05d40f8..c414bb1 100644 --- a/sigma.py +++ b/sigma.py @@ -1,48 +1,14 @@ -sent = ord('a') -while True: - print(chr(sent)) - if chr(sent)=='s': - word=chr(sent) - break - else: - sent= sent+1 -sent = ord('a') -while True: - print(word+chr(sent)) - if chr(sent)=='i': - word= word + chr(sent) - break - else: - sent= sent+1 -sent = ord('a') -while True: - print(word+chr(sent)) - if chr(sent)=='g': - word= word + chr(sent) - break - else: - sent= sent+1 -sent = ord('a') -while True: - print(word+chr(sent)) - if chr(sent)=='m': - word= word + chr(sent) - break - else: - sent= sent+1 -sent = ord('a') -while True: - print(word+chr(sent)) - if chr(sent)=='a': - word= word + chr(sent) - break - else: - sent= sent+1 +"""Small helper that demonstrates building the word 'sigma'. +The original file iteratively printed single letters; this version +exposes a testable function that returns the assembled string. +""" +def build_sigma() -> str: + """Return the string 'sigma'.""" + return "sigma" - - - \ No newline at end of file +if __name__ == "__main__": + print(build_sigma()) diff --git a/simble_to_emojy.py b/simble_to_emojy.py deleted file mode 100644 index b3cf963..0000000 --- a/simble_to_emojy.py +++ /dev/null @@ -1,28 +0,0 @@ -mas=input('enter: ') -word =mas.split(' ') -empje ={ - ";)":"😉", - ":)":"🙂", - ":(":"🙁", - ":|":"😐" -} -out=' ' -for item in word: - out+=empje.get(item, item) +' ' -print(out) - -def emoje(word): - empje ={ - ";)":"😉", - ":)":"🙂", - ":(":"🙁", - ":|":"😐"} - out=' ' - for item in word: - out+=empje.get(item, item) +' ' - print(out) - - -mas=input('enter: ') -word =mas.split(' ') -emoje(word) \ No newline at end of file diff --git a/symbol_to_emoji.py b/symbol_to_emoji.py new file mode 100644 index 0000000..0873213 --- /dev/null +++ b/symbol_to_emoji.py @@ -0,0 +1,29 @@ +"""Convert simple text emoticons into emoji characters. + +This small helper is intentionally minimal and demonstrates mapping +from ASCII emoticons to emoji. It is a utility for demonstration and +learning purposes. +""" + +from typing import List + +EMOJI_MAP = { + ";)": "😉", + ":)": "🙂", + ":(": "🙁", + ":|": "😐", +} + + +def emojify(tokens: List[str]) -> str: + """Map a list of tokens to their emoji equivalents where available.""" + return " ".join(EMOJI_MAP.get(t, t) for t in tokens) + + +def emojify_text(text: str) -> str: + """Map tokens in a whitespace-separated string to emojis.""" + return " ".join(EMOJI_MAP.get(tok, tok) for tok in text.split()) + + +if __name__ == "__main__": + print(emojify_text(":) :(")) diff --git a/tests/test_basic.py b/tests/test_basic.py new file mode 100644 index 0000000..f7b3c78 --- /dev/null +++ b/tests/test_basic.py @@ -0,0 +1,33 @@ +from from_roman_to_english import roman_to_int +from max_number import find_max +from number_to_words import number_to_words +from roll_dice import Dice +from sigma import build_sigma +from symbol_to_emoji import emojify_text + + +def test_roman_basic(): + assert roman_to_int("XII") == 12 + assert roman_to_int("IX") == 9 + + +def test_find_max(): + assert find_max([1, 3, 2]) == 3 + + +def test_number_to_words(): + assert number_to_words("201") == "two zero one" + + +def test_dice_roll(): + r = Dice().roll() + assert isinstance(r, tuple) and len(r) == 2 + assert all(1 <= x <= 6 for x in r) + + +def test_sigma(): + assert build_sigma() == "sigma" + + +def test_emojify_text(): + assert emojify_text(":) :(") == "🙂 🙁"