diff --git a/.github/workflows/Python CI.yml b/.github/workflows/Python CI.yml new file mode 100644 index 00000000000..3d933b6b520 --- /dev/null +++ b/.github/workflows/Python CI.yml @@ -0,0 +1,43 @@ +name: Python CI +on: [pull_request, push] +jobs: + lint_python: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + cache: "pip" + cache-dependency-path: "pyproject.toml" + + - name: Install check dependencies + run: | + python -m pip install --upgrade pip wheel + pip install bandit ruff mypy safety codespell + + - name: Run Codespell + run: codespell --config pyproject.toml || true + + - name: Run Bandit + run: bandit --recursive --skip B101 . || true + + - name: Run Ruff (linting) + run: ruff check . --config pyproject.toml + + - name: Run Ruff (formatting check) + run: ruff format --check . --config pyproject.toml || true + + + + - name: Setup Mypy cache + run: mkdir -p .mypy_cache + + - name: Install type stubs + run: mypy --config-file pyproject.toml --install-types --non-interactive + + - name: Run Mypy + run: mypy --config-file pyproject.toml diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml deleted file mode 100644 index b90bd664f4a..00000000000 --- a/.github/workflows/lint_python.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: python -on: [pull_request, push] -jobs: - lint_python: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - run: pip install --upgrade pip wheel - - run: pip install bandit black codespell flake8 flake8-2020 flake8-bugbear - flake8-comprehensions isort mypy pytest pyupgrade safety - - run: bandit --recursive --skip B101 . || true # B101 is assert statements - - run: black --check . || true - - run: codespell || true # --ignore-words-list="" --skip="*.css,*.js,*.lock" - - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 - --show-source --statistics - - run: isort --check-only --profile black . || true - - run: pip install -r requirements.txt || pip install --editable . || true - - run: mkdir --parents --verbose .mypy_cache - - run: mypy --ignore-missing-imports --install-types --non-interactive . || true - - run: pytest . || pytest --doctest-modules . - - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true - - run: safety check diff --git a/.gitignore b/.gitignore index 0f3717818e6..25051c60b03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .idea +.vscode +_pycache_ *.pyc string=sorted(input()) lower="" @@ -18,10 +20,9 @@ print(lower+upper+odd+even) .venv # operating system-related files - # file properties cache/storage on macOS *.DS_Store - # thumbnail cache on Windows Thumbs.db -bankmanaging.db \ No newline at end of file +bankmanaging.db +.vscode/settings.json \ No newline at end of file diff --git a/1 File handle/File handle binary/Deleting record in a binary file.py b/1 File handle/File handle binary/Deleting record in a binary file.py index d3922a5afc4..2db16f962ee 100644 --- a/1 File handle/File handle binary/Deleting record in a binary file.py +++ b/1 File handle/File handle binary/Deleting record in a binary file.py @@ -1,17 +1,51 @@ import pickle +from typing import List, Tuple +def delete_student_record() -> None: + """ + Delete a student record from the binary data file 'studrec.dat' based on the provided roll number. + + This function performs the following operations: + 1. Reads the current student records from 'studrec.dat' + 2. Prompts the user to enter a roll number to delete + 3. Removes the record with the specified roll number + 4. Writes the updated records back to 'studrec.dat' + + Each student record is stored as a tuple in the format: (roll_number, ...) + + Raises: + FileNotFoundError: If 'studrec.dat' does not exist. + pickle.UnpicklingError: If the file contains corrupted data. + ValueError: If the user input cannot be converted to an integer. + """ + try: + # Read existing student records from file + with open("studrec.dat", "rb") as file: + student_records: List[Tuple[int, ...]] = pickle.load(file) + print("Current student records:", student_records) + + # Get roll number to delete + roll_number: int = int(input("Enter the roll number to delete: ")) + + # Filter out the record with the specified roll number + updated_records: List[Tuple[int, ...]] = [ + record for record in student_records if record[0] != roll_number + ] + + # Write updated records back to file + with open("studrec.dat", "wb") as file: + pickle.dump(updated_records, file) + + print(f"Record with roll number {roll_number} has been deleted.") + + except FileNotFoundError: + print("Error: The file 'studrec.dat' does not exist.") + except pickle.UnpicklingError: + print("Error: The file contains corrupted data.") + except ValueError as e: + print(f"Error: Invalid input. Please enter an integer. {e}") + except Exception as e: + print(f"An unexpected error occurred: {e}") -def bdelete(): - # Opening a file & loading it - with open("studrec.dat","rb") as F: - stud = pickle.load(F) - print(stud) - - # Deleting the Roll no. entered by user - rno = int(input("Enter the Roll no. to be deleted: ")) - with open("studrec.dat","wb") as F: - rec = [i for i in stud if i[0] != rno] - pickle.dump(rec, F) - - -bdelete() +if __name__ == "__main__": + delete_student_record() \ No newline at end of file diff --git a/1 File handle/File handle binary/File handle binary read (record in non list form).py b/1 File handle/File handle binary/File handle binary read (record in non list form).py index bb9f127ea0b..81313c05657 100644 --- a/1 File handle/File handle binary/File handle binary read (record in non list form).py +++ b/1 File handle/File handle binary/File handle binary read (record in non list form).py @@ -1,23 +1,47 @@ +import os import pickle - - -def binary_read(): - with open("studrec.dat","rb") as b: - stud = pickle.load(b) - print(stud) - - # prints the whole record in nested list format - print("contents of binary file") - - for ch in stud: - - print(ch) # prints one of the chosen rec in list - - rno = ch[0] - rname = ch[1] # due to unpacking the val not printed in list format - rmark = ch[2] - - print(rno, rname, rmark, end="\t") - - -binary_read() +from typing import List, Tuple + +def read_binary_file() -> None: + """ + Read student records from a binary file. + Automatically creates the file with empty records if it doesn't exist. + + Raises: + pickle.UnpicklingError: If file content is corrupted. + PermissionError: If unable to create or read the file. + """ + file_path = r"1 File handle\File handle binary\studrec.dat" + + # Ensure directory exists + directory = os.path.dirname(file_path) + if not os.path.exists(directory): + os.makedirs(directory, exist_ok=True) + print(f"Created directory: {directory}") + + # Create empty file if it doesn't exist + if not os.path.exists(file_path): + with open(file_path, "wb") as file: + pickle.dump([], file) # Initialize with empty list + print(f"Created new file: {file_path}") + + try: + # Read student records + with open(file_path, "rb") as file: + student_records: List[Tuple[int, str, float]] = pickle.load(file) + + # Print records in a formatted table + print("\nStudent Records:") + print(f"{'ROLL':<10}{'NAME':<20}{'MARK':<10}") + print("-" * 40) + for record in student_records: + roll, name, mark = record + print(f"{roll:<10}{name:<20}{mark:<10.1f}") + + except pickle.UnpicklingError: + print(f"ERROR: File {file_path} is corrupted.") + except Exception as e: + print(f"ERROR: Unexpected error - {str(e)}") + +if __name__ == "__main__": + read_binary_file() \ No newline at end of file diff --git a/1 File handle/File handle binary/Update a binary file.py b/1 File handle/File handle binary/Update a binary file.py index b72154345ae..fa847ebf440 100644 --- a/1 File handle/File handle binary/Update a binary file.py +++ b/1 File handle/File handle binary/Update a binary file.py @@ -1,30 +1,115 @@ -# Updating records in a binary file - +import os import pickle +from typing import List, Tuple +def initialize_file_if_not_exists(file_path: str) -> None: + """ + Check if file exists. If not, create it with an empty list of records. + + Args: + file_path (str): Path to the file. + + Raises: + ValueError: If file_path is empty. + """ + if not file_path: + raise ValueError("File path cannot be empty.") + + directory = os.path.dirname(file_path) + if directory and not os.path.exists(directory): + os.makedirs(directory, exist_ok=True) + + if not os.path.exists(file_path): + with open(file_path, "wb") as f: + pickle.dump([], f) + print(f"Created new file: {file_path}") -def update(): - with open("class.dat", "rb+") as F: - S = pickle.load(F) - found = False - rno = int(input("enter the roll number you want to update")) - - for i in S: - if rno == i[0]: - print(f"the currrent name is {i[1]}") - i[1] = input("enter the new name") - found = True - break - - if found: - print("Record not found") - - else: - F.seek(0) - pickle.dump(S, F) - +def update_student_record(file_path: str) -> None: + """ + Update a student's name in the binary file by roll number. + + Args: + file_path (str): Path to the binary file containing student records. + """ + initialize_file_if_not_exists(file_path) + + try: + with open(file_path, "rb+") as f: + # Load existing records + records: List[Tuple[int, str, float]] = pickle.load(f) + + if not records: + print("No records found in the file.") + return + + # Get roll number to update + roll_to_update = int(input("Enter roll number to update: ")) + found = False + + # Find and update the record + for i, record in enumerate(records): + if record[0] == roll_to_update: + current_name = record[1] + new_name = input(f"Current name: {current_name}. Enter new name: ").strip() + + if new_name: + # Create a new tuple with updated name + updated_record = (record[0], new_name, record[2]) + records[i] = updated_record + print("Record updated successfully.") + else: + print("Name cannot be empty. Update cancelled.") + + found = True + break + + if not found: + print(f"Record with roll number {roll_to_update} not found.") + return + + # Rewrite the entire file with updated records + f.seek(0) + pickle.dump(records, f) + f.truncate() # Ensure any remaining data is removed + + except ValueError: + print("Error: Invalid roll number. Please enter an integer.") + except pickle.UnpicklingError: + print("Error: File content is corrupted and cannot be read.") + except Exception as e: + print(f"An unexpected error occurred: {str(e)}") -update() +def display_all_records(file_path: str) -> None: + """ + Display all student records in the binary file. + + Args: + file_path (str): Path to the binary file. + """ + initialize_file_if_not_exists(file_path) + + try: + with open(file_path, "rb") as f: + records: List[Tuple[int, str, float]] = pickle.load(f) + + if not records: + print("No records found in the file.") + return + + print("\nAll Student Records:") + print(f"{'ROLL':<8}{'NAME':<20}{'PERCENTAGE':<12}") + print("-" * 40) + + for record in records: + print(f"{record[0]:<8}{record[1]:<20}{record[2]:<12.1f}") + + except pickle.UnpicklingError: + print("Error: File content is corrupted and cannot be read.") + except Exception as e: + print(f"An unexpected error occurred: {str(e)}") -with open("class.dat", "rb") as F: - print(pickle.load(F)) +if __name__ == "__main__": + FILE_PATH = r"class.dat" # Update with your actual file path + + update_student_record(FILE_PATH) + display_all_records(FILE_PATH) \ No newline at end of file diff --git a/1 File handle/File handle binary/Update a binary file2.py b/1 File handle/File handle binary/Update a binary file2.py index 88adeef443f..dbfd3b54b21 100644 --- a/1 File handle/File handle binary/Update a binary file2.py +++ b/1 File handle/File handle binary/Update a binary file2.py @@ -1,30 +1,109 @@ -# updating records in a binary file - +import os import pickle +from typing import List, Tuple - -def update(): - - with open("studrec.dat", "rb+") as File: - value = pickle.load(File) - found = False - roll = int(input("Enter the roll number of the record")) +def initialize_file_if_not_exists(file_path: str) -> None: + """ + Check if file exists. If not, create it with an empty list of records. + + Args: + file_path (str): Path to the file. - for i in value: - if roll == i[0]: - print(f"current name {i[1]}") - print(f"current marks {i[2]}") - i[1] = input("Enter the new name") - i[2] = int(input("Enter the new marks")) - found = True - - if not found: - print("Record not found") - - else: - pickle.dump(value, File) - File.seek(0) - print(pickle.load(File)) + Raises: + ValueError: If file_path is empty. + """ + if not file_path: + raise ValueError("File path cannot be empty.") + + directory = os.path.dirname(file_path) + if directory and not os.path.exists(directory): + os.makedirs(directory, exist_ok=True) + + if not os.path.exists(file_path): + with open(file_path, "wb") as f: + pickle.dump([], f) + print(f"Created new file: {file_path}") +def update_student_record(file_path: str) -> None: + """ + Update a student's name and marks in the binary file by roll number. + + Args: + file_path (str): Path to the binary file containing student records. + """ + initialize_file_if_not_exists(file_path) + + try: + with open(file_path, "rb+") as f: + # Load existing records + records: List[Tuple[int, str, int]] = pickle.load(f) + + if not records: + print("No records found in the file.") + return + + # Get roll number to update + roll_to_update = int(input("Enter roll number to update: ")) + found = False + + # Find and update the record + for i, record in enumerate(records): + if record[0] == roll_to_update: + current_name = record[1] + current_marks = record[2] + + print("\nCurrent Record:") + print(f"Roll: {roll_to_update}") + print(f"Name: {current_name}") + print(f"Marks: {current_marks}") + + new_name = input("Enter new name (leave blank to keep current): ").strip() + new_marks_input = input("Enter new marks (leave blank to keep current): ").strip() + + # Update name if provided + if new_name: + records[i] = (record[0], new_name, record[2]) + + # Update marks if provided + if new_marks_input: + try: + new_marks = int(new_marks_input) + if records[i][1] == new_name: # If name was updated + records[i] = (record[0], new_name, new_marks) + else: # If name was not updated + records[i] = (record[0], record[1], new_marks) + except ValueError: + print("Invalid marks input. Marks not updated.") + + print("Record updated successfully.") + found = True + break + + if not found: + print(f"Record with roll number {roll_to_update} not found.") + return + + # Rewrite the entire file with updated records + f.seek(0) + pickle.dump(records, f) + f.truncate() # Ensure any remaining data is removed + + # Display updated record + f.seek(0) + updated_records = pickle.load(f) + print("\nUpdated Records:") + print(f"{'ROLL':<8}{'NAME':<15}{'MARKS':<8}") + print("-" * 35) + for record in updated_records: + print(f"{record[0]:<8}{record[1]:<15}{record[2]:<8}") + + except ValueError: + print("Error: Invalid roll number. Please enter an integer.") + except pickle.UnpicklingError: + print("Error: File content is corrupted and cannot be read.") + except Exception as e: + print(f"An unexpected error occurred: {str(e)}") -update() +if __name__ == "__main__": + FILE_PATH = r"studrec.dat" # Update with your actual file path + update_student_record(FILE_PATH) \ No newline at end of file diff --git a/1 File handle/File handle binary/question 1 (elegible for remedial, top marks).py b/1 File handle/File handle binary/question 1 (elegible for remedial, top marks).py index bf84e9824ec..ca27cbf44fb 100644 --- a/1 File handle/File handle binary/question 1 (elegible for remedial, top marks).py +++ b/1 File handle/File handle binary/question 1 (elegible for remedial, top marks).py @@ -1,68 +1,141 @@ -"""Amit is a monitor of class XII-A and he stored the record of all -the students of his class in a file named “class.dat”. -Structure of record is [roll number, name, percentage]. His computer -teacher has assigned the following duty to Amit - -Write a function remcount( ) to count the number of students who need - remedial class (student who scored less than 40 percent) - - - """ -# also find no. of children who got top marks - +import os import pickle - -list = [ - [1, "Ramya", 30], - [2, "vaishnavi", 60], - [3, "anuya", 40], - [4, "kamala", 30], - [5, "anuraag", 10], - [6, "Reshi", 77], - [7, "Biancaa.R", 100], - [8, "sandhya", 65], -] - -with open("class.dat", "ab") as F: - pickle.dump(list, F) - F.close() - - -def remcount(): - with open("class.dat", "rb") as F: - val = pickle.load(F) - count = 0 - - for i in val: - if i[2] <= 40: - print(f"{i} eligible for remedial") - count += 1 - print(f"the total number of students are {count}") - - -remcount() - - -def firstmark(): - with open("class.dat", "rb") as F: - val = pickle.load(F) - count = 0 - main = [i[2] for i in val] - - top = max(main) - print(top, "is the first mark") - - F.seek(0) - for i in val: - if top == i[2]: - print(f"{i}\ncongrats") - count += 1 - - print("the total number of students who secured top marks are", count) - - -firstmark() - -with open("class.dat", "rb") as F: - val = pickle.load(F) - print(val) +from typing import List, Tuple + +def initialize_file_if_not_exists(file_path: str) -> None: + """ + Check if the file exists. If not, create it and initialize with an empty list. + + Args: + file_path (str): Path to the file to check/initialize. + + Raises: + ValueError: If file_path is empty. + """ + if not file_path: + raise ValueError("File path cannot be empty.") + + directory = os.path.dirname(file_path) + if directory and not os.path.exists(directory): + os.makedirs(directory, exist_ok=True) + print(f"Directory created: {directory}") + + if not os.path.exists(file_path): + with open(file_path, "wb") as file: + pickle.dump([], file) + print(f"File initialized: {file_path}") + +def write_sample_data(file_path: str) -> None: + """ + Write sample student data to the file for demonstration purposes. + + Args: + file_path (str): Path to the file to write data to. + """ + sample_data: List[Tuple[int, str, float]] = [ + (1, "Ramya", 30.0), + (2, "Vaishnavi", 60.0), + (3, "Anuya", 40.0), + (4, "Kamala", 30.0), + (5, "Anuraag", 10.0), + (6, "Reshi", 77.0), + (7, "Biancaa.R", 100.0), + (8, "Sandhya", 65.0), + ] + + with open(file_path, "wb") as file: + pickle.dump(sample_data, file) + print(f"Sample data written to {file_path}") + +def count_remedial_students(file_path: str) -> None: + """ + Count and print students who need remedial classes (percentage < 40). + + Args: + file_path (str): Path to the file containing student records. + """ + initialize_file_if_not_exists(file_path) + + try: + with open(file_path, "rb") as file: + students: List[Tuple[int, str, float]] = pickle.load(file) + + remedial_students = [student for student in students if student[2] < 40.0] + + print("\nStudents eligible for remedial classes:") + for student in remedial_students: + print(f"Roll: {student[0]}, Name: {student[1]}, Percentage: {student[2]:.1f}") + + print(f"\nTotal students needing remedial: {len(remedial_students)}") + + except pickle.UnpicklingError: + print(f"Error: File {file_path} is corrupted.") + except Exception as e: + print(f"Error: {str(e)}") + +def count_top_scorers(file_path: str) -> None: + """ + Count and print students who achieved the highest percentage. + + Args: + file_path (str): Path to the file containing student records. + """ + initialize_file_if_not_exists(file_path) + + try: + with open(file_path, "rb") as file: + students: List[Tuple[int, str, float]] = pickle.load(file) + + if not students: + print("No student records found.") + return + + max_percentage = max(student[2] for student in students) + top_scorers = [student for student in students if student[2] == max_percentage] + + print(f"\nTop scorers with {max_percentage:.1f}%:") + for student in top_scorers: + print(f"Roll: {student[0]}, Name: {student[1]}") + + print(f"\nTotal top scorers: {len(top_scorers)}") + + except pickle.UnpicklingError: + print(f"Error: File {file_path} is corrupted.") + except Exception as e: + print(f"Error: {str(e)}") + +def display_all_students(file_path: str) -> None: + """ + Display all student records. + + Args: + file_path (str): Path to the file containing student records. + """ + initialize_file_if_not_exists(file_path) + + try: + with open(file_path, "rb") as file: + students: List[Tuple[int, str, float]] = pickle.load(file) + + print("\nAll student records:") + print(f"{'ROLL':<8}{'NAME':<15}{'PERCENTAGE':<12}") + print("-" * 35) + + for student in students: + print(f"{student[0]:<8}{student[1]:<15}{student[2]:<12.1f}") + + except pickle.UnpicklingError: + print(f"Error: File {file_path} is corrupted.") + except Exception as e: + print(f"Error: {str(e)}") + +if __name__ == "__main__": + + FILE_PATH = r"1 File handle\File handle binary\class.dat" + + # Uncomment below line to write sample data (only needed once) + # write_sample_data(FILE_PATH) + + count_remedial_students(FILE_PATH) + count_top_scorers(FILE_PATH) + display_all_students(FILE_PATH) \ No newline at end of file diff --git a/1 File handle/File handle binary/search record in binary file.py b/1 File handle/File handle binary/search record in binary file.py index 80d2071134e..14bfad4ac80 100644 --- a/1 File handle/File handle binary/search record in binary file.py +++ b/1 File handle/File handle binary/search record in binary file.py @@ -1,21 +1,68 @@ -# binary file to search a given record - +import os import pickle +from typing import List, Tuple +def initialize_file_if_not_exists(file_path: str) -> None: + """ + Check if the file exists. If not, create it with an empty list. + + Args: + file_path (str): Path to the file. + + Raises: + ValueError: If file_path is empty. + """ + if not file_path: + raise ValueError("File path cannot be empty") + + directory = os.path.dirname(file_path) + if directory and not os.path.exists(directory): + os.makedirs(directory, exist_ok=True) + + if not os.path.exists(file_path): + with open(file_path, "wb") as f: + pickle.dump([], f) + print(f"Created new file: {file_path}") -def binary_search(): - with open("studrec.dat", "rb") as F: - # your file path will be different - search = 0 - rno = int(input("Enter the roll number of the student")) - - for i in pickle.load(F): - if i[0] == rno: - print(f"Record found successfully\n{i}") - search = 1 - - if search == 0: - print("Sorry! record not found") - +def search_student_record(file_path: str) -> None: + """ + Search for a student record by roll number in the binary file. + + Args: + file_path (str): Path to the binary file containing student records. + """ + initialize_file_if_not_exists(file_path) + + try: + with open(file_path, "rb") as f: + records: List[Tuple[int, str, float]] = pickle.load(f) + + if not records: + print("No records found in the file.") + return + + roll_to_search = int(input("Enter student roll number to search: ")) + found = False + + for record in records: + if record[0] == roll_to_search: + print("\nRecord found:") + print(f"Roll: {record[0]}") + print(f"Name: {record[1]}") + print(f"Percentage: {record[2]:.1f}%") + found = True + break + + if not found: + print(f"Record with roll number {roll_to_search} not found.") + + except ValueError: + print("Error: Invalid roll number. Please enter an integer.") + except pickle.UnpicklingError: + print("Error: File content is corrupted and cannot be read.") + except Exception as e: + print(f"An unexpected error occurred: {str(e)}") -binary_search() +if __name__ == "__main__": + FILE_PATH = r"studrec.dat" # Update with your actual file path + search_student_record(FILE_PATH) \ No newline at end of file diff --git a/1 File handle/File handle text/counter.py b/1 File handle/File handle text/counter.py index 1019eeacae8..74267c7c061 100644 --- a/1 File handle/File handle text/counter.py +++ b/1 File handle/File handle text/counter.py @@ -1,35 +1,120 @@ -""" - Class resposible for counting words for different files: - - Reduce redundant code - - Easier code management/debugging - - Code readability -""" +from typing import Dict -class Counter: - - def __init__(self, text:str) -> None: - self.text = text - - # Define the initial count of the lower and upper case. - self.count_lower = 0 - self.count_upper = 0 - self.count() - - def count(self) -> None: +class TextCounter: + """ + A utility class for counting characters in text or files. + + Features: + - Counts lowercase, uppercase, digits, and special characters + - Handles both text input and file reading + - Provides detailed character statistics + + Example usage: + >>> counter = TextCounter("Hello World!") + >>> counter.get_total_lower() + 8 + >>> counter.get_total_upper() + 2 + >>> counter.get_stats() + {'lowercase': 8, 'uppercase': 2, 'digits': 0, 'special': 1, 'total': 11} + """ + + def __init__(self, content: str, is_file_path: bool = False) -> None: + """ + Initialize the counter with text or file content. + + Args: + content: Text string or file path + is_file_path: Set to True if content is a file path + + Raises: + FileNotFoundError: If the specified file does not exist + PermissionError: If the file cannot be accessed + UnicodeDecodeError: If the file cannot be decoded properly + """ + self.text: str = self._read_content(content, is_file_path) + self.count_lower: int = 0 + self.count_upper: int = 0 + self.count_digits: int = 0 + self.count_special: int = 0 + self._count_characters() + + def _read_content(self, content: str, is_file_path: bool) -> str: + """ + Read content from file if needed, otherwise return the text directly. + + Args: + content: Text string or file path + is_file_path: Set to True if content is a file path + + Returns: + The content as a string + + Raises: + FileNotFoundError: If the file does not exist + PermissionError: If the file cannot be accessed + UnicodeDecodeError: If the file cannot be decoded properly + """ + if is_file_path: + with open(content, 'r', encoding='utf-8') as file: + return file.read() + return content + + def _count_characters(self) -> None: + """ + Count different character types in the text. + Updates the following attributes: + count_lower: Number of lowercase letters + count_upper: Number of uppercase letters + count_digits: Number of digits + count_special: Number of special characters (non-alphanumeric) + """ for char in self.text: - if char.lower(): + if char.islower(): self.count_lower += 1 - elif char.upper(): + elif char.isupper(): self.count_upper += 1 - - return (self.count_lower, self.count_upper) + elif char.isdigit(): + self.count_digits += 1 + else: + self.count_special += 1 def get_total_lower(self) -> int: + """Return the count of lowercase characters.""" return self.count_lower - + def get_total_upper(self) -> int: + """Return the count of uppercase characters.""" return self.count_upper - + + def get_total_digits(self) -> int: + """Return the count of digit characters.""" + return self.count_digits + + def get_total_special(self) -> int: + """Return the count of special characters.""" + return self.count_special + def get_total(self) -> int: - return self.count_lower + self.count_upper \ No newline at end of file + """Return the total count of characters.""" + return (self.count_lower + + self.count_upper + + self.count_digits + + self.count_special) + + def get_stats(self) -> Dict[str, int]: + """ + Return detailed character statistics. + + Returns: + A dictionary containing counts for lowercase, uppercase, + digits, special characters, and the total count. + """ + return { + 'lowercase': self.count_lower, + 'uppercase': self.count_upper, + 'digits': self.count_digits, + 'special': self.count_special, + 'total': self.get_total() + } \ No newline at end of file diff --git a/1 File handle/File handle text/file handle 12 length of line in text file.py b/1 File handle/File handle text/file handle 12 length of line in text file.py index d14ef16a4ea..ad155f9e360 100644 --- a/1 File handle/File handle text/file handle 12 length of line in text file.py +++ b/1 File handle/File handle text/file handle 12 length of line in text file.py @@ -1,38 +1,156 @@ - import os +import sys import time -file_name= input("Enter the file name to create:- ") +from typing import NoReturn, List, Tuple + + +def _get_invalid_filename_chars() -> Tuple[str, ...]: + """Return a tuple of invalid filename characters for the current OS.""" + if sys.platform.startswith('win'): + return ('\\', '/', ':', '*', '?', '"', '<', '>', '|') + else: # Linux/macOS + return ('/',) + -print(file_name) +def is_valid_filename(filename: str) -> Tuple[bool, str]: + """ + Validate if a filename is valid for the current operating system. + + Args: + filename: Filename to validate + + Returns: + Tuple containing (is_valid: bool, error_message: str) + """ + if not filename.strip(): + return False, "Filename cannot be empty or contain only whitespace" + + invalid_chars = _get_invalid_filename_chars() + for char in filename: + if char in invalid_chars: + return False, f"Contains invalid character: '{char}' (invalid: {', '.join(invalid_chars)})" + + max_length = 255 if not sys.platform.startswith('win') else 260 + if len(filename) > max_length: + return False, f"Too long (max {max_length} characters)" + + if sys.platform.startswith('win'): + reserved_names = {'CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', + 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', + 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'} + base_name = filename.split('.')[0].upper() + if base_name in reserved_names: + return False, f"'{filename}' is a reserved system name" + + return True, "Valid filename" -def write_to_file(file_name): +def write_to_file(file_name: str) -> None: + """Create a new file and allow user to add content line by line.""" if os.path.exists(file_name): - print(f"Error: {file_name} already exists.") + print(f"Error: File '{file_name}' already exists (preventing overwrite).") return - with open(file_name, "a") as F: + parent_dir = os.path.dirname(file_name) + if parent_dir and not os.path.exists(parent_dir): + print(f"Error: Parent directory '{parent_dir}' does not exist.") + return - while True: - text = input("enter any text to add in the file:- ") - F.write( f"{text}\n" ) - choice = input("Do you want to enter more, y/n").lower() - if choice == "n": - break - -def longlines(): + try: + with open(file_name, "a", encoding="utf-8") as file: + print(f"Created file: '{file_name}'") + + while True: + text = input("Enter text to add (press Enter to skip): ").rstrip('\n') + + if text: + file.write(f"{text}\n") + print(f"Added: {text}") + else: + print("Note: Empty input will not be written.") - with open(file_name, encoding='utf-8') as F: - lines = F.readlines() - lines_less_than_50 = list( filter(lambda line: len(line) < 50, lines ) ) + while True: + choice = input("Add more content? (y/n): ").strip().lower() + if choice in ('y', 'n'): + break + print("Invalid input. Please enter 'y' or 'n'.") + + if choice == 'n': + print("Content writing completed.") + break - if not lines_less_than_50: - print("There is no line which is less than 50") - else: - for i in lines_less_than_50: - print(i, end="\t") + except PermissionError: + print(f"Error: Permission denied for '{file_name}'.") + except OSError as e: + print(f"System error while writing: {str(e)}") + except Exception as e: + print(f"Unexpected error: {str(e)}") -if __name__ == "__main__": + +def print_short_lines(file_name: str) -> None: + """Read a file and print lines with length < 50 characters (excluding newline).""" + if not os.path.exists(file_name): + print(f"Error: File '{file_name}' does not exist.") + return + + if not os.path.isfile(file_name): + print(f"Error: '{file_name}' is a directory, not a file.") + return + + try: + with open(file_name, "r", encoding="utf-8") as file: + lines: List[str] = file.readlines() + if not lines: + print(f"Info: File '{file_name}' is empty.") + return + + short_lines: List[str] = [line for line in lines if len(line.rstrip('\n')) < 50] + + if not short_lines: + print("No lines with length < 50 characters (excluding newline).") + else: + print(f"\nLines in '{file_name}' with length < 50:") + for line_num, line in enumerate(short_lines, 1): + print(f"Line {line_num}: {line.rstrip('\n')}") + + except PermissionError: + print(f"Error: Permission denied to read '{file_name}'.") + except UnicodeDecodeError: + print(f"Error: '{file_name}' is not a valid text file.") + except OSError as e: + print(f"System error while reading: {str(e)}") + except Exception as e: + print(f"Unexpected error: {str(e)}") + + +def get_valid_filename() -> str: + """Prompt user for a filename until a valid one is provided.""" + while True: + file_name = input("Enter the name of the file to create: ").strip() + print(f"Selected filename: '{file_name}'") + + is_valid, msg = is_valid_filename(file_name) + if is_valid: + return file_name + print(f"Invalid filename: {msg}. Please try again.\n") + + +def main() -> NoReturn: + """Main function coordinating file operations with retry for invalid filenames.""" + # Get valid filename with retry mechanism + file_name = get_valid_filename() + + # Write content to file write_to_file(file_name) + + # Brief pause to ensure file operations complete time.sleep(1) - longlines() \ No newline at end of file + + # Read and display short lines + print_short_lines(file_name) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/1 File handle/File handle text/input,output and error streams.py b/1 File handle/File handle text/input,output and error streams.py index cecd268979b..2a80e5bac21 100644 --- a/1 File handle/File handle text/input,output and error streams.py +++ b/1 File handle/File handle text/input,output and error streams.py @@ -1,16 +1,169 @@ -# practicing with streams import sys +from typing import NoReturn, List -sys.stdout.write("Enter the name of the file") -file = sys.stdin.readline() -with open(file.strip(), ) as F: +def read_and_print_file(file_path: str) -> None: + """ + Read a text file and print its contents line by line. + + Reads the entire file content, prints each line to standard output, + and sends an "End of file reached" message to standard error upon completion. + + Args: + file_path: Path to the text file to be read. + + Raises: + FileNotFoundError: If the specified file does not exist. + PermissionError: If the user lacks permission to read the file. + IsADirectoryError: If the provided path points to a directory instead of a file. + UnicodeDecodeError: If the file contains non-UTF-8 encoded data. + OSError: For other OS-related errors (e.g., disk full). + """ + try: + # Open file in read mode with explicit UTF-8 encoding + with open(file_path, mode='r', encoding='utf-8') as file: + # Read and print lines in chunks for better memory efficiency + for line in file: + sys.stdout.write(line) + + # Notify end of file via standard error stream + sys.stderr.write("End of file reached\n") + + except FileNotFoundError: + raise # Re-raise for higher-level handling + except PermissionError: + raise + except IsADirectoryError: + raise + except UnicodeDecodeError: + raise + except OSError as e: + # Handle other OS-related errors + raise OSError(f"OS error occurred while reading file: {e}") from e - while True: - ch = F.readlines() - for (i) in ch: # ch is the whole file,for i in ch gives lines, for j in i gives letters,for j in i.split gives words - print(i, end="") - else: - sys.stderr.write("End of file reached") - break +def is_valid_path(path: str) -> bool: + """ + Validate if a given path is syntactically valid. + + Note: This does not check if the file exists, only if the path format is valid. + + Args: + path: Path string to validate + + Returns: + True if the path is syntactically valid, False otherwise + """ + # Basic path validation (platform-independent) + if not path.strip(): + return False + + # Check for invalid characters (simplified version) + invalid_chars = ['\0'] # Null character is invalid on all platforms + if any(c in path for c in invalid_chars): + return False + + # Additional platform-specific checks + if sys.platform.startswith('win'): + # Windows-specific invalid characters + win_invalid = ['<', '>', ':', '"', '/', '\\', '|', '?', '*'] + if any(c in path for c in win_invalid): + return False + + return True + + +def get_valid_file_path() -> str: + """ + Prompt the user for a file path until a valid and non-empty path is provided. + + Returns: + A validated file path string + """ + max_attempts = 5 + attempt = 0 + + while attempt < max_attempts: + attempt += 1 + + # Prompt user for file name/path + sys.stdout.write(f"Enter the name of the file (attempt {attempt}/{max_attempts}, or 'q' to quit): ") + sys.stdout.flush() + + # Read and process input + file_path = sys.stdin.readline().strip() + + # Check for quit command + if file_path.lower() == 'q': + sys.stderr.write("Operation cancelled by user.\n") + sys.exit(0) + + # Validate path syntax + if not is_valid_path(file_path): + sys.stderr.write("Error: Invalid path syntax. Please enter a valid file path.\n") + continue + + return file_path + + # If max attempts exceeded + sys.stderr.write("Error: Maximum number of attempts reached. Exiting.\n") + exit() + + +def handle_exception(e: Exception, file_path: str) -> None: + """ + Handle exceptions in a centralized manner for better consistency. + + Args: + e: The exception to handle + file_path: Path of the file involved in the exception + """ + error_messages = { + FileNotFoundError: f"Error: File '{file_path}' not found", + PermissionError: f"Error: Permission denied to read '{file_path}'", + IsADirectoryError: f"Error: '{file_path}' is a directory, not a file", + UnicodeDecodeError: f"Error: '{file_path}' contains non-UTF-8 data (not a text file)", + OSError: f"OS Error: {str(e)}", + } + + # Log the exception (optional, but useful for debugging) + # logging.error(f"Exception occurred: {str(e)}", exc_info=True) + + # Determine the appropriate error message + error_type = type(e) + message = error_messages.get(error_type, f"Unexpected error: {str(e)}") + + # Output the error message + sys.stderr.write(f"{message}\n") + + # Optionally provide suggestions based on the error type + if error_type == FileNotFoundError: + sys.stderr.write("Suggestion: Check if the file path is correct and the file exists.\n") + elif error_type == PermissionError: + sys.stderr.write("Suggestion: Ensure you have the necessary permissions to access the file.\n") + + # Exit with appropriate status code + exit() + + +def main() -> NoReturn: + """ + Main function to handle user input and coordinate file reading. + + Prompts the user for a file path, validates the input, + and triggers the file reading process with robust error handling. + """ + # Get a valid file path from user + file_path = get_valid_file_path() + + try: + read_and_print_file(file_path) + except Exception as e: + handle_exception(e, file_path) + + # Exit successfully if all operations complete + exit() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/1 File handle/File handle text/question 2.py b/1 File handle/File handle text/question 2.py index cbb84fcd13f..41ec218454c 100644 --- a/1 File handle/File handle text/question 2.py +++ b/1 File handle/File handle text/question 2.py @@ -1,33 +1,94 @@ -""" Write a method/function DISPLAYWORDS() in python to read lines - from a text file STORY.TXT, - using read function -and display those words, which are less than 4 characters. """ +import sys +from typing import List, Tuple, Optional -print("Hey!! You can print the word which are less then 4 characters") +def display_short_words(file_path: str) -> Tuple[List[str], int]: + """ + Read a text file and extract words with fewer than 4 characters. + + Reads the entire content of the file using the `read()` method, + splits it into words (whitespace-separated), filters words with length < 4, + and returns both the filtered words and their count. + + Args: + file_path: Path to the text file (e.g., "STORY.TXT") + + Returns: + Tuple containing: + - List of words with fewer than 4 characters + - Count of such words + + Raises: + FileNotFoundError: If the specified file does not exist + PermissionError: If the user lacks permission to read the file + IsADirectoryError: If the provided path points to a directory + UnicodeDecodeError: If the file contains non-UTF-8 encoded data + OSError: For other OS-related errors (e.g., invalid path) + """ + # Read entire file content + with open(file_path, 'r', encoding='utf-8') as file: + content: str = file.read() + + # Split content into words (handles multiple whitespace characters) + words: List[str] = content.split() + + # Filter words with length < 4 using a generator expression for memory efficiency + short_words: List[str] = [word for word in words if len(word) < 4] + + return short_words, len(short_words) -def display_words(file_path): +def main() -> None: + """ + Main function to handle user interaction and coordinate the word extraction process. + + Guides the user through inputting a file path, validates inputs, + calls the word extraction function, and displays results with error handling. + """ + print("=== Short Word Extractor ===") + print("This program reads a text file and displays words with fewer than 4 characters.\n") + + # Get and validate file path input + while True: + file_path: str = input("Enter the path to the text file (e.g., 'STORY.TXT'): ").strip() + + if not file_path: + print("Error: File path cannot be empty. Please try again.\n") + continue + break + try: - with open(file_path) as F: - words = F.read().split() - words_less_than_40 = list( filter(lambda word: len(word) < 4, words) ) - - for word in words_less_than_40: - print(word) + # Extract short words + short_words, count = display_short_words(file_path) - return "The total number of the word's count which has less than 4 characters", (len(words_less_than_40)) + # Display results + print(f"\nFound {count} words with fewer than 4 characters:") + if short_words: + # Print words in chunks of 10 for readability + for i in range(0, count, 10): + chunk = short_words[i:i+10] + print(" ".join(chunk)) + else: + print("No words with fewer than 4 characters found.") except FileNotFoundError: - print("File not found") - -print("Just need to pass the path of your file..") + print(f"\nError: File '{file_path}' not found. Please check the path.") + except PermissionError: + print(f"\nError: Permission denied to read '{file_path}'. Check your access rights.") + except IsADirectoryError: + print(f"\nError: '{file_path}' is a directory, not a file.") + except UnicodeDecodeError: + print(f"\nError: '{file_path}' is not a valid UTF-8 text file.") + except OSError as e: + print(f"\nSystem error: {str(e)}") + except Exception as e: + print(f"\nUnexpected error: {str(e)}") + finally: + print("\nProgram completed.") -file_path = input("Please, Enter file path: ") if __name__ == "__main__": - - print(display_words(file_path)) + main() diff --git a/1 File handle/File handle text/question 5.py b/1 File handle/File handle text/question 5.py index e8df3730b18..9caec9696fc 100644 --- a/1 File handle/File handle text/question 5.py +++ b/1 File handle/File handle text/question 5.py @@ -1,38 +1,118 @@ -"""Write a function in python to count the number of lowercase -alphabets present in a text file “happy.txt""" - -import time import os -from counter import Counter +import sys +from typing import Tuple, NoReturn -print("You will see the count of lowercase, uppercase and total count of alphabets in provided file..") +def count_lowercase_letters(file_path: str) -> Tuple[int, int]: + """ + Count lowercase and uppercase alphabetic characters in a text file. + + Reads the entire content of the file, iterates through each character, + and counts letters that are lowercase (a-z) and uppercase (A-Z). Non-alphabetic + characters (numbers, symbols, whitespace) are ignored. + + Args: + file_path: Path to the text file (e.g., "happy.txt") + + Returns: + Tuple containing: + - Count of lowercase letters (a-z) + - Count of uppercase letters (A-Z) + + Raises: + FileNotFoundError: If the specified file does not exist + PermissionError: If the user lacks permission to read the file + IsADirectoryError: If the path points to a directory instead of a file + UnicodeDecodeError: If the file contains non-UTF-8 encoded data + OSError: For other system-related errors (e.g., invalid path) + """ + lowercase_count: int = 0 + uppercase_count: int = 0 -file_path = input("Please, Enter file path: ") + # Read file content with explicit encoding + with open(file_path, 'r', encoding='utf-8') as file: + content: str = file.read() -if os.path.exists(file_path): - print('The file exists and this is the path:\n',file_path) + # Iterate through each character to count letters + for char in content: + if char.islower(): + lowercase_count += 1 + elif char.isupper(): + uppercase_count += 1 + return lowercase_count, uppercase_count -def lowercase(file_path): - try: - with open(file_path) as F: - word_counter = Counter(F.read()) - - print(f"The total number of lower case letters are {word_counter.get_total_lower()}") - time.sleep(0.5) - print(f"The total number of upper case letters are {word_counter.get_total_upper()}") - time.sleep(0.5) - print(f"The total number of letters are {word_counter.get_total()}") - time.sleep(0.5) +def validate_file_path(file_path: str) -> bool: + """ + Validate if the provided file path points to an existing, readable file. + + Args: + file_path: Path to check + + Returns: + True if the path is valid and points to a readable file; False otherwise + """ + # Check if path is empty + if not file_path.strip(): + print("Error: File path cannot be empty.") + return False - except FileNotFoundError: - print("File is not exist.. Please check AGAIN") + # Check if file exists + if not os.path.exists(file_path): + print(f"Error: File '{file_path}' does not exist.") + return False + # Check if path is a file (not directory) + if not os.path.isfile(file_path): + print(f"Error: '{file_path}' is a directory, not a file.") + return False + # Check if file is readable + if not os.access(file_path, os.R_OK): + print(f"Error: No permission to read '{file_path}'.") + return False + return True -if __name__ == "__main__": - lowercase(file_path) +def main() -> NoReturn: + """ + Main function to handle user interaction and coordinate the counting process. + + Guides the user to input a file path, validates it, triggers the counting function, + and displays the results with clear formatting. + """ + print("=== Alphabet Case Counter ===") + print("This program counts lowercase (a-z) and uppercase (A-Z) letters in a text file.\n") + + # Get and validate file path from user + while True: + file_path: str = input("Enter the path to the text file (e.g., 'happy.txt'): ").strip() + if validate_file_path(file_path): + break + print("Please try again.\n") + + try: + # Count lowercase and uppercase letters + lowercase_count, uppercase_count = count_lowercase_letters(file_path) + + # Display results + print(f"\nStatistics for '{os.path.basename(file_path)}':") + print(f"Lowercase letters (a-z): {lowercase_count}") + print(f"Uppercase letters (A-Z): {uppercase_count}") + print(f"Total alphabetic letters: {lowercase_count + uppercase_count}") + + except UnicodeDecodeError: + print(f"\nError: '{file_path}' contains non-UTF-8 data. Cannot read as text file.") + except OSError as e: + print(f"\nSystem error: {str(e)}") + except Exception as e: + print(f"\nUnexpected error: {str(e)}") + finally: + print("\nOperation completed.") + sys.exit(0) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Armstrong_number b/Armstrong_number2.py similarity index 100% rename from Armstrong_number rename to Armstrong_number2.py diff --git a/Assembler/requirements.txt b/Assembler/requirements.txt deleted file mode 100644 index ee239c1bd87..00000000000 --- a/Assembler/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -print_function -sys diff --git a/Automated Scheduled Call Reminders/caller.py b/Automated Scheduled Call Reminders/caller.py index 0526ea7c31a..04706505f91 100644 --- a/Automated Scheduled Call Reminders/caller.py +++ b/Automated Scheduled Call Reminders/caller.py @@ -41,7 +41,7 @@ def search(): for doc in list_of_docs: if doc["from"][0:5] == five_minutes_prior: phone_number = doc["phone"] - call = client.calls.create( + client.calls.create( to=phone_number, from_="add your twilio number", url="http://demo.twilio.com/docs/voice.xml", diff --git a/Automated Scheduled Call Reminders/requirements.txt b/Automated Scheduled Call Reminders/requirements.txt deleted file mode 100644 index f5635170c24..00000000000 --- a/Automated Scheduled Call Reminders/requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -APScheduler -search -os -time -gmtime -strftime -Client -twilio -datetime -timedelta -credentials -firestore -initialize_app -Twilio \ No newline at end of file diff --git a/BlackJack_game/blackjack.py b/BlackJack_game/blackjack.py index 275b0d7368d..51f010baea1 100644 --- a/BlackJack_game/blackjack.py +++ b/BlackJack_game/blackjack.py @@ -1,121 +1,268 @@ -# master -# master -# BLACK JACK - CASINO A GAME OF FORTUNE!!! -from time import sleep - -# BLACK JACK - CASINO -# PYTHON CODE BASE - - -# master import random +import time -deck = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11] * 4 - -random.shuffle(deck) - -print(f'{"*"*58} \n Welcome to the game Casino - BLACK JACK ! \n{"*"*58}') -sleep(2) -print("So Finally You Are Here To Accept Your Fate") -sleep(2) -print("I Mean Your Fortune") -sleep(2) -print("Lets Check How Lucky You Are Wish You All The Best") -sleep(2) -print("Loading---") -sleep(2) - -print("Still Loading---") -sleep(2) -print( - "So You Are Still Here Not Gone I Gave You Chance But No Problem May Be You Trust Your Fortune A Lot \n Lets Begin Then" -) -sleep(2) -d_cards = [] # Initialising dealer's cards -p_cards = [] # Initialising player's cards -sleep(2) -while len(d_cards) != 2: - random.shuffle(deck) - d_cards.append(deck.pop()) - if len(d_cards) == 2: - print("The cards dealer has are X ", d_cards[1]) - -# Displaying the Player's cards -while len(p_cards) != 2: - random.shuffle(deck) - p_cards.append(deck.pop()) - if len(p_cards) == 2: - print("The total of player is ", sum(p_cards)) - print("The cards Player has are ", p_cards) - -if sum(p_cards) > 21: - print(f"You are BUSTED !\n {'*'*14}Dealer Wins !!{'*'*14}\n") - exit() - -if sum(d_cards) > 21: - print(f"Dealer is BUSTED !\n {'*'*14} You are the Winner !!{'*'*18}\n") - exit() - -if sum(d_cards) == 21: - print(f"{'*'*24}Dealer is the Winner !!{'*'*14}") - exit() - -if sum(d_cards) == 21 and sum(p_cards) == 21: - print(f"{'*'*17}The match is tie !!{'*'*25}") - exit() - - -# function to show the dealer's choice -def dealer_choice(): - if sum(d_cards) < 17: - while sum(d_cards) < 17: - random.shuffle(deck) - d_cards.append(deck.pop()) +class Card: + """Represents a single playing card""" + def __init__(self, rank: str, suit: str): + self.rank = rank + self.suit = suit + + def get_value(self, hand_value: int) -> int: + """Returns the value of the card in the context of the current hand""" + if self.rank in ['J', 'Q', 'K']: + return 10 + elif self.rank == 'A': + # Ace can be 11 or 1, choose the optimal value + return 11 if hand_value + 11 <= 21 else 1 + else: + return int(self.rank) + + def __str__(self) -> str: + return f"{self.rank}{self.suit}" - print("Dealer has total " + str(sum(d_cards)) + "with the cards ", d_cards) +class Deck: + """Represents a deck of playing cards""" + def __init__(self): + self.reset() + + def reset(self) -> None: + """Reset the deck to its initial state""" + suits = ['♥', '♦', '♣', '♠'] + ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'] + self.cards = [Card(rank, suit) for suit in suits for rank in ranks] * 4 + self.shuffle() + + def shuffle(self) -> None: + """Shuffle the deck of cards""" + random.shuffle(self.cards) + + def deal_card(self) -> Card: + """Deal a single card from the deck""" + if not self.cards: + self.reset() # Auto-reset the deck if empty + return self.cards.pop() - if sum(p_cards) == sum(d_cards): - print(f"{'*'*15}The match is tie !!{'*'*15}") - exit() +class Hand: + """Represents a player's or dealer's hand of cards""" + def __init__(self): + self.cards = [] + + def add_card(self, card: Card) -> None: + """Add a card to the hand""" + self.cards.append(card) + + def get_value(self) -> int: + """Calculate the value of the hand, considering soft and hard aces""" + total = 0 + aces = 0 + + for card in self.cards: + if card.rank == 'A': + aces += 1 + total += card.get_value(total) + + # Adjust for aces (if total > 21, convert aces from 11 to 1) + while total > 21 and aces > 0: + total -= 10 + aces -= 1 + + return total + + def is_bust(self) -> bool: + """Check if the hand is bust (over 21)""" + return self.get_value() > 21 + + def is_blackjack(self) -> bool: + """Check if the hand is a blackjack (21 with 2 cards)""" + return len(self.cards) == 2 and self.get_value() == 21 + + def __str__(self) -> str: + return ", ".join(str(card) for card in self.cards) - if sum(d_cards) == 21: - if sum(p_cards) < 21: - print(f"{'*'*23}Dealer is the Winner !!{'*'*18}") - elif sum(p_cards) == 21: - print(f"{'*'*20}There is tie !!{'*'*26}") +class BlackjackGame: + """Main game class that manages the game flow""" + def __init__(self): + self.deck = Deck() + self.player_hand = Hand() + self.dealer_hand = Hand() + self.player_balance = 1000 # Starting balance + self.current_bet = 0 + + def display_welcome_message(self) -> None: + """Display the welcome message and game introduction""" + title = f""" +{"*"*58} + Welcome to the Casino - BLACK JACK ! +{"*"*58} +""" + self._animate_text(title, 0.01) + time.sleep(1) + + messages = [ + "So finally you are here to test your luck...", + "I mean your fortune!", + "Let's see how lucky you are. Wish you all the best!", + "Loading...", + "Still loading...", + "So you're still here. I gave you a chance to leave, but no problem.", + "Maybe you trust your fortune a lot. Let's begin then!" + ] + + for message in messages: + self._animate_text(message) + time.sleep(0.8) + + def _animate_text(self, text: str, delay: float = 0.03) -> None: + """Animate text by printing each character with a small delay""" + for char in text: + print(char, end='', flush=True) + time.sleep(delay) + print() # Newline at the end + + def place_bet(self) -> None: + """Prompt the player to place a bet""" + while True: + try: + print(f"\nYour current balance: ${self.player_balance}") + bet = int(input("Place your bet: $")) + if 1 <= bet <= self.player_balance: + self.current_bet = bet + self.player_balance -= bet + break + else: + print(f"Please bet between $1 and ${self.player_balance}") + except ValueError: + print("Invalid input. Please enter a valid number.") + + def deal_initial_cards(self) -> None: + """Deal the initial cards to the player and dealer""" + self.player_hand = Hand() + self.dealer_hand = Hand() + + # Deal two cards to the player and dealer + for _ in range(2): + self.player_hand.add_card(self.deck.deal_card()) + self.dealer_hand.add_card(self.deck.deal_card()) + + # Show the player's hand and the dealer's up card + self._display_hands(show_dealer=False) + + def _display_hands(self, show_dealer: bool = True) -> None: + """Display the player's and dealer's hands""" + print("\n" + "-"*40) + if show_dealer: + print(f"Dealer's Hand ({self.dealer_hand.get_value()}): {self.dealer_hand}") else: - print(f"{'*'*23}Dealer is the Winner !!{'*'*18}") - - elif sum(d_cards) < 21: - if sum(p_cards) < 21 and sum(p_cards) < sum(d_cards): - print(f"{'*'*23}Dealer is the Winner !!{'*'*18}") - if sum(p_cards) == 21: - print(f"{'*'*22}Player is winner !!{'*'*22}") - if 21 > sum(p_cards) > sum(d_cards): - print(f"{'*'*22}Player is winner !!{'*'*22}") - - else: - if sum(p_cards) < 21: - print(f"{'*'*22}Player is winner !!{'*'*22}") - elif sum(p_cards) == 21: - print(f"{'*'*22}Player is winner !!{'*'*22}") + print(f"Dealer's Hand: X, {self.dealer_hand.cards[1]}") + print(f"Your Hand ({self.player_hand.get_value()}): {self.player_hand}") + print("-"*40 + "\n") + + def player_turn(self) -> None: + """Handle the player's turn (hit or stand)""" + while not self.player_hand.is_bust() and not self.player_hand.is_blackjack(): + choice = input("Do you want to [H]it or [S]tand? ").strip().lower() + + if choice == 'h': + self.player_hand.add_card(self.deck.deal_card()) + self._display_hands(show_dealer=False) + + if self.player_hand.is_bust(): + print("BUST! You went over 21.") + return + elif self.player_hand.is_blackjack(): + print("BLACKJACK! You got 21!") + return + elif choice == 's': + print("You chose to stand.") + return + else: + print("Invalid choice. Please enter 'H' or 'S'.") + + def dealer_turn(self) -> None: + """Handle the dealer's turn (automatically hits until 17 or higher)""" + print("\nDealer's Turn...") + self._display_hands(show_dealer=True) + + while self.dealer_hand.get_value() < 17: + print("Dealer hits...") + self.dealer_hand.add_card(self.deck.deal_card()) + time.sleep(1) + self._display_hands(show_dealer=True) + + if self.dealer_hand.is_bust(): + print("Dealer BUSTS!") + return + + def determine_winner(self) -> None: + """Determine the winner of the game and update the player's balance""" + player_value = self.player_hand.get_value() + dealer_value = self.dealer_hand.get_value() + + if self.player_hand.is_bust(): + print(f"{'*'*20} Dealer Wins! {'*'*20}") + return + elif self.dealer_hand.is_bust(): + winnings = self.current_bet * 2 + self.player_balance += winnings + print(f"{'*'*20} You Win! +${winnings} {'*'*20}") + return + elif self.player_hand.is_blackjack() and not self.dealer_hand.is_blackjack(): + # Blackjack pays 3:2 + winnings = int(self.current_bet * 2.5) + self.player_balance += winnings + print(f"{'*'*15} Blackjack! You Win! +${winnings} {'*'*15}") + return + elif self.dealer_hand.is_blackjack() and not self.player_hand.is_blackjack(): + print(f"{'*'*20} Dealer Blackjack! Dealer Wins! {'*'*20}") + return + + # Compare values if neither is bust or blackjack + if player_value > dealer_value: + winnings = self.current_bet * 2 + self.player_balance += winnings + print(f"{'*'*20} You Win! +${winnings} {'*'*20}") + elif player_value < dealer_value: + print(f"{'*'*20} Dealer Wins! {'*'*20}") else: - print(f"{'*'*23}Dealer is the Winner !!{'*'*18}") - - -while sum(p_cards) < 21: - - # to continue the game again and again !! - k = input("Want to hit or stay?\n Press 1 for hit and 0 for stay ") - if k == "1": #Ammended 1 to a string - random.shuffle(deck) - p_cards.append(deck.pop()) - print("You have a total of " + str(sum(p_cards)) + " with the cards ", p_cards) - if sum(p_cards) > 21: - print(f'{"*"*13}You are BUSTED !{"*"*13}\n Dealer Wins !!') - if sum(p_cards) == 21: - print(f'{"*"*19}You are the Winner !!{"*"*29}') + # Tie (push) + self.player_balance += self.current_bet + print(f"{'*'*20} It's a Tie! Your bet is returned. {'*'*20}") + + def play_again(self) -> bool: + """Ask the player if they want to play another round""" + if self.player_balance <= 0: + print("\nYou're out of money! Game over.") + return False + + choice = input("\nDo you want to play another round? [Y/N] ").strip().lower() + return choice == 'y' + + def run(self) -> None: + """Run the main game loop""" + self.display_welcome_message() + + while True: + if self.player_balance <= 0: + print("\nYou've run out of money! Thanks for playing.") + break + + self.place_bet() + self.deal_initial_cards() + + # Check for immediate blackjack + if self.player_hand.is_blackjack(): + self._display_hands(show_dealer=True) + self.determine_winner() + else: + self.player_turn() + if not self.player_hand.is_bust(): + self.dealer_turn() + self.determine_winner() + + if not self.play_again(): + print(f"\nThanks for playing! Your final balance: ${self.player_balance}") + break - else: - dealer_choice() - break \ No newline at end of file +if __name__ == "__main__": + game = BlackjackGame() + game.run() \ No newline at end of file diff --git a/BlackJack_game/requirements.txt b/BlackJack_game/requirements.txt deleted file mode 100644 index 3320ad5cf21..00000000000 --- a/BlackJack_game/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -time -random diff --git a/BoardGame-CLI/snakeLadder.py b/BoardGame-CLI/snakeLadder.py index d8892ed4339..ceb3cb5f74c 100644 --- a/BoardGame-CLI/snakeLadder.py +++ b/BoardGame-CLI/snakeLadder.py @@ -1,173 +1,170 @@ import random -# Taking players data -players = {} # stores players name their locations -isReady = {} -current_loc = 1 # vaiable for iterating location - -imp = True - - -# players input function -def player_input(): - global players - global current_loc - global isReady - - x = True - while x: - player_num = int(input("Enter the number of players: ")) - if player_num > 0: - for i in range(player_num): - name = input(f"Enter player {i+1} name: ") - players[name] = current_loc - isReady[name] = False - x = False - play() # play funtion call - - else: - print("Number of player cannot be zero") - print() - - -# Dice roll method -def roll(): - # print(players) - return random.randrange(1, 7) - - -# play method -def play(): - global players - global isReady - global imp - - while imp: +# Game state variables +players = {} # Stores player names and their positions +is_ready = {} # Tracks if player has rolled a 6 to start +current_position = 1 # Initial position for new players +game_active = True # Controls the main game loop + +def get_valid_integer(prompt: str, min_value: int = None, max_value: int = None) -> int: + """ + Get a valid integer input from the user within a specified range. + + Args: + prompt: The message to display to the user. + min_value: The minimum acceptable value (inclusive). + max_value: The maximum acceptable value (inclusive). + + Returns: + A valid integer within the specified range. + """ + while True: + try: + value = int(input(prompt)) + if (min_value is not None and value < min_value) or (max_value is not None and value > max_value): + print(f"Please enter a number between {min_value} and {max_value}.") + continue + return value + except ValueError: + print("Invalid input. Please enter a valid number.") + +def initialize_players() -> None: + """Initialize players for the game""" + global players, is_ready, current_position + + while True: + player_count = get_valid_integer("Enter the number of players: ", min_value=1) + + for i in range(player_count): + name = input(f"Enter player {i+1} name: ").strip() + if not name: + name = f"Player {i+1}" + players[name] = current_position + is_ready[name] = False + + start_game() + break + +def roll_dice() -> int: + """Roll a 6-sided dice""" + return random.randint(1, 6) + +def start_game() -> None: + """Start the main game loop""" + global game_active, players, is_ready + + while game_active: print("/"*20) - print("1 -> roll the dice (or enter)") - print("2 -> start new game") - print("3 -> exit the game") + print("1 -> Roll the dice") + print("2 -> Start new game") + print("3 -> Exit the game") print("/"*20) - - for i in players: - n = input("{}'s turn: ".format(i)) or 1 - n = int(n) - - if players[i] < 100: - if n == 1: - temp1 = roll() - print(f"you got {temp1}") - print("") - - if isReady[i] == False and temp1 == 6: - isReady[i] = True - - if isReady[i]: - looproll = temp1 - counter_6 = 0 - while looproll == 6: - counter_6 += 1 - looproll = roll() - temp1 += looproll - print(f"you got {looproll} ") - if counter_6 == 3 : - temp1 -= 18 - print("Three consectutives 6 got cancelled") - print("") - # print(temp1) - if (players[i] + temp1) > 100: - pass - elif (players[i] + temp1) < 100: - players[i] += temp1 - players[i] = move(players[i], i) - elif (players[i] + temp1) == 100: - print(f"congrats {i} you won !!!") - imp = False + + for player in players: + if not game_active: + break + + choice = input(f"{player}'s turn (press Enter to roll or enter option): ").strip() or "1" + + try: + choice = int(choice) + except ValueError: + print("Invalid input. Please enter a number.") + continue + + if players[player] < 100: + if choice == 1: + dice_roll = roll_dice() + print(f"You rolled a {dice_roll}") + + # Check if player can start moving + if not is_ready[player] and dice_roll == 6: + is_ready[player] = True + print(f"{player} can now start moving!") + + # Process move if player is active + if is_ready[player]: + total_move = dice_roll + consecutive_sixes = 0 + + # Handle consecutive sixes + while dice_roll == 6 and consecutive_sixes < 2: + consecutive_sixes += 1 + print("You rolled a 6! Roll again...") + dice_roll = roll_dice() + print(f"You rolled a {dice_roll}") + total_move += dice_roll + + # Check for three consecutive sixes penalty + if consecutive_sixes == 2: + print("Three consecutive sixes! You lose your turn.") + total_move = 0 + + # Calculate new position + new_position = players[player] + total_move + + # Validate move + if new_position > 100: + new_position = 100 - (new_position - 100) + print(f"Overshot! You bounce back to {new_position}") + elif new_position == 100: + print(f"Congratulations, {player}! You won the game!") + game_active = False return - - print(f"you are at position {players[i]}") - - elif n == 2: - players = {} # stores player ans their locations - isReady = {} - current_loc = 0 # vaiable for iterating location - player_input() - - elif n == 3: - print("Bye Bye") - imp = False - + + # Apply snakes and ladders + players[player] = new_position + players[player] = check_snakes(players[player], player) + players[player] = check_ladders(players[player], player) + + print(f"{player} is now at position {players[player]}") + + elif choice == 2: + # Reset game state + players = {} + is_ready = {} + current_position = 1 + initialize_players() + return + + elif choice == 3: + print("Thanks for playing! Goodbye.") + game_active = False + return + else: - print("pls enter a valid input") - - -# Move method -def move(a, i): - global players - global imp - temp_loc = players[i] - - if (temp_loc) < 100: - temp_loc = ladder(temp_loc, i) - temp_loc = snake(temp_loc, i) - - return temp_loc - - -# snake bite code -def snake(c, i): - if (c == 32): - players[i] = 10 - elif (c == 36): - players[i] = 6 - elif (c == 48): - players[i] = 26 - elif (c == 63): - players[i] = 18 - elif (c == 88): - players[i] = 24 - elif (c == 95): - players[i] = 56 - elif (c == 97): - players[i] = 78 - else: - return players[i] - print(f"You got bitten by a snake now you are at {players[i]}") - - return players[i] - - -# ladder code -def ladder(a, i): - global players - - if (a == 4): - players[i] = 14 - elif (a == 8): - players[i] = 30 - elif (a == 20): - players[i] = 38 - elif (a == 40): - players[i] = 42 - elif (a == 28): - players[i] = 76 - elif (a == 50): - players[i] = 67 - elif (a == 71): - players[i] = 92 - elif (a == 88): - players[i] = 99 - else: - return players[i] - print(f"You got a ladder now you are at {players[i]}") - - return players[i] - - -# while run: -print("/"*40) -print("Welcome to the snake ladder game !!!!!!!") -print("/"*40) - - -player_input() + print("Invalid choice. Please enter 1, 2, or 3.") + else: + print(f"{player} has already won the game!") + +def check_snakes(position: int, player: str) -> int: + """Check if the player landed on a snake""" + snakes = { + 32: 10, 36: 6, 48: 26, + 63: 18, 88: 24, 95: 56, 97: 78 + } + + if position in snakes: + new_position = snakes[position] + print(f"Snake bite! {player} slides down from {position} to {new_position}") + return new_position + return position + +def check_ladders(position: int, player: str) -> int: + """Check if the player landed on a ladder""" + ladders = { + 4: 14, 8: 30, 20: 38, 28: 76, + 40: 42, 50: 67, 71: 92, 80: 99 + } + + if position in ladders: + new_position = ladders[position] + print(f"Ladder! {player} climbs from {position} to {new_position}") + return new_position + return position + +if __name__ == "__main__": + print("/"*40) + print("Welcome to the Snake and Ladder Game!") + print("/"*40) + initialize_players() \ No newline at end of file diff --git a/BoardGame-CLI/uno.py b/BoardGame-CLI/uno.py index 4f36372a5f8..db283d5de66 100644 --- a/BoardGame-CLI/uno.py +++ b/BoardGame-CLI/uno.py @@ -1,186 +1,331 @@ -# uno game # - import random -""" -Generate the UNO deck of 108 cards. -Parameters: None -Return values: deck=>list -""" - -def buildDeck(): +def build_deck() -> list: + """ + Generate the UNO deck of 108 cards. + + Returns: + list: A list containing all 108 UNO cards. + """ deck = [] - # example card:Red 7,Green 8, Blue skip - colours = ["Red", "Green", "Yellow", "Blue"] + colors = ["Red", "Green", "Yellow", "Blue"] values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "Draw Two", "Skip", "Reverse"] wilds = ["Wild", "Wild Draw Four"] - for colour in colours: + + # Add numbered and action cards + for color in colors: for value in values: - cardVal = "{} {}".format(colour, value) - deck.append(cardVal) - if value != 0: - deck.append(cardVal) - for i in range(4): + card = f"{color} {value}" + deck.append(card) + if value != 0: # Each non-zero card appears twice + deck.append(card) + + # Add wild cards + for _ in range(4): deck.append(wilds[0]) deck.append(wilds[1]) - print(deck) + + print(f"Deck built with {len(deck)} cards.") return deck - -""" -Shuffles a list of items passed into it -Parameters: deck=>list -Return values: deck=>list -""" - - -def shuffleDeck(deck): - for cardPos in range(len(deck)): - randPos = random.randint(0, 107) - deck[cardPos], deck[randPos] = deck[randPos], deck[cardPos] +def shuffle_deck(deck: list) -> list: + """ + Shuffle the given deck of cards using Fisher-Yates algorithm. + + Args: + deck (list): The deck of cards to shuffle. + + Returns: + list: The shuffled deck. + """ + for i in range(len(deck) - 1, 0, -1): + j = random.randint(0, i) + deck[i], deck[j] = deck[j], deck[i] + print("Deck shuffled.") return deck +def draw_cards(num_cards: int, deck: list, discards: list) -> list: + """ + Draw a specified number of cards from the top of the deck. + Reshuffles discard pile if deck is empty. + + Args: + num_cards (int): Number of cards to draw. + deck (list): The deck to draw from. + discards (list): The discard pile for reshuffling. + + Returns: + list: The cards drawn from the deck. + """ + drawn_cards = [] + for _ in range(num_cards): + if not deck: # If deck is empty, reshuffle discard pile + print("Reshuffling discard pile into deck...") + deck = shuffle_deck(discards[:-1]) # Keep the top discard card + discards.clear() + discards.append(deck.pop()) # Move top card to discard pile + + drawn_cards.append(deck.pop(0)) + + return drawn_cards -"""Draw card function that draws a specified number of cards off the top of the deck -Parameters: numCards -> integer -Return: cardsDrawn -> list -""" +def show_hand(player: int, player_hand: list) -> None: + """ + Display the player's current hand in a formatted manner. + + Args: + player (int): The player number. + player_hand (list): The player's current hand of cards. + """ + print(f"\n=== {players_name[player]}'s Turn ===") + print(f"Your Hand ({len(player_hand)} cards):") + print("--------------------------------") + for i, card in enumerate(player_hand, 1): + print(f"{i}) {card}") + print("") +def can_play(current_color: str, current_value: str, player_hand: list) -> bool: + """ + Check if the player can play any card from their hand. + + Args: + current_color (str): The current color on the discard pile. + current_value (str): The current value on the discard pile. + player_hand (list): The player's current hand of cards. + + Returns: + bool: True if the player can play a card, False otherwise. + """ + for card in player_hand: + if "Wild" in card: + return True + card_color, card_value = card.split(" ", 1) + if card_color == current_color or card_value == current_value: + return True + return False -def drawCards(numCards): - cardsDrawn = [] - for x in range(numCards): - cardsDrawn.append(unoDeck.pop(0)) - return cardsDrawn +def get_valid_input(prompt: str, min_val: int, max_val: int, input_type: type = int) -> any: + """ + Get a valid input from the user within a specified range and type. + + Args: + prompt (str): The message to display to the user. + min_val (int): The minimum acceptable value (for numeric inputs). + max_val (int): The maximum acceptable value (for numeric inputs). + input_type (type): The expected data type of the input. + + Returns: + any: A valid input of the specified type. + """ + while True: + user_input = input(prompt) + + try: + if input_type == int: + value = int(user_input) + if min_val <= value <= max_val: + return value + print(f"Please enter a number between {min_val} and {max_val}.") + elif input_type == str: + if user_input.lower() in ['y', 'n']: + return user_input.lower() + print("Please enter 'y' or 'n'.") + else: + print(f"Unsupported input type: {input_type}") + return None + + except ValueError: + print(f"Invalid input. Please enter a valid {input_type.__name__}.") +def show_game_status() -> None: + """Display current game status including player hands and scores""" + print("\n=== Game Status ===") + for i, name in enumerate(players_name): + print(f"{name}: {len(players[i])} cards") + print(f"Direction: {'Clockwise' if play_direction == 1 else 'Counter-clockwise'}") + print(f"Next player: {players_name[(player_turn + play_direction) % num_players]}") + print("-------------------") -""" -Print formatted list of player's hand -Parameter: player->integer , playerHand->list -Return: None -""" +# Initialize game +uno_deck = build_deck() +uno_deck = shuffle_deck(uno_deck) +discards = [] +players_name = [] +players = [] +colors = ["Red", "Green", "Yellow", "Blue"] -def showHand(player, playerHand): - print("Player {}'s Turn".format(players_name[player])) - print("Your Hand") - print("------------------") - y = 1 - for card in playerHand: - print("{}) {}".format(y, card)) - y += 1 - print("") +# Get number of players +num_players = get_valid_input("How many players? (2-4): ", 2, 4) +# Get player names +for i in range(num_players): + while True: + name = input(f"Enter player {i+1} name: ").strip() + if name: + players_name.append(name) + break + print("Name cannot be empty. Please try again.") -""" -Check whether a player is able to play a card, or not -Parameters: discardCard->string,value->string, playerHand->list -Return: boolean -""" +print("\n=== Game Starting ===") +print(f"Players: {', '.join(players_name)}") +# Deal initial cards +for i in range(num_players): + players.append(draw_cards(7, uno_deck, discards)) + print(f"{players_name[i]} received 7 cards.") -def canPlay(colour, value, playerHand): - for card in playerHand: - if "Wild" in card: - return True - elif colour in card or value in card: - return True - return False +# Initialize game state +player_turn = 0 +play_direction = 1 # 1 for clockwise, -1 for counter-clockwise +game_active = True +# Start with first card on discard pile +discards.append(uno_deck.pop(0)) +top_card = discards[-1].split(" ", 1) +current_color = top_card[0] +current_value = top_card[1] if len(top_card) > 1 else "Any" -unoDeck = buildDeck() -unoDeck = shuffleDeck(unoDeck) -unoDeck = shuffleDeck(unoDeck) -discards = [] +# Handle wild cards as starting card +if current_color == "Wild": + print("Starting card is Wild. Choosing random color...") + current_color = random.choice(colors) -players_name = [] -players = [] -colours = ["Red", "Green", "Yellow", "Blue"] -numPlayers = int(input("How many players?")) -while numPlayers < 2 or numPlayers > 4: - numPlayers = int( - input("Invalid. Please enter a number between 2-4.\nHow many players?")) -for player in range(numPlayers): - players_name.append(input("Enter player {} name: ".format(player+1))) - players.append(drawCards(5)) - - -playerTurn = 0 -playDirection = 1 -playing = True -discards.append(unoDeck.pop(0)) -splitCard = discards[0].split(" ", 1) -currentColour = splitCard[0] -if currentColour != "Wild": - cardVal = splitCard[1] -else: - cardVal = "Any" - -while playing: - showHand(playerTurn, players[playerTurn]) - print("Card on top of discard pile: {}".format(discards[-1])) - if canPlay(currentColour, cardVal, players[playerTurn]): - cardChosen = int(input("Which card do you want to play?")) - while not canPlay(currentColour, cardVal, [players[playerTurn][cardChosen-1]]): - cardChosen = int( - input("Not a valid card. Which card do you want to play?")) - print("You played {}".format(players[playerTurn][cardChosen-1])) - discards.append(players[playerTurn].pop(cardChosen-1)) - - # cheak if player won - if len(players[playerTurn]) == 0: - playing = False - # winner = "Player {}".format(playerTurn+1) - winner = players_name[playerTurn] - else: - # cheak for special cards - splitCard = discards[-1].split(" ", 1) - currentColour = splitCard[0] - if len(splitCard) == 1: - cardVal = "Any" +print(f"\nGame begins with: {discards[-1]} ({current_color})") + +# Main game loop +while game_active: + current_hand = players[player_turn] + + # Show game status before each turn + show_game_status() + show_hand(player_turn, current_hand) + print(f"Current card: {discards[-1]} ({current_color})") + + # Check if player can play + if can_play(current_color, current_value, current_hand): + print(f"Valid moves: {[i+1 for i, card in enumerate(current_hand) if 'Wild' in card or card.startswith(current_color) or current_value in card]}") + + # Get valid card choice + card_count = len(current_hand) + card_chosen = get_valid_input( + f"Which card do you want to play? (1-{card_count}): ", 1, card_count + ) + + # Validate selected card + while True: + selected_card = current_hand[card_chosen - 1] + card_color, card_value = selected_card.split(" ", 1) if "Wild" not in selected_card else (selected_card, "") + + if "Wild" in selected_card or card_color == current_color or card_value == current_value: + break else: - cardVal = splitCard[1] - if currentColour == "Wild": - for x in range(len(colours)): - print("{}) {}".format(x+1, colours[x])) - newColour = int( - input("What colour would you like to choose? ")) - while newColour < 1 or newColour > 4: - newColour = int( - input("Invalid option. What colour would you like to choose")) - currentColour = colours[newColour-1] - if cardVal == "Reverse": - playDirection = playDirection * -1 - elif cardVal == "Skip": - playerTurn += playDirection - if playerTurn >= numPlayers: - playerTurn = 0 - elif playerTurn < 0: - playerTurn = numPlayers-1 - elif cardVal == "Draw Two": - playerDraw = playerTurn+playDirection - if playerDraw == numPlayers: - playerDraw = 0 - elif playerDraw < 0: - playerDraw = numPlayers-1 - players[playerDraw].extend(drawCards(2)) - elif cardVal == "Draw Four": - playerDraw = playerTurn+playDirection - if playerDraw == numPlayers: - playerDraw = 0 - elif playerDraw < 0: - playerDraw = numPlayers-1 - players[playerDraw].extend(drawCards(4)) - print("") + print("Invalid card selection.") + card_chosen = get_valid_input( + f"Please choose a valid card. (1-{card_count}): ", 1, card_count + ) + + # Play the card + played_card = current_hand.pop(card_chosen - 1) + discards.append(played_card) + print(f"\n{players_name[player_turn]} played: {played_card}") + + # Check if player won + if not current_hand: + game_active = False + winner = players_name[player_turn] + print("\n=== Game Over! ===") + print(f"{winner} is the Winner!") + break + + # Process special cards + top_card = discards[-1].split(" ", 1) + current_color = top_card[0] + current_value = top_card[1] if len(top_card) > 1 else "Any" + + # Handle wild cards + if current_color == "Wild": + print("\n=== Wild Card ===") + print("Choose a new color:") + for i, color in enumerate(colors, 1): + print(f"{i}) {color}") + color_choice = get_valid_input("Enter color choice (1-4): ", 1, 4) + current_color = colors[color_choice - 1] + print(f"Color changed to {current_color}") + + # Handle action cards + if current_value == "Reverse": + play_direction *= -1 + print("\n=== Reverse ===") + print("Direction reversed!") + elif current_value == "Skip": + skipped_player = (player_turn + play_direction) % num_players + player_turn = skipped_player + print("\n=== Skip ===") + print(f"{players_name[skipped_player]} has been skipped!") + elif current_value == "Draw Two": + next_player = (player_turn + play_direction) % num_players + drawn_cards = draw_cards(2, uno_deck, discards) + players[next_player].extend(drawn_cards) + print("\n=== Draw Two ===") + print(f"{players_name[next_player]} draws 2 cards!") + elif current_value == "Draw Four": + next_player = (player_turn + play_direction) % num_players + drawn_cards = draw_cards(4, uno_deck, discards) + players[next_player].extend(drawn_cards) + print("\n=== Draw Four ===") + print(f"{players_name[next_player]} draws 4 cards!") else: - print("You can't play. You have to draw a card.") - players[playerTurn].extend(drawCards(1)) - - playerTurn += playDirection - if playerTurn >= numPlayers: - playerTurn = 0 - elif playerTurn < 0: - playerTurn = numPlayers-1 - -print("Game Over") -print("{} is the Winner!".format(winner)) + print("\nYou can't play. Drawing a card...") + drawn_card = draw_cards(1, uno_deck, discards)[0] + players[player_turn].append(drawn_card) + print(f"You drew: {drawn_card}") + + # Check if drawn card can be played + card_color, card_value = drawn_card.split(" ", 1) if "Wild" not in drawn_card else (drawn_card, "") + if "Wild" in drawn_card or card_color == current_color or card_value == current_value: + print("You can play the drawn card!") + play_choice = get_valid_input("Do you want to play it? (y/n): ", 0, 1, str) + if play_choice == 'y': + players[player_turn].remove(drawn_card) + discards.append(drawn_card) + print(f"\n{players_name[player_turn]} played: {drawn_card}") + + # Process special cards (similar to above) + top_card = discards[-1].split(" ", 1) + current_color = top_card[0] + current_value = top_card[1] if len(top_card) > 1 else "Any" + + if current_color == "Wild": + print("\n=== Wild Card ===") + print("Choose a new color:") + for i, color in enumerate(colors, 1): + print(f"{i}) {color}") + color_choice = get_valid_input("Enter color choice (1-4): ", 1, 4) + current_color = colors[color_choice - 1] + print(f"Color changed to {current_color}") + + if current_value == "Reverse": + play_direction *= -1 + print("\n=== Reverse ===") + print("Direction reversed!") + elif current_value == "Skip": + skipped_player = (player_turn + play_direction) % num_players + player_turn = skipped_player + print("\n=== Skip ===") + print(f"{players_name[skipped_player]} has been skipped!") + elif current_value == "Draw Two": + next_player = (player_turn + play_direction) % num_players + drawn_cards = draw_cards(2, uno_deck, discards) + players[next_player].extend(drawn_cards) + print("\n=== Draw Two ===") + print(f"{players_name[next_player]} draws 2 cards!") + elif current_value == "Draw Four": + next_player = (player_turn + play_direction) % num_players + drawn_cards = draw_cards(4, uno_deck, discards) + players[next_player].extend(drawn_cards) + print("\n=== Draw Four ===") + print(f"{players_name[next_player]} draws 4 cards!") + + # Move to next player + player_turn = (player_turn + play_direction) % num_players \ No newline at end of file diff --git a/BrowserHistory/backend.py b/BrowserHistory/backendBrowserHistory.py similarity index 100% rename from BrowserHistory/backend.py rename to BrowserHistory/backendBrowserHistory.py diff --git a/BrowserHistory/tests/test_browser_history.py b/BrowserHistory/tests/test_browser_history.py index 829f326c238..60a4974feac 100644 --- a/BrowserHistory/tests/test_browser_history.py +++ b/BrowserHistory/tests/test_browser_history.py @@ -4,7 +4,8 @@ # Add parent directory to path to import backend sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from backend import BrowserHistory + +from backendBrowserHistory import BrowserHistory class TestBrowserHistory(unittest.TestCase): def setUp(self): diff --git a/CRC/crc.py b/CRC/crc.py index d3d302244b7..0a533ba9b51 100644 --- a/CRC/crc.py +++ b/CRC/crc.py @@ -1,55 +1,94 @@ -def crc_check(data, div): - l = len(div) - ct = 0 - data = [int(i) for i in data] - div = [int(i) for i in div] - zero = [0 for i in range(l)] - temp_data = [data[i] for i in range(l)] - result = [] - for j in range(len(data) - len(div) + 1): +from typing import List + +def crc_check(data: str, div: str) -> List[int]: + """ + Perform CRC (Cyclic Redundancy Check) calculation. + + Args: + data: Input data string (binary digits) + div: Divisor string (binary digits, typically a polynomial) + + Returns: + List of integers representing the CRC remainder + """ + divisor_length: int = len(div) # Renamed from 'l' to 'divisor_length' + data_list: List[int] = [int(i) for i in data] + div_list: List[int] = [int(i) for i in div] + zero: List[int] = [0] * divisor_length + temp_data: List[int] = data_list[:divisor_length] + result: List[int] = [] + + for j in range(len(data_list) - divisor_length + 1): print("Temp_dividend", temp_data) - msb = temp_data[0] + msb: int = temp_data[0] + if msb == 0: result.append(0) - for i in range(l - 1, -1, -1): - temp_data[i] = temp_data[i] ^ zero[i] + temp_data = [temp_data[i] ^ zero[i] for i in range(divisor_length-1, -1, -1)] else: result.append(1) - for i in range(l - 1, -1, -1): - temp_data[i] = temp_data[i] ^ div[i] + temp_data = [temp_data[i] ^ div_list[i] for i in range(divisor_length-1, -1, -1)] + temp_data.pop(0) - if l + j < len(data): - temp_data.append(data[l + j]) - crc = temp_data + if divisor_length + j < len(data_list): + temp_data.append(data_list[divisor_length + j]) + + crc: List[int] = temp_data print("Quotient: ", result, "remainder", crc) return crc +def validate_binary_input(input_str: str) -> bool: + """Check if input string contains only 0s and 1s""" + return all(c in {'0', '1'} for c in input_str) -# returning crc value - +def main() -> None: + """Main program loop for CRC calculation and verification""" + while True: + try: + # Get input from user + data: str = input("Enter data: ").strip() + if not validate_binary_input(data): + raise ValueError("Data must be a binary string (0s and 1s)") + + div: str = input("Enter divisor: ").strip() + if not validate_binary_input(div): + raise ValueError("Divisor must be a binary string (0s and 1s)") + if len(div) < 2: + raise ValueError("Divisor length must be at least 2") + + original_data: str = data + padded_data: str = data + "0" * (len(div) - 1) + + # Calculate CRC + crc: List[int] = crc_check(padded_data, div) + crc_str: str = ''.join(str(c) for c in crc) + + # Display sent data + sent_data: str = original_data + crc_str + print("Sent data: ", sent_data) + + # Verify CRC at receiver side + print("If again applying CRC algorithm, the remainder/CRC must be zero if errorless.") + receiver_crc: List[int] = crc_check(sent_data, div) + print("Receiver side remainder: ", receiver_crc) + + # Check if remainder is zero + if all(bit == 0 for bit in receiver_crc): + print("CRC verification successful - no detected errors.") + else: + print("CRC verification failed - errors detected.") + + except ValueError as ve: + print(f"Input Error: {ve}") + continue + except Exception as e: + print(f"An unexpected error occurred: {e}") + continue + + # Ask user to continue + ch: str = input("Continue [Y/N]: ").strip().upper() + if ch == "N": + break -while 1 > 0: - print("Enter data: ") - data = input() # can use it like int(input()) - print("Enter divisor") - div = input() # can use it like int(input()) - original_data = data - data = data + ("0" * (len(div) - 1)) - crc = crc_check(data, div) - crc_str = "" - for c in crc: - crc_str += c - print("Sent data: ", original_data + crc_str) - sent_data = original_data + crc_str - print( - "If again applying CRC algorithm, the remainder/CRC must be zero if errorless." - ) - crc = crc_check(sent_data, div) - remainder = crc - print("Receiver side remainder: ", remainder) - print("Continue [Y/N]:") - ch = input() - if ch == "N" or ch == "n": - break - else: - continue +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/CliYoutubeDownloader/requirements.txt b/CliYoutubeDownloader/requirements.txt deleted file mode 100644 index 30257302458..00000000000 --- a/CliYoutubeDownloader/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pytube \ No newline at end of file diff --git a/Colors/multicoloredline.py b/Colors/multicoloredline.py index fdbe1c45881..b832348c41a 100644 --- a/Colors/multicoloredline.py +++ b/Colors/multicoloredline.py @@ -2,23 +2,24 @@ from rich.syntax import Syntax from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn from rich.table import Table +from typing import Dict, Any import time import json -console = Console() +console: Console = Console() # Fancy separator console.rule("[bold]Welcome to Rich Terminal[/bold]", style="rainbow") # Define some JSON data -json_data = { +json_data: Dict[str, Any] = { "message": "Hello, World!", "status": "success", "code": 200 } # Print JSON with syntax highlighting -syntax = Syntax(json.dumps(json_data, indent=4), "json", theme="monokai", line_numbers=True) +syntax: Syntax = Syntax(json.dumps(json_data, indent=4), "json", theme="monokai", line_numbers=True) console.print(syntax) # Simulating a progress bar @@ -31,15 +32,15 @@ TextColumn("{task.percentage:>3.0f}%"), console=console, ) as progress: - task = progress.add_task("[cyan]Loading...", total=100) + task_id: int = progress.add_task("[cyan]Loading...", total=100) for _ in range(100): time.sleep(0.02) - progress.update(task, advance=1) + progress.update(task_id, advance=1) # Create a rich table console.print("\n[bold magenta]Results Summary:[/bold magenta]\n") -table = Table(title="System Report", show_header=True, header_style="bold cyan") +table: Table = Table(title="System Report", show_header=True, header_style="bold cyan") table.add_column("Metric", style="bold yellow") table.add_column("Value", justify="right", style="bold green") @@ -51,4 +52,4 @@ # Success message console.print("\n[bold green]🎉 Process completed successfully![/bold green]\n") -console.rule(style="rainbow") +console.rule(style="rainbow") \ No newline at end of file diff --git a/Colors/pixel_sort.py b/Colors/pixel_sort.py index 920e034817f..937f44892f9 100644 --- a/Colors/pixel_sort.py +++ b/Colors/pixel_sort.py @@ -1,169 +1,233 @@ -"""Pixel Sorting""" +"""Pixel Sorting with Audio Visualization + +This script processes an image by sorting its pixels based on color attributes, +generates a video of the sorting process, and converts pixel data into audio. +""" -# Importing Libraries import cv2 import numpy as np import math import colorsys import pandas as pd -import os import argparse from tqdm import tqdm - -# Importing the external file Library -import sound - -# Taking arguments from command line -parser = argparse.ArgumentParser() # you iniatize as such -parser.add_argument("-f", required=True, help="enter fileName of your picture") -# parser.add_argument("-s", required=True, help="Speed factor of the audio to be increased or decreased") -# parser.add_argument("-av", required=True, help="Speed factor of the audio visualizer to be increased or decreased") - -# the add_argument tells you what needs to be given as an input sp its help -args = parser.parse_args() # you take the arguments from command line - -os.makedirs("Image_sort/" + str(args.f)) -print(str(args.f).capitalize() + " directory is created.") - -# Defining all global variables -df = [] -total = 0 -dict, final, img_list = {}, [], [] - -# Create dataframe and save it as an excel file -def createDataSet(val=0, data=[]): - global dict - dict[len(data)] = data - if val != 0: - if val == max(dict.keys()): - final_df = pd.DataFrame(dict[val], columns=["Blue", "Green", "Red"]) - final_df.to_excel("Image_sort/" + str(args.f) + "/" + "output.xlsx") - - -# Generating colors for each row of the frame -def generateColors(c_sorted, frame, row): - global df, img_list - height = 15 - img = np.zeros((height, len(c_sorted), 3), np.uint8) - for x in range(0, len(c_sorted)): - r, g, b = c_sorted[x][0] * 255, c_sorted[x][1] * 255, c_sorted[x][2] * 255 - c = [r, g, b] - df.append(c) - img[:, x] = c # the color value for the xth column , this gives the color band - frame[row, x] = c # changes added for every row in the frame - - createDataSet(data=df) - return img, frame - - -# Measures the total number of pixels that were involved in pixel sort -def measure(count, row, col, height, width): - global total - total += count - if row == height - 1 and col == width - 1: - createDataSet(val=total) - - -# Step Sorting Algorithm -def step(bgr, repetitions=1): +from scipy import signal +from pathlib import Path +from typing import Tuple + +# Configuration - Modify these for different results +AUDIO_SAMPLE_RATE: int = 44100 # Audio sampling rate (Hz) +AUDIO_DURATION: float = 10 # Audio duration (seconds) +BASE_FREQUENCY: float = 220 # Lowest frequency for audio conversion (Hz) +MAX_FREQUENCY: float = 880 # Highest frequency for audio conversion (Hz) +SORT_REPETITIONS: int = 8 # Controls color sorting smoothness +OUTPUT_VIDEO_FPS: int = 16 # Frames per second for output video + +def parse_arguments() -> argparse.Namespace: + """Parse command line arguments with default values""" + parser: argparse.ArgumentParser = argparse.ArgumentParser(description="Pixel sorting with audio visualization") + parser.add_argument("-f", "--filename", required=True, + help="Input image filename (without extension)") + parser.add_argument("-i", "--input_dir", default="Image", + help="Input directory for images (default: Image)") + parser.add_argument("-o", "--output_dir", default="Image_sort", + help="Output directory for results (default: Image_sort)") + parser.add_argument("-d", "--duration", type=float, default=AUDIO_DURATION, + help="Audio duration in seconds (default: 10)") + return parser.parse_args() + +def validate_input_image(args: argparse.Namespace) -> str: + """Validate input image exists and return its path""" + image_path: Path = Path(args.input_dir) / f"{args.filename}.jpg" + if not image_path.exists(): + print(f"Error: Image file '{image_path}' not found") + exit(1) + return str(image_path) + +def create_output_directories(args: argparse.Namespace) -> Path: + """Create necessary output directories""" + output_base: Path = Path(args.output_dir) + output_subdir: Path = output_base / args.filename + output_subdir.mkdir(parents=True, exist_ok=True) + print(f"Output directory created: {output_subdir}") + return output_subdir + +def sort_pixels_by_row(img: np.ndarray) -> np.ndarray: + """Sort image pixels row by row using HSV color space""" + height, width = img.shape[:2] + sorted_img: np.ndarray = img.copy().astype(np.float32) / 255.0 # Normalize to [0,1] + + for row in tqdm(range(height), desc="Sorting rows"): + # Extract row pixels and sort based on HSV luminance + row_pixels: np.ndarray = sorted_img[row].copy() + row_pixels_sorted: np.ndarray = sort_pixels_by_hsv(row_pixels) + sorted_img[row] = row_pixels_sorted + + return (sorted_img * 255).astype(np.uint8) # Convert back to [0,255] + +def sort_pixels_by_hsv(pixels: np.ndarray) -> np.ndarray: + """Sort pixels using HSV color space for better visual coherence""" + # Calculate HSV-based sorting key for each pixel + sort_keys: np.ndarray = np.array([step_sort_key(pixel) for pixel in pixels]) + # Sort pixels based on the computed keys + sort_indices: np.ndarray = np.argsort(sort_keys, axis=0)[:, 0] + return pixels[sort_indices] + +def step_sort_key(bgr: np.ndarray, repetitions: int = SORT_REPETITIONS) -> Tuple[int, float, int]: + """Generate sort key based on HSV color space and luminance""" b, g, r = bgr - # lum is calculated as per the way the humans view the colors - lum = math.sqrt(0.241 * r + 0.691 * g + 0.068 * b) - - # conversion of rgb to hsv values - h, s, v = colorsys.rgb_to_hsv( - r, g, b - ) # h,s,v is a better option for classifying each color - - # Repetitions are taken to decrease the noise - h2 = int(h * repetitions) - v2 = int(v * repetitions) - - # To get a smoother color band - if h2 % 2 == 1: - v2 = repetitions - v2 - lum = repetitions - lum - - return h2, lum, v2 - - -# Threshold set for avoiding extreme sorting of the pixels -def findThreshold(lst, add): - for i in lst: - add.append(sum(i)) - return (max(add) + min(add)) / 2 - - -def makeVideo(): - out = cv2.VideoWriter( - "Image_sort/" + str(args.f) + "/" + str(args.f) + ".mp4", - cv2.VideoWriter_fourcc(*"mp4v"), - 16, - (800, 500), - ) - for count in tqdm(range(1, 500 + 1)): - fileName = "Image_sort/" + str(args.f) + "/" + str(count) + ".jpg" - img = cv2.imread(fileName) - out.write(img) - os.remove(fileName) - out.release() - - -def main(): - global img_list - img = cv2.imread("Image/" + str(args.f) + ".jpg") - img = cv2.resize(img, (800, 500)) - img_list.append(img) - - height, width, _ = img.shape - print(">>> Row-wise Color sorting") - for row in tqdm(range(0, height)): - color, color_n = [], [] - add = [] - - for col in range(0, width): - val = img[row][col].tolist() - - # val includes all rgb values between the range of 0 to 1 - # This makes the sorting easier and efficient - val = [i / 255.0 for i in val] - color.append(val) - - thresh = findThreshold( - color, add - ) # setting the threshold value for every row in the frame - - # For the specific row , if all the values are non-zero then it is sorted with color - if np.all(np.asarray(color)) == True: - color.sort(key=lambda bgr: step(bgr, 8)) # step sorting - band, img = generateColors(color, img, row) - measure(len(color), row, col, height, width) - - # For the specific row , if any of the values are zero it gets sorted with color_n - if np.all(np.asarray(color)) == False: - for ind, i in enumerate(color): - # Accessing every list within color - # Added to color_n if any of the element in the list is non-zero - # and their sum is less than threshold value - - if np.any(np.asarray(i)) == True and sum(i) < thresh: - color_n.append(i) - - color_n.sort(key=lambda bgr: step(bgr, 8)) # step sorting - band, img = generateColors(color_n, img, row) - measure(len(color_n), row, col, height, width) - cv2.imwrite("Image_sort/" + str(args.f) + "/" + str(row + 1) + ".jpg", img) - - # Writing down the final sorted image - cv2.imwrite( - "Image_sort/" + str(args.f) + "/" + str(args.f) + ".jpg", img - ) # Displaying the final picture - - print("\n>>> Formation of the Video progress of the pixel-sorted image") - makeVideo() - sound.main( - args.f - ) # Calling the external python file to create the audio of the pixel-sorted image - - -main() + # Calculate luminance (weighted for human perception) + luminance: float = math.sqrt(0.241 * r + 0.691 * g + 0.068 * b) + # Convert to HSV for better color sorting + h, s, v = colorsys.rgb_to_hsv(r, g, b) + # Adjust for repetitions to reduce noise and create smoother bands + h_scaled: int = int(h * repetitions) + v_scaled: int = int(v * repetitions) + # Invert for smoother transitions between colors + if h_scaled % 2 == 1: + v_scaled = repetitions - v_scaled + luminance = repetitions - luminance + return (h_scaled, luminance, v_scaled) + +def generate_sorting_video(original_img: np.ndarray, sorted_img: np.ndarray, output_path: Path, fps: int = OUTPUT_VIDEO_FPS) -> None: + """Generate video showing the pixel sorting process""" + print("\n>>> Generating sorting process video...") + height, width = original_img.shape[:2] + fourcc: int = cv2.VideoWriter_fourcc(*"mp4v") + video_writer: cv2.VideoWriter = cv2.VideoWriter(str(output_path), fourcc, fps, (width, height)) + + # Create intermediate frames showing progressive sorting + for row in tqdm(range(height), desc="Creating video frames"): + frame: np.ndarray = original_img.copy() + frame[:row+1] = sorted_img[:row+1] + video_writer.write(frame) + + # Add a few extra frames of the fully sorted image + for _ in range(fps): + video_writer.write(sorted_img) + + video_writer.release() + print(f"Video saved to: {output_path}") + +def save_pixel_data(pixels: np.ndarray, output_path: Path) -> None: + """Save pixel data to Excel file for analysis""" + try: + df: pd.DataFrame = pd.DataFrame(pixels.reshape(-1, 3), columns=["Blue", "Green", "Red"]) + df.to_excel(str(output_path), index=False) + print(f"Pixel data saved to: {output_path}") + except Exception as e: + print(f"Warning: Could not save pixel data - {e}") + +def pixels_to_audio(pixels: np.ndarray, duration: float = AUDIO_DURATION, sample_rate: int = AUDIO_SAMPLE_RATE) -> np.ndarray: + """Convert pixel data to audio signal""" + print("\n>>> Generating audio from pixel data...") + num_samples: int = int(sample_rate * duration) + + # Resize pixel data to match audio length + if pixels.size > 0: + # Flatten and normalize pixel data + pixels_flat: np.ndarray = pixels.reshape(-1, 3) / 255.0 + # Interpolate to match audio sample count + x_old: np.ndarray = np.linspace(0, 1, len(pixels_flat)) + x_new: np.ndarray = np.linspace(0, 1, num_samples) + pixel_data: np.ndarray = np.array([ + np.interp(x_new, x_old, pixels_flat[:, i]) for i in range(3) + ]).T + else: + # Generate random pixel data if input is empty + pixel_data: np.ndarray = np.random.rand(num_samples, 3) + + # Map pixel channels to audio parameters + frequencies: np.ndarray = calculate_frequencies(pixel_data) + amplitudes: np.ndarray = calculate_amplitudes(pixel_data) + waveforms: np.ndarray = calculate_waveforms(pixel_data) + + # Generate audio signal + t: np.ndarray = np.linspace(0, duration, num_samples, endpoint=False) + audio_signal: np.ndarray = np.zeros(num_samples) + + for i in range(num_samples): + # Generate waveform based on pixel data + if waveforms[i] < 0.25: # Sine wave + audio_signal[i] = amplitudes[i] * np.sin(2 * np.pi * frequencies[i] * t[i]) + elif waveforms[i] < 0.5: # Square wave + audio_signal[i] = amplitudes[i] * signal.square(2 * np.pi * frequencies[i] * t[i]) + elif waveforms[i] < 0.75: # Triangle wave + audio_signal[i] = amplitudes[i] * signal.sawtooth(2 * np.pi * frequencies[i] * t[i], width=0.5) + else: # Sawtooth wave + audio_signal[i] = amplitudes[i] * signal.sawtooth(2 * np.pi * frequencies[i] * t[i]) + + # Normalize audio signal + if np.max(np.abs(audio_signal)) > 0: + audio_signal /= np.max(np.abs(audio_signal)) + + return audio_signal + +def calculate_frequencies(pixels: np.ndarray) -> np.ndarray: + """Map pixel red channel to audio frequencies""" + return BASE_FREQUENCY + pixels[:, 2] * (MAX_FREQUENCY - BASE_FREQUENCY) + +def calculate_amplitudes(pixels: np.ndarray) -> np.ndarray: + """Map pixel green channel to audio amplitudes""" + return 0.1 + pixels[:, 1] * 0.7 # Range [0.1, 0.8] + +def calculate_waveforms(pixels: np.ndarray) -> np.ndarray: + """Map pixel blue channel to waveform types""" + return pixels[:, 0] # Range [0, 1] + +def save_audio(audio_signal: np.ndarray, output_path: Path, sample_rate: int = AUDIO_SAMPLE_RATE) -> None: + """Save audio signal to WAV file""" + try: + from scipy.io import wavfile + # Convert to 16-bit integer format + audio_int16: np.ndarray = (audio_signal * 32767).astype(np.int16) + wavfile.write(str(output_path), sample_rate, audio_int16) + print(f"Audio saved to: {output_path}") + except Exception as e: + print(f"Error saving audio: {e}") + print("Hint: Ensure scipy is installed (pip install scipy)") + +def main() -> None: + """Main processing pipeline""" + # Parse command line arguments + args: argparse.Namespace = parse_arguments() + + # Validate input and create output directories + input_path: str = validate_input_image(args) + output_dir: Path = create_output_directories(args) + + # Load and process image + try: + img: np.ndarray = cv2.imread(input_path) + img = cv2.resize(img, (800, 500)) + print(f"Loaded image: {input_path} ({img.shape[1]}x{img.shape[0]})") + except Exception as e: + print(f"Error loading image: {e}") + exit(1) + + # Perform pixel sorting + sorted_img: np.ndarray = sort_pixels_by_row(img) + + # Save sorted image + sorted_img_path: Path = output_dir / f"{args.filename}.jpg" + cv2.imwrite(str(sorted_img_path), sorted_img) + print(f"Sorted image saved to: {sorted_img_path}") + + # Generate and save video + video_path: Path = output_dir / f"{args.filename}.mp4" + generate_sorting_video(img, sorted_img, video_path) + + # Save pixel data + pixel_data_path: Path = output_dir / "pixel_data.xlsx" + save_pixel_data(sorted_img, pixel_data_path) + + # Generate and save audio + audio_path: Path = output_dir / f"{args.filename}.wav" + audio_signal: np.ndarray = pixels_to_audio(sorted_img, args.duration) + save_audio(audio_signal, audio_path) + + print("\n=== Processing Complete ===") + print(f"All results saved to: {output_dir}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Colors/primary_colors.py b/Colors/primary_colors.py index 107056fbd7d..949e5122583 100644 --- a/Colors/primary_colors.py +++ b/Colors/primary_colors.py @@ -1,180 +1,194 @@ -def diff(a, b): +def diff(a: int | float, b: int | float) -> int | float: """ - TODO: fix this function!! + Calculate the absolute difference between two values. + This helps in determining the variance between color channels. + + Args: + a (int/float): First value + b (int/float): Second value + + Returns: + int/float: Absolute difference between a and b """ - return a - b + return abs(a - b) # Fixed to return absolute difference (critical for color comparison) -def simpleColor(r, g, b): - """simpleColor obtiene el nombre del color mas general al cual se acerca su formato R G B""" - r = int(r) - g = int(g) - b = int(b) - bg = ir = 0 # TODO: Fix these variables +def simpleColor(r: int | float, g: int | float, b: int | float) -> str: + """ + Determines the general color name a given RGB value approximates to. + Classification is based on comparing the intensity of red, green, and blue channels, + as well as their mutual differences. + + Args: + r (int/float): Red channel value (0-255) + g (int/float): Green channel value (0-255) + b (int/float): Blue channel value (0-255) + + Returns: + str: General color name (e.g., "ROJO", "VERDE") or error message if invalid + """ try: - # ROJO -------------------------------------------------- + # Convert inputs to integers and validate range + r = int(r) + g = int(g) + b = int(b) + + if not (0 <= r <= 255 and 0 <= g <= 255 and 0 <= b <= 255): + return "Error: RGB values must be between 0 and 255" + + # RED DOMINANT -------------------------------------------------- if r > g and r > b: + red_green_diff = diff(r, g) # Difference between red and green + red_blue_diff = diff(r, b) # Difference between red and blue - rg = diff(r, g) # distancia rojo a verde - rb = diff(r, b) # distancia rojo a azul - - if g < 65 and b < 65 and rg > 60: # azul y verde sin luz + # Pure red (green and blue are very low) + if g < 65 and b < 65 and red_green_diff > 60: return "ROJO" - gb = diff(g, b) # distancia de verde a azul + green_blue_diff = diff(g, b) # Difference between green and blue - if rg < rb: # Verde mayor que Azul - if gb < rg: # Verde mas cerca de Azul - if gb >= 30 and rg >= 80: + # Green is more prominent than blue + if red_green_diff < red_blue_diff: + if green_blue_diff < red_green_diff: # Green closer to blue + if green_blue_diff >= 30 and red_green_diff >= 80: return "NARANJA" - elif gb <= 20 and rg >= 80: + elif green_blue_diff <= 20 and red_green_diff >= 80: return "ROJO" - elif gb <= 20 and b > 175: + elif green_blue_diff <= 20 and b > 175: return "CREMA" - else: return "CHOCOLATE" - else: # Verde mas cerca de Rojo - if rg > 60: + else: # Green closer to red + if red_green_diff > 60: return "NARANJA*" elif r > 125: return "AMARILLO" else: - return "COCHOLATE" - elif rg > rb: # Azul mayor que verde - if bg < rb: # Verde mas cerca de Azul - if gb < 60: - if r > 150: - return "ROJO 2" - else: - return "MARRON" + return "CHOCOLATE" # Fixed typo from "COCHOLATE" + + # Blue is more prominent than green + elif red_green_diff > red_blue_diff: + if green_blue_diff < red_blue_diff: # Green closer to blue + if green_blue_diff < 60: + return "ROJO 2" if r > 150 else "MARRON" elif g > 125: return "ROSADO" else: return "ROJO 3" - else: # Verde mas cerca de Rojo - if rb < 60: - if r > 160: - return "ROSADO*" - else: - return "ROJO" + else: # Green closer to red + if red_blue_diff < 60: + return "ROSADO*" if r > 160 else "ROJO" else: return "ROJO" - - else: # g y b iguales - if rg > 20: - if r >= 100 and b < 60: - return "ROJO" - elif r >= 100: - return "ROJO" - else: - return "MARRON" - + + # Green and blue are nearly equal + else: + if red_green_diff > 20: + return "ROJO" if (r >= 100 and (b < 60 or r >= 100)) else "MARRON" else: return "GRIS" - # VERDE --------------------------------------------------- + + # GREEN DOMINANT --------------------------------------------------- elif g > r and g > b: - gb = diff(g, b) # distancia verde a azul - gr = diff(g, r) # distancia verde a rojo + green_blue_diff = diff(g, b) # Difference between green and blue + green_red_diff = diff(g, r) # Difference between green and red - if r < 65 and b < 65 and gb > 60: # rojo y azul sin luz + # Pure green (red and blue are very low) + if r < 65 and b < 65 and green_blue_diff > 60: return "VERDE" - rb = diff(r, b) # distancia de rojo a azul - - if r > b: # ROJO > AZUL - if gr < gb: # Verde con Rojo - - if rb >= 150 and gr <= 20: - return "AMARILLO" - else: - return "VERDE" - else: # ...Verde - return "VERDE" - - elif r < b: # AZUL > ROJO - if gb < gr: # Verde con Azul - - if gb <= 20: - return "TURQUESA" - else: - return "VERDE" - else: # ...Verde - return "VERDE" + red_blue_diff = diff(r, b) # Difference between red and blue - else: # r y b iguales - if gb > 10: + # Red is more prominent than blue + if r > b: + if green_red_diff < green_blue_diff: # Green mixed with red + return "AMARILLO" if (red_blue_diff >= 150 and green_red_diff <= 20) else "VERDE" + else: return "VERDE" + + # Blue is more prominent than red + elif r < b: + if green_blue_diff < green_red_diff: # Green mixed with blue + return "TURQUESA" if green_blue_diff <= 20 else "VERDE" else: - return "GRIS" + return "VERDE" + + # Red and blue are nearly equal + else: + return "VERDE" if green_blue_diff > 10 else "GRIS" - # AZUL ------------------------------------------------------ + # BLUE DOMINANT ------------------------------------------------------ elif b > r and b > g: - bg = diff(b, g) # distancia azul a verde - br = diff(b, r) # distancia azul a rojo + blue_green_diff = diff(b, g) # Difference between blue and green + blue_red_diff = diff(b, r) # Difference between blue and red - if r < 65 and g < 65 and bg > 60: # rojo y verde sin luz + # Pure blue (red and green are very low) + if r < 65 and g < 65 and blue_green_diff > 60: return "AZUL" - rg = diff(r, g) # distancia de rojo a verde + red_green_diff = diff(r, g) # Difference between red and green - if g < r: # ROJO > VERDE - if bg < rg: # Azul con Verde - if bg <= 20: - return "TURQUESA" - else: - return "CELESTE" - else: # ...Azul - if rg <= 20: - if r >= 150: - return "LILA" - else: - return "AZUL *************" + # Red is more prominent than green + if g < r: + if blue_green_diff < red_green_diff: # Blue mixed with green + return "TURQUESA" if blue_green_diff <= 20 else "CELESTE" + else: + if red_green_diff <= 20: + return "LILA" if r >= 150 else "AZUL *************" else: return "AZUL" - - elif g > r: # VERDE > ROJO - if br < rg: # Azul con rojo - if br <= 20: + + # Green is more prominent than red + elif g > r: + if blue_red_diff < red_green_diff: # Blue mixed with red + if blue_red_diff <= 20: if r > 150 and g < 75: return "ROSADO FIUSHA" - elif ir > 150: + elif r > 150: # Fixed undefined variable "ir" to "r" return "LILA" else: return "MORADO" else: return "MORADO" - - else: # ...Azul - if rg <= 20: - if bg <= 20: - return "GRIS" - else: - return "AZUL" - else: # r y g iguales - if bg > 20: - if r >= 100 and b < 60: - return "ROJO" - elif r >= 100: - return "ROJO" + else: + if red_green_diff <= 20: + return "GRIS" if blue_green_diff <= 20 else "AZUL" else: - return "MARRON" + return "AZUL" + + # Red and green are nearly equal + else: + if blue_green_diff > 20: + return "ROJO" if (r >= 100 and (b < 60 or r >= 100)) else "MARRON" else: return "GRIS" - # IGUALES--------------------------------------- + # ALL CHANNELS NEARLY EQUAL --------------------------------------- else: return "GRIS" - except: - - return "Not Color" + except Exception as e: + return f"Error: Invalid input - {str(e)}" -# --------------------------------------------------------------------------------------------------- -# Puedes probar asi: python primary_colors.py 120,0,0 , esto resultara en un ROJO como respuesta -# -------------------------------------------------------------------------------------------------- if __name__ == "__main__": import sys - print(simpleColor(sys.argv[1], sys.argv[2], sys.argv[3])) + # Set default RGB values if no arguments are provided + default_r: int = 255 + default_g: int = 0 + default_b: int = 0 # Default to red + + # Parse command line arguments with fallback to defaults + try: + if len(sys.argv) == 4: + r, g, b = sys.argv[1], sys.argv[2], sys.argv[3] + else: + print(f"No arguments provided. Using default RGB: ({default_r}, {default_g}, {default_b})") + r, g, b = default_r, default_g, default_b + except IndexError: + print(f"Invalid arguments. Using default RGB: ({default_r}, {default_g}, {default_b})") + r, g, b = default_r, default_g, default_b + + # Get and print the color result + print(simpleColor(r, g, b)) \ No newline at end of file diff --git a/Colors/print_colors.py b/Colors/print_colors.py index 6aacaa9d4b4..389fb625d55 100644 --- a/Colors/print_colors.py +++ b/Colors/print_colors.py @@ -1,15 +1,44 @@ import sys +from typing import NoReturn + class colors: - CYAN = "\033[36m" - GREEN = "\033[32m" - YELLOW = "\033[33m" - BLUE = "\033[34m" - RED = "\033[31m" - ENDC = "\033[0m" -def printc(color, message): - print(color + message + colors.ENDC) -printc(colors.CYAN, sys.argv[1]) -printc(colors.GREEN, sys.argv[1]) -printc(colors.YELLOW, sys.argv[1]) -printc(colors.BLUE, sys.argv[1]) -printc(colors.RED, sys.argv[1]) + """ANSI color codes for terminal output""" + CYAN: str = "\033[36m" + GREEN: str = "\033[32m" + YELLOW: str = "\033[33m" + BLUE: str = "\033[34m" + RED: str = "\033[31m" + ENDC: str = "\033[0m" # Reset color to default + +def printc(color: str, message: str) -> None: + """Print a message with specified ANSI color""" + print(f"{color}{message}{colors.ENDC}") + +def exit_with_error(message: str) -> NoReturn: + """Print error message and exit with status code 1""" + printc(colors.RED, f"Error: {message}") + sys.exit(1) + +def main() -> None: + """Main function with default message support""" + # Set default message if no command-line argument is provided + default_message: str = "Hello, Colorful World!" + + try: + message: str = sys.argv[1] if len(sys.argv) > 1 else default_message + except IndexError: + exit_with_error("Invalid command-line arguments") + + # Print usage hint (optional, helps users understand) + if len(sys.argv) <= 1: + print("Using default message (provide an argument to customize: python print_colors.py 'your text')\n") + + # Print message in different colors + printc(colors.CYAN, message) + printc(colors.GREEN, message) + printc(colors.YELLOW, message) + printc(colors.BLUE, message) + printc(colors.RED, message) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Compression_Analysis/PSNR.py b/Compression_Analysis/PSNR.py index b3148c64c77..64ed01136c9 100644 --- a/Compression_Analysis/PSNR.py +++ b/Compression_Analysis/PSNR.py @@ -1,41 +1,90 @@ import math - -# using opencv3 import cv2 import numpy as np +import argparse +import os +from pathlib import Path +def rgb_to_luminance(r: np.ndarray, g: np.ndarray, b: np.ndarray) -> np.ndarray: + """Convert RGB channels to luminance using the ITU-R BT.601 standard.""" + return 0.299 * r + 0.587 * g + 0.114 * b -def Representational(r, g, b): - return 0.299 * r + 0.287 * g + 0.114 * b - - -def calculate(img): - b, g, r = cv2.split(img) - pixelAt = Representational(r, g, b) - return pixelAt - - -def main(): - # Loading images (orignal image and compressed image) - orignal_image = cv2.imread("orignal_image.png", 1) - compressed_image = cv2.imread("compressed_image.png", 1) +def calculate_luminance(image: np.ndarray) -> np.ndarray: + """Calculate luminance from a BGR image (OpenCV's default format).""" + b, g, r = cv2.split(image) + return rgb_to_luminance(r, g, b) - # Getting image height and width - height, width = orignal_image.shape[:2] +def calculate_psnr(original: np.ndarray, compressed: np.ndarray) -> float: + """Calculate PSNR between two luminance arrays (higher values indicate better quality).""" + mse = np.mean(np.square(original - compressed)) + return float('inf') if mse == 0 else 10 * math.log10((255.0 **2) / mse) - orignalPixelAt = calculate(orignal_image) - compressedPixelAt = calculate(compressed_image) +def load_image(image_path: str) -> np.ndarray: + """Load an image with cross-platform path handling and error debugging.""" + # Automatically convert path separators (Windows: \, Linux: /) + path = Path(image_path).resolve() # Convert to absolute path for clarity + + # Check if file exists + if not path.is_file(): + # Show system-specific debug info + print("\nSystem Debug Info:") + print(f"Operating System: {'Windows' if os.name == 'nt' else 'Linux/Unix'}") + print(f"Current Working Directory: {Path.cwd()}") + print(f"Target Path (auto-converted): {path}") + raise FileNotFoundError( + f"Image file not found! Check the path:\n{path}\n" + f"Note: Path automatically adjusted for your operating system." + ) + + # Load image with OpenCV + image = cv2.imread(str(path)) + if image is None: + raise ValueError(f"Invalid image format: {path} (supported: PNG, JPG, BMP, TIFF)") + + return image - diff = orignalPixelAt - compressedPixelAt - error = np.sum(np.abs(diff) ** 2) +def main() -> None: + """Cross-platform PSNR calculation tool with automatic path handling.""" + # Define default paths using Path (auto-handles separators) + default_original: Path = Path("Compression_Analysis") / "orignal_image.png" + default_compressed: Path = Path("Compression_Analysis") / "compressed_image.png" + + # Set up command-line arguments + parser: argparse.ArgumentParser = argparse.ArgumentParser(description="Cross-Platform PSNR Calculator (supports Windows/Linux)") + parser.add_argument( + "--original", + default=str(default_original), + help=f"Path to original image (default: {default_original})" + ) + parser.add_argument( + "--compressed", + default=str(default_compressed), + help=f"Path to compressed image (default: {default_compressed})" + ) + args: argparse.Namespace = parser.parse_args() - error = error / (height * width) + try: + # Load images with cross-platform path handling + original_image: np.ndarray = load_image(args.original) + compressed_image: np.ndarray = load_image(args.compressed) - # MSR = error_sum/(height*width) - PSNR = -(10 * math.log10(error / (255 * 255))) + # Verify image dimensions match + if original_image.shape != compressed_image.shape: + raise ValueError( + f"Dimension mismatch! Original: {original_image.shape[:2]}, " + f"Compressed: {compressed_image.shape[:2]}" + ) - print("PSNR value is {}".format(PSNR)) + # Calculate and display PSNR + original_lum: np.ndarray = calculate_luminance(original_image) + compressed_lum: np.ndarray = calculate_luminance(compressed_image) + psnr: float = calculate_psnr(original_lum, compressed_lum) + + print(f"PSNR Value: {psnr:.2f} dB") + print("Interpretation: 20-30 dB = low quality, 30-40 dB = good quality, >40 dB = excellent quality") + except Exception as error: + print(f"Error: {error}") if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/CountMillionCharacters-Variations/variation1.py b/CountMillionCharacters-Variations/variation1.py index 101620f911a..5cd4832576b 100644 --- a/CountMillionCharacters-Variations/variation1.py +++ b/CountMillionCharacters-Variations/variation1.py @@ -1,32 +1,64 @@ -try: - input = raw_input -except NameError: - pass +"""Character Count Utility +A program that counts the frequency of each uppercase character in a specified file. +Handles user input gracefully and provides clear error messages for missing files. +Compatible with Python 3.13.5 and all modern Python 3 versions. +""" -def count_chars(filename): - count = {} +from typing import Dict - with open(filename) as info: # inputFile Replaced with filename - readfile = info.read() - for character in readfile.upper(): - count[character] = count.get(character, 0) + 1 - return count +def count_chars(filename: str) -> Dict[str, int]: + """Count the frequency of each uppercase character in a file. + Args: + filename: Path to the file to be analyzed. -def main(): - is_exist = True - # Try to open file if exist else raise exception and try again - while is_exist: + Returns: + A dictionary where keys are uppercase characters and values are their counts. + Includes all whitespace, punctuation, and special characters present in the file. + """ + char_counts: Dict[str, int] = {} + + with open(filename, 'r') as file: # Open file in read mode + content: str = file.read() + for char in content.upper(): # Convert to uppercase to ensure case insensitivity + # Update count for current character (default to 0 if not found) + char_counts[char] = char_counts.get(char, 0) + 1 + + return char_counts + + +def main() -> None: + """Main function to handle user interaction and coordinate the character counting process. + + Prompts the user for a filename, processes the file, and displays character counts. + Allows the user to exit by entering '0'. Handles missing files with friendly error messages. + """ + print("Character Count Utility") + print("Enter filename to analyze (or '0' to exit)\n") + + while True: try: - inputFile = input("File Name / (0)exit : ").strip() - if inputFile == "0": + # Get user input with prompt + user_input: str = input("File name / (0)exit: ").strip() + + # Check for exit condition + if user_input == "0": + print("Exiting program. Goodbye!") break - print(count_chars(inputFile)) + + # Process file and display results + counts: Dict[str, int] = count_chars(user_input) + print(f"Character counts for '{user_input}':") + print(counts) + print() # Add blank line for readability + except FileNotFoundError: - print("File not found...Try again!") + print(f"Error: File '{user_input}' not found. Please try again.\n") + except IOError as e: + print(f"Error reading file: {str(e)}. Please try again.\n") if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/Downloaded Files Organizer/browser_status.py b/Downloaded Files Organizer/browser_status.py index 537269b62a3..9564e95400e 100644 --- a/Downloaded Files Organizer/browser_status.py +++ b/Downloaded Files Organizer/browser_status.py @@ -1,16 +1,36 @@ +import os import psutil +from typing import List from obs import watcher -browsers = ["chrome.exe", "firefox.exe", "edge.exe", "iexplore.exe"] +# List of browser process names to monitor +BROWSERS: List[str] = ["chrome.exe", "firefox.exe", "edge.exe", "iexplore.exe"] -# ADD DOWNLOADS PATH HERE::: r is for raw string enter the path -# Example: path_to_watch=r"C:\Users\Xyz\Downloads" -# find downloads path . +def get_downloads_path() -> str: + """Automatically detect the user's Downloads folder path""" + return os.path.join(os.path.expanduser("~"), "Downloads") +# Path to monitor (default to user's Downloads folder) +path_to_watch: str = get_downloads_path() -path_to_watch = r" " +def is_browser_running() -> bool: + """Check if any browser from the list is running""" + running_processes = [p.name() for p in psutil.process_iter()] + return any(browser in running_processes for browser in BROWSERS) +def start_watcher() -> None: + """Start monitoring the specified folder""" + if not os.path.exists(path_to_watch): + raise FileNotFoundError(f"Monitoring path does not exist: {path_to_watch}") + + print(f"Monitoring folder: {path_to_watch}") + watcher(path_to_watch) -for browser in browsers: - while browser in (process.name() for process in psutil.process_iter()): - watcher(path_to_watch) +if __name__ == "__main__": + try: + # Continuously monitor while any browser is running + while is_browser_running(): + start_watcher() + print("No browsers detected. Exiting program.") + except Exception as e: + print(f"An error occurred: {str(e)}") \ No newline at end of file diff --git a/Downloaded Files Organizer/move_to_directory.py b/Downloaded Files Organizer/move_to_directory.py index 6bb0d522959..7065e5adb56 100644 --- a/Downloaded Files Organizer/move_to_directory.py +++ b/Downloaded Files Organizer/move_to_directory.py @@ -1,58 +1,131 @@ import os import shutil +from typing import Dict, List +import platform -ext = { - "web": "css less scss wasm ", - "audio": "aac aiff ape au flac gsm it m3u m4a mid mod mp3 mpa pls ra s3m sid wav wma xm ", - "code": "c cc class clj cpp cs cxx el go h java lua m m4 php pl po py rb rs swift vb vcxproj xcodeproj xml diff patch html js ", - "slide": "ppt odp ", - "sheet": "ods xls xlsx csv ics vcf ", - "image": "3dm 3ds max bmp dds gif jpg jpeg png psd xcf tga thm tif tiff ai eps ps svg dwg dxf gpx kml kmz webp ", - "archiv": "7z a apk ar bz2 cab cpio deb dmg egg gz iso jar lha mar pea rar rpm s7z shar tar tbz2 tgz tlz war whl xpi zip zipx xz pak ", - "book": "mobi epub azw1 azw3 azw4 azw6 azw cbr cbz ", - "text": "doc docx ebook log md msg odt org pages pdf rtf rst tex txt wpd wps ", - "exec": "exe msi bin command sh bat crx ", - "font": "eot otf ttf woff woff2 ", - "video": "3g2 3gp aaf asf avchd avi drc flv m2v m4p m4v mkv mng mov mp2 mp4 mpe mpeg mpg mpv mxf nsv ogg ogv ogm qt rm rmvb roq srt svi vob webm wmv yuv ", +# File extension categories and their associated extensions +FILE_EXTENSIONS: Dict[str, List[str]] = { + "web": ["css", "less", "scss", "wasm"], + "audio": ["aac", "aiff", "ape", "au", "flac", "gsm", "it", "m3u", "m4a", + "mid", "mod", "mp3", "mpa", "pls", "ra", "s3m", "sid", "wav", "wma", "xm"], + "code": ["c", "cc", "class", "clj", "cpp", "cs", "cxx", "el", "go", "h", "java", + "lua", "m", "m4", "php", "pl", "po", "py", "rb", "rs", "swift", "vb", + "vcxproj", "xcodeproj", "xml", "diff", "patch", "html", "js"], + "slide": ["ppt", "odp"], + "sheet": ["ods", "xls", "xlsx", "csv", "ics", "vcf"], + "image": ["3dm", "3ds", "max", "bmp", "dds", "gif", "jpg", "jpeg", "png", "psd", + "xcf", "tga", "thm", "tif", "tiff", "ai", "eps", "ps", "svg", "dwg", + "dxf", "gpx", "kml", "kmz", "webp"], + "archive": ["7z", "a", "apk", "ar", "bz2", "cab", "cpio", "deb", "dmg", "egg", + "gz", "iso", "jar", "lha", "mar", "pea", "rar", "rpm", "s7z", "shar", + "tar", "tbz2", "tgz", "tlz", "war", "whl", "xpi", "zip", "zipx", "xz", "pak"], + "book": ["mobi", "epub", "azw1", "azw3", "azw4", "azw6", "azw", "cbr", "cbz"], + "text": ["doc", "docx", "ebook", "log", "md", "msg", "odt", "org", "pages", "pdf", + "rtf", "rst", "tex", "txt", "wpd", "wps"], + "executable": ["exe", "msi", "bin", "command", "sh", "bat", "crx"], + "font": ["eot", "otf", "ttf", "woff", "woff2"], + "video": ["3g2", "3gp", "aaf", "asf", "avchd", "avi", "drc", "flv", "m2v", "m4p", + "m4v", "mkv", "mng", "mov", "mp2", "mp4", "mpe", "mpeg", "mpg", "mpv", + "mxf", "nsv", "ogg", "ogv", "ogm", "qt", "rm", "rmvb", "roq", "srt", + "svi", "vob", "webm", "wmv", "yuv"] } -for key, value in ext.items(): - value = value.split() - ext[key] = value +def get_unique_filename(base_name: str, extension: str, target_dir: str) -> str: + """ + Generate a unique filename to avoid overwriting existing files. + + Args: + base_name: Original filename without extension + extension: File extension (without dot) + target_dir: Directory where the file will be moved + + Returns: + Unique filename in format "base_name[counter].extension" + """ + counter = 0 + # Check existing files in target directory + for filename in os.listdir(target_dir): + if filename.startswith(base_name) and filename.endswith(f".{extension}"): + counter += 1 + + return f"{base_name}{counter}.{extension}" if counter > 0 else f"{base_name}.{extension}" -def add_to_dir(ex, src_path, path): - file_with_ex = os.path.basename(src_path) - file_without_ex = file_with_ex[: file_with_ex.find(ex) - 1] - for cat, extensions in ext.items(): - if ex in extensions: - os.chdir(path) - dest_path = path + "\\" + cat - if cat in os.listdir(): - try: - shutil.move(src_path, dest_path) - except shutil.Error: - renamed_file = rename(file_without_ex, ex, dest_path) - os.chdir(path) - os.rename(file_with_ex, renamed_file) - os.chdir(dest_path) - shutil.move(path + "\\" + renamed_file, dest_path) - else: - os.mkdir(cat) - try: - shutil.move(src_path, dest_path) - except Exception as e: - print(e) - if os.path.exists(src_path): - os.unlink(src_path) +def move_to_category_dir(extension: str, source_path: str, base_dir: str) -> None: + """ + Move a file to its corresponding category directory based on extension. + + Args: + extension: File extension to determine category + source_path: Full path to the source file + base_dir: Base directory where category folders will be created + """ + # Get original filename components + filename = os.path.basename(source_path) + base_name = filename[:filename.rfind(f".{extension}")] # Name without extension + + # Find matching category for the extension + target_category = None + for category, extensions in FILE_EXTENSIONS.items(): + if extension in extensions: + target_category = category + break + + if not target_category: + return # Skip uncategorized files + + # Create target directory path + target_dir = os.path.join(base_dir, target_category) + + try: + # Create category directory if it doesn't exist + if not os.path.exists(target_dir): + os.makedirs(target_dir) + + # Move file to target directory + destination_path = os.path.join(target_dir, filename) + + # Handle existing files by renaming + if os.path.exists(destination_path): + new_filename = get_unique_filename(base_name, extension, target_dir) + new_destination = os.path.join(target_dir, new_filename) + shutil.move(source_path, new_destination) + else: + shutil.move(source_path, destination_path) + + except shutil.Error as e: + print(f"Error moving file: {e}") + except Exception as e: + print(f"Unexpected error: {e}") -def rename(search, ex, dest_path): - count = 0 - os.chdir(dest_path) - for filename in os.listdir(): - if filename.find(search, 0, len(search) - 1): - count = count + 1 - - return search + str(count) + "." + ex +# Example usage: +if __name__ == "__main__": + # Detect the operating system + current_os = platform.system() + + # Set base directory based on the detected OS + if current_os == 'Windows': + base_directory = os.path.join(os.path.expanduser('~'), 'Downloads') + elif current_os in ['Linux', 'Darwin']: # Darwin is macOS + base_directory = os.path.join(os.path.expanduser('~'), 'Downloads') + else: + print(f"Unsupported operating system: {current_os}") + base_directory = os.getcwd() # Use current directory as fallback + + print(f"Sorting files in: {base_directory}") + + # Process all files in the base directory + try: + for item in os.listdir(base_directory): + item_path = os.path.join(base_directory, item) + + # Skip directories, process only files + if os.path.isfile(item_path): + # Get file extension (lowercase) + if "." in item: + ext = item.split(".")[-1].lower() + move_to_category_dir(ext, item_path, base_directory) + except Exception as e: + print(f"Error processing directory: {e}") \ No newline at end of file diff --git a/Downloaded Files Organizer/obs.py b/Downloaded Files Organizer/obs.py index 1489f257041..69b30fec69e 100644 --- a/Downloaded Files Organizer/obs.py +++ b/Downloaded Files Organizer/obs.py @@ -1,22 +1,49 @@ -def watcher(path): - # python script to observe changes in a folder - import time - import os - from watchdog.observers import Observer - from watchdog.events import FileSystemEventHandler - from move_to_directory import add_to_dir +import time +import os +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler, FileSystemEvent # Import missing type +from typing import Callable, Optional - class Handler(FileSystemEventHandler): - def on_created(self, event): - if event.event_type == "created": - file_name = os.path.basename(event.src_path) - ext = os.path.splitext(event.src_path)[1] +def watcher(directory_path: str, callback: Optional[Callable[[str, str, str], None]] = None) -> None: + """ + Watches a specified directory for file creation events and processes new files. + + Args: + directory_path: The path to the directory to monitor. + callback: Optional callback function to handle the file processing. + Defaults to the internal add_to_dir function. + """ + # Import add_to_dir here if not available globally + try: + from move_to_directory import add_to_dir + except ImportError: + def add_to_dir(extension: str, file_path: str, target_dir: str) -> None: + """Default implementation (replace with actual import if available)""" + print(f"Processing file: {file_path}, Extension: {extension}") + + class FileCreationHandler(FileSystemEventHandler): + def on_created(self, event: FileSystemEvent) -> None: + """Handles file creation events.""" + if not event.is_directory: + file_path = event.src_path + file_extension = os.path.splitext(file_path)[1].lstrip('.').lower() + + # Allow file to fully write before processing time.sleep(2) - add_to_dir(ext[1:], event.src_path, path) - observer.stop() + + # Process the new file + handler = callback or add_to_dir + handler(file_extension, file_path, directory_path) + # Setup and start the file system observer + event_handler = FileCreationHandler() observer = Observer() - event_handler = Handler() - observer.schedule(event_handler, path, recursive=True) + observer.schedule(event_handler, directory_path, recursive=True) observer.start() - observer.join() + + try: + while observer.is_alive(): + observer.join(1) + except KeyboardInterrupt: + observer.stop() + observer.join() \ No newline at end of file diff --git a/Downloaded Files Organizer/requirements.txt b/Downloaded Files Organizer/requirements.txt deleted file mode 100644 index 0f0482e781e..00000000000 --- a/Downloaded Files Organizer/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -sys -os -time -psutil -watchdog diff --git a/Droplistmenu/GamesCalender.py b/Droplistmenu/GamesCalender.py index 990a164bd93..9b6c403d557 100644 --- a/Droplistmenu/GamesCalender.py +++ b/Droplistmenu/GamesCalender.py @@ -1,71 +1,312 @@ - -from tkinter import * -from tkcalendar import Calendar import tkinter as tk +from tkinter import StringVar, Label, Button, OptionMenu, Text, Scrollbar, Toplevel +from tkcalendar import Calendar # Install via: pip install tkcalendar +from typing import List +def main() -> None: + """Create and run the sports schedule management application""" + # Create main application window + window = tk.Tk() + window.title("Sports Schedule Manager") + window.geometry("600x500") + window.resizable(True, True) # Allow window resizing + + # Initialize list to store scheduled games + game_list: List[str] = ["Game Schedule:"] + + # Available teams for dropdown selection + team_options: List[str] = [ + "Eagles", "Tigers", "Bears", + "Sharks", "Falcons", "Dragons" + ] + + # Create and arrange all GUI components + create_widgets(window, game_list, team_options) + + # Start the main event loop + window.mainloop() -window = tk.Tk() - -# Adjust size -window.geometry("600x500") - -gameList =["Game List:"] -# Change the label text -def show(): - game = selected1.get() + " vs " + selected2.get()+" on "+cal.get_date() - gameList.append(game) - #print(gameList) - gameListshow = "\n".join(gameList) - #print(gameList) - label.config(text=gameListshow) - - -# Dropdown menu options -options = [ - "Team 1", - "Team 2", - "Team 3", - "Team 4", - "Team 5", - "Team 6" -] - -# datatype of menu text -selected1 = StringVar() -selected2 = StringVar() - -# initial menu text -selected1.set("Team 1") -selected2.set("Team 2") - -# Create Dropdown menu -L1 = Label(window, text="Visitor") -L1.place(x=40, y=35) -drop1 = OptionMenu(window, selected1, *options) -drop1.place(x=100, y=30) - -L2 = Label(window, text="VS") -L2.place(x=100, y=80) - -L3 = Label(window, text="Home") -L3.place(x=40, y=115) -drop2 = OptionMenu(window, selected2, *options) -drop2.place(x=100, y=110) - -# Add Calendar -cal = Calendar(window, selectmode='day', - year=2022, month=12, - day=1) - -cal.place(x=300, y=20) - +def create_widgets( + window: tk.Tk, + game_list: List[str], + team_options: List[str] +) -> None: + """Create and position all GUI widgets in the main window""" + # Configure theme colors + primary_color = "#4A7A8C" + secondary_color = "#F2E2D2" + accent_color = "#D96941" + text_color = "#262626" + + # Configure font styles + title_font = ("Arial", 14, "bold") + label_font = ("Arial", 10) + button_font = ("Arial", 10, "bold") + + # Variables to store selected teams + visitor_var: StringVar = StringVar(window) + home_var: StringVar = StringVar(window) + + # Set default selections + visitor_var.set(team_options[0]) + home_var.set(team_options[1]) + + # Configure grid weights for responsive layout + window.columnconfigure(0, weight=1) + window.columnconfigure(1, weight=1) + window.rowconfigure(0, weight=1) + window.rowconfigure(1, weight=1) + window.rowconfigure(2, weight=3) + + # Create left frame for team selection with styling + left_frame = tk.Frame(window, padx=15, pady=15, bg=secondary_color) + left_frame.grid(row=0, column=0, rowspan=2, sticky="nsew") + + # Title label + title_label = Label( + left_frame, + text="Team Selection", + font=title_font, + bg=secondary_color, + fg=text_color + ) + title_label.grid(row=0, column=0, columnspan=2, pady=(0, 15), sticky="n") + + # Visitor team selection + visitor_label = Label( + left_frame, + text="Visitor Team:", + font=label_font, + bg=secondary_color, + fg=text_color + ) + visitor_label.grid(row=1, column=0, sticky="w", pady=5) + + visitor_dropdown = OptionMenu( + left_frame, + visitor_var, + *team_options + ) + visitor_dropdown.config( + font=label_font, + bg="white", + fg=text_color, + relief=tk.RAISED, + bd=2 + ) + visitor_dropdown["menu"].config( + bg="white", + fg=text_color + ) + visitor_dropdown.grid(row=1, column=1, sticky="ew", pady=5) + + # Home team selection + home_label = Label( + left_frame, + text="Home Team:", + font=label_font, + bg=secondary_color, + fg=text_color + ) + home_label.grid(row=2, column=0, sticky="w", pady=5) + + home_dropdown = OptionMenu( + left_frame, + home_var, + *team_options + ) + home_dropdown.config( + font=label_font, + bg="white", + fg=text_color, + relief=tk.RAISED, + bd=2 + ) + home_dropdown["menu"].config( + bg="white", + fg=text_color + ) + home_dropdown.grid(row=2, column=1, sticky="ew", pady=5) + + # Create calendar frame on the right with styling + right_frame = tk.Frame(window, padx=15, pady=15, bg=secondary_color) + right_frame.grid(row=0, column=1, rowspan=2, sticky="nsew") + + # Calendar title + cal_title = Label( + right_frame, + text="Select Date", + font=title_font, + bg=secondary_color, + fg=text_color + ) + cal_title.pack(pady=(0, 10)) + + calendar = Calendar( + right_frame, + selectmode='day', + year=2023, + month=7, + day=16, + font=label_font, + background=primary_color, + foreground="white", + selectbackground=accent_color, + selectforeground="white", + borderwidth=2, + relief=tk.RAISED, + locale='en_US' + + ) + calendar.pack(fill="both", expand=True) + + # Create game list display area with styling + display_frame = tk.Frame(window, padx=15, pady=15, bg=secondary_color) + display_frame.grid(row=2, column=0, columnspan=2, sticky="nsew") + + # Display title + display_title = Label( + display_frame, + text="Game Schedule", + font=title_font, + bg=secondary_color, + fg=text_color + ) + display_title.pack(pady=(0, 10)) + + # Text widget with scrollbar for game list + game_display = Text( + display_frame, + wrap=tk.WORD, + height=10, + font=label_font, + bg="white", + fg=text_color, + bd=2, + relief=tk.SUNKEN + ) + game_display.pack(side=tk.LEFT, fill="both", expand=True) + + scrollbar = Scrollbar( + display_frame, + command=game_display.yview, + bg=primary_color, + troughcolor=secondary_color, + relief=tk.FLAT + ) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + game_display.config(yscrollcommand=scrollbar.set) + + # Initialize display with empty list + update_game_display(game_display, game_list) + + # Add to schedule button - pass game_display to add_game + add_button = Button( + left_frame, + text="Add to Schedule", + command=lambda: add_game( + window, game_list, visitor_var, home_var, calendar, game_display + ), + font=button_font, + bg=accent_color, + fg="white", + activebackground="#BF4924", + activeforeground="white", + relief=tk.RAISED, + bd=3 + ) + add_button.grid(row=3, column=0, columnspan=2, pady=20, sticky="ew") + + # Configure weights for responsive resizing + left_frame.columnconfigure(1, weight=1) + right_frame.columnconfigure(0, weight=1) + right_frame.rowconfigure(0, weight=1) + display_frame.columnconfigure(0, weight=1) + display_frame.rowconfigure(0, weight=1) +def add_game( + window: tk.Tk, + game_list: List[str], + visitor_var: StringVar, + home_var: StringVar, + calendar: Calendar, + game_display: Text +) -> None: + """Add a new game to the schedule and update the display""" + # Get selected values + visitor = visitor_var.get() + home = home_var.get() + date = calendar.get_date() + + # Validate input (prevent same team match) + if visitor == home: + show_error(window, "Input Error", "Visitor and home teams cannot be the same!") + return + + # Create game entry and add to list + game_entry = f"{visitor} vs {home} on {date}" + game_list.append(game_entry) + + # Update the display with new list + update_game_display(game_display, game_list) -# Create button, it will change label text -button = Button( window, text="Add to calender", command=show).place(x=100,y=200) +def update_game_display(display: Text, game_list: List[str]) -> None: + """Update the text widget with current game list""" + # Clear existing content + display.delete(1.0, tk.END) + # Insert updated list + display.insert(tk.END, "\n".join(game_list)) -# Create Label -label = Label(window, text=" ") -label.place(x=150, y=250) +def show_error(window: tk.Tk, title: str, message: str) -> None: + """Display an error message in a modal dialog""" + error_window = Toplevel(window) + error_window.title(title) + error_window.geometry("350x180") + error_window.resizable(False, False) + error_window.configure(bg="#F8D7DA") # Light red background + + # Center error window over main window + error_window.geometry("+%d+%d" % ( + window.winfo_rootx() + window.winfo_width() // 2 - 175, + window.winfo_rooty() + window.winfo_height() // 2 - 90 + )) + + # Error message frame + message_frame = tk.Frame(error_window, bg="#F8D7DA") + message_frame.pack(fill="both", expand=True, padx=20, pady=20) + + # Error message label + message_label = Label( + message_frame, + text=message, + font=("Arial", 10), + bg="#F8D7DA", + fg="#842029", + wraplength=300 + ) + message_label.pack(fill="both", expand=True) + + # Close button + close_button = Button( + error_window, + text="OK", + command=error_window.destroy, + font=("Arial", 10, "bold"), + bg="#DC3545", + fg="white", + activebackground="#C82333", + activeforeground="white", + relief=tk.RAISED, + bd=2, + padx=15, + pady=5 + ) + close_button.pack(pady=10) + + # Make dialog modal + error_window.transient(window) + error_window.grab_set() + window.wait_window(error_window) -window.mainloop() \ No newline at end of file +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Electronics_Algorithms/resistance.py b/Electronics_Algorithms/resistance.py index c088732d90c..ebb4ec22ba3 100644 --- a/Electronics_Algorithms/resistance.py +++ b/Electronics_Algorithms/resistance.py @@ -1,69 +1,65 @@ -def resistance_calculator(material:str, lenght:float, section:float, temperature:float): - """ - material is a string indicating the material of the wire - - lenght is a floating value indicating the lenght of the wire in meters +def resistance_calculator( + material: str, + length: float, + section: float, + temperature: float +) -> str: + """ + Calculate the electrical resistance of a wire based on its properties. - diameter is a floating value indicating the diameter of the wire in millimeters + Parameters: + material (str): Material of the wire (available: silver, copper, aluminium, tungsten, iron, steel, zinc, solder) + length (float): Length of the wire in meters + section (float): Cross-sectional area of the wire in square millimeters (mm²) + temperature (float): Operating temperature of the wire in °C - temperature is a floating value indicating the temperature at which the wire is operating in °C + Returns: + str: Calculated resistance in ohms (Ω) + """ + # Material properties: resistivity at 20°C (ρ in Ω·mm²/m) and temperature coefficient (α in 1/°C) + materials: dict[str, dict[str, float]] = { + "silver": { + "rho": 0.0163, + "coefficient": 0.0038 + }, + "copper": { + "rho": 0.0178, + "coefficient": 0.00381 + }, + "aluminium": { + "rho": 0.0284, + "coefficient": 0.004 + }, + "tungsten": { + "rho": 0.055, + "coefficient": 0.0045 + }, + "iron": { + "rho": 0.098, + "coefficient": 0.006 + }, + "steel": { + "rho": 0.15, + "coefficient": 0.0047 + }, + "zinc": { + "rho": 0.06, + "coefficient": 0.0037 + }, + "solder": { + "rho": 0.12, + "coefficient": 0.0043 + } + } - Available materials: - - silver - - copper - - aluminium - - tungsten - - iron - - steel - - zinc - - solder""" + # Get material properties (will raise KeyError for invalid materials) + rho_20deg: float = materials[material]["rho"] + temp_coefficient: float = materials[material]["coefficient"] - materials = { - "silver": { - "rho": 0.0163, - "coefficient": 0.0038 - }, + # Calculate resistivity at operating temperature + rho: float = rho_20deg * (1 + temp_coefficient * (temperature - 20)) - "copper": { - "rho": 0.0178, - "coefficient": 0.00381 - }, + # Calculate resistance (R = ρ * L / S) + resistance: float = rho * length / section - "aluminium": { - "rho": 0.0284, - "coefficient": 0.004 - }, - - "tungsten": { - "rho": 0.055, - "coefficient": 0.0045 - }, - - "iron": { - "rho": 0.098, - "coefficient": 0.006 - }, - - "steel": { - "rho": 0.15, - "coefficient": 0.0047 - }, - - "zinc": { - "rho": 0.06, - "coefficient": 0.0037 - }, - - "solder": { - "rho": 0.12, - "coefficient": 0.0043 - } - } - - rho_20deg = materials[material]["rho"] - temp_coefficient = materials[material]["coefficient"] - - rho = rho_20deg * (1 + temp_coefficient * (temperature - 20)) - resistance = rho * lenght / section - - return f"{resistance}Ω" + return f"{resistance}Ω" \ No newline at end of file diff --git a/Emoji Dictionary/QT_GUI.py b/Emoji Dictionary/QT_GUI.py index a4dd819ccb8..d6668a2be3e 100644 --- a/Emoji Dictionary/QT_GUI.py +++ b/Emoji Dictionary/QT_GUI.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- - import sys from PyQt5.QtCore import * from PyQt5.QtGui import * diff --git a/Emoji Dictionary/emoji_dictionary.py b/Emoji Dictionary/emoji_dictionary.py index db13725f1fe..1c6b761eecf 100644 --- a/Emoji Dictionary/emoji_dictionary.py +++ b/Emoji Dictionary/emoji_dictionary.py @@ -1,349 +1,362 @@ -# Emoji Dictionary - -# ----------------------------------------------------------------------------------------------------- -from tkinter import * # importing the necessary libraries +import tkinter as tk +from tkinter import StringVar, Entry, Text, Button, Label, Event import tkinter.messagebox as mbox -import tkinter as tk # imported tkinter as tk import emoji - -# ----------------------------------------------------------------------------------------------- +from typing import Optional, List, Any class Keypad(tk.Frame): - - cells = [ + """A custom keypad frame containing emoji buttons and control functions""" + + cells: List[List[str]] = [ ["😀", "🥰", "😴", "🤓", "🤮", "🤬", "😨", "🤑", "😫", "😎"], [ - "🐒", - "🐕", - "🐎", - "🐪", - "🐁", - "🐘", - "🦘", - "🦈", - "🐓", - "🐝", - "👀", - "🦴", - "👩🏿", - "‍🤝", - "🧑", - "🏾", - "👱🏽", - "‍♀", - "🎞", - "🎨", - "⚽", + "🐒", "🐕", "🐎", "🐪", "🐁", "🐘", "🦘", "🦈", "🐓", "🐝", + "👀", "🦴", "👩🏿", "‍🤝", "🧑", "🏾", "👱🏽", "‍♀", "🎞", "🎨", "⚽" ], [ - "🍕", - "🍗", - "🍜", - "☕", - "🍴", - "🍉", - "🍓", - "🌴", - "🌵", - "🛺", - "🚲", - "🛴", - "🚉", - "🚀", - "✈", - "🛰", - "🚦", - "🏳", - "‍🌈", - "🌎", - "🧭", + "🍕", "🍗", "🍜", "☕", "🍴", "🍉", "🍓", "🌴", "🌵", "🛺", + "🚲", "🛴", "🚉", "🚀", "✈", "🛰", "🚦", "🏳", "‍🌈", "🌎", "🧭" ], [ - "🔥", - "❄", - "🌟", - "🌞", - "🌛", - "🌝", - "🌧", - "🧺", - "🧷", - "🪒", - "⛲", - "🗼", - "🕌", - "👁", - "‍🗨", - "💬", - "™", - "💯", - "🔕", - "💥", - "❤", + "🔥", "❄", "🌟", "🌞", "🌛", "🌝", "🌧", "🧺", "🧷", "🪒", + "⛲", "🗼", "🕌", "👁", "‍🗨", "💬", "™", "💯", "🔕", "💥", "❤" ], ] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.target = None - self.memory = "" - - for y, row in enumerate(self.cells): - for x, item in enumerate(row): - b = tk.Button( + def __init__(self, parent: tk.Tk, *args: Any, **kwargs: Any) -> None: + """Initialize the keypad frame with emoji buttons and controls""" + super().__init__(parent, *args, **kwargs) + self.target: Optional[Entry] = None + self.memory: str = "" + self.label: Optional[Label] = None + + self._create_buttons() + + def _create_buttons(self) -> None: + """Create and arrange all buttons in the keypad""" + try: + for row_idx, row in enumerate(self.cells): + for col_idx, emoji_char in enumerate(row): + btn = Button( + self, + text=emoji_char, + command=lambda text=emoji_char: self.append(text), + font=("Arial", 14), + bg="yellow", + fg="blue", + borderwidth=3, + relief="raised" + ) + btn.grid(row=row_idx, column=col_idx, sticky="news") + + control_buttons = [ + ("Space", self.space, 0, 10, 2), + ("Tab", self.tab, 0, 12, 2), + ("Backspace", self.backspace, 0, 14, 3), + ("Clear", self.clear, 0, 17, 2), + ("Hide", self.hide, 0, 19, 2) + ] + + for text, cmd, row, col, colspan in control_buttons: + btn = Button( self, - text=item, - command=lambda text=item: self.append(text), + text=text, + command=cmd, font=("Arial", 14), bg="yellow", fg="blue", borderwidth=3, - relief="raised", + relief="raised" ) - b.grid(row=y, column=x, sticky="news") - - x = tk.Button( - self, - text="Space", - command=self.space, - font=("Arial", 14), - bg="yellow", + btn.grid(row=row, column=col, columnspan=colspan, sticky="news") + + except Exception as e: + print(f"Error creating keypad buttons: {str(e)}") + # Optionally show error message to user + # mbox.showerror("Error", f"Failed to initialize keypad: {str(e)}") + + def get(self) -> Optional[str]: + """Get current text from target entry widget""" + try: + if self.target: + return self.target.get() + return None + except Exception as e: + print(f"Error getting text: {str(e)}") + return None + + def append(self, text: str) -> None: + """Append text to target entry widget""" + try: + if self.target: + self.target.insert("end", text) + except Exception as e: + print(f"Error appending text: {str(e)}") + mbox.showerror("Error", f"Failed to append text: {str(e)}") + + def clear(self) -> None: + """Clear all text from target entry widget""" + try: + if self.target: + self.target.delete(0, tk.END) + except Exception as e: + print(f"Error clearing text: {str(e)}") + mbox.showerror("Error", f"Failed to clear text: {str(e)}") + + def backspace(self) -> None: + """Remove last character from target entry widget""" + try: + if self.target: + current_text = self.get() + if current_text: + new_text = current_text[:-1] + self.clear() + self.append(new_text) + except Exception as e: + print(f"Error during backspace: {str(e)}") + mbox.showerror("Error", f"Failed to perform backspace: {str(e)}") + + def space(self) -> None: + """Add a space to target entry widget""" + try: + if self.target: + self.append(" ") + except Exception as e: + print(f"Error adding space: {str(e)}") + mbox.showerror("Error", f"Failed to add space: {str(e)}") + + def tab(self) -> None: + """Add 5 spaces (simulated tab) to target entry widget""" + try: + if self.target: + self.append(" ") + except Exception as e: + print(f"Error adding tab: {str(e)}") + mbox.showerror("Error", f"Failed to add tab: {str(e)}") + + def copy(self) -> None: + """Copy current text to memory (clipboard simulation)""" + try: + if self.target: + self.memory = self.get() or "" + print(f"Copied to memory: {self.memory}") + except Exception as e: + print(f"Error copying text: {str(e)}") + mbox.showerror("Error", f"Failed to copy text: {str(e)}") + + def paste(self) -> None: + """Paste text from memory to target entry widget""" + try: + if self.target and self.memory: + self.append(self.memory) + except Exception as e: + print(f"Error pasting text: {str(e)}") + mbox.showerror("Error", f"Failed to paste text: {str(e)}") + + def show(self, entry: Entry) -> None: + """Display keypad and set target entry widget""" + try: + self.target = entry + self.place(relx=0.5, rely=0.6, anchor="c") + except Exception as e: + print(f"Error showing keypad: {str(e)}") + mbox.showerror("Error", f"Failed to show keypad: {str(e)}") + + def hide(self) -> None: + """Hide keypad and clear target entry widget""" + try: + self.target = None + self.place_forget() + except Exception as e: + print(f"Error hiding keypad: {str(e)}") + mbox.showerror("Error", f"Failed to hide keypad: {str(e)}") + + +def clear_text() -> None: + """Clear both input entry and output text widgets""" + try: + inputentry.delete(0, tk.END) + outputtxt.delete("1.0", tk.END) + except Exception as e: + print(f"Error clearing text: {str(e)}") + mbox.showerror("Error", f"Failed to clear text fields: {str(e)}") + + +def search_emoji() -> None: + """Search for meaning of entered emoji and display result""" + try: + emoji_input = inputentry.get() + if not emoji_input: + outputtxt.delete("1.0", tk.END) + outputtxt.insert(tk.END, "No emoji entered. Please input an emoji first.") + return + + # Check if input contains non-emoji characters + if not all(emoji.is_emoji(char) for char in emoji_input): + outputtxt.delete("1.0", tk.END) + outputtxt.insert(tk.END, "Invalid input! Please enter only emojis.") + return + + meaning = emoji.demojize(emoji_input) + outputtxt.delete("1.0", tk.END) + outputtxt.insert(tk.END, f"Meaning of Emoji: {emoji_input}\n\n{meaning}") + + except emoji.EmojiNotFoundError: + outputtxt.delete("1.0", tk.END) + outputtxt.insert(tk.END, "Emoji not recognized. Please try another emoji.") + except Exception as e: + print(f"Error processing emoji: {str(e)}") + outputtxt.delete("1.0", tk.END) + outputtxt.insert(tk.END, f"An error occurred: {str(e)}") + + +def exit_win() -> None: + """Handle window closing confirmation""" + try: + if mbox.askokcancel("Exit", "Do you want to exit?"): + window.destroy() + except Exception as e: + print(f"Error exiting application: {str(e)}") + mbox.showerror("Error", f"Failed to exit application: {str(e)}") + + +def on_inputentry_click(event: Event) -> None: + """Handle initial click on input entry to clear placeholder""" + try: + global firstclick1 + if firstclick1: + firstclick1 = False + inputentry.delete(0, tk.END) + except Exception as e: + print(f"Error handling input click: {str(e)}") + + +if __name__ == "__main__": + try: + # Initialize main window + window: tk.Tk = tk.Tk() + window.title("Emoji Dictionary") + window.geometry("1000x700") + + # Title label + title_label: Label = Label( + window, + text="EMOJI DICTIONARY", + font=("Arial", 50, "underline"), + fg="magenta" + ) + title_label.place(x=160, y=10) + + # Instruction label + input_label: Label = Label( + window, + text="Enter any Emoji you want to search...", + font=("Arial", 30), + fg="green" + ) + input_label.place(x=160, y=120) + + # Input entry widget + myname: StringVar = StringVar(window) + firstclick1: bool = True + inputentry: Entry = Entry( + window, + font=("Arial", 35), + width=28, + border=2, + bg="light yellow", + fg="brown", + textvariable=myname + ) + inputentry.insert(0, "Click to enter emoji...") + inputentry.bind('', on_inputentry_click) + inputentry.place(x=120, y=180) + + # Search button + search_btn: Button = Button( + window, + text="🔍 SEARCH", + command=search_emoji, + font=("Arial", 20), + bg="light green", fg="blue", borderwidth=3, - relief="raised", + relief="raised" ) - x.grid(row=0, column=10, columnspan="2", sticky="news") - - x = tk.Button( - self, - text="tab", - command=self.tab, - font=("Arial", 14), - bg="yellow", + search_btn.place(x=270, y=250) + + # Clear button + clear_btn: Button = Button( + window, + text="🧹 CLEAR", + command=clear_text, + font=("Arial", 20), + bg="orange", fg="blue", borderwidth=3, - relief="raised", + relief="raised" ) - x.grid(row=0, column=12, columnspan="2", sticky="news") - - x = tk.Button( - self, - text="Backspace", - command=self.backspace, - font=("Arial", 14), - bg="yellow", - fg="blue", + clear_btn.place(x=545, y=250) + + # Output label + output_label: Label = Label( + window, + text="Meaning...", + font=("Arial", 30), + fg="green" + ) + output_label.place(x=160, y=340) + + # Output text widget + outputtxt: Text = Text( + window, + height=7, + width=57, + font=("Arial", 17), + bg="light yellow", + fg="brown", borderwidth=3, - relief="raised", + relief="solid" ) - x.grid(row=0, column=14, columnspan="3", sticky="news") - - x = tk.Button( - self, - text="Clear", - command=self.clear, - font=("Arial", 14), - bg="yellow", - fg="blue", + outputtxt.place(x=120, y=400) + + # Exit button + exit_btn: Button = Button( + window, + text="❌ EXIT", + command=exit_win, + font=("Arial", 20), + bg="red", + fg="black", borderwidth=3, - relief="raised", + relief="raised" ) - x.grid(row=0, column=17, columnspan="2", sticky="news") - - x = tk.Button( - self, - text="Hide", - command=self.hide, - font=("Arial", 14), - bg="yellow", - fg="blue", + exit_btn.place(x=435, y=610) + + # Initialize keypad + keypad: Keypad = Keypad(window) + + # Keypad toggle button + keypad_btn: Button = Button( + window, + text="⌨", + command=lambda: keypad.show(inputentry), + font=("Arial", 18), + bg="light yellow", + fg="green", borderwidth=3, - relief="raised", + relief="raised" ) - x.grid(row=0, column=19, columnspan="2", sticky="news") - - def get(self): - if self.target: - return self.target.get() - - def append(self, text): - if self.target: - self.target.insert("end", text) - - def clear(self): - if self.target: - self.target.delete(0, END) - - def backspace(self): - if self.target: - text = self.get() - text = text[:-1] - self.clear() - self.append(text) - - def space(self): - if self.target: - text = self.get() - text = text + " " - self.clear() - self.append(text) - - def tab(self): # 5 spaces - if self.target: - text = self.get() - text = text + " " - self.clear() - self.append(text) - - def copy(self): - # TODO: copy to clipboad - if self.target: - self.memory = self.get() - self.label["text"] = "memory: " + self.memory - print(self.memory) - - def paste(self): - # TODO: copy from clipboad - if self.target: - self.append(self.memory) - - def show(self, entry): - self.target = entry - - self.place(relx=0.5, rely=0.6, anchor="c") - - def hide(self): - self.target = None - - self.place_forget() - - -# function defined th=o clear both the input text and output text -------------------------------------------------- -def clear_text(): - inputentry.delete(0, END) - outputtxt.delete("1.0", "end") - - -# function to search emoji -def search_emoji(): - word = inputentry.get() - if word == "": - outputtxt.insert(END, "You have entered no emoji.") - else: - means = emoji.demojize(word) - outputtxt.insert(END, "Meaning of Emoji : " + str(word) + "\n\n" + means) - - -# main window created -window = tk.Tk() -window.title("Emoji Dictionary") -window.geometry("1000x700") - -# for writing Dictionary label, at the top of window -dic = tk.Label( - text="EMOJI DICTIONARY", font=("Arial", 50, "underline"), fg="magenta" -) # same way bg -dic.place(x=160, y=10) - -start1 = tk.Label( - text="Enter any Emoji you want to search...", font=("Arial", 30), fg="green" -) # same way bg -start1.place(x=160, y=120) - -myname = StringVar(window) -firstclick1 = True - - -def on_inputentry_click(event): - """function that gets called whenever entry1 is clicked""" - global firstclick1 - - if firstclick1: # if this is the first time they clicked it - firstclick1 = False - inputentry.delete(0, "end") # delete all the text in the entry - - -# Taking input from TextArea -# inputentry = Entry(window,font=("Arial", 35), width=33, border=2) -inputentry = Entry( - window, font=("Arial", 35), width=28, border=2, bg="light yellow", fg="brown" -) -inputentry.place(x=120, y=180) - -# # Creating Search Button -Button( - window, - text="🔍 SEARCH", - command=search_emoji, - font=("Arial", 20), - bg="light green", - fg="blue", - borderwidth=3, - relief="raised", -).place(x=270, y=250) - -# # creating clear button -Button( - window, - text="🧹 CLEAR", - command=clear_text, - font=("Arial", 20), - bg="orange", - fg="blue", - borderwidth=3, - relief="raised", -).place(x=545, y=250) - -# meaning label -start1 = tk.Label(text="Meaning...", font=("Arial", 30), fg="green") # same way bg -start1.place(x=160, y=340) - -# # Output TextBox Creation -outputtxt = tk.Text( - window, - height=7, - width=57, - font=("Arial", 17), - bg="light yellow", - fg="brown", - borderwidth=3, - relief="solid", -) -outputtxt.place(x=120, y=400) - -# function for exiting -def exit_win(): - if mbox.askokcancel("Exit", "Do you want to exit?"): - window.destroy() - - -# # creating exit button -Button( - window, - text="❌ EXIT", - command=exit_win, - font=("Arial", 20), - bg="red", - fg="black", - borderwidth=3, - relief="raised", -).place(x=435, y=610) - -keypad = Keypad(window) - -# # creating speech to text button -v_keypadb = Button( - window, - text="⌨", - command=lambda: keypad.show(inputentry), - font=("Arial", 18), - bg="light yellow", - fg="green", - borderwidth=3, - relief="raised", -).place(x=870, y=183) - -window.protocol("WM_DELETE_WINDOW", exit_win) -window.mainloop() + keypad_btn.place(x=870, y=183) + + # Configure window close protocol + window.protocol("WM_DELETE_WINDOW", exit_win) + + # Start main event loop + window.mainloop() + + except Exception as e: + print(f"Fatal error during application initialization: {str(e)}") + # Consider showing a critical error message here + # mbox.showerror("Critical Error", f"Failed to start application: {str(e)}") \ No newline at end of file diff --git a/Emoji Dictionary/requirements.txt b/Emoji Dictionary/requirements.txt deleted file mode 100644 index 840507a2445..00000000000 --- a/Emoji Dictionary/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -tkinter -messagebox -emoji diff --git a/Extract-Table-from-pdf-txt-docx/main.py b/Extract-Table-from-pdf-txt-docx/main.py index d74649cd054..7001c642e6a 100644 --- a/Extract-Table-from-pdf-txt-docx/main.py +++ b/Extract-Table-from-pdf-txt-docx/main.py @@ -1,104 +1,84 @@ -# %% import pandas as pd import os import tabula from docx.api import Document -# %% +def process_child_directory(child_dir: str) -> dict[str, pd.DataFrame]: + """Process a child directory and return dataframes from its files""" + results = {} + + if not os.path.isdir(child_dir): + print(f"Skipping {child_dir} - directory not found") + return results + + original_dir = os.getcwd() # Save current directory + os.chdir(child_dir) + + # Process PDF + pdf_name = f"Pdf1_{os.path.basename(child_dir)}.pdf" + if os.path.isfile(pdf_name): + try: + results['pdf'] = tabula.read_pdf(pdf_name, pages="all") + print(f"Read PDF: {pdf_name}") + except Exception as e: + print(f"Error reading PDF {pdf_name}: {e}") + + # Process DOCX + docx_name = f"Document_{os.path.basename(child_dir)}.docx" + if os.path.isfile(docx_name): + try: + doc = Document(docx_name) + table = doc.tables[0] + data = [] + keys = None + for i, row in enumerate(table.rows): + texts = [cell.text for cell in row.cells] + if i == 0: + keys = tuple(texts) + continue + data.append(dict(zip(keys, texts))) + results['document'] = pd.DataFrame(data) + print(f"Read DOCX: {docx_name}") + except Exception as e: + print(f"Error reading DOCX {docx_name}: {e}") + + # Process TXT + txt_name = f"Text_{os.path.basename(child_dir)}.txt" + if os.path.isfile(txt_name): + try: + results['text'] = pd.read_csv(txt_name) + print(f"Read TXT: {txt_name}") + except Exception as e: + print(f"Error reading TXT {txt_name}: {e}") + + os.chdir(original_dir) # Return to original directory + return results -if os.path.isdir("Parent") == True: - os.chdir("Parent") -# FOR CHILD1 DIRECTORY -if os.path.isdir("Child1") == True: - os.chdir("Child1") -# PDF FILE READING -if os.path.isfile("Pdf1_Child1.pdf") == True: - df_pdf_child1 = tabula.read_pdf("Pdf1_Child1.pdf", pages="all") -# DOCUMENT READING -if os.path.isfile("Document_Child1.docx") == True: - document = Document("Document_Child1.docx") - table = document.tables[0] - data = [] - - keys = None - for i, row in enumerate(table.rows): - text = (cell.text for cell in row.cells) - if i == 0: - keys = tuple(text) - continue - row_data = dict(zip(keys, text)) - data.append(row_data) -df_document_child1 = pd.DataFrame(data) -# TEXT READING -if os.path.isfile("Text_Child1.txt") == True: - df_text_child1 = pd.read_csv("Text_Child1.txt") - -# %% -df_text_child1 - - -# %% -os.chdir("../") -if os.path.isdir("Parent") == True: - os.chdir("Parent") -# FOR CHILD2 DIRECTORY -if os.path.isdir("Child2") == True: - os.chdir("Child2") -# PDF FILE READING -if os.path.isfile("Pdf1_Child2.pdf") == True: - df_pdf_child2 = tabula.read_pdf("Pdf1_Child2.pdf", pages="all") -# DOCUMENT READING -if os.path.isfile("Document_Child2.docx") == True: - document = Document("Document_Child2.docx") - table = document.tables[0] - data = [] - - keys = None - for i, row in enumerate(table.rows): - text = (cell.text for cell in row.cells) - if i == 0: - keys = tuple(text) - continue - row_data = dict(zip(keys, text)) - data.append(row_data) -df_document_child2 = pd.DataFrame(data) -# TEXT READING -if os.path.isfile("Text_Child2.txt") == True: - df_text_child2 = pd.read_csv("Text_Child2.txt") - -# %% -df_pdf_child2[0].head(4) - -# %% -os.chdir("../") -if os.path.isdir("Parent") == True: - os.chdir("Parent") -# FOR CHILD3 DIRECTORY -if os.path.isdir("Child3") == True: - os.chdir("Child3") -# PDF FILE READING -if os.path.isfile("Pdf1_Child3.pdf") == True: - df_pdf_child3 = tabula.read_pdf("Pdf1_Child3.pdf", pages="all") -# DOCUMENT READING -if os.path.isfile("Document_Child3.docx") == True: - document = Document("Document_Child3.docx") - table = document.tables[0] - data = [] - - keys = None - for i, row in enumerate(table.rows): - text = (cell.text for cell in row.cells) - if i == 0: - keys = tuple(text) - continue - row_data = dict(zip(keys, text)) - data.append(row_data) -df_document_child3 = pd.DataFrame(data) -# TEXT READING -if os.path.isfile("Text_Child3.txt") == True: - df_text_child3 = pd.read_csv("Text_Child3.txt") - -# %% -df_text_child3 - -# %% +if __name__ == "__main__": + # Get the directory where the script (main.py) is located + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Correct path to Parent directory (no extra nested folder) + parent_dir = os.path.join(script_dir, "Parent") # Fixed path + + if not os.path.isdir(parent_dir): + print(f"Parent directory NOT found at: {parent_dir}") + print("Please check your folder structure! It should be:") + print(f" {script_dir}/Parent/") + print(f" {script_dir}/Parent/Child1/") + print(f" {script_dir}/Parent/Child2/") + print(f" {script_dir}/Parent/Child3/") + else: + print(f"Processing Parent directory: {parent_dir}") + + # Process Child1, Child2, Child3 + all_data = {} + for i in range(1, 4): + child_name = f"Child{i}" + child_path = os.path.join(parent_dir, child_name) + all_data[child_name] = process_child_directory(child_path) + + # Example: Show Child1 text data if available + if 'Child1' in all_data and 'text' in all_data['Child1']: + print("\n--- Child1 Text Data ---") + print(all_data['Child1']['text'].head()) \ No newline at end of file diff --git a/ExtractThumbnailFromVideo/extract_thumbnail_from_video.py b/ExtractThumbnailFromVideo/extract_thumbnail_from_video.py index 76ca3b43eb7..4a3a1915bf3 100644 --- a/ExtractThumbnailFromVideo/extract_thumbnail_from_video.py +++ b/ExtractThumbnailFromVideo/extract_thumbnail_from_video.py @@ -1,13 +1,14 @@ import cv2 import os +from typing import Tuple -def extract_thumbnail(video_path, frame_size): +def extract_thumbnail(video_path: str, frame_size: Tuple[int, int]) -> None: """ Extracts a thumbnail frame from a video and saves it as an image file. Args: - video_path (str): The path to the input video file. - frame_size (tuple): A tuple containing the desired dimensions (width, height) for the thumbnail frame. + video_path: The path to the input video file. + frame_size: A tuple containing the desired dimensions (width, height) for the thumbnail frame. Raises: Exception: If the function fails to extract a frame from the video. @@ -20,20 +21,42 @@ def extract_thumbnail(video_path, frame_size): extract_thumbnail('my_video.mp4', (320, 240)) Required Packages: - cv2 (pip install cv2) + OpenCV (pip install opencv-python) This function is useful for generating thumbnail images from videos. """ - video_capture = cv2.VideoCapture(video_path) # Open the video file for reading - total_frames = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT)) # Get the total number of frames in the video - middle_frame_index = total_frames // 2 # Calculate the index of the middle frame - video_capture.set(cv2.CAP_PROP_POS_FRAMES, middle_frame_index) # Seek to the middle frame - success, frame = video_capture.read() # Read the middle frame - video_capture.release() # Release the video capture object - - if success: - frame = cv2.resize(frame, frame_size) # Resize the frame to the specified dimensions - thumbnail_filename = f"{os.path.basename(video_path)}_thumbnail.jpg" # Create a filename for the thumbnail - cv2.imwrite(thumbnail_filename, frame) # Save the thumbnail frame as an image - else: - raise Exception("Could not extract frame") # Raise an exception if frame extraction fails + # Open the video file + video_capture = cv2.VideoCapture(video_path) + + try: + # Get the total number of frames + total_frames = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT)) + + # Calculate the middle frame index + middle_frame_index = total_frames // 2 + + # Seek to the middle frame + video_capture.set(cv2.CAP_PROP_POS_FRAMES, middle_frame_index) + + # Read the frame + success, frame = video_capture.read() + + if not success: + raise Exception("Failed to read frame from video") + + # Resize the frame to the specified dimensions + resized_frame = cv2.resize(frame, frame_size) + + # Generate the thumbnail filename + base_name = os.path.basename(video_path) + file_name, _ = os.path.splitext(base_name) + thumbnail_filename = f"{file_name}_thumbnail.jpg" + + # Save the thumbnail image + cv2.imwrite(thumbnail_filename, resized_frame) + + print(f"Thumbnail saved as: {thumbnail_filename}") + + finally: + # Release the video capture object + video_capture.release() \ No newline at end of file diff --git a/Face and eye Recognition/face_recofnation_first.py b/Face and eye Recognition/face_recofnation_first.py index c60faba84db..3e837e337a9 100644 --- a/Face and eye Recognition/face_recofnation_first.py +++ b/Face and eye Recognition/face_recofnation_first.py @@ -1,52 +1,108 @@ import cv2 as cv +from cv2 import CascadeClassifier, VideoCapture +import numpy as np +import os +from typing import Tuple, List - -def detect_faces_and_eyes(): +def detect_faces_and_eyes(camera_index: int = 0, + face_scale_factor: float = 1.1, + face_min_neighbors: int = 7, + eye_scale_factor: float = 1.1, + eye_min_neighbors: int = 7) -> None: """ - Detects faces and eyes in real-time using the webcam. - + Detects faces and eyes in real-time using the webcam or a specified video source. + + Args: + camera_index: Index of the camera to use (default is 0 for built-in webcam). + face_scale_factor: Parameter specifying how much the image size is reduced at each image scale for face detection. + face_min_neighbors: Parameter specifying how many neighbors each candidate rectangle should have to retain it for face detection. + eye_scale_factor: Parameter specifying how much the image size is reduced at each image scale for eye detection. + eye_min_neighbors: Parameter specifying how many neighbors each candidate rectangle should have to retain it for eye detection. + + Raises: + FileNotFoundError: If the Haar cascade classifier files are not found. + IOError: If the webcam cannot be opened. + Press 'q' to exit the program. """ + # Get the directory where OpenCV's data files are located + cv2_data_dir = os.path.join(os.path.dirname(cv.__file__), 'data') + + # Construct absolute paths to the Haar cascade files + face_cascade_path = os.path.join(cv2_data_dir, 'haarcascade_frontalface_default.xml') + eye_cascade_path = os.path.join(cv2_data_dir, 'haarcascade_eye.xml') + # Load the pre-trained classifiers for face and eye detection - face_cascade = cv.CascadeClassifier(r"..\libs\haarcascade_frontalface_default.xml") - eye_cascade = cv.CascadeClassifier(r"..\libs\haarcascade_eye.xml") + face_cascade: CascadeClassifier = cv.CascadeClassifier(face_cascade_path) + eye_cascade: CascadeClassifier = cv.CascadeClassifier(eye_cascade_path) + + # Validate classifier loading + if face_cascade.empty() or eye_cascade.empty(): + raise FileNotFoundError(f"Unable to load Haar cascade classifiers from:\n" + f"Face: {face_cascade_path}\n" + f"Eye: {eye_cascade_path}\n\n" + f"Please verify the files exist in your OpenCV installation.") # Open the webcam - cap = cv.VideoCapture(0) + cap: VideoCapture = cv.VideoCapture(camera_index) + + if not cap.isOpened(): + raise IOError(f"Cannot open camera with index {camera_index}") + + try: + while True: + # Read a frame from the camera + success: bool + frame: np.ndarray + success, frame = cap.read() - while cap.isOpened(): - # Read a frame from the webcam - flag, img = cap.read() + if not success: + print("Failed to grab frame") + break - # Convert the frame to grayscale for better performance - gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) + # Convert the frame to grayscale for better performance + gray_frame: np.ndarray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) - # Detect faces in the frame - faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=7) + # Detect faces in the grayscale frame + faces: List[Tuple[int, int, int, int]] = face_cascade.detectMultiScale( + gray_frame, + scaleFactor=face_scale_factor, + minNeighbors=face_min_neighbors, + minSize=(30, 30), + flags=cv.CASCADE_SCALE_IMAGE + ) - # Detect eyes in the frame - eyes = eye_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=7) + # Detect eyes in the grayscale frame + eyes: List[Tuple[int, int, int, int]] = eye_cascade.detectMultiScale( + gray_frame, + scaleFactor=eye_scale_factor, + minNeighbors=eye_min_neighbors, + minSize=(10, 10), + flags=cv.CASCADE_SCALE_IMAGE + ) - # Draw rectangles around faces and eyes - for x, y, w, h in faces: - cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 1) + # Draw rectangles around detected faces + for (x, y, w, h) in faces: + cv.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) - for a, b, c, d in eyes: - cv.rectangle(img, (a, b), (a + c, b + d), (255, 0, 0), 1) + # Draw rectangles around detected eyes + for (x, y, w, h) in eyes: + cv.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 1) - # Display the resulting frame - cv.imshow("Face and Eye Detection", img) + # Display the resulting frame with detections + cv.imshow("Face and Eye Detection", frame) - # Check for the 'q' key to exit the program - key = cv.waitKey(1) - if key == ord("q"): - break + # Check for the 'q' key to exit the loop + key: int = cv.waitKey(1) + if key == ord("q"): + break - # Release the webcam and close all windows - cap.release() - cv.destroyAllWindows() + finally: + # Release the camera and close all OpenCV windows + cap.release() + cv.destroyAllWindows() if __name__ == "__main__": - # Call the main function - detect_faces_and_eyes() \ No newline at end of file + # Call the main function with default parameters + detect_faces_and_eyes() \ No newline at end of file diff --git a/Face and eye Recognition/gesture_control.py b/Face and eye Recognition/gesture_control.py index 8afd63af13f..dc7167ab1e5 100644 --- a/Face and eye Recognition/gesture_control.py +++ b/Face and eye Recognition/gesture_control.py @@ -1,27 +1,57 @@ import cv2 as cv +import numpy as np +from typing import List -# Read the image in grayscale -img = cv.imread(r"..\img\hand1.jpg", cv.IMREAD_GRAYSCALE) +def process_hand_image(image_path: str) -> None: + """ + Processes an image to detect hand contours and convex hulls. + + Args: + image_path: Path to the input image file. + + Raises: + FileNotFoundError: If the specified image file cannot be found. + cv.error: If there is an error during image processing. + """ + # Read the image in grayscale + img: np.ndarray = cv.imread(image_path, cv.IMREAD_GRAYSCALE) + + # Validate image loading + if img is None: + raise FileNotFoundError(f"Could not read image from path: {image_path}") + + # Apply thresholding to create a binary image + thresholded: np.ndarray + _, thresholded = cv.threshold(img, 70, 255, cv.THRESH_BINARY) + + # Find contours in the binary image + contours: List[np.ndarray] + hierarchy: np.ndarray + contours, hierarchy = cv.findContours(thresholded.copy(), cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) + + # Compute convex hull for each contour + convex_hulls: List[np.ndarray] = [cv.convexHull(contour) for contour in contours] + + # Convert grayscale image to BGR for colored drawing + color_img: np.ndarray = cv.cvtColor(img, cv.COLOR_GRAY2BGR) + + # Draw contours and convex hulls on the colored image + contours_image: np.ndarray = color_img.copy() + cv.drawContours(contours_image, contours, -1, (0, 255, 0), 2) + + convex_hulls_image: np.ndarray = color_img.copy() + cv.drawContours(convex_hulls_image, convex_hulls, -1, (0, 0, 255), 2) + + # Display the images + cv.imshow("Original Image", img) + cv.imshow("Thresholded Image", thresholded) + cv.imshow("Contours", contours_image) + cv.imshow("Convex Hulls", convex_hulls_image) + + # Wait for a key press and then close all windows + cv.waitKey(0) + cv.destroyAllWindows() -# Apply thresholding to create a binary image -_, thresholded = cv.threshold(img, 70, 255, cv.THRESH_BINARY) - -# Find contours in the binary image -contours, _ = cv.findContours(thresholded.copy(), cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) - -# Convex Hull for each contour -convex_hulls = [cv.convexHull(contour) for contour in contours] - -# Draw contours and convex hulls on the original image -original_with_contours = cv.drawContours(img.copy(), contours, -1, (0, 0, 0), 2) -original_with_convex_hulls = cv.drawContours(img.copy(), convex_hulls, -1, (0, 0, 0), 2) - -# Display the images -cv.imshow("Original Image", img) -cv.imshow("Thresholded Image", thresholded) -cv.imshow("Contours", original_with_contours) -cv.imshow("Convex Hulls", original_with_convex_hulls) - -# Wait for a key press and close windows -cv.waitKey(0) -cv.destroyAllWindows() +if __name__ == "__main__": + # Example usage + process_hand_image(r"..\img\hand1.jpg") \ No newline at end of file diff --git a/Flappy Bird - created with tkinter/Bird.py b/Flappy Bird - created with tkinter/Bird.py index 56fdcd1d31c..85033f04ab4 100644 --- a/Flappy Bird - created with tkinter/Bird.py +++ b/Flappy Bird - created with tkinter/Bird.py @@ -1,255 +1,278 @@ from threading import Thread - -from Background import Background -from PIL.Image import open as openImage +from typing import Callable, List, Optional, Tuple +from PIL.Image import Image, open as openImage from PIL.ImageTk import PhotoImage +class Background: + """Mock Background class for type checking""" + def __init__(self): + self.bird_image = None + + def create_image(self, x: int, y: int, image: PhotoImage, tag: str) -> int: + return 0 + + def bbox(self, tag: str) -> Tuple[int, int, int, int]: + return (0, 0, 0, 0) + + def find_overlapping(self, x1: int, y1: int, x2: int, y2: int) -> List[int]: + return [] + + def move(self, tag: str, x: int, y: int) -> None: + pass + + def after(self, delay: int, callback: Callable) -> None: + pass + + def bind(self, event: str, callback: Callable) -> None: + pass + + def focus_force(self) -> None: + pass + + def getBackgroundID(self) -> List[int]: + return [] class Bird(Thread): """ - Classe para criar um pássaro + Class for creating a bird object that can fly in a game environment. """ - - __tag = "Bird" - __isAlive = None - __going_up = False - __going_down = 0 - __times_skipped = 0 - __running = False - - decends = 0.00390625 - climbsUp = 0.0911458333 + + __tag: str = "Bird" + __isAlive: bool = None + __going_up: bool = False + __going_down: float = 0 + __times_skipped: int = 0 + __running: bool = False + + decends: float = 0.00390625 + climbsUp: float = 0.0911458333 def __init__( self, - background, - gameover_function, - *screen_geometry, - fp="bird.png", - event="", - descend_speed=5 - ): - - # Verifica se "background" é uma instância de Background e se o "gamerover_method" é chamável - + background: Background, + gameover_function: Callable[[], None], + *screen_geometry: int, + fp: str = "bird.png", + event: str = "", + descend_speed: int = 5 + ) -> None: + """ + Initialize the bird object. + + Args: + background: The game background canvas. + gameover_function: Function to call when the bird dies. + screen_geometry: Width and height of the game screen. + fp: Path to the bird image file. + event: Keyboard event to trigger the bird's jump. + descend_speed: Speed at which the bird descends. + """ + # Validate input types if not isinstance(background, Background): - raise TypeError( - "The background argument must be an instance of Background." - ) + raise TypeError("The background argument must be an instance of Background.") if not callable(gameover_function): - raise TypeError("The gameover_method argument must be a callable object.") - - # Instância os parâmetros - self.__canvas = background - self.image_path = fp - self.__descend_speed = descend_speed - self.gameover_method = gameover_function - - # Recebe a largura e altura do background - self.__width = screen_geometry[0] - self.__height = screen_geometry[1] - - # Define a decida e subida do pássaro com base na altura do background + raise TypeError("The gameover_function argument must be a callable object.") + + # Instance parameters + self.__canvas: Background = background + self.image_path: str = fp + self.__descend_speed: int = descend_speed + self.gameover_method: Callable[[], None] = gameover_function + + # Get screen dimensions + if len(screen_geometry) != 2: + raise ValueError("screen_geometry must contain width and height") + self.__width: int = screen_geometry[0] + self.__height: int = screen_geometry[1] + + # Adjust bird movement speeds based on screen height self.decends *= self.__height self.decends = int(self.decends + 0.5) self.climbsUp *= self.__height self.climbsUp = int(self.climbsUp + 0.5) - - # Invoca o método construtor de Thread - Thread.__init__(self) - - # Calcula o tamanho do pássaro com base na largura e altura da janela - self.width = (self.__width // 100) * 6 - self.height = (self.__height // 100) * 11 - - # Carrega e cria a imagem do pássaro no background - self.__canvas.bird_image = self.getPhotoImage( + + # Initialize Thread + super().__init__() + + # Calculate bird dimensions + self.width: int = (self.__width // 100) * 6 + self.height: int = (self.__height // 100) * 11 + + # Load and create bird image + self.__canvas.bird_image, _, _ = self.getPhotoImage( image_path=self.image_path, width=self.width, height=self.height, closeAfter=True, - )[0] - self.__birdID = self.__canvas.create_image( + ) + self.__birdID: int = self.__canvas.create_image( self.__width // 2, self.__height // 2, image=self.__canvas.bird_image, tag=self.__tag, ) - - # Define evento para fazer o pássaro subir + + # Bind jump event self.__canvas.focus_force() self.__canvas.bind(event, self.jumps) self.__isAlive = True - def birdIsAlive(self): - """ - Método para verificar se o pássaro está vivo - """ - + def birdIsAlive(self) -> bool: + """Check if the bird is alive.""" return self.__isAlive - def checkCollision(self): + def checkCollision(self) -> bool: """ - Método para verificar se o pássaro ultrapassou a borda da janela ou colidiu com algo + Check if the bird has collided with the boundaries or other objects. + + Returns: + True if a collision occurred, False otherwise. """ - - # Recebe a posição do pássaro no background - position = list(self.__canvas.bbox(self.__tag)) - - # Se o pássaro tiver ultrapassado a borda de baixo do background, ele será declarado morto + # Get bird position + position: List[int] = list(self.__canvas.bbox(self.__tag)) + + # Check boundary collisions if position[3] >= self.__height + 20: self.__isAlive = False - - # Se o pássaro tiver ultrapassado a borda de cima do background, ele será declarado morto + if position[1] <= -20: self.__isAlive = False - - # Dá uma margem de erro ao pássaro de X pixels + + # Adjust collision boundaries with a margin of error position[0] += int(25 / 78 * self.width) position[1] += int(25 / 77 * self.height) position[2] -= int(20 / 78 * self.width) position[3] -= int(10 / 77 * self.width) - - # Define os objetos a serem ignorados em colisões - ignored_collisions = self.__canvas.getBackgroundID() + + # Define objects to ignore in collisions + ignored_collisions: List[int] = self.__canvas.getBackgroundID() ignored_collisions.append(self.__birdID) - - # Verifica possíveis colisões com o pássaro - possible_collisions = list(self.__canvas.find_overlapping(*position)) - - # Remove das possíveis colisões os objetos ignorados - for _id in ignored_collisions: + + # Check for overlapping objects + possible_collisions: List[int] = list(self.__canvas.find_overlapping(*position)) + + # Remove ignored objects from collision list + for obj_id in ignored_collisions: try: - possible_collisions.remove(_id) - except BaseException: + possible_collisions.remove(obj_id) + except ValueError: continue - - # Se houver alguma colisão o pássaro morre + + # Collision detected if len(possible_collisions) >= 1: self.__isAlive = False - + return not self.__isAlive - def getTag(self): - """ - Método para retornar a tag do pássaro - """ - + def getTag(self) -> str: + """Return the bird's tag.""" return self.__tag @staticmethod def getPhotoImage( - image=None, image_path=None, width=None, height=None, closeAfter=False - ): + image: Optional[Image] = None, + image_path: Optional[str] = None, + width: Optional[int] = None, + height: Optional[int] = None, + closeAfter: bool = False + ) -> Tuple[PhotoImage, Optional[Image], Optional[Image]]: """ - Retorna um objeto da classe PIL.ImageTk.PhotoImage de uma imagem e as imagens criadas de PIL.Image - (photoImage, new, original) - - @param image: Instância de PIL.Image.open - @param image_path: Diretório da imagem - @param width: Largura da imagem - @param height: Altura da imagem - @param closeAfter: Se True, a imagem será fechada após ser criado um PhotoImage da mesma + Create a PhotoImage from a PIL Image or image path. + + Args: + image: PIL Image object. + image_path: Path to an image file. + width: Desired width of the image. + height: Desired height of the image. + closeAfter: Whether to close the image after creating the PhotoImage. + + Returns: + A tuple containing the PhotoImage, the resized image, and the original image. """ - - if not image: - if not image_path: - return - - # Abre a imagem utilizando o caminho dela + if image is None: + if image_path is None: + raise ValueError("Either image or image_path must be provided") + + # Open image from path image = openImage(image_path) - - # Será redimesionada a imagem somente se existir um width ou height - if not width: + + # Resize image if dimensions are provided + if width is None: width = image.width - if not height: + if height is None: height = image.height - - # Cria uma nova imagem já redimensionada - newImage = image.resize([width, height]) - - # Cria um photoImage - photoImage = PhotoImage(newImage) - - # Se closeAfter for True, ele fecha as imagens + + # Create resized image + newImage: Image = image.resize([width, height]) + + # Create PhotoImage + photoImage: PhotoImage = PhotoImage(newImage) + + # Close images if requested if closeAfter: - # Fecha a imagem nova newImage.close() newImage = None - - # Fecha a imagem original image.close() image = None - - # Retorna o PhotoImage da imagem,a nova imagem que foi utilizada e a imagem original + return photoImage, newImage, image - def jumps(self, event=None): + def jumps(self, event: Optional[object] = None) -> None: """ - Método para fazer o pássaro pular + Make the bird jump. + + Args: + event: Keyboard event (automatically passed by Tkinter). """ - - # Verifica se o pássaro saiu da área do background + # Check collision status self.checkCollision() - - # Se o pássaro estiver morto, esse método não pode ser executado + + # Prevent jumping if bird is dead or not running if not self.__isAlive or not self.__running: self.__going_up = False return - - # Declara que o pássaro está subindo + + # Bird is going up self.__going_up = True self.__going_down = 0 - - # Move o pássaro enquanto o limite de subida por animação não tiver excedido + + # Animate the jump if self.__times_skipped < self.climbsUp: - - # Move o pássaro para cima + # Move bird upwards self.__canvas.move(self.__tag, 0, -1) self.__times_skipped += 1 - - # Executa o método novamente + + # Continue jump animation self.__canvas.after(3, self.jumps) - else: - - # Declara que o pássaro não está mais subindo + # Jump animation complete self.__going_up = False self.__times_skipped = 0 - def kill(self): - """ - Método para matar o pássaro - """ - + def kill(self) -> None: + """Kill the bird (set isAlive to False).""" self.__isAlive = False - def run(self): - """ - #Método para iniciar a animação do passáro caindo - """ - + def run(self) -> None: + """Main animation loop for the bird's falling motion.""" self.__running = True - - # Verifica se o pássaro saiu da área do background + + # Check collision status self.checkCollision() - - # Enquanto o pássaro não tiver chegado em sua velocidade máxima, a velocidade aumentará em 0.05 + + # Increase falling speed up to maximum if self.__going_down < self.decends: self.__going_down += 0.05 - - # Executa a animação de descida somente se o pássaro estiver vivo + + # Continue animation if bird is alive if self.__isAlive: - - # Executa a animação de descida somente se o pássaro não estiver subindo + # Only move down if not jumping if not self.__going_up: - # Move o pássaro para baixo self.__canvas.move(self.__tag, 0, self.__going_down) - - # Executa novamente o método + + # Schedule next frame self.__canvas.after(self.__descend_speed, self.run) - - # Se o pássaro estiver morto, será executado um método de fim de jogo else: + # Bird is dead, trigger game over self.__running = False - self.gameover_method() + self.gameover_method() \ No newline at end of file diff --git a/Flappy Bird - created with tkinter/Flappy Bird.py b/Flappy Bird - created with tkinter/Flappy Bird.py index 7dfe9564dfb..a0bda281084 100644 --- a/Flappy Bird - created with tkinter/Flappy Bird.py +++ b/Flappy Bird - created with tkinter/Flappy Bird.py @@ -1,5 +1,6 @@ import pygame import random +import os # Initialize Pygame pygame.init() @@ -10,12 +11,36 @@ screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("Flappy Bird") +# Get the directory where the script is located +script_dir = os.path.dirname(os.path.abspath(__file__)) +# Use the Images folder under the script directory +images_dir = os.path.join(script_dir, "Images") + +# Debugging: Print paths +print(f"Script directory: {script_dir}") +print(f"Images directory: {images_dir}") + +# Check if image files exist +required_images = ["bird.png", "pipe.png", "background.png"] +missing_files = [] + +for img in required_images: + img_path = os.path.join(images_dir, img) + if not os.path.exists(img_path): + missing_files.append(img) + print(f"Error: Missing {img} at {img_path}") + +if missing_files: + print(f"Please place the missing files in {images_dir}") + pygame.quit() + exit(1) + # Load images -bird_image = pygame.image.load("bird.png").convert_alpha() -pipe_image = pygame.image.load("pipe.png").convert_alpha() -background_image = pygame.image.load("background.png").convert_alpha() +bird_image = pygame.image.load(os.path.join(images_dir, "bird.png")).convert_alpha() +pipe_image = pygame.image.load(os.path.join(images_dir, "pipe.png")).convert_alpha() +background_image = pygame.image.load(os.path.join(images_dir, "background.png")).convert_alpha() -# Bird class +# Bird class (unchanged) class Bird: def __init__(self): self.image = bird_image @@ -34,7 +59,7 @@ def flap(self): def draw(self, screen): screen.blit(self.image, (self.x, self.y)) -# Pipe class +# Pipe class (unchanged) class Pipe: def __init__(self): self.image = pipe_image @@ -82,4 +107,4 @@ def main(): pygame.quit() if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/Flappy Bird - created with tkinter/Images/pipe.png b/Flappy Bird - created with tkinter/Images/pipe.png new file mode 100644 index 00000000000..a49457d3e51 Binary files /dev/null and b/Flappy Bird - created with tkinter/Images/pipe.png differ diff --git a/Google_Image_Downloader/create_dir.py b/Google_Image_Downloader/create_dir.py index 0734f836802..79aef4c6abf 100644 --- a/Google_Image_Downloader/create_dir.py +++ b/Google_Image_Downloader/create_dir.py @@ -1,66 +1,85 @@ """ -Code to directly use in file to -create directory in home location +Code to manage directories in the project's parent directory. -Note:- I Have used python package so if you want -to create in the main directory of your project use -pardir+"\\"+name in functions - -All the folder operations are done on home -project directory. +Note: Uses pathlib for platform-agnostic path handling. +All operations default to the project's parent directory. """ -from os import chdir -from os import makedirs -from os import removedirs -from os import rename -from os.path import exists -from os.path import pardir -from shutil import copytree -from shutil import move +from pathlib import Path +from shutil import copytree, move +# Get the project's parent directory +PROJECT_ROOT = Path(__file__).parent.parent -# Creates a directory -def create_directory(name): - if exists(pardir + "\\" + name): - print("Folder already exists... Cannot Overwrite this") +def create_directory(name: str) -> None: + """Create a directory in the project's parent directory.""" + target_dir = PROJECT_ROOT / name + if target_dir.exists(): + print(f"Error: Folder '{name}' already exists.") else: - makedirs(pardir + "\\" + name) - - -# Deletes a directory -def delete_directory(name): - removedirs(name) - - -# Rename a directory -def rename_directory(direct, name): - rename(direct, name) - - -# Sets the working directory -def set_working_directory(): - chdir(pardir) - - -# Backup the folder tree -def backup_files(name_dir, folder): - copytree(pardir, name_dir + ":\\" + folder) - - -# Move folder to specific location -# Overwrites the file if it already exists -def move_folder(filename, name_dir, folder): - if not exists(name_dir + ":\\" + folder): - makedirs(name_dir + ":\\" + folder) - move(filename, name_dir + ":\\" + folder + "\\") - - -""" -For test purpose: - 1. create_directory("test") - 2. rename_directory("test","demo") - 3. delete_directory("demo") - 4. backup_files('D', 'backup_project') - 5. move_folder(pardir+'\\'+'test.txt', 'D', 'name') -""" + target_dir.mkdir(exist_ok=False) + print(f"Created directory: {target_dir}") + +def delete_directory(name: str) -> None: + """Delete a directory and its contents recursively.""" + target_dir = PROJECT_ROOT / name + if not target_dir.exists(): + print(f"Error: Directory '{name}' does not exist.") + return + # Safely delete non-empty directory + for item in target_dir.rglob('*'): + if item.is_file(): + item.unlink() + else: + item.rmdir() + target_dir.rmdir() + print(f"Deleted directory: {target_dir}") + +def rename_directory(old_name: str, new_name: str) -> None: + """Rename a directory in the project's parent directory.""" + old_path = PROJECT_ROOT / old_name + new_path = PROJECT_ROOT / new_name + + if not old_path.exists(): + print(f"Error: Directory '{old_name}' does not exist.") + return + if new_path.exists(): + print(f"Error: Directory '{new_name}' already exists.") + return + + old_path.rename(new_path) + print(f"Renamed '{old_name}' to '{new_name}'") + +def backup_files(drive_letter: str, backup_folder: str) -> None: + """Backup the project directory to another drive.""" + source = PROJECT_ROOT + destination = Path(f"{drive_letter}:/{backup_folder}") + + if destination.exists(): + print(f"Error: Backup directory '{destination}' already exists.") + return + + copytree(source, destination) + print(f"Backed up project to: {destination}") + +def move_folder(file_or_folder: str, drive_letter: str, target_folder: str) -> None: + """Move a file/folder to a specific location, overwriting if it exists.""" + source = PROJECT_ROOT / file_or_folder + destination = Path(f"{drive_letter}:/{target_folder}") + + if not source.exists(): + print(f"Error: Source '{source}' does not exist.") + return + + destination.mkdir(parents=True, exist_ok=True) + move(str(source), str(destination / source.name)) + print(f"Moved '{source}' to '{destination}'") + +# Example usage +if __name__ == "__main__": + # create_directory("test") + # rename_directory("test", "demo") + # delete_directory("demo") + # backup_files('D', 'backup_project') + # move_folder('test.txt', 'D', 'name') + pass \ No newline at end of file diff --git a/Google_Image_Downloader/image_grapper.py b/Google_Image_Downloader/image_grapper.py index a922894a8d0..6c3cd591b52 100644 --- a/Google_Image_Downloader/image_grapper.py +++ b/Google_Image_Downloader/image_grapper.py @@ -9,7 +9,7 @@ from urllib.parse import urlencode from urllib.request import urlopen, Request -import requests +import httpx import ssl from bs4 import BeautifulSoup from create_dir import create_directory @@ -62,7 +62,7 @@ def search_for_image(): images.append(link) counter = 0 for re in images: - rs = requests.get(re) + rs = httpx.get(re) with open("img" + str(counter) + ".jpg", "wb") as file: file.write(rs.content) @@ -100,7 +100,7 @@ def download_wallpapers_1080p(): # Goes to Each link and downloads high resolution images for re in temp: - rs = requests.get(re) + rs = httpx.get(re) with open("img" + str(count) + ".jpg", "wb") as file: file.write(rs.content) diff --git a/Hand-Motion-Detection/requirements.txt b/Hand-Motion-Detection/requirements.txt deleted file mode 100644 index 123ef71ed1e..00000000000 --- a/Hand-Motion-Detection/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -numpy==2.2.3 -opencv_python==4.12.0.88 -mediapipe==0.10.21 diff --git a/HangMan Game.py b/HangMan Game.py index 56d106f8c88..072cc44dbf8 100644 --- a/HangMan Game.py +++ b/HangMan Game.py @@ -1,5 +1,6 @@ # Program for HangMan Game. -import random, HangMan_Includes as incl +import random +import HangMan_Includes as incl while True: chances=6 diff --git a/Image-watermarker/requirements.txt b/Image-watermarker/requirements.txt deleted file mode 100644 index f6fcb76c983..00000000000 Binary files a/Image-watermarker/requirements.txt and /dev/null differ diff --git a/ImageDownloader/img_downloader.py b/ImageDownloader/img_downloader.py index 4ed7c65d8ff..04df275e19c 100644 --- a/ImageDownloader/img_downloader.py +++ b/ImageDownloader/img_downloader.py @@ -4,9 +4,9 @@ def ImageDownloader(url): import os import re - import requests + import httpx - response = requests.get(url) + response = httpx.get(url) text = response.text p = r']+>' diff --git a/ImageDownloader/requirements.txt b/ImageDownloader/requirements.txt deleted file mode 100644 index bd6f2345868..00000000000 --- a/ImageDownloader/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -requests==2.32.4 diff --git a/Industrial_developed_hangman/Makefile b/Industrial_developed_hangman/Makefile deleted file mode 100644 index e4e05f18fb2..00000000000 --- a/Industrial_developed_hangman/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -lint: - poetry run isort src tests - poetry run flake8 src tests - poetry run mypy src - poetry run mypy tests - -test: - poetry run pytest - -build: - python src/hangman/main.py -install: - pip install poetry - poetry install \ No newline at end of file diff --git a/Industrial_developed_hangman/poetry.lock b/Industrial_developed_hangman/poetry.lock deleted file mode 100644 index 99bcf936a62..00000000000 --- a/Industrial_developed_hangman/poetry.lock +++ /dev/null @@ -1,1235 +0,0 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. - -[[package]] -name = "astor" -version = "0.8.1" -description = "Read/rewrite/write Python ASTs" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, - {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] - -[[package]] -name = "bandit" -version = "1.7.5" -description = "Security oriented static analyser for python code." -optional = false -python-versions = ">=3.7" -files = [ - {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, - {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] - -[[package]] -name = "beautifulsoup4" -version = "4.12.0" -description = "Screen-scraping library" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.12.0-py3-none-any.whl", hash = "sha256:2130a5ad7f513200fae61a17abb5e338ca980fa28c439c0571014bc0217e9591"}, - {file = "beautifulsoup4-4.12.0.tar.gz", hash = "sha256:c5fceeaec29d09c84970e47c65f2f0efe57872f7cff494c9691a26ec0ff13234"}, -] - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "certifi" -version = "2023.7.22" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.3.2" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, - {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, - {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, - {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, - {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, - {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, - {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, - {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, - {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, - {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, - {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, - {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, - {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, - {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "darglint" -version = "1.8.1" -description = "A utility for ensuring Google-style docstrings stay up to date with the source code." -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, - {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, -] - -[[package]] -name = "docutils" -version = "0.20.1" -description = "Docutils -- Python Documentation Utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, - {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, -] - -[[package]] -name = "eradicate" -version = "2.3.0" -description = "Removes commented-out code." -optional = false -python-versions = "*" -files = [ - {file = "eradicate-2.3.0-py3-none-any.whl", hash = "sha256:2b29b3dd27171f209e4ddd8204b70c02f0682ae95eecb353f10e8d72b149c63e"}, - {file = "eradicate-2.3.0.tar.gz", hash = "sha256:06df115be3b87d0fc1c483db22a2ebb12bcf40585722810d809cc770f5031c37"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.3" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "flake8" -version = "6.1.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" - -[[package]] -name = "flake8-bandit" -version = "4.1.1" -description = "Automated security testing with bandit and flake8." -optional = false -python-versions = ">=3.6" -files = [ - {file = "flake8_bandit-4.1.1-py3-none-any.whl", hash = "sha256:4c8a53eb48f23d4ef1e59293657181a3c989d0077c9952717e98a0eace43e06d"}, - {file = "flake8_bandit-4.1.1.tar.gz", hash = "sha256:068e09287189cbfd7f986e92605adea2067630b75380c6b5733dab7d87f9a84e"}, -] - -[package.dependencies] -bandit = ">=1.7.3" -flake8 = ">=5.0.0" - -[[package]] -name = "flake8-broken-line" -version = "1.0.0" -description = "Flake8 plugin to forbid backslashes for line breaks" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "flake8_broken_line-1.0.0-py3-none-any.whl", hash = "sha256:96c964336024a5030dc536a9f6fb02aa679e2d2a6b35b80a558b5136c35832a9"}, - {file = "flake8_broken_line-1.0.0.tar.gz", hash = "sha256:e2c6a17f8d9a129e99c1320fce89b33843e2963871025c4c2bb7b8b8d8732a85"}, -] - -[package.dependencies] -flake8 = ">5" - -[[package]] -name = "flake8-bugbear" -version = "23.9.16" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-bugbear-23.9.16.tar.gz", hash = "sha256:90cf04b19ca02a682feb5aac67cae8de742af70538590509941ab10ae8351f71"}, - {file = "flake8_bugbear-23.9.16-py3-none-any.whl", hash = "sha256:b182cf96ea8f7a8595b2f87321d7d9b28728f4d9c3318012d896543d19742cb5"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "flake8-commas" -version = "2.1.0" -description = "Flake8 lint for trailing commas." -optional = false -python-versions = "*" -files = [ - {file = "flake8-commas-2.1.0.tar.gz", hash = "sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263"}, - {file = "flake8_commas-2.1.0-py2.py3-none-any.whl", hash = "sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54"}, -] - -[package.dependencies] -flake8 = ">=2" - -[[package]] -name = "flake8-comprehensions" -version = "3.14.0" -description = "A flake8 plugin to help you write better list/set/dict comprehensions." -optional = false -python-versions = ">=3.8" -files = [ - {file = "flake8_comprehensions-3.14.0-py3-none-any.whl", hash = "sha256:7b9d07d94aa88e62099a6d1931ddf16c344d4157deedf90fe0d8ee2846f30e97"}, - {file = "flake8_comprehensions-3.14.0.tar.gz", hash = "sha256:81768c61bfc064e1a06222df08a2580d97de10cb388694becaf987c331c6c0cf"}, -] - -[package.dependencies] -flake8 = ">=3.0,<3.2.0 || >3.2.0" - -[[package]] -name = "flake8-debugger" -version = "4.1.2" -description = "ipdb/pdb statement checker plugin for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, - {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, -] - -[package.dependencies] -flake8 = ">=3.0" -pycodestyle = "*" - -[[package]] -name = "flake8-docstrings" -version = "1.7.0" -description = "Extension for flake8 which uses pydocstyle to check docstrings" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, - {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, -] - -[package.dependencies] -flake8 = ">=3" -pydocstyle = ">=2.1" - -[[package]] -name = "flake8-eradicate" -version = "1.5.0" -description = "Flake8 plugin to find commented out code" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "flake8_eradicate-1.5.0-py3-none-any.whl", hash = "sha256:18acc922ad7de623f5247c7d5595da068525ec5437dd53b22ec2259b96ce9d22"}, - {file = "flake8_eradicate-1.5.0.tar.gz", hash = "sha256:aee636cb9ecb5594a7cd92d67ad73eb69909e5cc7bd81710cf9d00970f3983a6"}, -] - -[package.dependencies] -attrs = "*" -eradicate = ">=2.0,<3.0" -flake8 = ">5" - -[[package]] -name = "flake8-isort" -version = "6.1.0" -description = "flake8 plugin that integrates isort ." -optional = false -python-versions = ">=3.8" -files = [ - {file = "flake8-isort-6.1.0.tar.gz", hash = "sha256:d4639343bac540194c59fb1618ac2c285b3e27609f353bef6f50904d40c1643e"}, -] - -[package.dependencies] -flake8 = "*" -isort = ">=5.0.0,<6" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "flake8-quotes" -version = "3.3.2" -description = "Flake8 lint for quotes." -optional = false -python-versions = "*" -files = [ - {file = "flake8-quotes-3.3.2.tar.gz", hash = "sha256:6e26892b632dacba517bf27219c459a8396dcfac0f5e8204904c5a4ba9b480e1"}, -] - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "flake8-rst-docstrings" -version = "0.3.0" -description = "Python docstring reStructuredText (RST) validator for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-rst-docstrings-0.3.0.tar.gz", hash = "sha256:d1ce22b4bd37b73cd86b8d980e946ef198cfcc18ed82fedb674ceaa2f8d1afa4"}, - {file = "flake8_rst_docstrings-0.3.0-py3-none-any.whl", hash = "sha256:f8c3c6892ff402292651c31983a38da082480ad3ba253743de52989bdc84ca1c"}, -] - -[package.dependencies] -flake8 = ">=3" -pygments = "*" -restructuredtext-lint = "*" - -[package.extras] -develop = ["build", "twine"] - -[[package]] -name = "flake8-string-format" -version = "0.3.0" -description = "string format checker, plugin for flake8" -optional = false -python-versions = "*" -files = [ - {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, - {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, -] - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "freezegun" -version = "1.2.2" -description = "Let your Python tests travel through time" -optional = false -python-versions = ">=3.6" -files = [ - {file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"}, - {file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"}, -] - -[package.dependencies] -python-dateutil = ">=2.7" - -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.40" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, - {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "importlib-metadata" -version = "6.8.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "mypy" -version = "1.5.1" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, - {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, - {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, - {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, - {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, - {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, - {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, - {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, - {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, - {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, - {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, - {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, - {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, - {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, - {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, - {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, - {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, - {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, - {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pbr" -version = "5.11.1" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, - {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, -] - -[[package]] -name = "pep8-naming" -version = "0.13.3" -description = "Check PEP-8 naming conventions, plugin for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, - {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, -] - -[package.dependencies] -flake8 = ">=5.0.0" - -[[package]] -name = "pluggy" -version = "1.3.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pycodestyle" -version = "2.11.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, -] - -[[package]] -name = "pydocstyle" -version = "6.3.0" -description = "Python docstring style checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, - {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, -] - -[package.dependencies] -snowballstemmer = ">=2.2.0" - -[package.extras] -toml = ["tomli (>=1.2.3)"] - -[[package]] -name = "pyflakes" -version = "3.1.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, -] - -[[package]] -name = "pygments" -version = "2.16.1" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, -] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pytest" -version = "7.4.2" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "4.1.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, -] - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] - -[[package]] -name = "pytest-freezer" -version = "0.4.8" -description = "Pytest plugin providing a fixture interface for spulec/freezegun" -optional = false -python-versions = ">= 3.6" -files = [ - {file = "pytest_freezer-0.4.8-py3-none-any.whl", hash = "sha256:644ce7ddb8ba52b92a1df0a80a699bad2b93514c55cf92e9f2517b68ebe74814"}, - {file = "pytest_freezer-0.4.8.tar.gz", hash = "sha256:8ee2f724b3ff3540523fa355958a22e6f4c1c819928b78a7a183ae4248ce6ee6"}, -] - -[package.dependencies] -freezegun = ">=1.0" -pytest = ">=3.6" - -[[package]] -name = "pytest-randomly" -version = "3.15.0" -description = "Pytest plugin to randomly order tests and control random.seed." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest_randomly-3.15.0-py3-none-any.whl", hash = "sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6"}, - {file = "pytest_randomly-3.15.0.tar.gz", hash = "sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -pytest = "*" - -[[package]] -name = "pytest-timeout" -version = "2.2.0" -description = "pytest plugin to abort hanging tests" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-timeout-2.2.0.tar.gz", hash = "sha256:3b0b95dabf3cb50bac9ef5ca912fa0cfc286526af17afc806824df20c2f72c90"}, - {file = "pytest_timeout-2.2.0-py3-none-any.whl", hash = "sha256:bde531e096466f49398a59f2dde76fa78429a09a12411466f88a07213e220de2"}, -] - -[package.dependencies] -pytest = ">=5.0.0" - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "requests-mock" -version = "1.11.0" -description = "Mock out responses from the requests package" -optional = false -python-versions = "*" -files = [ - {file = "requests-mock-1.11.0.tar.gz", hash = "sha256:ef10b572b489a5f28e09b708697208c4a3b2b89ef80a9f01584340ea357ec3c4"}, - {file = "requests_mock-1.11.0-py2.py3-none-any.whl", hash = "sha256:f7fae383f228633f6bececebdab236c478ace2284d6292c6e7e2867b9ab74d15"}, -] - -[package.dependencies] -requests = ">=2.3,<3" -six = "*" - -[package.extras] -fixture = ["fixtures"] -test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"] - -[[package]] -name = "restructuredtext-lint" -version = "1.4.0" -description = "reStructuredText linter" -optional = false -python-versions = "*" -files = [ - {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, -] - -[package.dependencies] -docutils = ">=0.11,<1.0" - -[[package]] -name = "rich" -version = "13.6.0" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, - {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "setuptools" -version = "68.2.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, - {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -optional = false -python-versions = "*" -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - -[[package]] -name = "soupsieve" -version = "2.5" -description = "A modern CSS selector implementation for Beautiful Soup." -optional = false -python-versions = ">=3.8" -files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, -] - -[[package]] -name = "stevedore" -version = "5.1.0" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, - {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, -] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "types-requests" -version = "2.31.0.2" -description = "Typing stubs for requests" -optional = false -python-versions = "*" -files = [ - {file = "types-requests-2.31.0.2.tar.gz", hash = "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40"}, - {file = "types_requests-2.31.0.2-py3-none-any.whl", hash = "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a"}, -] - -[package.dependencies] -types-urllib3 = "*" - -[[package]] -name = "types-urllib3" -version = "1.26.25.14" -description = "Typing stubs for urllib3" -optional = false -python-versions = "*" -files = [ - {file = "types-urllib3-1.26.25.14.tar.gz", hash = "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f"}, - {file = "types_urllib3-1.26.25.14-py3-none-any.whl", hash = "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e"}, -] - -[[package]] -name = "typing-extensions" -version = "4.8.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, -] - -[[package]] -name = "urllib3" -version = "2.0.7" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.7" -files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "wemake-python-styleguide" -version = "0.18.0" -description = "The strictest and most opinionated python linter ever" -optional = false -python-versions = ">=3.8.1,<4.0" -files = [ - {file = "wemake_python_styleguide-0.18.0-py3-none-any.whl", hash = "sha256:2219be145185edcd5e01f4ce49e3dea11acc34f2c377face0c175bb6ea6ac988"}, - {file = "wemake_python_styleguide-0.18.0.tar.gz", hash = "sha256:69139858cf5b2a9ba09dac136e2873a4685515768f68fdef2684ebefd7b1dafd"}, -] - -[package.dependencies] -astor = ">=0.8,<0.9" -attrs = "*" -darglint = ">=1.2,<2.0" -flake8 = ">5" -flake8-bandit = ">=4.1,<5.0" -flake8-broken-line = ">=1.0,<2.0" -flake8-bugbear = ">=23.5,<24.0" -flake8-commas = ">=2.0,<3.0" -flake8-comprehensions = ">=3.1,<4.0" -flake8-debugger = ">=4.0,<5.0" -flake8-docstrings = ">=1.3,<2.0" -flake8-eradicate = ">=1.5,<2.0" -flake8-isort = ">=6.0,<7.0" -flake8-quotes = ">=3.0,<4.0" -flake8-rst-docstrings = ">=0.3,<0.4" -flake8-string-format = ">=0.3,<0.4" -pep8-naming = ">=0.13,<0.14" -pygments = ">=2.4,<3.0" -setuptools = "*" -typing_extensions = ">=4.0,<5.0" - -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.9" -content-hash = "b725c780b419b14540a1d4801f1849230d4e8a1b51a9381e36ff476eb8ab598c" diff --git a/Industrial_developed_hangman/pyproject.toml b/Industrial_developed_hangman/pyproject.toml deleted file mode 100644 index b70606ab82f..00000000000 --- a/Industrial_developed_hangman/pyproject.toml +++ /dev/null @@ -1,35 +0,0 @@ -[tool.poetry] -name = "Hangman" -version = "0.2.0" -description = "" -authors = ["DiodDan "] -readme = "README.md" -packages = [{include = "hangman", from = "src"}] - -[tool.poetry.dependencies] -python = "^3.9" -requests = "2.31.0" -colorama = "0.4.6" -beautifulsoup4 = "4.12" - - -[tool.poetry.group.dev.dependencies] -mypy = "1.5.1" -wemake-python-styleguide = "0.18.0" -isort = "5.12.0" -pytest = "7.4.2" -pytest-cov = "4.1.0" -pytest-timeout = "2.2.0" -pytest-randomly = "3.15.0" -requests-mock = "1.11.0" -pytest-freezer = "0.4.8" -types-requests = " 2.31.0.2" - -[build-system] -requires = ["poetry-core", "colorama", "bs4", "requests"] -build-backend = "poetry.core.masonry.api" - -[tool.isort] -line_length = 80 -multi_line_output = 3 -include_trailing_comma = true diff --git a/Industrial_developed_hangman/pytest.ini b/Industrial_developed_hangman/pytest.ini deleted file mode 100644 index f51da414608..00000000000 --- a/Industrial_developed_hangman/pytest.ini +++ /dev/null @@ -1,5 +0,0 @@ -[pytest] -markers = - internet_required: marks tests that requires internet connection (deselect with '-m "not internet_required"') - serial -timeout = 20 diff --git a/Industrial_developed_hangman/setup.cfg b/Industrial_developed_hangman/setup.cfg deleted file mode 100644 index f57029a0492..00000000000 --- a/Industrial_developed_hangman/setup.cfg +++ /dev/null @@ -1,48 +0,0 @@ -[flake8] -max-line-length = 120 -docstring_style = sphinx -max-arguments = 6 -exps-for-one-empty-line = 0 -ignore = - D100, - D104, - -per-file-ignores = - tests/*: - # Missing docstring in public class - D101, - # Missing docstring in public method - D102, - # Missing docstring in public function - D103, - # Missing docstring in magic method - D105, - # Missing docstring in __init__ - D107, - # Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. - S101, - # Found magic number - WPS432, - # Found wrong keyword: pass - WPS420, - # Found incorrect node inside `class` body - WPS604, - # Found outer scope names shadowing: message_update - WPS442, - # Found comparison with float or complex number - WPS459, - # split between test action and assert - WPS473, - # Found compare with falsy constant - WPS520, - # Found string literal over-use - WPS226 - # Found overused expression - WPS204 - # Found too many module members - WPS202 - -[mypy] -ignore_missing_imports = True -check_untyped_defs = True -disallow_untyped_calls = True diff --git a/Industrial_developed_hangman/src/hangman/main.py b/Industrial_developed_hangman/src/hangman/main.py index b2a7e780ac3..0e3ff1104a0 100644 --- a/Industrial_developed_hangman/src/hangman/main.py +++ b/Industrial_developed_hangman/src/hangman/main.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Callable, List -import requests +import httpx from colorama import Fore, Style DEBUG = False @@ -18,8 +18,8 @@ class Source(Enum): """Enum that represents switch between local and web word parsing.""" - FROM_FILE = 0 # noqa: WPS115 - FROM_INTERNET = 1 # noqa: WPS115 + FROM_FILE = 0 + FROM_INTERNET = 1 def print_wrong(text: str, print_function: Callable[[str], None]) -> None: @@ -60,7 +60,6 @@ def parse_word_from_local(choice_function: Callable[[List[str]], str] = random.c def parse_word_from_site(url: str = 'https://random-word-api.herokuapp.com/word') -> str: - # noqa: DAR201 """ Parse word from website. @@ -70,8 +69,8 @@ def parse_word_from_site(url: str = 'https://random-word-api.herokuapp.com/word' :raises RuntimeError: something go wrong with getting the word from site. """ try: - response: requests.Response = requests.get(url, timeout=request_timeout) - except ConnectionError: + response: httpx.Response = httpx.get(url, timeout=request_timeout) + except httpx.ConnectError: raise ConnectionError('There is no connection to the internet') if response.status_code == success_code: return json.loads(response.content.decode())[0] @@ -99,7 +98,7 @@ def __init__(self, source: Enum, pr_func: Callable, in_func: Callable, ch_func: self._choice_function = ch_func def get_word(self) -> str: - # noqa: DAR201 + """ Parse word(wrapper for local and web parse). @@ -114,14 +113,14 @@ def get_word(self) -> str: def user_lose(self) -> None: """Print text for end of game and exits.""" - print_wrong(f"YOU LOST(the word was '{self._answer_word}')", self._print_function) # noqa:WPS305 + print_wrong(f"YOU LOST(the word was '{self._answer_word}')", self._print_function) def user_win(self) -> None: """Print text for end of game and exits.""" - print_wrong(f'{self._word_string_to_show} YOU WON', self._print_function) # noqa:WPS305 - + print_wrong(f'{self._word_string_to_show} YOU WON', self._print_function) + def game_process(self, user_character: str) -> bool: - # noqa: DAR201 + """ Process user input. @@ -155,8 +154,8 @@ def start_game(self) -> None: print_right(self._answer_word, self._print_function) for attempts in range(attempts_amount): user_remaining_attempts = attempts_amount - attempts - print_right(f'You have {user_remaining_attempts} more attempts', self._print_function) # noqa:WPS305 - print_right(f'{self._word_string_to_show} enter character to guess: ', self._print_function) # noqa:WPS305 + print_right(f'You have {user_remaining_attempts} more attempts', self._print_function) + print_right(f'{self._word_string_to_show} enter character to guess: ', self._print_function) user_character = self._input_function().lower() if self.game_process(user_character): break diff --git a/JARVIS/requirements.txt b/JARVIS/requirements.txt deleted file mode 100644 index ca6bbccddbd..00000000000 --- a/JARVIS/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -datetime -pyjokes -requests -Pillow -Image -ImageGrab -gTTS -keyboard -key -playsound -pyttsx3 -SpeechRecognition -openai \ No newline at end of file diff --git a/JustDialScrapperGUI/Justdial Scrapper GUI.py b/JustDialScrapperGUI/Justdial Scrapper GUI.py index 2dd4803f0bb..4bc6d2128d6 100644 --- a/JustDialScrapperGUI/Justdial Scrapper GUI.py +++ b/JustDialScrapperGUI/Justdial Scrapper GUI.py @@ -139,9 +139,10 @@ def start_scrapping_logic(self): print("{0} {1}, {2}".format("Scrapping page number: ", page_number, url)) req = urllib.request.Request( url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64)"} - ) + ) + page = urllib.request.urlopen(req) - + soup = BeautifulSoup(page.read(), "html.parser") services = soup.find_all("li", {"class": "cntanr"}) diff --git a/Key_Binding/key_binding.py b/Key_Binding/key_binding.py index dfd448497b1..d3231739416 100644 --- a/Key_Binding/key_binding.py +++ b/Key_Binding/key_binding.py @@ -7,4 +7,4 @@ def _(event): print("Hello, World") -session.prompt("") +session.prompt("") \ No newline at end of file diff --git a/Key_Binding/requirement.txt b/Key_Binding/requirement.txt deleted file mode 100644 index 51d89fc61fc..00000000000 --- a/Key_Binding/requirement.txt +++ /dev/null @@ -1 +0,0 @@ -quo>=2022.4 diff --git a/PDF/requirements.txt b/PDF/requirements.txt deleted file mode 100644 index 919182ca16b..00000000000 --- a/PDF/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Pillow==11.3.0 -fpdf==1.7.2 \ No newline at end of file diff --git a/PRACTICEPROJECT-DISREGARD.txt b/PRACTICEPROJECT-DISREGARD.txt deleted file mode 100644 index f7855aa5340..00000000000 --- a/PRACTICEPROJECT-DISREGARD.txt +++ /dev/null @@ -1,5 +0,0 @@ -This is practice for my first time using GitHub - -Please disregard as I'm getting used to using CLI and GitHub - -Thanks! diff --git a/Password Generator/requirements.txt b/Password Generator/requirements.txt deleted file mode 100644 index 8fb084425cf..00000000000 --- a/Password Generator/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -colorama==0.4.6 -inquirer==3.4.0 \ No newline at end of file diff --git a/Password Generator/requirements_new.txt b/Password Generator/requirements_new.txt deleted file mode 100644 index 8b137891791..00000000000 --- a/Password Generator/requirements_new.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/PongPong_Game/requirements.txt b/PongPong_Game/requirements.txt deleted file mode 100644 index 71000361bd6..00000000000 --- a/PongPong_Game/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pyglet==2.1.6 diff --git a/Python Program for Product of unique prime factors of a number.py b/Python Program/Python Program for Product of unique prime factors of a number.py similarity index 100% rename from Python Program for Product of unique prime factors of a number.py rename to Python Program/Python Program for Product of unique prime factors of a number.py diff --git a/Python Program for Tower of Hanoi.py b/Python Program/Python Program for Tower of Hanoi.py similarity index 100% rename from Python Program for Tower of Hanoi.py rename to Python Program/Python Program for Tower of Hanoi.py diff --git a/Python Program for factorial of a number.py b/Python Program/Python Program for factorial of a number.py similarity index 89% rename from Python Program for factorial of a number.py rename to Python Program/Python Program for factorial of a number.py index fb75b99de87..7e47519be5f 100644 --- a/Python Program for factorial of a number.py +++ b/Python Program/Python Program for factorial of a number.py @@ -11,10 +11,10 @@ def factorial(n): # single line to find factorial - return 1 if (n==1 or n==0) else n * factorial(n - 1); + return 1 if (n==1 or n==0) else n * factorial(n - 1) # Driver Code -num = 5; +num = 5 print("Factorial of",num,"is", factorial((num))) """ @@ -34,5 +34,5 @@ def factorial(n): return fact # Driver Code -num = 5; +num = 5 print("Factorial of",num,"is", factorial(num)) diff --git a/Python Program to Count the Number of Each Vowel.py b/Python Program/Python Program to Count the Number of Each Vowel.py similarity index 100% rename from Python Program to Count the Number of Each Vowel.py rename to Python Program/Python Program to Count the Number of Each Vowel.py diff --git a/Python Program to Display Fibonacci Sequence Using Recursion.py b/Python Program/Python Program to Display Fibonacci Sequence Using Recursion.py similarity index 100% rename from Python Program to Display Fibonacci Sequence Using Recursion.py rename to Python Program/Python Program to Display Fibonacci Sequence Using Recursion.py diff --git a/Python Program to Find LCM.py b/Python Program/Python Program to Find LCM.py similarity index 100% rename from Python Program to Find LCM.py rename to Python Program/Python Program to Find LCM.py diff --git a/Python Program to Merge Mails.py b/Python Program/Python Program to Merge Mails.py similarity index 100% rename from Python Program to Merge Mails.py rename to Python Program/Python Program to Merge Mails.py diff --git a/Python Program to Print the Fibonacci sequence.py b/Python Program/Python Program to Print the Fibonacci sequence.py similarity index 100% rename from Python Program to Print the Fibonacci sequence.py rename to Python Program/Python Program to Print the Fibonacci sequence.py diff --git a/Python Program to Remove Punctuations from a String.py b/Python Program/Python Program to Remove Punctuations from a String.py similarity index 100% rename from Python Program to Remove Punctuations from a String.py rename to Python Program/Python Program to Remove Punctuations from a String.py diff --git a/Python Program to Reverse a linked list.py b/Python Program/Python Program to Reverse a linked list.py similarity index 100% rename from Python Program to Reverse a linked list.py rename to Python Program/Python Program to Reverse a linked list.py diff --git a/Python Program to Sort Words in Alphabetic Order.py b/Python Program/Python Program to Sort Words in Alphabetic Order.py similarity index 100% rename from Python Program to Sort Words in Alphabetic Order.py rename to Python Program/Python Program to Sort Words in Alphabetic Order.py diff --git a/Python Program to Transpose a Matrix.py b/Python Program/Python Program to Transpose a Matrix.py similarity index 100% rename from Python Program to Transpose a Matrix.py rename to Python Program/Python Program to Transpose a Matrix.py diff --git a/python program for finding square root for positive number.py b/Python Program/python program for finding square root for positive number.py similarity index 100% rename from python program for finding square root for positive number.py rename to Python Program/python program for finding square root for positive number.py diff --git a/QuestionAnswerVirtualAssistant/requirements.txt b/QuestionAnswerVirtualAssistant/requirements.txt deleted file mode 100644 index fb4d28890ad..00000000000 --- a/QuestionAnswerVirtualAssistant/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pandas -scikit-learn \ No newline at end of file diff --git a/Recursion Visulaizer/git b/Recursion Visulaizer/git deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/Street_Fighter/docs/requirements.txt b/Street_Fighter/docs/requirements.txt deleted file mode 100644 index 3c0b6f57287..00000000000 --- a/Street_Fighter/docs/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -pygame -numpy -opencv-python diff --git a/Task1.2.txt b/Task1.2.txt deleted file mode 100644 index e100a2ca4ab..00000000000 --- a/Task1.2.txt +++ /dev/null @@ -1 +0,0 @@ -Task 1.2 diff --git a/Trending youtube videos b/Trending youtube videos deleted file mode 100644 index a14535e4ddc..00000000000 --- a/Trending youtube videos +++ /dev/null @@ -1,43 +0,0 @@ -''' - Python program that uses the YouTube Data API to fetch the top 10 trending YouTube videos. -You’ll need to have an API key from Google Cloud Platform to use the YouTube Data API. - -First, install the google-api-python-client library if you haven’t already: -pip install google-api-python-client - -Replace 'YOUR_API_KEY' with your actual API key. This script will fetch and print the titles, -channels, and view counts of the top 10 trending YouTube videos in India. -You can change the regionCode to any other country code if needed. - -Then, you can use the following code: - -''' - -from googleapiclient.discovery import build - -# Replace with your own API key -API_KEY = 'YOUR_API_KEY' -YOUTUBE_API_SERVICE_NAME = 'youtube' -YOUTUBE_API_VERSION = 'v3' - -def get_trending_videos(): - youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=API_KEY) - - # Call the API to get the top 10 trending videos - request = youtube.videos().list( - part='snippet,statistics', - chart='mostPopular', - regionCode='IN', # Change this to your region code - maxResults=10 - ) - response = request.execute() - - # Print the video details - for item in response['items']: - title = item['snippet']['title'] - channel = item['snippet']['channelTitle'] - views = item['statistics']['viewCount'] - print(f'Title: {title}\nChannel: {channel}\nViews: {views}\n') - -if __name__ == '__main__': - get_trending_videos() diff --git a/Turn your PDFs into audio books/requirements.txt b/Turn your PDFs into audio books/requirements.txt deleted file mode 100644 index 5ba5e21515e..00000000000 --- a/Turn your PDFs into audio books/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -PyPDF2 -pyttsx3 diff --git a/async_downloader/requirements.txt b/async_downloader/requirements.txt deleted file mode 100644 index 8e9fbabcce6..00000000000 --- a/async_downloader/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -aiohttp==3.12.14 diff --git a/binod.txt b/binod.txt deleted file mode 100644 index 796c41c9f48..00000000000 --- a/binod.txt +++ /dev/null @@ -1 +0,0 @@ -I am binod diff --git a/check if a number positive , negative or zero b/check if a number positive , negative or zero.py similarity index 100% rename from check if a number positive , negative or zero rename to check if a number positive , negative or zero.py diff --git a/cicd b/cicd deleted file mode 100644 index 8b137891791..00000000000 --- a/cicd +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cli_master/database_import_countries.py b/cli_master/database_import_countries.py index 27255834e9e..8d6127cd674 100644 --- a/cli_master/database_import_countries.py +++ b/cli_master/database_import_countries.py @@ -1,9 +1,9 @@ -import requests +import httpx url = "https://api.countrystatecity.in/v1/countries" headers = {"X-CSCAPI-KEY": "API_KEY"} -response = requests.request("GET", url, headers=headers) +response = httpx.get(url, headers=headers) print(response.text) diff --git a/communication/file.py b/communication/file.py index 4198d95ec0e..8cf73d3a4d9 100755 --- a/communication/file.py +++ b/communication/file.py @@ -1,41 +1,86 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # coding: utf-8 import math -import os import sys +from multiprocessing import Process, Pipe +from typing import List - -def slice(mink, maxk): - s = 0.0 - for k in range(int(mink), int(maxk)): +def slice(mink: int, maxk: int) -> float: + """ + Calculate the partial sum of the series 1/(2k+1)² from mink to maxk-1. + + Args: + mink: Start index (inclusive) + maxk: End index (exclusive) + + Returns: + The computed partial sum + """ + s: float = 0.0 + for k in range(mink, maxk): s += 1.0 / (2 * k + 1) / (2 * k + 1) return s +def worker(mink: int, maxk: int, conn) -> None: + """Worker function to compute slice and send result via pipe""" + try: + result = slice(mink, maxk) + conn.send(result) + conn.close() + except Exception as e: + conn.send(f"Error: {str(e)}") + conn.close() -def pi(n): - pids = [] - unit = n / 10 - for i in range(10): # 分10个子进程 +def pi(n: int) -> float: + """ + Compute an approximation of π using multi-processing. + + Args: + n: Number of terms in the series to compute (divided by 10 processes) + + Returns: + Approximation of π using the computed sum + """ + processes: List[Process] = [] + parent_conns: List = [] + unit: int = n // 10 + + for i in range(10): mink = unit * i maxk = mink + unit - pid = os.fork() - if pid > 0: - pids.append(pid) + parent_conn, child_conn = Pipe() + p = Process(target=worker, args=(mink, maxk, child_conn)) + processes.append(p) + parent_conns.append(parent_conn) + p.start() + + # Collect results + sums: List[float] = [] + for conn in parent_conns: + result = conn.recv() + if isinstance(result, str) and result.startswith("Error:"): + print(result) + sums.append(0.0) else: - s = slice(mink, maxk) # 子进程开始计算 - with open("%d" % os.getpid(), "w") as f: - f.write(str(s)) - sys.exit(0) # 子进程结束 - sums = [] - for pid in pids: - os.waitpid(pid, 0) # 等待子进程结束 - with open("%d" % pid, "r") as f: - sums.append(float(f.read())) - os.remove("%d" % pid) # 删除通信的文件 + sums.append(float(result)) + conn.close() + + # Wait for all processes to finish + for p in processes: + p.join() + return math.sqrt(sum(sums) * 8) - if __name__ == "__main__": - print("start") - print(pi(10000000)) + try: + n: int = int(sys.argv[1]) if len(sys.argv) > 1 else 10000000 + print(f"Calculating pi with {n} iterations using 10 processes...") + print(pi(n)) + except (ValueError, IndexError): + print(f"Usage: {sys.argv[0]} [iterations]") + print(" iterations: Number of terms (default: 10000000)") + sys.exit(1) + except KeyboardInterrupt: + print("\nInterrupted by user") + sys.exit(1) \ No newline at end of file diff --git a/communication/pipe.py b/communication/pipe.py index 4138c519570..8cf73d3a4d9 100644 --- a/communication/pipe.py +++ b/communication/pipe.py @@ -1,43 +1,86 @@ +#!/usr/bin/env python3 # coding: utf-8 -from __future__ import print_function - import math -import os import sys +from multiprocessing import Process, Pipe +from typing import List - -def slice(mink, maxk): - s = 0.0 +def slice(mink: int, maxk: int) -> float: + """ + Calculate the partial sum of the series 1/(2k+1)² from mink to maxk-1. + + Args: + mink: Start index (inclusive) + maxk: End index (exclusive) + + Returns: + The computed partial sum + """ + s: float = 0.0 for k in range(mink, maxk): s += 1.0 / (2 * k + 1) / (2 * k + 1) return s +def worker(mink: int, maxk: int, conn) -> None: + """Worker function to compute slice and send result via pipe""" + try: + result = slice(mink, maxk) + conn.send(result) + conn.close() + except Exception as e: + conn.send(f"Error: {str(e)}") + conn.close() -def pi(n): - childs = {} - unit = n / 10 - for i in range(10): # 分10个子进程 +def pi(n: int) -> float: + """ + Compute an approximation of π using multi-processing. + + Args: + n: Number of terms in the series to compute (divided by 10 processes) + + Returns: + Approximation of π using the computed sum + """ + processes: List[Process] = [] + parent_conns: List = [] + unit: int = n // 10 + + for i in range(10): mink = unit * i maxk = mink + unit - r, w = os.pipe() - pid = os.fork() - if pid > 0: - childs[pid] = r # 将子进程的pid和读描述符存起来 - os.close(w) # 父进程关闭写描述符,只读 + parent_conn, child_conn = Pipe() + p = Process(target=worker, args=(mink, maxk, child_conn)) + processes.append(p) + parent_conns.append(parent_conn) + p.start() + + # Collect results + sums: List[float] = [] + for conn in parent_conns: + result = conn.recv() + if isinstance(result, str) and result.startswith("Error:"): + print(result) + sums.append(0.0) else: - os.close(r) # 子进程关闭读描述符,只写 - s = slice(mink, maxk) # 子进程开始计算 - os.write(w, str(s)) - os.close(w) # 写完了,关闭写描述符 - sys.exit(0) # 子进程结束 - sums = [] - - for pid, r in childs.items(): - sums.append(float(os.read(r, 1024))) - os.close(r) # 读完了,关闭读描述符 - os.waitpid(pid, 0) # 等待子进程结束 + sums.append(float(result)) + conn.close() + + # Wait for all processes to finish + for p in processes: + p.join() + return math.sqrt(sum(sums) * 8) - -print(pi(10000000)) +if __name__ == "__main__": + try: + n: int = int(sys.argv[1]) if len(sys.argv) > 1 else 10000000 + print(f"Calculating pi with {n} iterations using 10 processes...") + print(pi(n)) + except (ValueError, IndexError): + print(f"Usage: {sys.argv[0]} [iterations]") + print(" iterations: Number of terms (default: 10000000)") + sys.exit(1) + except KeyboardInterrupt: + print("\nInterrupted by user") + sys.exit(1) \ No newline at end of file diff --git a/communication/socket_conn.py b/communication/socket_conn.py index ffe1ed437fa..565acb7c109 100644 --- a/communication/socket_conn.py +++ b/communication/socket_conn.py @@ -1,43 +1,86 @@ +#!/usr/bin/env python3 # coding: utf-8 -from __future__ import print_function - import math -import os -import socket import sys +from multiprocessing import Process, Pipe +from typing import List - -def slice(mink, maxk): - s = 0.0 +def slice(mink: int, maxk: int) -> float: + """ + Calculate the partial sum of the series 1/(2k+1)² from mink to maxk-1. + + Args: + mink: Start index (inclusive) + maxk: End index (exclusive) + + Returns: + The computed partial sum + """ + s: float = 0.0 for k in range(mink, maxk): s += 1.0 / (2 * k + 1) / (2 * k + 1) return s +def worker(mink: int, maxk: int, conn) -> None: + """Worker function to compute slice and send result via pipe""" + try: + result = slice(mink, maxk) + conn.send(result) # Send float directly + conn.close() + except Exception as e: + conn.send(f"Error: {str(e)}") + conn.close() -def pi(n): - childs = {} - unit = n / 10 - for i in range(10): # 分10个子进程 +def pi(n: int) -> float: + """ + Compute an approximation of π using multi-processing. + + Args: + n: Number of terms in the series to compute (divided by 10 processes) + + Returns: + Approximation of π using the computed sum + """ + processes: List[Process] = [] + parent_conns: List = [] + unit: int = n // 10 # Ensure integer division + + for i in range(10): mink = unit * i maxk = mink + unit - rsock, wsock = socket.socketpair() - pid = os.fork() - if pid > 0: - childs[pid] = rsock - wsock.close() + parent_conn, child_conn = Pipe() + p = Process(target=worker, args=(mink, maxk, child_conn)) + processes.append(p) + parent_conns.append(parent_conn) + p.start() + + # Collect results + sums: List[float] = [] + for conn in parent_conns: + result = conn.recv() + if isinstance(result, str) and result.startswith("Error:"): + print(result) + sums.append(0.0) # Add zero if error occurred else: - rsock.close() - s = slice(mink, maxk) # 子进程开始计算 - wsock.send(str(s)) - wsock.close() - sys.exit(0) # 子进程结束 - sums = [] - for pid, rsock in childs.items(): - sums.append(float(rsock.recv(1024))) - rsock.close() - os.waitpid(pid, 0) # 等待子进程结束 + sums.append(float(result)) # Ensure float conversion + conn.close() + + # Wait for all processes to finish + for p in processes: + p.join() + return math.sqrt(sum(sums) * 8) - -print(pi(10000000)) +if __name__ == "__main__": + try: + n: int = int(sys.argv[1]) if len(sys.argv) > 1 else 10000000 + print(f"Calculating pi with {n} iterations using 10 processes...") + print(pi(n)) + except (ValueError, IndexError): + print(f"Usage: {sys.argv[0]} [iterations]") + print(" iterations: Number of terms (default: 10000000)") + sys.exit(1) + except KeyboardInterrupt: + print("\nInterrupted by user") + sys.exit(1) \ No newline at end of file diff --git a/contribution.txt b/contribution.txt deleted file mode 100644 index 181a276c94d..00000000000 --- a/contribution.txt +++ /dev/null @@ -1 +0,0 @@ -Add a dark mode toggle for better UX diff --git a/currency converter/country.txt b/currency converter/country.txt deleted file mode 100644 index 0398e381859..00000000000 --- a/currency converter/country.txt +++ /dev/null @@ -1,177 +0,0 @@ -Australia Dollar-AUD -Great Britain Pound-GBP -Euro-EUR -Japan Yen-JPY -Switzerland Franc-CHF -USA Dollar-USD -Afghanistan Afghani-AFN -Albania Lek-ALL -Algeria Dinar-DZD -Angola Kwanza-AOA -Argentina Peso-ARS -Armenia Dram-AMD -Aruba Florin-AWG -Australia Dollar-AUD -Austria Schilling-ATS (EURO) -Belgium Franc-BEF (EURO) -Azerbaijan New Manat-AZN -Bahamas Dollar-BSD -Bahrain Dinar-BHD -Bangladesh Taka-BDT -Barbados Dollar-BBD -Belarus Ruble-BYR -Belize Dollar-BZD -Bermuda Dollar-BMD -Bhutan Ngultrum-BTN -Bolivia Boliviano-BOB -Bosnia Mark-BAM -Botswana Pula-BWP -Brazil Real-BRL -Great Britain Pound-GBP -Brunei Dollar-BND -Bulgaria Lev-BGN -Burundi Franc-BIF -CFA Franc BCEAO-XOF -CFA Franc BEAC-XAF -CFP Franc-XPF -Cambodia Riel-KHR -Canada Dollar-CAD -Cape Verde Escudo-CVE -Cayman Islands Dollar-KYD -Chili Peso-CLP -China Yuan/Renminbi-CNY -Colombia Peso-COP -Comoros Franc-KMF -Congo Franc-CDF -Costa Rica Colon-CRC -Croatia Kuna-HRK -Cuba Convertible Peso-CUC -Cuba Peso-CUP -Cyprus Pound-CYP (EURO) -Czech Koruna-CZK -Denmark Krone-DKK -Djibouti Franc-DJF -Dominican Republich Peso-DOP -East Caribbean Dollar-XCD -Egypt Pound-EGP -El Salvador Colon-SVC -Estonia Kroon-EEK (EURO) -Ethiopia Birr-ETB -Euro-EUR -Falkland Islands Pound-FKP -Finland Markka-FIM (EURO) -Fiji Dollar-FJD -Gambia Dalasi-GMD -Georgia Lari-GEL -Germany Mark-DMK (EURO) -Ghana New Cedi-GHS -Gibraltar Pound-GIP -Greece Drachma-GRD (EURO) -Guatemala Quetzal-GTQ -Guinea Franc-GNF -Guyana Dollar-GYD -Haiti Gourde-HTG -Honduras Lempira-HNL -Hong Kong Dollar-HKD -Hungary Forint-HUF -Iceland Krona-ISK -India Rupee-INR -Indonesia Rupiah-IDR -Iran Rial-IRR -Iraq Dinar-IQD -Ireland Pound-IED (EURO) -Israel New Shekel-ILS -Italy Lira-ITL (EURO) -Jamaica Dollar-JMD -Japan Yen-JPY -Jordan Dinar-JOD -Kazakhstan Tenge-KZT -Kenya Shilling-KES -Kuwait Dinar-KWD -Kyrgyzstan Som-KGS -Laos Kip-LAK -Latvia Lats-LVL (EURO) -Lebanon Pound-LBP -Lesotho Loti-LSL -Liberia Dollar-LRD -Libya Dinar-LYD -Lithuania Litas-LTL (EURO) -Luxembourg Franc-LUF (EURO) -Macau Pataca-MOP -Macedonia Denar-MKD -Malagasy Ariary-MGA -Malawi Kwacha-MWK -Malaysia Ringgit-MYR -Maldives Rufiyaa-MVR -Malta Lira-MTL (EURO) -Mauritania Ouguiya-MRO -Mauritius Rupee-MUR -Mexico Peso-MXN -Moldova Leu-MDL -Mongolia Tugrik-MNT -Morocco Dirham-MAD -Mozambique New Metical-MZN -Myanmar Kyat-MMK -NL Antilles Guilder-ANG -Namibia Dollar-NAD -Nepal Rupee-NPR -Netherlands Guilder-NLG (EURO) -New Zealand Dollar-NZD -Nicaragua Cordoba Oro-NIO -Nigeria Naira-NGN -North Korea Won-KPW -Norway Kroner-NOK -Oman Rial-OMR -Pakistan Rupee-PKR -Panama Balboa-PAB -Papua New Guinea Kina-PGK -Paraguay Guarani-PYG -Peru Nuevo Sol-PEN -Philippines Peso-PHP -Poland Zloty-PLN -Portugal Escudo-PTE (EURO) -Qatar Rial-QAR -Romania New Lei-RON -Russia Rouble-RUB -Rwanda Franc-RWF -Samoa Tala-WST -Sao Tome/Principe Dobra-STD -Saudi Arabia Riyal-SAR -Serbia Dinar-RSD -Seychelles Rupee-SCR -Sierra Leone Leone-SLL -Singapore Dollar-SGD -Slovakia Koruna-SKK (EURO) -Slovenia Tolar-SIT (EURO) -Solomon Islands Dollar-SBD -Somali Shilling-SOS -South Africa Rand-ZAR -South Korea Won-KRW -Spain Peseta-ESP (EURO) -Sri Lanka Rupee-LKR -St Helena Pound-SHP -Sudan Pound-SDG -Suriname Dollar-SRD -Swaziland Lilangeni-SZL -Sweden Krona-SEK -Switzerland Franc-CHF -Syria Pound-SYP -Taiwan Dollar-TWD -Tanzania Shilling-TZS -Thailand Baht-THB -Tonga Pa'anga-TOP -Trinidad/Tobago Dollar-TTD -Tunisia Dinar-TND -Turkish New Lira-TRY -Turkmenistan Manat-TMM -USA Dollar-USD -Uganda Shilling-UGX -Ukraine Hryvnia-UAH -Uruguay Peso-UYU -United Arab Emirates Dirham-AED -Vanuatu Vatu-VUV -Venezuela Bolivar-VEB -Vietnam Dong-VND -Yemen Rial-YER -Zambia Kwacha-ZMK -Zimbabwe Dollar-ZWD \ No newline at end of file diff --git a/currency converter/gui.ui b/currency converter/gui.ui index a2b39c9e6a4..178bd0edcbc 100644 --- a/currency converter/gui.ui +++ b/currency converter/gui.ui @@ -114,7 +114,7 @@ Arial - -1 + 28 75 true @@ -181,7 +181,7 @@ Arial - -1 + 20 50 true false @@ -203,7 +203,7 @@ Arial - -1 + 20 50 true false @@ -227,4 +227,4 @@ - + \ No newline at end of file diff --git a/currency converter/main.py b/currency converter/main.py index b656e7bdf3b..2b4fe127ad7 100644 --- a/currency converter/main.py +++ b/currency converter/main.py @@ -1,57 +1,309 @@ -# cc program -from PyQt5.QtGui import * -from PyQt5.QtCore import * -from PyQt5.QtWidgets import * -from PyQt5 import QtWidgets, uic -from PyQt5.QtCore import * +"""Currency Converter Application + +A PyQt5-based currency converter with embedded currency data (no external files required). +Fetches real-time exchange rates and provides a user-friendly interface. +""" + +import sys +from pathlib import Path +from typing import Optional, Dict, List +from PyQt5.QtGui import QDoubleValidator +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import (QApplication, QMainWindow, QLineEdit, QPushButton, + QComboBox, QLCDNumber, QWidget) +from PyQt5 import uic import httpx -from bs4 import BeautifulSoup -def getVal(cont1, cont2): - cont1val = cont1.split("-")[1] - cont2val = cont2.split("-")[1] - url = f"https://free.currconv.com/api/v7/convert?q={cont1val}_{cont2val}&compact=ultra&apiKey=b43a653672c4a94c4c26" - r = httpx.get(url) - htmlContent = r.content - soup = BeautifulSoup(htmlContent, "html.parser") - try: - valCurr = float(soup.get_text().split(":")[1].removesuffix("}")) # {USD:70.00} - except Exception: - print("Server down.") - exit() - return valCurr +# Embedded currency data (originally from country.txt) +CURRENCIES = [ + "Australia Dollar-AUD", + "Great Britain Pound-GBP", + "Euro-EUR", + "Japan Yen-JPY", + "Switzerland Franc-CHF", + "USA Dollar-USD", + "Afghanistan Afghani-AFN", + "Albania Lek-ALL", + "Algeria Dinar-DZD", + "Angola Kwanza-AOA", + "Argentina Peso-ARS", + "Armenia Dram-AMD", + "Aruba Florin-AWG", + "Austria Schilling-ATS (EURO)", + "Belgium Franc-BEF (EURO)", + "Azerbaijan New Manat-AZN", + "Bahamas Dollar-BSD", + "Bahrain Dinar-BHD", + "Bangladesh Taka-BDT", + "Barbados Dollar-BBD", + "Belarus Ruble-BYR", + "Belize Dollar-BZD", + "Bermuda Dollar-BMD", + "Bhutan Ngultrum-BTN", + "Bolivia Boliviano-BOB", + "Bosnia Mark-BAM", + "Botswana Pula-BWP", + "Brazil Real-BRL", + "Brunei Dollar-BND", + "Bulgaria Lev-BGN", + "Burundi Franc-BIF", + "CFA Franc BCEAO-XOF", + "CFA Franc BEAC-XAF", + "CFP Franc-XPF", + "Cambodia Riel-KHR", + "Canada Dollar-CAD", + "Cape Verde Escudo-CVE", + "Cayman Islands Dollar-KYD", + "Chili Peso-CLP", + "China Yuan/Renminbi-CNY", + "Colombia Peso-COP", + "Comoros Franc-KMF", + "Congo Franc-CDF", + "Costa Rica Colon-CRC", + "Croatia Kuna-HRK", + "Cuba Convertible Peso-CUC", + "Cuba Peso-CUP", + "Cyprus Pound-CYP (EURO)", + "Czech Koruna-CZK", + "Denmark Krone-DKK", + "Djibouti Franc-DJF", + "Dominican Republich Peso-DOP", + "East Caribbean Dollar-XCD", + "Egypt Pound-EGP", + "El Salvador Colon-SVC", + "Estonia Kroon-EEK (EURO)", + "Ethiopia Birr-ETB", + "Falkland Islands Pound-FKP", + "Finland Markka-FIM (EURO)", + "Fiji Dollar-FJD", + "Gambia Dalasi-GMD", + "Georgia Lari-GEL", + "Germany Mark-DMK (EURO)", + "Ghana New Cedi-GHS", + "Gibraltar Pound-GIP", + "Greece Drachma-GRD (EURO)", + "Guatemala Quetzal-GTQ", + "Guinea Franc-GNF", + "Guyana Dollar-GYD", + "Haiti Gourde-HTG", + "Honduras Lempira-HNL", + "Hong Kong Dollar-HKD", + "Hungary Forint-HUF", + "Iceland Krona-ISK", + "India Rupee-INR", + "Indonesia Rupiah-IDR", + "Iran Rial-IRR", + "Iraq Dinar-IQD", + "Ireland Pound-IED (EURO)", + "Israel New Shekel-ILS", + "Italy Lira-ITL (EURO)", + "Jamaica Dollar-JMD", + "Jordan Dinar-JOD", + "Kazakhstan Tenge-KZT", + "Kenya Shilling-KES", + "Kuwait Dinar-KWD", + "Kyrgyzstan Som-KGS", + "Laos Kip-LAK", + "Latvia Lats-LVL (EURO)", + "Lebanon Pound-LBP", + "Lesotho Loti-LSL", + "Liberia Dollar-LRD", + "Libya Dinar-LYD", + "Lithuania Litas-LTL (EURO)", + "Luxembourg Franc-LUF (EURO)", + "Macau Pataca-MOP", + "Macedonia Denar-MKD", + "Malagasy Ariary-MGA", + "Malawi Kwacha-MWK", + "Malaysia Ringgit-MYR", + "Maldives Rufiyaa-MVR", + "Malta Lira-MTL (EURO)", + "Mauritania Ouguiya-MRO", + "Mauritius Rupee-MUR", + "Mexico Peso-MXN", + "Moldova Leu-MDL", + "Mongolia Tugrik-MNT", + "Morocco Dirham-MAD", + "Mozambique New Metical-MZN", + "Myanmar Kyat-MMK", + "NL Antilles Guilder-ANG", + "Namibia Dollar-NAD", + "Nepal Rupee-NPR", + "Netherlands Guilder-NLG (EURO)", + "New Zealand Dollar-NZD", + "Nicaragua Cordoba Oro-NIO", + "Nigeria Naira-NGN", + "North Korea Won-KPW", + "Norway Kroner-NOK", + "Oman Rial-OMR", + "Pakistan Rupee-PKR", + "Panama Balboa-PAB", + "Papua New Guinea Kina-PGK", + "Paraguay Guarani-PYG", + "Peru Nuevo Sol-PEN", + "Philippines Peso-PHP", + "Poland Zloty-PLN", + "Portugal Escudo-PTE (EURO)", + "Qatar Rial-QAR", + "Romania New Lei-RON", + "Russia Rouble-RUB", + "Rwanda Franc-RWF", + "Samoa Tala-WST", + "Sao Tome/Principe Dobra-STD", + "Saudi Arabia Riyal-SAR", + "Serbia Dinar-RSD", + "Seychelles Rupee-SCR", + "Sierra Leone Leone-SLL", + "Singapore Dollar-SGD", + "Slovakia Koruna-SKK (EURO)", + "Slovenia Tolar-SIT (EURO)", + "Solomon Islands Dollar-SBD", + "Somali Shilling-SOS", + "South Africa Rand-ZAR", + "South Korea Won-KRW", + "Spain Peseta-ESP (EURO)", + "Sri Lanka Rupee-LKR", + "St Helena Pound-SHP", + "Sudan Pound-SDG", + "Suriname Dollar-SRD", + "Swaziland Lilangeni-SZL", + "Sweden Krona-SEK", + "Syria Pound-SYP", + "Taiwan Dollar-TWD", + "Tanzania Shilling-TZS", + "Thailand Baht-THB", + "Tonga Pa'anga-TOP", + "Trinidad/Tobago Dollar-TTD", + "Tunisia Dinar-TND", + "Turkish New Lira-TRY", + "Turkmenistan Manat-TMM", + "Uganda Shilling-UGX", + "Ukraine Hryvnia-UAH", + "Uruguay Peso-UYU", + "United Arab Emirates Dirham-AED", + "Vanuatu Vatu-VUV", + "Venezuela Bolivar-VEB", + "Vietnam Dong-VND", + "Yemen Rial-YER", + "Zambia Kwacha-ZMK", + "Zimbabwe Dollar-ZWD" +] + + +class CurrencyConverter(QMainWindow): + """Main application window for currency conversion""" + + def __init__(self) -> None: + super().__init__() + self.base_dir: Path = Path(__file__).parent # Directory of current script + self.ui: QWidget = self.load_ui() + self.setup_ui_elements() + self.load_currency_data() # Load from embedded list + + # Type hints for UI elements (assigned via UI file) + self.lineEdit: QLineEdit + self.pushButton: QPushButton + self.dropDown1: QComboBox + self.dropDown2: QComboBox + self.lcdpanel: QLCDNumber + + def load_ui(self) -> QWidget: + """Load UI file with proper path handling""" + ui_path: Path = self.base_dir / "gui.ui" + + try: + return uic.loadUi(str(ui_path), self) + except FileNotFoundError: + print(f"Error: UI file not found at {ui_path}") + print(f"Please ensure 'gui.ui' exists in: {self.base_dir}") + sys.exit(1) -app = QtWidgets.QApplication([]) -window = uic.loadUi("gui.ui") -f = open("country.txt", "r") + def setup_ui_elements(self) -> None: + """Initialize UI components and connections""" + self.lineEdit.setValidator(QDoubleValidator()) + self.pushButton.clicked.connect(self.convert_currency) + self.setWindowTitle("Dynamic Currency Converter") -window = uic.loadUi("C:/Users/prath/Desktop/Currency-Calculator-Dynamic/gui.ui") -f = open("C:/Users/prath/Desktop/Currency-Calculator-Dynamic/country.txt", "r") -window.dropDown1.addItem("Select") -window.dropDown2.addItem("Select") -for i in f.readlines(): - window.dropDown1.addItem(i) - window.dropDown2.addItem(i) -intOnly = QDoubleValidator() -window.lineEdit.setValidator(intOnly) + def load_currency_data(self) -> None: + """Load currency list from embedded data (no external file)""" + # Remove duplicates while preserving order + seen: set = set() + unique_currencies: List[str] = [] + for currency in CURRENCIES: + if currency not in seen: + seen.add(currency) + unique_currencies.append(currency) + + # Populate dropdowns + self.dropDown1.addItem("Select Currency") + self.dropDown2.addItem("Select Currency") + for currency in unique_currencies: + self.dropDown1.addItem(currency) + self.dropDown2.addItem(currency) -def main(): - window.pushButton.clicked.connect(changeBtn) + def get_exchange_rate(self, from_currency: str, to_currency: str) -> Optional[float]: + """Fetch exchange rate from API""" + try: + # Extract currency codes (format: "Name-CODE") + from_code: str = from_currency.split("-")[-1].strip() + to_code: str = to_currency.split("-")[-1].strip() + + # Clean up codes with extra info (e.g., "(EURO)") + from_code = from_code.split()[0] + to_code = to_code.split()[0] + + # API request + api_key: str = "b43a653672c4a94c4c26" # Replace with your API key if needed + url: str = f"https://free.currconv.com/api/v7/convert?q={from_code}_{to_code}&compact=ultra&apiKey={api_key}" + + response: httpx.Response = httpx.get(url, timeout=10) + response.raise_for_status() + data: Dict[str, float] = response.json() + + rate_key: str = f"{from_code}_{to_code}" + return float(data[rate_key]) if rate_key in data else None + + except Exception as e: + print(f"Error fetching exchange rate: {str(e)}") + return None -def changeBtn(): - val = window.lineEdit.text() - cont1 = window.dropDown1.currentText() - cont2 = window.dropDown2.currentText() - valCurr = getVal(cont1.rstrip(), cont2.rstrip()) - window.lcdpanel.display(float(val) * valCurr) + def convert_currency(self) -> None: + """Handle currency conversion when button is clicked""" + amount_text: str = self.lineEdit.text() + from_currency: str = self.dropDown1.currentText() + to_currency: str = self.dropDown2.currentText() + + # Validate inputs + if not amount_text or from_currency == "Select Currency" or to_currency == "Select Currency": + self.lcdpanel.display(0) + return + + try: + amount: float = float(amount_text) + rate: Optional[float] = self.get_exchange_rate(from_currency, to_currency) + + if rate is not None: + result: float = amount * rate + self.lcdpanel.display(round(result, 2)) + else: + self.lcdpanel.display(0) + + except ValueError: + self.lcdpanel.display(0) -main() -window.show() -app.exec() +if __name__ == "__main__": + # High-DPI support + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) + QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) + + app: QApplication = QApplication(sys.argv) + window: CurrencyConverter = CurrencyConverter() + window.show() + sys.exit(app.exec_()) \ No newline at end of file diff --git a/depreciated_programs/corona_cases.py b/depreciated_programs/corona_cases.py index e93e7cd99f9..934330888b1 100644 --- a/depreciated_programs/corona_cases.py +++ b/depreciated_programs/corona_cases.py @@ -1,50 +1,73 @@ import sys +from typing import Dict, List, Any, Optional +import httpx +from time import sleep + +# Type aliases +CovidData = Dict[str, Any] +CountryData = List[Dict[str, Any]] + +# API request and data processing +url: str = "https://api.covid19api.com/summary" try: - import requests + response: httpx.Response = httpx.get(url) + response.raise_for_status() # Check if request was successful + visit: CovidData = response.json() except ImportError: - print("Please Install Requests Module With Command 'pip install requests'") + print("Please install the HTTPX module using 'pip install httpx'") + sys.exit(1) +except (httpx.RequestError, ValueError) as e: + print(f"Failed to fetch data: {e}") sys.exit(1) -from time import sleep - -url = "https://api.covid19api.com/summary" -visit = requests.get(url).json() - -NewConfirmed = visit["Global"]["NewConfirmed"] -TotalConfirmed = visit["Global"]["TotalConfirmed"] -NewDeaths = visit["Global"]["NewDeaths"] -TotalDeaths = visit["Global"]["TotalDeaths"] -NewRecovered = visit["Global"]["NewRecovered"] -TotalRecovered = visit["Global"]["TotalRecovered"] -india = visit["Countries"] -name = india[76]["Country"] -indiaconfirmed = india[76]["NewConfirmed"] -indiatotal = india[76]["TotalConfirmed"] -indiaDeaths = india[76]["NewDeaths"] -deathstotal = india[76]["TotalDeaths"] -indianewr = india[76]["NewRecovered"] -totalre = india[76]["TotalRecovered"] -DateUpdate = india[76]["Date"] +# Extract global data +global_data: Dict[str, int] = visit["Global"] +NewConfirmed: int = global_data["NewConfirmed"] +TotalConfirmed: int = global_data["TotalConfirmed"] +NewDeaths: int = global_data["NewDeaths"] +TotalDeaths: int = global_data["TotalDeaths"] +NewRecovered: int = global_data["NewRecovered"] +TotalRecovered: int = global_data["TotalRecovered"] + +# Extract India data (using country name instead of index for reliability) +countries: CountryData = visit["Countries"] +india_data: Optional[Dict[str, Any]] = next( + (country for country in countries if country["Country"] == "India"), + None +) +if india_data is None: + print("Error: India's data not found in the API response.") + sys.exit(1) -def world(): - world = f""" +name: str = india_data["Country"] +indiaconfirmed: int = india_data["NewConfirmed"] +indiatotal: int = india_data["TotalConfirmed"] +indiaDeaths: int = india_data["NewDeaths"] +deathstotal: int = india_data["TotalDeaths"] +indianewr: int = india_data["NewRecovered"] +totalre: int = india_data["TotalRecovered"] +DateUpdate: str = india_data["Date"] + +def world() -> None: + """Display global COVID-19 statistics""" + world_stats = f""" ▀▀█▀▀ █▀▀█ ▀▀█▀▀ █▀▀█ █░░   ▒█▀▀█ █▀▀█ █▀▀ █▀▀ █▀▀   ▀█▀ █▀▀▄   ▒█░░▒█ █▀▀█ █▀▀█ █░░ █▀▀▄ ░▒█░░ █░░█ ░░█░░ █▄▄█ █░░   ▒█░░░ █▄▄█ ▀▀█ █▀▀ ▀▀█   ▒█░ █░░█   ▒█▒█▒█ █░░█ █▄▄▀ █░░ █░░█ ░▒█░░ ▀▀▀▀ ░░▀░░ ▀░░▀ ▀▀▀   ▒█▄▄█ ▀░░▀ ▀▀▀ ▀▀▀ ▀▀▀   ▄█▄ ▀░░▀   ▒█▄▀▄█ ▀▀▀▀ ▀░▀▀ ▀▀▀ ▀▀▀░\n -New Confirmed Cases :- {NewConfirmed} -Total Confirmed Cases :- {TotalConfirmed} -New Deaths :- {NewDeaths} -Total Deaths :- {TotalDeaths} -New Recovered :- {NewRecovered} -Total Recovered :- {TotalRecovered} +New Confirmed Cases: {NewConfirmed} +Total Confirmed Cases: {TotalConfirmed} +New Deaths: {NewDeaths} +Total Deaths: {TotalDeaths} +New Recovered: {NewRecovered} +Total Recovered: {TotalRecovered} """ - print(world) - + print(world_stats) -def india(): - cases = f""" +def india() -> None: + """Display COVID-19 statistics for India""" + india_stats = f""" ██╗███╗░░██╗██████╗░██╗░█████╗░ ██║████╗░██║██╔══██╗██║██╔══██╗ ██║██╔██╗██║██║░░██║██║███████║ @@ -52,34 +75,32 @@ def india(): ██║██║░╚███║██████╔╝██║██║░░██║ ╚═╝╚═╝░░╚══╝╚═════╝░╚═╝╚═╝░░╚═╝ -Country Name :- {name} -New Confirmed Cases :- {indiaonfirmed} -Total Confirmed Cases :- {indiatotal} -New Deaths :- {indiaDeaths} -Total Deaths :- {deathstotal} -New Recovered :- {indianewr} -Total Recovered :- {totalre} -Information Till :- {DateUpdate} +Country: {name} +New Confirmed Cases: {indiaconfirmed} +Total Confirmed Cases: {indiatotal} +New Deaths: {indiaDeaths} +Total Deaths: {deathstotal} +New Recovered: {indianewr} +Total Recovered: {totalre} +Updated As Of: {DateUpdate} """ - print(cases) + print(india_stats) - -print( - """ +# ASCII art title +print(""" ░█████╗░░█████╗░██████╗░░█████╗░███╗░░██╗░█████╗░  ██╗░░░██╗██╗██████╗░██╗░░░██╗░██████╗ ██╔══██╗██╔══██╗██╔══██╗██╔══██╗████╗░██║██╔══██╗  ██║░░░██║██║██╔══██╗██║░░░██║██╔════╝ ██║░░╚═╝██║░░██║██████╔╝██║░░██║██╔██╗██║███████║  ╚██╗░██╔╝██║██████╔╝██║░░░██║╚█████╗░ ██║░░██╗██║░░██║██╔══██╗██║░░██║██║╚████║██╔══██║  ░╚████╔╝░██║██╔══██╗██║░░░██║░╚═══██╗ ╚█████╔╝╚█████╔╝██║░░██║╚█████╔╝██║░╚███║██║░░██║  ░░╚██╔╝░░██║██║░░██║╚██████╔╝██████╔╝ -░╚════╝░░╚════╝░╚═╝░░╚═╝░╚════╝░╚═╝░░╚══╝╚═╝░░╚═╝  ░░░╚═╝░░░╚═╝╚═╝░░╚═╝░╚═════╝░╚═════╝░""" -) +░╚════╝░░╚════╝░╚═╝░░╚═╝░╚════╝░╚═╝░░╚══╝╚═╝░░╚═╝  ░░░╚═╝░░░╚═╝╚═╝░░╚═╝░╚═════╝░╚═════╝░""") print("\nDeveloped By @TheDarkW3b") - -def choices(): - print("\n1 - To Know Corona Virus Update Across World") - print("\n2 - To Know Corona Virus Update In India") - choice = input("Enter 1 Or 2 :- ") +def choices() -> None: + """Main menu for user choices""" + print("\n1 - View Global COVID-19 Updates") + print("\n2 - View COVID-19 Updates in India") + choice = input("Enter 1 or 2: ") if choice == "1": world() @@ -90,8 +111,8 @@ def choices(): sleep(1) choices() else: - print("\nYou Have Entered Something Wrong, Please Enter Again") + print("\nInvalid input. Please try again.") choices() - -choices() +# Start the interactive menu +choices() \ No newline at end of file diff --git a/dialogs/messagebox.py b/dialogs/messagebox.py deleted file mode 100644 index 14a88dc185d..00000000000 --- a/dialogs/messagebox.py +++ /dev/null @@ -1,7 +0,0 @@ -# Use the MessageBox() function to display a simple message box. - -from quo.dialog import MessageBox - -MessageBox( - title='Example dialog window', - text='Do you want to continue?') diff --git a/dialogs/requirements.txt b/dialogs/requirements.txt deleted file mode 100644 index 51d89fc61fc..00000000000 --- a/dialogs/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -quo>=2022.4 diff --git a/dialogs/show_dialogs.py b/dialogs/show_dialogs.py new file mode 100644 index 00000000000..0d44ac14770 --- /dev/null +++ b/dialogs/show_dialogs.py @@ -0,0 +1,31 @@ +import tkinter as tk +from tkinter import messagebox +from typing import Optional + +def display_message_box() -> Optional[str]: + """Display a message box and return user's response""" + root = tk.Tk() + root.withdraw() # Hide the main window + + # Show a custom message box + result = messagebox.askyesnocancel( + title="Example Dialog Window", + message="Do you want to continue?" + ) + + # Map boolean result to string values + if result is None: + return "cancel" + elif result: + return "yes" + else: + return "no" + +if __name__ == "__main__": + # Optional: Set font to ensure Chinese characters display correctly + # import matplotlib.pyplot as plt + # plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"] + + # Display the message box + response = display_message_box() + print(f"User response: {response}") \ No newline at end of file diff --git a/example.txt b/example.txt deleted file mode 100644 index cb511a2b55e..00000000000 --- a/example.txt +++ /dev/null @@ -1 +0,0 @@ -Change from feature-branch diff --git a/fF b/fF.py similarity index 100% rename from fF rename to fF.py diff --git a/fibonacci_SIMPLIFIED b/fibonacci_SIMPLIFIED.py similarity index 100% rename from fibonacci_SIMPLIFIED rename to fibonacci_SIMPLIFIED.py diff --git a/flappyBird_pygame/flappy_bird.py b/flappyBird_pygame/flappy_bird.py index 34b3206b7e2..0cdf9079c95 100644 --- a/flappyBird_pygame/flappy_bird.py +++ b/flappyBird_pygame/flappy_bird.py @@ -149,9 +149,18 @@ def collides_with(self, bird): def load_images(): + # 获取脚本所在目录 + script_dir = os.path.dirname(os.path.abspath(__file__)) + # 构建图片目录路径 + images_dir = os.path.join(script_dir, "images") + def load_image(img_file_name): - - file_name = os.path.join(".", "images", img_file_name) + file_name = os.path.join(images_dir, img_file_name) + # 检查文件是否存在并打印调试信息 + if not os.path.exists(file_name): + print(f"错误: 找不到图片文件 {file_name}") + print(f"脚本所在目录: {script_dir}") + print(f"图片目录应该是: {images_dir}") img = pygame.image.load(file_name) img.convert() return img @@ -272,4 +281,4 @@ def main(): if __name__ == "__main__": # If this module had been imported, __name__ would be 'flappybird'. # It was executed (e.g. by double-clicking the file), so call main. - main() + main() \ No newline at end of file diff --git a/floodfill/floodfill.py b/floodfill/floodfill.py index b4c39735f23..60bad944c80 100644 --- a/floodfill/floodfill.py +++ b/floodfill/floodfill.py @@ -1,132 +1,116 @@ import pygame - -""" -Visualises how a floodfill algorithm runs and work using pygame -Pass two int arguments for the window width and the window height -`python floodfill.py ` -""" - +import sys +from collections import deque class FloodFill: - def __init__(self, window_width, window_height): - self.window_width = int(window_width) - self.window_height = int(window_height) - + def __init__(self, window_width: int = 800, window_height: int = 600): + """Initialize the FloodFill visualization with specified window dimensions.""" + self.window_width = window_width + self.window_height = window_height + pygame.init() - pygame.display.set_caption("Floodfill") - self.display = pygame.display.set_mode((self.window_width, self.window_height)) + pygame.display.set_caption("Floodfill Visualization") + self.display = pygame.display.set_mode((window_width, window_height)) self.surface = pygame.Surface(self.display.get_size()) self.surface.fill((0, 0, 0)) - - self.generateClosedPolygons() # for visualisation purposes - - self.queue = [] - - def generateClosedPolygons(self): + + self.generate_closed_polygons() + self.queue = deque() # Use deque for O(1) append/pop operations + self.white = self.surface.map_rgb((255, 255, 255)) + self.black = self.surface.map_rgb((0, 0, 0)) + + def generate_closed_polygons(self) -> None: + """Generate random closed polygons for visualization.""" if self.window_height < 128 or self.window_width < 128: - return # surface too small - + return # Surface too small + from random import randint, uniform from math import pi, sin, cos - for n in range(0, randint(0, 5)): + for _ in range(randint(0, 5)): x = randint(50, self.window_width - 50) y = randint(50, self.window_height - 50) - - angle = 0 - angle += uniform(0, 0.7) + angle = uniform(0, 0.7) vertices = [] - for i in range(0, randint(3, 7)): + for _ in range(randint(3, 7)): dist = randint(10, 50) - vertices.append( - (int(x + cos(angle) * dist), int(y + sin(angle) * dist)) - ) + vertices.append((int(x + cos(angle) * dist), int(y + sin(angle) * dist))) angle += uniform(0, pi / 2) - for i in range(0, len(vertices) - 1): - pygame.draw.line( - self.surface, (255, 0, 0), vertices[i], vertices[i + 1] - ) - - pygame.draw.line( - self.surface, (255, 0, 0), vertices[len(vertices) - 1], vertices[0] - ) - - def run(self): - looping = True - while looping: - evsforturn = [] - for ev in pygame.event.get(): - if ev.type == pygame.QUIT: - looping = False + # Draw polygon edges + for i in range(len(vertices) - 1): + pygame.draw.line(self.surface, (255, 0, 0), vertices[i], vertices[i + 1]) + pygame.draw.line(self.surface, (255, 0, 0), vertices[-1], vertices[0]) + + def run(self) -> None: + """Main loop for the visualization.""" + clock = pygame.time.Clock() + running = True + + while running: + events = [] + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False else: - evsforturn.append(ev) # TODO: Maybe extend with more events - self.update(evsforturn) + events.append(event) + + self.update(events) self.display.blit(self.surface, (0, 0)) pygame.display.flip() - + clock.tick(60) # Limit FPS for performance + pygame.quit() - def update(self, events): - for ev in events: - if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1: - self.queue.append(ev.pos) + def update(self, events: list) -> None: + """Handle events and update floodfill algorithm.""" + for event in events: + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + self.queue.append(event.pos) - if not len(self.queue): + if not self.queue: return - point = self.queue.pop(0) - - pixArr = pygame.PixelArray(self.surface) - - if pixArr[point[0], point[1]] == self.surface.map_rgb((255, 255, 255)): - return - - pixArr[point[0], point[1]] = (255, 255, 255) - - left = (point[0] - 1, point[1]) - right = (point[0] + 1, point[1]) - top = (point[0], point[1] + 1) - bottom = (point[0], point[1] - 1) - - if ( - self.inBounds(left) - and left not in self.queue - and pixArr[left[0], left[1]] == self.surface.map_rgb((0, 0, 0)) - ): - self.queue.append(left) - if ( - self.inBounds(right) - and right not in self.queue - and pixArr[right[0], right[1]] == self.surface.map_rgb((0, 0, 0)) - ): - self.queue.append(right) - if ( - self.inBounds(top) - and top not in self.queue - and pixArr[top[0], top[1]] == self.surface.map_rgb((0, 0, 0)) - ): - self.queue.append(top) - if ( - self.inBounds(bottom) - and bottom not in self.queue - and pixArr[bottom[0], bottom[1]] == self.surface.map_rgb((0, 0, 0)) - ): - self.queue.append(bottom) - - del pixArr - - def inBounds(self, coord): - if coord[0] < 0 or coord[0] >= self.window_width: - return False - elif coord[1] < 0 or coord[1] >= self.window_height: - return False - return True - + # Process multiple points per frame for faster filling + for _ in range(min(100, len(self.queue))): + x, y = self.queue.popleft() + + # Check bounds and color + if not (0 <= x < self.window_width and 0 <= y < self.window_height): + continue + + with pygame.PixelArray(self.surface) as pixels: + if pixels[x, y] != self.black: + continue + + pixels[x, y] = (255, 255, 255) + + # Check and add neighbors + for dx, dy in [(-1, 0), (1, 0), (0, 1), (0, -1)]: + nx, ny = x + dx, y + dy + if (0 <= nx < self.window_width and 0 <= ny < self.window_height + and pixels[nx, ny] == self.black): + self.queue.append((nx, ny)) if __name__ == "__main__": - import sys - - floodfill = FloodFill(sys.argv[1], sys.argv[2]) - floodfill.run() + # Set default dimensions + DEFAULT_WIDTH = 800 + DEFAULT_HEIGHT = 600 + + # Parse command-line arguments with defaults + try: + width = int(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_WIDTH + height = int(sys.argv[2]) if len(sys.argv) > 2 else DEFAULT_HEIGHT + except ValueError: + print("Error: Width and height must be integers.") + sys.exit(1) + + # Validate dimensions + if width <= 0 or height <= 0: + print("Error: Width and height must be positive integers.") + sys.exit(1) + + # Initialize and run visualization + floodfill = FloodFill(width, height) + floodfill.run() \ No newline at end of file diff --git a/game_of_life/game_o_life.py b/game_of_life/game_o_life.py index 7d2ee832dcf..6db0cdb53b4 100644 --- a/game_of_life/game_o_life.py +++ b/game_of_life/game_o_life.py @@ -1,134 +1,96 @@ -"""Conway's Game Of Life, Author Anurag Kumar(mailto:anuragkumarak95@gmail.com) +""" +Conway's Game of Life +Author: Anurag Kumar (mailto:anuragkumarak95@gmail.com) Requirements: - numpy - - random - - time - matplotlib Python: - - 3.5 + - 3.13.5+ Usage: - - $python3 game_o_life + - $ python game_o_life.py [canvas_size:int=50] -Game-Of-Life Rules: - - 1. - Any live cell with fewer than two live neighbours - dies, as if caused by under-population. - 2. - Any live cell with two or three live neighbours lives - on to the next generation. - 3. - Any live cell with more than three live neighbours - dies, as if by over-population. - 4. - Any dead cell with exactly three live neighbours be- - comes a live cell, as if by reproduction. - """ -import random -import sys +Rules: +1. Any live cell with fewer than two live neighbours dies. +2. Any live cell with two or three live neighbours survives. +3. Any live cell with more than three live neighbours dies. +4. Any dead cell with exactly three live neighbours becomes alive. +""" +import sys import numpy as np - -from matplotlib import use as mpluse - -mpluse("TkAgg") -from matplotlib import pyplot as plt +import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap - -usage_doc = "Usage of script: script_nama " - -choice = [0] * 100 + [1] * 10 -random.shuffle(choice) - - -def create_canvas(size): - canvas = [[False for i in range(size)] for j in range(size)] - return canvas - - -def seed(canvas): - for i, row in enumerate(canvas): - for j, _ in enumerate(row): - canvas[i][j] = bool(random.getrandbits(1)) - - -def run(canvas): - """This function runs the rules of game through all points, and changes their status accordingly.(in the same canvas) - @Args: - -- - canvas : canvas of population to run the rules on. - - @returns: - -- - None - """ - canvas = np.array(canvas) - next_gen_canvas = np.array(create_canvas(canvas.shape[0])) - for r, row in enumerate(canvas): - for c, pt in enumerate(row): - # print(r-1,r+2,c-1,c+2) - next_gen_canvas[r][c] = __judge_point( - pt, canvas[r - 1 : r + 2, c - 1 : c + 2] - ) - - canvas = next_gen_canvas - del next_gen_canvas # cleaning memory as we move on. - return canvas.tolist() - - -def __judge_point(pt, neighbours): - dead = 0 - alive = 0 - # finding dead or alive neighbours count. - for i in neighbours: - for status in i: - if status: - alive += 1 - else: - dead += 1 - - # handling duplicate entry for focus pt. - if pt: - alive -= 1 - else: - dead -= 1 - - # running the rules of game here. - state = pt - if pt: - if alive < 2: - state = False - elif alive == 2 or alive == 3: - state = True - elif alive > 3: - state = False - else: - if alive == 3: - state = True - - return state - - -if __name__ == "__main__": - if len(sys.argv) != 2: - raise Exception(usage_doc) - - canvas_size = int(sys.argv[1]) - # main working structure of this module. - c = create_canvas(canvas_size) - seed(c) - fig, ax = plt.subplots() - fig.show() - cmap = ListedColormap(["w", "k"]) +from matplotlib.animation import FuncAnimation + +# Configuration +DEFAULT_CANVAS_SIZE = 50 +USAGE = "Usage: python game_o_life.py [canvas_size:int]" + +def main(): + # Parse command-line arguments with default + canvas_size = DEFAULT_CANVAS_SIZE + + if len(sys.argv) > 2: + sys.exit(f"Error: Too many arguments\n{USAGE}") + + if len(sys.argv) == 2: + try: + canvas_size = int(sys.argv[1]) + if canvas_size <= 0: + raise ValueError("Canvas size must be a positive integer") + except ValueError as e: + sys.exit(f"Error: {e}\n{USAGE}") + + # Initialize the game board + board = initialize_board(canvas_size) + + # Set up matplotlib visualization + plt.style.use('dark_background') + fig, ax = plt.subplots(figsize=(10, 10)) + fig.canvas.manager.set_window_title(f"Conway's Game of Life (Size: {canvas_size}x{canvas_size})") + cmap = ListedColormap(['#111111', '#FFFFFF']) # Black and white + img = ax.matshow(board, cmap=cmap) + ax.set_axis_off() + + # Define animation update function + def update(frame): + nonlocal board + board = compute_next_generation(board) + img.set_data(board) + return [img] + + # Create animation + ani = FuncAnimation(fig, update, interval=100, blit=True, cache_frame_data=False) + try: - while True: - c = run(c) - ax.matshow(c, cmap=cmap) - fig.canvas.draw() - ax.cla() + plt.tight_layout() + plt.show() except KeyboardInterrupt: - # do nothing. - pass + sys.exit("\nExiting...") + +def initialize_board(size: int) -> np.ndarray: + """Initialize the game board with random alive/dead cells.""" + return np.random.choice([0, 1], size=(size, size), p=[0.7, 0.3]) + +def compute_next_generation(board: np.ndarray) -> np.ndarray: + """Compute the next generation using vectorized operations.""" + # Count neighbors using convolution + neighbors = ( + np.roll(board, 1, 0) + np.roll(board, -1, 0) + + np.roll(board, 1, 1) + np.roll(board, -1, 1) + + np.roll(board, (1, 1), (0, 1)) + np.roll(board, (1, -1), (0, 1)) + + np.roll(board, (-1, 1), (0, 1)) + np.roll(board, (-1, -1), (0, 1)) + ) + + # Apply Conway's rules + return np.where( + (board == 1) & ((neighbors == 2) | (neighbors == 3)) | + (board == 0) & (neighbors == 3), + 1, 0 + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/google.py b/googlerequests.py old mode 100755 new mode 100644 similarity index 100% rename from google.py rename to googlerequests.py diff --git a/image2pdf/requirements.txt b/image2pdf/requirements.txt deleted file mode 100644 index 3868fb16b89..00000000000 --- a/image2pdf/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pillow diff --git a/insta_image_saving/instagram_image_scrapping.ipynb b/insta_image_saving/instagram_image_scrapping.ipynb index 0d5d2e5ad35..870721d4cca 100644 --- a/insta_image_saving/instagram_image_scrapping.ipynb +++ b/insta_image_saving/instagram_image_scrapping.ipynb @@ -4,7 +4,19 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'scrapy'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpandas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpd\u001b[39;00m\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mselenium\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m webdriver\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mscrapy\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mselector\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Selector\n\u001b[32m 5\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mio\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m BytesIO\n\u001b[32m 6\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mPIL\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Image\n", + "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'scrapy'" + ] + } + ], "source": [ "from time import sleep\n", "import pandas as pd\n", @@ -21,10 +33,18 @@ "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "1951\n" + "ename": "AttributeError", + "evalue": "'str' object has no attribute 'capabilities'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[32m 5\u001b[39m instaccountlink = \u001b[33m\"\u001b[39m\u001b[33mhttps://instagram.com/audi\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 6\u001b[39m instaaccountname = \u001b[33m\"\u001b[39m\u001b[33mAudi\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m driver = \u001b[43mwebdriver\u001b[49m\u001b[43m.\u001b[49m\u001b[43mChrome\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mdriver/driver\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 8\u001b[39m driver.get(instaccountlink)\n\u001b[32m 9\u001b[39m unique_urls = []\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\chrome\\webdriver.py:47\u001b[39m, in \u001b[36mWebDriver.__init__\u001b[39m\u001b[34m(self, options, service, keep_alive)\u001b[39m\n\u001b[32m 44\u001b[39m service = service \u001b[38;5;28;01mif\u001b[39;00m service \u001b[38;5;28;01melse\u001b[39;00m Service()\n\u001b[32m 45\u001b[39m options = options \u001b[38;5;28;01mif\u001b[39;00m options \u001b[38;5;28;01melse\u001b[39;00m Options()\n\u001b[32m---> \u001b[39m\u001b[32m47\u001b[39m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[34;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[32m 48\u001b[39m \u001b[43m \u001b[49m\u001b[43mbrowser_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mDesiredCapabilities\u001b[49m\u001b[43m.\u001b[49m\u001b[43mCHROME\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mbrowserName\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 49\u001b[39m \u001b[43m \u001b[49m\u001b[43mvendor_prefix\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mgoog\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 50\u001b[39m \u001b[43m \u001b[49m\u001b[43moptions\u001b[49m\u001b[43m=\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 51\u001b[39m \u001b[43m \u001b[49m\u001b[43mservice\u001b[49m\u001b[43m=\u001b[49m\u001b[43mservice\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 52\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeep_alive\u001b[49m\u001b[43m=\u001b[49m\u001b[43mkeep_alive\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 53\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\chromium\\webdriver.py:53\u001b[39m, in \u001b[36mChromiumDriver.__init__\u001b[39m\u001b[34m(self, browser_name, vendor_prefix, options, service, keep_alive)\u001b[39m\n\u001b[32m 50\u001b[39m \u001b[38;5;28mself\u001b[39m.service = service\n\u001b[32m 52\u001b[39m finder = DriverFinder(\u001b[38;5;28mself\u001b[39m.service, options)\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mfinder\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_browser_path\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[32m 54\u001b[39m options.binary_location = finder.get_browser_path()\n\u001b[32m 55\u001b[39m options.browser_version = \u001b[38;5;28;01mNone\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\common\\driver_finder.py:47\u001b[39m, in \u001b[36mDriverFinder.get_browser_path\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 46\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mget_browser_path\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28mstr\u001b[39m:\n\u001b[32m---> \u001b[39m\u001b[32m47\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_binary_paths\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m[\u001b[33m\"\u001b[39m\u001b[33mbrowser_path\u001b[39m\u001b[33m\"\u001b[39m]\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\common\\driver_finder.py:56\u001b[39m, in \u001b[36mDriverFinder._binary_paths\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._paths[\u001b[33m\"\u001b[39m\u001b[33mdriver_path\u001b[39m\u001b[33m\"\u001b[39m]:\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._paths\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m browser = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_options\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcapabilities\u001b[49m[\u001b[33m\"\u001b[39m\u001b[33mbrowserName\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m 57\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 58\u001b[39m path = \u001b[38;5;28mself\u001b[39m._service.path\n", + "\u001b[31mAttributeError\u001b[39m: 'str' object has no attribute 'capabilities'" ] } ], @@ -65,10 +85,14 @@ "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "file saved successfully\n" + "ename": "NameError", + "evalue": "name 'unique_urls' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m file = \u001b[38;5;28mopen\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33moutput/audi_instagram_11_07_2019.csv\u001b[39m\u001b[33m\"\u001b[39m,\u001b[33m\"\u001b[39m\u001b[33ma\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m u \u001b[38;5;129;01min\u001b[39;00m \u001b[43munique_urls\u001b[49m :\n\u001b[32m 3\u001b[39m file.write(u)\n\u001b[32m 4\u001b[39m file.write(\u001b[33m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m)\n", + "\u001b[31mNameError\u001b[39m: name 'unique_urls' is not defined" ] } ], @@ -85,7 +109,23 @@ "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'str' object has no attribute 'capabilities'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# saving the images to specified directory\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m driver = \u001b[43mwebdriver\u001b[49m\u001b[43m.\u001b[49m\u001b[43mChrome\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mdriver/driver\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m image_urls = []\n\u001b[32m 5\u001b[39m count = \u001b[32m0\u001b[39m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\chrome\\webdriver.py:47\u001b[39m, in \u001b[36mWebDriver.__init__\u001b[39m\u001b[34m(self, options, service, keep_alive)\u001b[39m\n\u001b[32m 44\u001b[39m service = service \u001b[38;5;28;01mif\u001b[39;00m service \u001b[38;5;28;01melse\u001b[39;00m Service()\n\u001b[32m 45\u001b[39m options = options \u001b[38;5;28;01mif\u001b[39;00m options \u001b[38;5;28;01melse\u001b[39;00m Options()\n\u001b[32m---> \u001b[39m\u001b[32m47\u001b[39m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[34;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[32m 48\u001b[39m \u001b[43m \u001b[49m\u001b[43mbrowser_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mDesiredCapabilities\u001b[49m\u001b[43m.\u001b[49m\u001b[43mCHROME\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mbrowserName\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 49\u001b[39m \u001b[43m \u001b[49m\u001b[43mvendor_prefix\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mgoog\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 50\u001b[39m \u001b[43m \u001b[49m\u001b[43moptions\u001b[49m\u001b[43m=\u001b[49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 51\u001b[39m \u001b[43m \u001b[49m\u001b[43mservice\u001b[49m\u001b[43m=\u001b[49m\u001b[43mservice\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 52\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeep_alive\u001b[49m\u001b[43m=\u001b[49m\u001b[43mkeep_alive\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 53\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\chromium\\webdriver.py:53\u001b[39m, in \u001b[36mChromiumDriver.__init__\u001b[39m\u001b[34m(self, browser_name, vendor_prefix, options, service, keep_alive)\u001b[39m\n\u001b[32m 50\u001b[39m \u001b[38;5;28mself\u001b[39m.service = service\n\u001b[32m 52\u001b[39m finder = DriverFinder(\u001b[38;5;28mself\u001b[39m.service, options)\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mfinder\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_browser_path\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[32m 54\u001b[39m options.binary_location = finder.get_browser_path()\n\u001b[32m 55\u001b[39m options.browser_version = \u001b[38;5;28;01mNone\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\common\\driver_finder.py:47\u001b[39m, in \u001b[36mDriverFinder.get_browser_path\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 46\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mget_browser_path\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28mstr\u001b[39m:\n\u001b[32m---> \u001b[39m\u001b[32m47\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_binary_paths\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m[\u001b[33m\"\u001b[39m\u001b[33mbrowser_path\u001b[39m\u001b[33m\"\u001b[39m]\n", + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\selenium\\webdriver\\common\\driver_finder.py:56\u001b[39m, in \u001b[36mDriverFinder._binary_paths\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._paths[\u001b[33m\"\u001b[39m\u001b[33mdriver_path\u001b[39m\u001b[33m\"\u001b[39m]:\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._paths\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m browser = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_options\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcapabilities\u001b[49m[\u001b[33m\"\u001b[39m\u001b[33mbrowserName\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m 57\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 58\u001b[39m path = \u001b[38;5;28mself\u001b[39m._service.path\n", + "\u001b[31mAttributeError\u001b[39m: 'str' object has no attribute 'capabilities'" + ] + } + ], "source": [ "# saving the images to specified directory\n", "driver = webdriver.Chrome('driver/driver')\n", @@ -163,7 +203,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.4" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/insta_monitering/insta_api.py b/insta_monitering/insta_api.py index 957f240730d..e089bc7b8c0 100644 --- a/insta_monitering/insta_api.py +++ b/insta_monitering/insta_api.py @@ -1,152 +1,277 @@ from concurrent.futures import ThreadPoolExecutor - import tornado.ioloop import tornado.web from tornado.concurrent import run_on_executor from tornado.gen import coroutine +import traceback +import ujson + +import con_file -# import file +# Import required classes from local modules try: - from instagram_monitering.insta_datafetcher import * - from instagram_monitering.subpinsta import * -except: - from insta_datafetcher import * - from subpinsta import * -MAX_WORKERS = 10 + # Attempt relative import (when running as part of a package) + from .insta_datafetcher import MonitoringClass, InstaProcessManager, DataFetcher + from .subpinsta import start_monitoring_process +except ImportError: + # Fallback to absolute import (when running standalone) + from insta_datafetcher import InstaProcessManager, DataFetcher + from subpinsta import start_monitoring_process +# Configuration constants +MAX_WORKERS = 10 # Maximum number of threads for background tasks class StartHandlerinsta(tornado.web.RequestHandler): + """ + API handler to initiate Instagram monitoring for a specified hashtag or profile. + + Query Parameters: + q (str): Hashtag or profile name to monitor + userId (str): User identifier + type (str): Monitoring type ('hashtags' or 'profile') + productId (str): Product identifier for database storage + """ executor = ThreadPoolExecutor(max_workers=MAX_WORKERS) @run_on_executor - def background_task(self, user, tags, type, productId): + def background_task(self, user: str, tags: str, monitor_type: str, product_id: str) -> None: + """ + Execute monitoring process in a background thread. + + Args: + user: User identifier + tags: Hashtag or profile name + monitor_type: Type of monitoring ('hashtags' or 'profile') + product_id: Product identifier for database + """ try: - instasubprocess(user=user, tags=tags, type=type, productId=productId) - except: - print("error::background_task>>", sys.exc_info()[1]) + # Start monitoring process with configuration + start_monitoring_process( + user=user, + tags=tags, + monitor_type=monitor_type, + product_id=product_id, + db_host=con_file.host, # Use configured database host + db_port=con_file.mongoPort # Use configured database port + ) + except Exception as e: + print(f"Error starting monitoring process: {str(e)}") + traceback.print_exc() @coroutine - def get(self): + def get(self) -> None: + """Handle GET request to start monitoring""" try: + # Extract and validate query parameters q = self.get_argument("q") user = self.get_argument("userId") - type = self.get_argument("type") - productId = self.get_argument("productId") - except: - self.send_error(400) - if " " in q: - q = q.replace(" ", "") - self.background_task(user=user, tags=q, type=type, productId=productId) - temp = {} - temp["query"] = q - temp["userId"] = user - temp["status"] = True - temp["productId"] = productId - print( - "{0}, {1}, {2}, {3}".format( - temp["userId"], temp["productId"], temp["query"], temp["status"] - ) - ) - self.write(ujson.dumps(temp)) - + monitor_type = self.get_argument("type") + product_id = self.get_argument("productId") + + # Sanitize input (remove spaces from tags) + if " " in q: + q = q.replace(" ", "") + + # Start background monitoring task + self.background_task(user, tags=q, monitor_type=monitor_type, product_id=product_id) + + # Return success response + response = { + "query": q, + "userId": user, + "status": True, + "productId": product_id, + "message": f"Monitoring started for {monitor_type}: {q}" + } + + print(f"Monitoring started: User={user}, Tags={q}, Type={monitor_type}") + self.write(ujson.dumps(response)) + + except Exception as e: + # Handle missing parameters or other errors + self.send_error(400, reason=f"Invalid request: {str(e)}") class StopHandlerinsta(tornado.web.RequestHandler): - def get(self): + """ + API handler to terminate Instagram monitoring for a specified hashtag or profile. + + Query Parameters: + q (str): Hashtag or profile name to stop monitoring + userId (str): User identifier + productId (str): Product identifier + """ + def get(self) -> None: + """Handle GET request to stop monitoring""" try: + # Extract and validate query parameters q = self.get_argument("q") user = self.get_argument("userId") - # tags = self.get_argument("hashtags") - productId = self.get_argument("productId") - except: - self.send_error(400) - obj = InstaPorcessClass() - result = obj.deletProcess(tags=q, user=user, productId=productId) - temp = {} - temp["query"] = q - temp["userId"] = user - temp["productId"] = productId - temp["status"] = result - print( - "{0}, {1}, {2}, {3}".format( - temp["userId"], temp["productId"], temp["query"], temp["status"] + product_id = self.get_argument("productId") + + # Stop monitoring process with configuration + process_manager = InstaProcessManager( + db_host=con_file.host, # Use configured database host + db_port=con_file.mongoPort # Use configured database port ) - ) - self.write(ujson.dumps(temp)) - + result = process_manager.stop_monitoring(user, q, product_id) + + # Return status response + response = { + "query": q, + "userId": user, + "productId": product_id, + "status": result, + "message": f"Monitoring stopped for {q}" if result else f"Monitoring not found for {q}" + } + + print(f"Monitoring status: {q} - {'Stopped' if result else 'Not Found'}") + self.write(ujson.dumps(response)) + + except Exception as e: + # Handle missing parameters or other errors + self.send_error(400, reason=f"Invalid request: {str(e)}") class StatusHandlerinsta(tornado.web.RequestHandler): - def get(self): + """ + API handler to check the status of Instagram monitoring. + + Query Parameters: + q (str): Hashtag or profile name + userId (str): User identifier + productId (str): Product identifier + """ + def get(self) -> None: + """Handle GET request to check monitoring status""" try: + # Extract and validate query parameters q = self.get_argument("q") user = self.get_argument("userId") - productId = self.get_argument("productId") - # tags = self.get_argument("hashtags") - except: - self.send_error(400) - obj = InstaPorcessClass() - result = obj.statusCheck(tags=q, user=user, productId=productId) - temp = {} - temp["query"] = q - temp["userId"] = user - temp["status"] = result - temp["productId"] = productId - print( - "{0}, {1}, {2}, {3}".format( - temp["userId"], temp["productId"], temp["query"], temp["status"] + product_id = self.get_argument("productId") + + # Check monitoring status with configuration + process_manager = InstaProcessManager( + db_host=con_file.host, # Use configured database host + db_port=con_file.mongoPort # Use configured database port ) - ) - self.write(ujson.dumps(temp)) - - -# class SenderHandlerinsta(tornado.web.RequestHandler): -# def get(self): -# try: -# q = self.get_argument("q") -# user = self.get_argument("userId") -# type = self.get_argument("type") -# productId = self.get_argument("productId") -# except: -# self.send_error(400) -# recordsobj = DBDataFetcher(user=user, tags=q, type=type, productId=productId) -# data = recordsobj.dbFetcher() -# self.write(data) - + is_running = process_manager.is_process_running(user, q, product_id) + + # Return status response + response = { + "query": q, + "userId": user, + "status": is_running, + "productId": product_id, + "message": f"Monitoring is {'active' if is_running else 'inactive'} for {q}" + } + + print(f"Status check: {q} - {'Active' if is_running else 'Inactive'}") + self.write(ujson.dumps(response)) + + except Exception as e: + # Handle missing parameters or other errors + self.send_error(400, reason=f"Invalid request: {str(e)}") class SenderHandlerinstaLess(tornado.web.RequestHandler): - def get(self): + """ + API handler to retrieve Instagram posts older than a specified timestamp. + + Query Parameters: + q (str): Hashtag or profile name + userId (str): User identifier + type (str): Monitoring type + productId (str): Product identifier + date (int): Unix timestamp + limit (int): Maximum number of posts to return + """ + def get(self) -> None: + """Handle GET request to fetch older posts""" try: + # Extract and validate query parameters q = self.get_argument("q") user = self.get_argument("userId") - type = self.get_argument("type") - productId = self.get_argument("productId") - date = self.get_argument("date") - limit = self.get_argument("limit") - except: - self.send_error(400) - recordsobj = DBDataFetcher(user=user, tags=q, type=type, productId=productId) - data = recordsobj.DBFetcherLess(limit=limit, date=date) - # print("{0}, {1}, {2}, {3}".format(temp["userId"], temp["productId"], temp["query"], temp["status"])) - self.write(data) - + monitor_type = self.get_argument("type") + product_id = self.get_argument("productId") + date = int(self.get_argument("date")) # Unix timestamp + limit = int(self.get_argument("limit")) # Number of posts + + # Fetch posts older than specified timestamp with configuration + data_fetcher = DataFetcher( + user=user, + tags=q, + product_id=product_id, + db_host=con_file.host, # Use configured database host + db_port=con_file.mongoPort # Use configured database port + ) + posts = data_fetcher.get_posts_before_timestamp(date, limit) + + # Return posts data + self.write(ujson.dumps({ + "query": q, + "userId": user, + "productId": product_id, + "count": len(posts), + "posts": posts + })) + + except ValueError as ve: + # Handle invalid parameter types + self.send_error(400, reason=f"Invalid parameter: {str(ve)}") + except Exception as e: + # Handle other errors + self.send_error(500, reason=f"Internal server error: {str(e)}") class SenderHandlerinstaGreater(tornado.web.RequestHandler): - def get(self): + """ + API handler to retrieve Instagram posts newer than a specified timestamp. + + Query Parameters: + q (str): Hashtag or profile name + userId (str): User identifier + type (str): Monitoring type + productId (str): Product identifier + date (int): Unix timestamp + limit (int): Maximum number of posts to return + """ + def get(self) -> None: + """Handle GET request to fetch newer posts""" try: + # Extract and validate query parameters q = self.get_argument("q") user = self.get_argument("userId") - type = self.get_argument("type") - productId = self.get_argument("productId") - date = self.get_argument("date") - limit = self.get_argument("limit") - except: - self.send_error(400) - recordsobj = DBDataFetcher(user=user, tags=q, type=type, productId=productId) - data = recordsobj.DBFetcherGreater(limit=limit, date=date) - # print("{0}, {1}, {2}, {3}".format(temp["userId"], temp["productId"], temp["query"], temp["status"])) - self.write(data) - + monitor_type = self.get_argument("type") + product_id = self.get_argument("productId") + date = int(self.get_argument("date")) # Unix timestamp + limit = int(self.get_argument("limit")) # Number of posts + + # Fetch posts newer than specified timestamp with configuration + data_fetcher = DataFetcher( + user=user, + tags=q, + product_id=product_id, + db_host=con_file.host, # Use configured database host + db_port=con_file.mongoPort # Use configured database port + ) + posts = data_fetcher.get_posts_after_timestamp(date, limit) + + # Return posts data + self.write(ujson.dumps({ + "query": q, + "userId": user, + "productId": product_id, + "count": len(posts), + "posts": posts + })) + + except ValueError as ve: + # Handle invalid parameter types + self.send_error(400, reason=f"Invalid parameter: {str(ve)}") + except Exception as e: + # Handle other errors + self.send_error(500, reason=f"Internal server error: {str(e)}") if __name__ == "__main__": + """Main entry point - Starts the Tornado server""" + # Define API routes and handlers application = tornado.web.Application( [ (r"/instagram/monitoring/start", StartHandlerinsta), @@ -154,9 +279,22 @@ def get(self): (r"/instagram/monitoring/status", StatusHandlerinsta), (r"/instagram/monitoring/less", SenderHandlerinstaLess), (r"/instagram/monitoring/greater", SenderHandlerinstaGreater), - ] + ], + debug=False, # Disable debug mode for production + autoreload=True # Automatically reload on code changes ) - application.listen(7074) - print("server running") - tornado.ioloop.IOLoop.instance().start() + # Start the server + port = 7074 + application.listen(port) + print(f"Instagram Monitoring API Server running on port {port}") + print(f"MongoDB connection: {con_file.host}:{con_file.mongoPort}") # Display MongoDB connection info + print("Available endpoints:") + print(" - /instagram/monitoring/start") + print(" - /instagram/monitoring/stop") + print(" - /instagram/monitoring/status") + print(" - /instagram/monitoring/less") + print(" - /instagram/monitoring/greater") + + # Start the I/O loop + tornado.ioloop.IOLoop.current().start() \ No newline at end of file diff --git a/insta_monitering/insta_datafetcher.py b/insta_monitering/insta_datafetcher.py index 8c5ed78b902..00c1b319b28 100644 --- a/insta_monitering/insta_datafetcher.py +++ b/insta_monitering/insta_datafetcher.py @@ -1,462 +1,866 @@ -# only god knows whats happening in the code -# if I forget the code structure -# please pray to god for help +# Instagram Monitoring Tool with Enhanced Error Handling and Default Parameters import asyncio import multiprocessing import os import random -import re import socket import sys import time +import httpx +import traceback import bs4 import pymongo -import requests + import socks import ujson -import urllib3 try: - import instagram_monitering.con_file as config -except Exception as e: - print(e) import con_file as config - - -class PorxyApplyingDecorator(object): - def __init__(self): - filename = os.getcwd() + "/" + "ipList.txt" - with open(filename, "r") as f: - ipdata = f.read() - self._IP = random.choice(ipdata.split(",")) - - def __call__(self, function_to_call_for_appling_proxy): - SOCKS5_PROXY_HOST = self._IP - # default_socket = socket.socket - socks.set_default_proxy( - socks.SOCKS5, - SOCKS5_PROXY_HOST, - config.SOCKS5_PROXY_PORT, - True, - config.auth, - config.passcode, - ) - socket.socket = socks.socksocket - - def wrapper_function(url): - # this is used for applyting socks5 proxy over the request - return function_to_call_for_appling_proxy(url) - - socks.set_default_proxy() - return wrapper_function - - -async def dataprocess(htmldata): - bs4obj = bs4.BeautifulSoup(htmldata, "html.parser") - scriptsdata = bs4obj.findAll("script", {"type": "text/javascript"}) - datatext = "" - for i in scriptsdata: - datatext = i.text - if "window._sharedData =" in datatext: - break - datajson = re.findall("{(.*)}", datatext) - datajson = "{" + datajson[0] + "}" - datadict = ujson.loads(datajson) - maindict = {} - datadict = datadict["entry_data"]["PostPage"][0]["graphql"]["shortcode_media"] - tofind = ["owner", "location"] - for i in tofind: +except ImportError: + import insta_monitering.con_file as config + + +class ProxyApplyingDecorator: + """ + Decorator class for applying SOCKS5 proxy to HTTP requests. + + Attributes: + proxy_file (str): Path to the file containing proxy list. + _IP (str): Selected proxy IP address and port. + """ + def __init__(self, proxy_file="ipList.txt"): + """ + Initialize the proxy decorator. + + Args: + proxy_file (str, optional): Path to the proxy list file. Defaults to "ipList.txt". + """ + self.proxy_file = proxy_file + self._IP = self._get_random_proxy() + + def _get_random_proxy(self): + """ + Read proxy list from file and select a random proxy. + + Returns: + str: Randomly selected proxy in "host:port" format. + """ try: - maindict[i] = datadict[i] + if not os.path.exists(self.proxy_file): + raise FileNotFoundError(f"Proxy file not found: {self.proxy_file}") + + with open(self.proxy_file, "r") as f: + ipdata = f.read().strip() + + if not ipdata: + raise ValueError("Proxy file is empty") + + return random.choice(ipdata.split(",")) except Exception as e: - print(e) - pass - return maindict - - -async def datapullpost(future, url): - while True: - - @PorxyApplyingDecorator() - async def request_pull(url): - data = None - print(url) - urllib3.disable_warnings() - user_agent = {"User-agent": "Mozilla/17.0"} + print(f"Error loading proxy: {e}") + return None + + def __call__(self, function_to_apply_proxy): + """ + Wrap the target function with proxy configuration. + + Args: + function_to_apply_proxy (callable): Function to be wrapped. + + Returns: + callable: Wrapped function with proxy applied. + """ + def wrapper_function(url): + if not self._IP: + print("No proxy available, using direct connection") + return function_to_apply_proxy(url) + + original_socket = socket.socket try: - data = requests.get( - url=url, headers=user_agent, timeout=10, verify=False - ).text + proxy_parts = self._IP.split(":") + proxy_host = proxy_parts[0] + proxy_port = int(proxy_parts[1]) if len(proxy_parts) > 1 else config.SOCKS5_PROXY_PORT + + # Configure SOCKS5 proxy + socks.set_default_proxy( + socks.SOCKS5, + proxy_host, + proxy_port, + True, + config.auth, + config.passcode, + ) + socket.socket = socks.socksocket + return function_to_apply_proxy(url) except Exception as e: - print(e) - data = None + print(f"Proxy error: {e}") + return function_to_apply_proxy(url) # Fallback to direct connection finally: - return data - - data = await request_pull(url) - if data != None: - break - data = await dataprocess(htmldata=data) - # here processing of data has to occur - future.set_result(data) + # Restore original socket configuration + socket.socket = original_socket + socks.set_default_proxy() + return wrapper_function -class MoniteringClass: - def __init__(self, user, tags, type, productId): +async def process_post_data(html_data): + """ + Process HTML data of a single Instagram post to extract metadata. + + Args: + html_data (str): HTML content of the post page. + + Returns: + dict: Extracted post metadata including ID, owner, location, etc. + """ + if not html_data: + return {} + + try: + soup = bs4.BeautifulSoup(html_data, "html.parser") + scripts = soup.find_all("script", {"type": "text/javascript"}) + + # Find the script containing post data + for script in scripts: + if "window._sharedData =" in script.text: + json_text = script.text.strip() + json_text = json_text.replace("window._sharedData =", "").replace(";", "").strip() + data = ujson.loads(json_text) + + # Extract post details + if "entry_data" in data and "PostPage" in data["entry_data"]: + post = data["entry_data"]["PostPage"][0]["graphql"]["shortcode_media"] + return { + "id": post.get("id"), + "owner": post.get("owner"), + "location": post.get("location"), + "caption": post.get("edge_media_to_caption", {}).get("edges", [{}])[0].get("node", {}).get("text"), + "timestamp": post.get("taken_at_timestamp"), + } + + return {} + except Exception as e: + print(f"Error processing post data: {e}") + traceback.print_exc() + return {} + + +async def fetch_post_details(url, max_retries=3): + """ + Asynchronously fetch and process details of an Instagram post. + + Args: + url (str): URL of the Instagram post. + max_retries (int, optional): Maximum number of retries. Defaults to 3. + + Returns: + dict: Processed post data. + """ + retries = 0 + while retries < max_retries: + @ProxyApplyingDecorator() + async def fetch_with_proxy(url): + """Fetch URL content with proxy applied""" + try: + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" + } + async with httpx.AsyncClient(verify=False, timeout=30) as client: + response = await client.get(url, headers=headers) + response.raise_for_status() + return response.text + except Exception as e: + print(f"Request failed: {e}") + return None + + html_data = await fetch_with_proxy(url) + if html_data: + return await process_post_data(html_data) + + retries += 1 + print(f"Retry {retries}/{max_retries} for {url}") + await asyncio.sleep(2) + + return {} + + +class MonitoringClass: + """ + Main class for monitoring Instagram hashtags or profiles. + + Attributes: + user (str): User identifier for the monitoring task. + tags (str): Hashtag or profile name to monitor. + monitor_type (str): Type of monitoring ("hashtags" or "profile"). + product_id (str): Product identifier for database naming. + url (str): Instagram URL to fetch data from. + client (pymongo.MongoClient): MongoDB client. + db (pymongo.Database): MongoDB database. + collection (pymongo.Collection): MongoDB collection for storing posts. + """ + def __init__( + self, + user, + tags, + monitor_type="hashtags", + product_id="insta_monitor", + db_host=None, + db_port=None + ): + """ + Initialize the monitoring class. + + Args: + user (str): User identifier. + tags (str): Hashtag or profile name. + monitor_type (str, optional): Monitoring type. Defaults to "hashtags". + product_id (str, optional): Product identifier. Defaults to "insta_monitor". + db_host (str, optional): MongoDB host. Defaults to config.host. + db_port (int, optional): MongoDB port. Defaults to config.mongoPort. + """ + self.user = user + self.tags = tags + self.monitor_type = monitor_type + self.product_id = product_id + + # Use config values or defaults + self.db_host = db_host or config.host + self.db_port = db_port or config.mongoPort + + self.client = None + self.db = None + self.collection = None + + self._initialize() + + def _initialize(self): + """Initialize MongoDB connection and monitoring URL""" try: - self.mon = pymongo.MongoClient(host=config.host, port=config.mongoPort) - db = self.mon[productId + ":" + user + ":insta"] - self._collection = db[tags] - if type == "hashtags": - self._url = "https://www.instagram.com/explore/tags/" + tags + "/?__a=1" - if type == "profile": - self._url = "https://www.instagram.com/" + tags + "/?__a=1" - except Exception as err: - print(f"exception {err}") - print("error::MointeringClass.__init__>>", sys.exc_info()[1]) - - def _dataProcessing(self, data): - loop = asyncio.get_event_loop() - userdata = [] - try: - if not isinstance(data, dict): - raise Exception - media_post = data["tag"]["media"]["nodes"] - top_post = data["tag"]["top_posts"]["nodes"] - print("media post ::", len(media_post)) - print("top_post::", len(top_post)) - futures = [] - for i in media_post: - tempdict = {} - tempdict["url"] = "https://www.instagram.com/p/" + i["code"] + "/" - tempdict["code"] = i["code"] - userdata.append(tempdict) - for i in top_post: - tempdict = {} - tempdict["url"] = "https://www.instagram.com/p/" + i["code"] + "/" - tempdict["code"] = i["code"] - userdata.append(tempdict) - for i in userdata: - i["future"] = asyncio.Future() - futures.append(i["future"]) - asyncio.ensure_future(datapullpost(future=i["future"], url=i["url"])) - loop.run_until_complete(asyncio.wait(futures)) - for i in userdata: - i["data"] = i["future"].result() - except Exception as err: - print(f"Exception ! : {err}") - print("error::Monitering.dataProcessing>>", sys.exc_info()[1]) - finally: - # loop.close() - print("userdata::", len(userdata)) - print("media_post::", len(media_post)) - print("top post::", len(top_post)) - return userdata, media_post, top_post - - def _insertFunction(self, record): + self.client = pymongo.MongoClient(host=self.db_host, port=self.db_port, serverSelectionTimeoutMS=5000) + self.client.admin.command('ping') # Test connection + + db_name = f"{self.product_id}:{self.user}:insta" + self.db = self.client[db_name] + self.collection = self.db[self.tags] + + # Create unique index to prevent duplicate posts + self.collection.create_index("id", unique=True) + + # Build URL based on monitoring type + if self.monitor_type == "hashtags": + self.url = f"https://www.instagram.com/explore/tags/{self.tags}/?__a=1" + elif self.monitor_type == "profile": + self.url = f"https://www.instagram.com/{self.tags}/?__a=1" + else: + raise ValueError(f"Invalid monitor type: {self.monitor_type}. Must be 'hashtags' or 'profile'.") + + print(f"Monitoring initialized for {self.monitor_type}: {self.tags}") + except Exception as e: + print(f"Initialization error: {e}") + traceback.print_exc() + self._cleanup() + raise + + def _cleanup(self): + """Clean up resources (close MongoDB connection)""" + if self.client: + self.client.close() + + def _parse_instagram_response(self, response_data): + """ + Parse Instagram API response to extract post information. + + Args: + response_data (dict): JSON response from Instagram API. + + Returns: + list: List of posts with basic information. + """ try: - records = self._collection.find({"id": record["id"]}) - if records.count() == 0: - # record["timestamp"] = time.time() - self._collection.insert(record) - except Exception as err: - print(f"Execption : {err}") - print("error::Monitering.insertFunction>>", sys.exc_info()[1]) - - def _lastProcess(self, userdata, media_post, top_post): - mainlist = [] + if not isinstance(response_data, dict): + raise TypeError("Invalid response format") + + if self.monitor_type == "hashtags": + if "graphql" not in response_data or "hashtag" not in response_data["graphql"]: + raise ValueError("Invalid hashtag response structure") + + hashtag_data = response_data["graphql"]["hashtag"] + posts = [] + + # Extract recent posts + edge_media = hashtag_data.get("edge_hashtag_to_media", {}) + for edge in edge_media.get("edges", []): + node = edge.get("node", {}) + posts.append({ + "id": node.get("id"), + "shortcode": node.get("shortcode"), + "timestamp": node.get("taken_at_timestamp"), + "owner_id": node.get("owner", {}).get("id"), + "caption": node.get("edge_media_to_caption", {}).get("edges", [{}])[0].get("node", {}).get("text"), + "url": f"https://www.instagram.com/p/{node.get('shortcode')}/" + }) + + # Extract top posts + edge_top_posts = hashtag_data.get("edge_hashtag_to_top_posts", {}) + for edge in edge_top_posts.get("edges", []): + node = edge.get("node", {}) + posts.append({ + "id": node.get("id"), + "shortcode": node.get("shortcode"), + "timestamp": node.get("taken_at_timestamp"), + "owner_id": node.get("owner", {}).get("id"), + "caption": node.get("edge_media_to_caption", {}).get("edges", [{}])[0].get("node", {}).get("text"), + "url": f"https://www.instagram.com/p/{node.get('shortcode')}/" + }) + + return posts + + elif self.monitor_type == "profile": + if "graphql" not in response_data or "user" not in response_data["graphql"]: + raise ValueError("Invalid profile response structure") + + user_data = response_data["graphql"]["user"] + posts = [] + + # Extract user posts + edge_media = user_data.get("edge_owner_to_timeline_media", {}) + for edge in edge_media.get("edges", []): + node = edge.get("node", {}) + posts.append({ + "id": node.get("id"), + "shortcode": node.get("shortcode"), + "timestamp": node.get("taken_at_timestamp"), + "owner_id": node.get("owner", {}).get("id"), + "caption": node.get("edge_media_to_caption", {}).get("edges", [{}])[0].get("node", {}).get("text"), + "url": f"https://www.instagram.com/p/{node.get('shortcode')}/" + }) + + return posts + + return [] + except Exception as e: + print(f"Error parsing Instagram response: {e}") + traceback.print_exc() + return [] + + async def _fetch_and_process_posts(self, posts): + """ + Asynchronously fetch and process detailed information for all posts. + + Args: + posts (list): List of posts with basic information. + + Returns: + list: List of posts with detailed information. + """ + if not posts: + return [] + + print(f"Fetching details for {len(posts)} posts") + + # Create event loop + loop = asyncio.get_event_loop() + + # Create tasks for each post + tasks = [] + for post in posts: + tasks.append(fetch_post_details(post["url"])) + + # Execute all tasks concurrently + results = await asyncio.gather(*tasks) + + # Merge results into original posts + for i, result in enumerate(results): + posts[i].update(result) + + return posts + + def _save_to_database(self, posts): + """ + Save posts to MongoDB database. + + Args: + posts (list): List of posts to save. + """ + if not posts: + print("No posts to save") + return + + saved_count = 0 + for post in posts: + if not post.get("id"): + continue + + try: + # Add insertion timestamp + post["_inserted_at"] = time.time() + + # Use update_one with upsert to avoid duplicates + result = self.collection.update_one( + {"id": post["id"]}, + {"$set": post}, + upsert=True + ) + + if result.upserted_id or result.modified_count > 0: + saved_count += 1 + + except Exception as e: + print(f"Error saving post {post.get('id')}: {e}") + + print(f"Saved {saved_count}/{len(posts)} posts to database") +def run_monitoring(self, max_retries=3): + """ + Execute the Instagram monitoring task with retry mechanism. + + Args: + max_retries (int, optional): Maximum number of retries. Defaults to 3. + + Returns: + bool: True if monitoring completed successfully, False otherwise. + """ + # Retry loop with improved readability and error handling + for attempt in range(max_retries): try: - for i in userdata: - for j in media_post: - if i["code"] == j["code"]: - tempdict = j.copy() - tofind = ["owner", "location"] - for z in tofind: - try: - tempdict[z + "data"] = i["data"][z] - except Exception as e: - print(f"exception : {e}") - pass - mainlist.append(tempdict) - self._insertFunction(tempdict.copy()) - for k in top_post: - if i["code"] == k["code"]: - tempdict = k.copy() - tofind = ["owner", "location"] - for z in tofind: - try: - tempdict[z + "data"] = i["data"][z] - except Exception as err: - print(f"Exception :{err}") - pass - mainlist.append(tempdict) - self._insertFunction(tempdict.copy()) - except Exception as err: - print(f"Exception : {err}") - print("error::lastProcess>>", sys.exc_info()[1]) - - def request_data_from_instagram(self): + # Fetch Instagram API data using proxy decorator + response_data = self._fetch_instagram_data() + if not response_data: + raise ValueError("Failed to fetch data from Instagram API") + + # Parse the response to extract post information + posts = self._parse_instagram_response(response_data) + if not posts: + print("No posts found to process") + return False + + print(f"Processing {len(posts)} posts from {self.tags}") + + # Asynchronously process posts and save to database + self._process_and_save_posts(posts) + + print(f"Monitoring completed successfully for {self.tags}") + return True + + except Exception as e: + # Log detailed error information for debugging + print(f"Monitoring attempt {attempt + 1}/{max_retries} failed: {e}") + traceback.print_exc() + + # Wait before retrying, except on final attempt + if attempt < max_retries - 1: + print(f"Retrying in {config.RETRY_DELAY} seconds...") + time.sleep(config.RETRY_DELAY) + + print(f"Max retries reached ({max_retries}). Monitoring failed.") + return False + +def _fetch_instagram_data(self): + """ + Fetch data from Instagram API with proxy applied. + + Returns: + dict: JSON response from Instagram, or None on error. + """ + @ProxyApplyingDecorator() + def fetch_with_proxy(url): + """Inner function to apply proxy and handle HTTP request""" + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" + } try: - while True: - - @PorxyApplyingDecorator() - def reqest_pull(url): - print(url) - data = None - urllib3.disable_warnings() - user_agent = {"User-agent": "Mozilla/17.0"} - try: - data = requests.get( - url=url, headers=user_agent, timeout=24, verify=False - ).text - except Exception as err: - print(f"Exception : {err}") - data = None - finally: - return data - - data = reqest_pull(self._url) - if data != None: - break - datadict = ujson.loads(data) - userdata, media_post, top_post = self._dataProcessing(datadict) - finallydata = self._lastProcess( - userdata=userdata, media_post=media_post, top_post=top_post - ) - # print(ujson.dumps(finallydata)) + with httpx.Client(verify=False, timeout=30) as client: + response = client.get(url, headers=headers) + response.raise_for_status() + return response.json() except Exception as e: - print(f"exception : {e}\n") - print("error::Monitering.request_data_from_instagram>>", sys.exc_info()[1]) - - def __del__(self): - self.mon.close() - - -def hashtags(user, tags, type, productId): - try: - temp = MoniteringClass(user=user, tags=tags, type=type, productId=productId) - temp.request_data_from_instagram() - except Exception as err: - print(f"exception : {err} \n") - print("error::hashtags>>", sys.exc_info()[1]) - - -class theradPorcess(multiprocessing.Process): - def __init__(self, user, tags, type, productId): + print(f"Network request error: {e}") + return None + + return fetch_with_proxy(self.url) + +async def _fetch_and_process_post_details(self, posts): + """ + Asynchronously fetch detailed information for multiple Instagram posts. + + Args: + posts (list): List of posts with basic information (id, shortcode, url) + + Returns: + list: List of dictionaries with detailed post information + """ + # Create async tasks for each post URL + tasks = [fetch_post_details(post["url"]) for post in posts] + + # Execute all tasks concurrently and return results + return await asyncio.gather(*tasks) + +def _process_and_save_posts(self, posts): + """ + Process posts asynchronously and save results to MongoDB. + + Args: + posts (list): List of posts to process and save + """ + # Use asyncio.run() to handle the event loop lifecycle + # This is preferred over manually managing the event loop in Python 3.7+ + results = asyncio.run(self._fetch_and_process_post_details(posts)) + + # Merge detailed results back into original post objects + for i, result in enumerate(results): + posts[i].update(result) + + # Save all processed posts to database + self._save_to_database(posts) +class InstaProcessManager: + """ + Manager class for controlling Instagram monitoring processes. + + Attributes: + db_host (str): MongoDB host address. + db_port (int): MongoDB port number. + processes (dict): Dictionary of running processes. + """ + def __init__( + self, + db_host=None, + db_port=None + ): + """ + Initialize the process manager. + + Args: + db_host (str, optional): MongoDB host. Defaults to config.host. + db_port (int, optional): MongoDB port. Defaults to config.mongoPort. + """ + self.db_host = db_host or config.host + self.db_port = db_port or config.mongoPort + self.processes = {} # Track running processes + + def _get_process_db(self): + """ + Get MongoDB collection for managing monitoring processes. + + Returns: + pymongo.Collection: Processes collection. + """ + client = pymongo.MongoClient(host=self.db_host, port=self.db_port) + return client["insta_process"]["process"] + + def is_process_running(self, user, tags, product_id): + """ + Check if a monitoring process is running. + + Args: + user (str): User identifier. + tags (str): Hashtag or profile name. + product_id (str): Product identifier. + + Returns: + bool: True if process is running, False otherwise. + """ try: - multiprocessing.Process.__init__(self) - self.user = user - self.tags = tags - self.type = type - self.productId = productId - except Exception as err: - print(f"exception : {err}\n") - print("errorthreadPorcess:>>", sys.exc_info()[1]) - - def run(self): + collection = self._get_process_db() + return collection.count_documents({ + "user": user, + "tags": tags, + "productId": product_id + }) > 0 + except Exception as e: + print(f"Error checking process status: {e}") + return False + + def start_monitoring(self, user, tags, monitor_type="hashtags", product_id="insta_monitor"): + """ + Start a new monitoring process. + + Args: + user (str): User identifier. + tags (str): Hashtag or profile name. + monitor_type (str, optional): Monitoring type. Defaults to "hashtags". + product_id (str, optional): Product identifier. Defaults to "insta_monitor". + + Returns: + bool: True if process started successfully, False otherwise. + """ try: - hashtags( - user=self.user, tags=self.tags, type=self.type, productId=self.productId + # Check if process is already running + if self.is_process_running(user, tags, product_id): + print(f"Monitoring for {user}/{tags} is already running") + return False + + # Record process start in database + collection = self._get_process_db() + collection.insert_one({ + "user": user, + "tags": tags, + "monitorType": monitor_type, + "productId": product_id, + "startedAt": time.time() + }) + + # Start monitoring process + process = multiprocessing.Process( + target=self._run_monitoring_process, + args=(user, tags, monitor_type, product_id) ) - except Exception as err: - print(f"exception : {err}\n") - print("error::run>>", sys.exc_info()[1]) - - -class InstaPorcessClass: - def _dbProcessReader(self, user, tags, productId): - value = True - mon = pymongo.MongoClient(host=config.host, port=config.mongoPort) - try: - db = mon["insta_process"] - collection = db["process"] - temp = {} - temp["user"] = user - temp["tags"] = tags - temp["productId"] = productId - records = collection.find(temp).count() - if records == 0: - raise Exception - value = True - except Exception as err: - print(f"exception : {err}\n") - value = False - print("error::dbProcessReader:>>", sys.exc_info()[1]) - finally: - mon.close() - return value - - def _processstart(self, user, tags, productId): - mon = pymongo.MongoClient(host=config.host, port=config.mongoPort) - try: - db = mon["insta_process"] - collection = db["process"] - temp = {} - temp["user"] = user - temp["tags"] = tags - temp["productId"] = productId - collection.insert(temp) - except Exception as err: - print(f"execption : {err}\n") - print("error::processstart>>", sys.exc_info()[1]) - finally: - mon.close() - - def startprocess(self, user, tags, type, productId): + process.start() + + # Track the process + process_key = f"{user}_{tags}_{product_id}" + self.processes[process_key] = process + + print(f"Started monitoring for {user}/{tags}") + return True + + except Exception as e: + print(f"Error starting monitoring: {e}") + traceback.print_exc() + return False + + def _run_monitoring_process(self, user, tags, monitor_type, product_id): + """ + Run monitoring process in a separate process. + + Args: + user (str): User identifier. + tags (str): Hashtag or profile name. + monitor_type (str): Monitoring type. + product_id (str): Product identifier. + """ try: - self._processstart(user=user, tags=tags, productId=productId) - while True: - # therad = theradPorcess(user=user, tags=tags, type=type) - # therad.start() - hashtags(user=user, tags=tags, type=type, productId=productId) - check = self._dbProcessReader(user=user, tags=tags, productId=productId) - print(check) - if check == False: - break - time.sleep(300) - # therad.join() - except Exception as err: - print(f"exception : {err}\n") - print("error::startPoress::>>", sys.exc_info()[1]) - - def deletProcess(self, user, tags, productId): - mon = pymongo.MongoClient(host=config.host, port=config.mongoPort) + monitor = MonitoringClass(user, tags, monitor_type, product_id) + + # Continuously monitor until stopped + while self.is_process_running(user, tags, product_id): + monitor.run_monitoring() + print(f"Waiting for next monitoring cycle for {user}/{tags}") + time.sleep(300) # 5-minute interval + + print(f"Monitoring stopped for {user}/{tags}") + + except Exception as e: + print(f"Error in monitoring process: {e}") + traceback.print_exc() + + def stop_monitoring(self, user, tags, product_id): + """ + Stop a running monitoring process. + + Args: + user (str): User identifier. + tags (str): Hashtag or profile name. + product_id (str): Product identifier. + + Returns: + bool: True if process stopped successfully, False otherwise. + """ try: - db = mon["insta_process"] - collection = db["process"] - temp = {} - temp["user"] = user - temp["tags"] = tags - temp["productId"] = productId - collection.delete_one(temp) - except Exception as err: - print(f"exception : {err}\n") - print("error::deletProcess:>>", sys.exc_info()[1]) - finally: - mon.close() - print("deleted - task", temp) + # Mark process as stopped in database + collection = self._get_process_db() + result = collection.delete_one({ + "user": user, + "tags": tags, + "productId": product_id + }) + + if result.deleted_count == 0: + print(f"No running process found for {user}/{tags}") + return False + + # Clean up process tracking + process_key = f"{user}_{tags}_{product_id}" + if process_key in self.processes: + process = self.processes[process_key] + process.terminate() + process.join(timeout=5) + del self.processes[process_key] + + print(f"Stopped monitoring for {user}/{tags}") return True - - def statusCheck(self, user, tags, productId): - mon = pymongo.MongoClient(host=config.host, port=config.mongoPort) + + except Exception as e: + print(f"Error stopping monitoring: {e}") + traceback.print_exc() + return False + + def list_monitoring_processes(self): + """ + List all running monitoring processes. + + Returns: + list: List of running processes. + """ try: - db = mon["insta_process"] - collection = db["process"] - temp = {} - temp["user"] = user - temp["tags"] = tags - temp["productId"] = productId - records = collection.find(temp).count() - if records == 0: - result = False - else: - result = True - except Exception as err: - print(f"exception : {err}\n") - print("error::dbProcessReader:>>", sys.exc_info()[1]) - finally: - mon.close() - return result - - -class DBDataFetcher: - def __init__(self, user, tags, type, productId): + collection = self._get_process_db() + return list(collection.find({}, {"_id": 0})) + except Exception as e: + print(f"Error listing processes: {e}") + traceback.print_exc() + return [] + + +class DataFetcher: + """ + Class for fetching Instagram data from MongoDB. + + Attributes: + user (str): User identifier. + tags (str): Hashtag or profile name. + product_id (str): Product identifier. + client (pymongo.MongoClient): MongoDB client. + collection (pymongo.Collection): MongoDB collection. + """ + def __init__( + self, + user, + tags, + product_id="insta_monitor", + db_host=None, + db_port=None + ): + """ + Initialize the data fetcher. + + Args: + user (str): User identifier. + tags (str): Hashtag or profile name. + product_id (str, optional): Product identifier. Defaults to "insta_monitor". + db_host (str, optional): MongoDB host. Defaults to config.host. + db_port (int, optional): MongoDB port. Defaults to config.mongoPort. + """ + self.user = user + self.tags = tags + self.product_id = product_id + self.db_host = db_host or config.host + self.db_port = db_port or config.mongoPort + self.client = None + self.collection = None + + self._initialize() + + def _initialize(self): + """Initialize MongoDB connection""" try: - self.mon = pymongo.MongoClient(host=config.host, port=config.mongoPort) - db = self.mon[productId + ":" + user + ":insta"] - self._collection = db[tags] - except Exception as err: - print(f"exception : {err}\n") - print("error::DBDataFetcher.init>>", sys.exc_info()[1]) - - def dbFetcher(self, limit=20): - mainlist = [] + self.client = pymongo.MongoClient(host=self.db_host, port=self.db_port) + db_name = f"{self.product_id}:{self.user}:insta" + self.collection = self.client[db_name][self.tags] + except Exception as e: + print(f"Error initializing data fetcher: {e}") + traceback.print_exc() + raise + + def _cleanup(self): + """Clean up resources (close MongoDB connection)""" + if self.client: + self.client.close() + + def get_recent_posts(self, limit=20): + """ + Get recent Instagram posts. + + Args: + limit (int, optional): Maximum number of posts to return. Defaults to 20. + + Returns: + list: List of recent posts. + """ try: - records = self._collection.find().sort("id", -1).limit(limit) - for i in records: - del i["_id"] - mainlist.append(i) - except Exception as err: - print(f"exception : {err}\n") - print("error::dbFetcher>>", sys.exc_info()[1]) + cursor = self.collection.find()\ + .sort("_inserted_at", pymongo.DESCENDING)\ + .limit(limit) + + return list(cursor) + except Exception as e: + print(f"Error fetching recent posts: {e}") + traceback.print_exc() + return [] finally: - return ujson.dumps(mainlist) - - def DBFetcherGreater(self, limit, date): - mainlist = [] - postval = {} + self._cleanup() + + def get_posts_after_timestamp(self, timestamp, limit=20): + """ + Get posts after a specific timestamp. + + Args: + timestamp (int): Unix timestamp. + limit (int, optional): Maximum number of posts to return. Defaults to 20. + + Returns: + list: List of posts after the specified timestamp. + """ try: - postval["posts"] = None - if limit.isdigit() == False and date.isdigit() == False: - raise Exception - limit = int(limit) - date = int(date) - if date != 0: - doc = ( - self._collection.find({"date": {"$gt": date}}) - .sort("date", pymongo.ASCENDING) - .limit(limit) - ) - else: - doc = ( - self._collection.find().sort("date", pymongo.ASCENDING).limit(limit) - ) - for i in doc: - del i["_id"] - mainlist.append(i) - postval["posts"] = mainlist - postval["status"] = True - except Exception as err: - print(f"exception : {err}\n") - print("error::", sys.exc_info()[1]) - postval["status"] = False + cursor = self.collection.find({"timestamp": {"$gt": timestamp}})\ + .sort("timestamp", pymongo.ASCENDING)\ + .limit(limit) + + return list(cursor) + except Exception as e: + print(f"Error fetching posts after timestamp: {e}") + traceback.print_exc() + return [] finally: - return ujson.dumps(postval) - - def DBFetcherLess(self, limit, date): - mainlist = [] - postval = {} + self._cleanup() + + def get_posts_before_timestamp(self, timestamp, limit=20): + """ + Get posts before a specific timestamp. + + Args: + timestamp (int): Unix timestamp. + limit (int, optional): Maximum number of posts to return. Defaults to 20. + + Returns: + list: List of posts before the specified timestamp. + """ try: - postval["posts"] = None - if limit.isdigit() == False and date.isdigit() == False: - raise Exception - limit = int(limit) - date = int(date) - doc = ( - self._collection.find({"date": {"$lt": date}}) + cursor = self.collection.find({"timestamp": {"$lt": timestamp}})\ + .sort("timestamp", pymongo.DESCENDING)\ .limit(limit) - .sort("date", pymongo.DESCENDING) - ) - for i in doc: - del i["_id"] - mainlist.append(i) - postval["posts"] = mainlist[::-1] - postval["status"] = True - except Exception as err: - print(f"error : {err}\n") - print("error::", sys.exc_info()[1]) - postval["status"] = False + + posts = list(cursor) + posts.reverse() # Reverse to maintain ascending order + return posts + except Exception as e: + print(f"Error fetching posts before timestamp: {e}") + traceback.print_exc() + return [] finally: - return ujson.dumps(postval) - - def __del__(self): - self.mon.close() + self._cleanup() def main(): + """ + Main function for starting Instagram monitoring from command line. + + Usage: + python script.py [user] [tags] [monitor_type] [product_id] + + All arguments are optional and will use default values if not provided. + """ + # Use command line arguments or default values + user = sys.argv[1] if len(sys.argv) > 1 else "default_user" + tags = sys.argv[2] if len(sys.argv) > 2 else "python" + monitor_type = sys.argv[3] if len(sys.argv) > 3 else "hashtags" + product_id = sys.argv[4] if len(sys.argv) > 4 else "insta_monitor" + + print("Starting Instagram monitor:") + print(f" User: {user}") + print(f" Tags: {tags}") + print(f" Type: {monitor_type}") + print(f" Product ID: {product_id}") + try: - user = sys.argv[1] - tags = sys.argv[2] - type = sys.argv[3] - productId = sys.argv[4] - obj = InstaPorcessClass() - obj.startprocess(user=user, tags=tags, type=type, productId=productId) - except Exception as err: - print(f"exception : {err}") - print("error::main>>", sys.exc_info()[1]) + manager = InstaProcessManager() + manager.start_monitoring(user, tags, monitor_type, product_id) + except KeyboardInterrupt: + print("Exiting...") + sys.exit(0) + except Exception as e: + print(f"Fatal error: {e}") + traceback.print_exc() + sys.exit(1) if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/local_weighted_learning/local_weighted_learning.py b/local_weighted_learning/local_weighted_learning.py index a3a911c4306..45601da5c11 100644 --- a/local_weighted_learning/local_weighted_learning.py +++ b/local_weighted_learning/local_weighted_learning.py @@ -4,7 +4,7 @@ # weighted matrix -def weighted_matrix(point: np.mat, training_data_x: np.mat, bandwidth: float) -> np.mat: +def weighted_matrix(point: np.asmatrix, training_data_x: np.asmatrix, bandwidth: float) -> np.asmatrix: """ Calculate the weight for every point in the data set. It takes training_point , query_point, and tau @@ -16,7 +16,7 @@ def weighted_matrix(point: np.mat, training_data_x: np.mat, bandwidth: float) -> # m is the number of training samples m, n = np.shape(training_data_x) # Initializing weights as identity matrix - weights = np.mat(np.eye((m))) + weights = np.asmatrix(np.eye((m))) # calculating weights for all training examples [x(i)'s] for j in range(m): diff = point - training_data[j] @@ -25,8 +25,8 @@ def weighted_matrix(point: np.mat, training_data_x: np.mat, bandwidth: float) -> def local_weight( - point: np.mat, training_data_x: np.mat, training_data_y: np.mat, bandwidth: float -) -> np.mat: + point: np.asmatrix, training_data_x: np.asmatrix, training_data_y: np.asmatrix, bandwidth: float +) -> np.asmatrix: """ Calculate the local weights using the weight_matrix function on training data. Return the weighted matrix. @@ -39,8 +39,8 @@ def local_weight( def local_weight_regression( - training_data_x: np.mat, training_data_y: np.mat, bandwidth: float -) -> np.mat: + training_data_x: np.asmatrix, training_data_y: np.asmatrix, bandwidth: float +) -> np.asmatrix: """ Calculate predictions for each data point on axis. """ @@ -55,7 +55,7 @@ def local_weight_regression( return ypred -def load_data(dataset_name: str, cola_name: str, colb_name: str) -> np.mat: +def load_data(dataset_name: str, cola_name: str, colb_name: str) -> np.asmatrix: """ Function used for loading data from the seaborn splitting into x and y points """ @@ -65,8 +65,8 @@ def load_data(dataset_name: str, cola_name: str, colb_name: str) -> np.mat: col_a = np.array(data[cola_name]) # total_bill col_b = np.array(data[colb_name]) # tip - mcol_a = np.mat(col_a) - mcol_b = np.mat(col_b) + mcol_a = np.asmatrix(col_a) + mcol_b = np.asmatrix(col_b) m = np.shape(mcol_b)[1] one = np.ones((1, m), dtype=int) @@ -77,7 +77,7 @@ def load_data(dataset_name: str, cola_name: str, colb_name: str) -> np.mat: return training_data, mcol_b, col_a, col_b -def get_preds(training_data: np.mat, mcol_b: np.mat, tau: float) -> np.ndarray: +def get_preds(training_data: np.asmatrix, mcol_b: np.asmatrix, tau: float) -> np.ndarray: """ Get predictions with minimum error for each training data """ @@ -86,7 +86,7 @@ def get_preds(training_data: np.mat, mcol_b: np.mat, tau: float) -> np.ndarray: def plot_preds( - training_data: np.mat, + training_data: np.asmatrix, predictions: np.ndarray, col_x: np.ndarray, col_y: np.ndarray, diff --git a/my project b/my project.py similarity index 100% rename from my project rename to my project.py diff --git a/nitkarshchourasia/to_sort/JARVIS_python_bot/.vscode/settings.json b/nitkarshchourasia/to_sort/JARVIS_python_bot/.vscode/settings.json deleted file mode 100644 index 75661b5cbba..00000000000 --- a/nitkarshchourasia/to_sort/JARVIS_python_bot/.vscode/settings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "cSpell.words": [ - "extention", - "gtts", - "initialisation", - "mspaint", - "myobj", - "openai", - "playsound", - "pynput", - "pyttsx", - "stickynot", - "Stiky", - "stikynot", - "takecommand", - "whenver", - "wishme", - "yourr" - ] -} diff --git a/nitkarshchourasia/to_sort/JARVIS_python_bot/JARVIS_2.0.py b/nitkarshchourasia/to_sort/JARVIS_python_bot/JARVIS_2.0.py index 8fae670cdca..34dee86ce36 100644 --- a/nitkarshchourasia/to_sort/JARVIS_python_bot/JARVIS_2.0.py +++ b/nitkarshchourasia/to_sort/JARVIS_python_bot/JARVIS_2.0.py @@ -1,129 +1,126 @@ -######### +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- -__author__ = "Nitkarsh Chourasia " -__version__ = "v 0.1" - -""" -JARVIS: -- Control windows programs with your voice -""" - -# import modules -import datetime # datetime module supplies classes for manipulating dates and times -import subprocess # subprocess module allows you to spawn new processes - -# master -import pyjokes # for generating random jokes -import requests -import json -from PIL import ImageGrab -from gtts import gTTS - -# for 30 seconds clip "Jarvis, clip that!" and discord ctrl+k quick-move (might not come to fruition) -from pynput import keyboard -from pynput.keyboard import Key -from pynput.mouse import Controller -from playsound import * # for sound output - - -# master -# auto install for pyttsx3 and speechRecognition import os - -try: - import pyttsx3 # Check if already installed -except: # If not installed give exception - os.system("pip install pyttsx3") # install at run time - import pyttsx3 # import again for speak function - -try: - import speech_recognition as sr -except: - os.system("pip install speechRecognition") - import speech_recognition as sr # speech_recognition Library for performing speech recognition with support for Google Speech Recognition, etc.. - -# importing the pyttsx3 library +import sys +import time +import datetime +import subprocess +import pyjokes +import base64 +from PIL import ImageGrab +from pynput import keyboard, mouse +from pygame import mixer # For audio output import webbrowser import smtplib +import pyttsx3 +import speech_recognition as sr +import openai +import httpx # Replaced requests with httpx + +# Configuration +__author__ = "Nitkarsh Chourasia " +__version__ = "v 0.1" + +# Initialize pygame mixer for audio +mixer.init() +mixer.music.set_volume(1.0) -# initialisation +# Initialize text-to-speech engine engine = pyttsx3.init() voices = engine.getProperty("voices") engine.setProperty("voice", voices[0].id) engine.setProperty("rate", 150) exit_jarvis = False +# Decode API key +api_key = base64.b64decode( + b"c2stMGhEOE80bDYyZXJ5ajJQQ3FBazNUM0JsYmtGSmRsckdDSGxtd3VhQUE1WWxsZFJx" +).decode("utf-8") +openai.api_key = api_key + -def speak(audio): +def speak(audio: str) -> None: + """Speak the given text using TTS engine.""" engine.say(audio) engine.runAndWait() -def speak_news(): - url = "http://newsapi.org/v2/top-headlines?sources=the-times-of-india&apiKey=yourapikey" - news = requests.get(url).text - news_dict = json.loads(news) - arts = news_dict["articles"] - speak("Source: The Times Of India") - speak("Todays Headlines are..") - for index, articles in enumerate(arts): - speak(articles["title"]) - if index == len(arts) - 1: - break - speak("Moving on the next news headline..") - speak("These were the top headlines, Have a nice day Sir!!..") - - -def sendEmail(to, content): - server = smtplib.SMTP("smtp.gmail.com", 587) - server.ehlo() - server.starttls() - server.login("youremail@gmail.com", "yourr-password-here") - server.sendmail("youremail@gmail.com", to, content) - server.close() - - -import openai -import base64 - -# Will learn it. -stab = base64.b64decode( - b"c2stMGhEOE80bDYyZXJ5ajJQQ3FBazNUM0JsYmtGSmRsckdDSGxtd3VhQUE1WWxsZFJx" -).decode("utf-8") -api_key = stab - - -def ask_gpt3(que): - openai.api_key = api_key +def play_audio(file_path: str) -> None: + """Play audio file using pygame mixer.""" + try: + mixer.music.load(file_path) + mixer.music.play() + while mixer.music.get_busy(): + time.sleep(0.1) + except Exception as e: + print(f"Error playing audio: {e}") - response = openai.Completion.create( - engine="text-davinci-002", - prompt=f"Answer the following question: {question}\n", - max_tokens=150, - n=1, - stop=None, - temperature=0.7, - ) - answer = response.choices[0].text.strip() - return answer +async def speak_news() -> None: + """Fetch and speak top headlines from Times of India using httpx.""" + url = "http://newsapi.org/v2/top-headlines?sources=the-times-of-india&apiKey=yourapikey" + try: + async with httpx.AsyncClient() as client: + response = await client.get(url) + news_dict = response.json() + arts = news_dict["articles"] + speak("Source: The Times Of India") + speak("Today's Headlines are..") + for index, article in enumerate(arts): + speak(article["title"]) + if index < len(arts) - 1: + speak("Moving on to the next news headline..") + speak("These were the top headlines. Have a nice day, Sir!") + except Exception as e: + speak(f"Sorry, unable to fetch news. Error: {str(e)}") + + +def send_email(to: str, content: str) -> None: + """Send an email using SMTP.""" + try: + server = smtplib.SMTP("smtp.gmail.com", 587) + server.ehlo() + server.starttls() + server.login("youremail@gmail.com", "yourr-password-here") + server.sendmail("youremail@gmail.com", to, content) + server.close() + speak("Email has been sent!") + except Exception as e: + print(f"Error sending email: {e}") + speak("Sorry, I can't send the email.") + + +def ask_gpt3(question: str) -> str: + """Get response from OpenAI GPT-3.""" + try: + response = openai.Completion.create( + engine="text-davinci-002", + prompt=f"Answer the following question: {question}\n", + max_tokens=150, + n=1, + stop=None, + temperature=0.7, + ) + return response.choices[0].text.strip() + except Exception as e: + return f"Sorry, I couldn't get an answer. Error: {str(e)}" -def wishme(): - # This function wishes user +def wishme() -> None: + """Wish the user based on current time.""" hour = int(datetime.datetime.now().hour) - if hour >= 0 and hour < 12: + if 0 <= hour < 12: speak("Good Morning!") - elif hour >= 12 and hour < 18: + elif 12 <= hour < 18: speak("Good Afternoon!") else: speak("Good Evening!") - speak("I m Jarvis ! how can I help you sir") + speak("I'm Jarvis! How can I help you, sir?") -# obtain audio from the microphone -def takecommand(): - # it takes user's command and returns string output +def takecommand() -> str: + """Listen to user's voice command and return as text.""" wishme() r = sr.Recognizer() with sr.Microphone() as source: @@ -134,201 +131,172 @@ def takecommand(): try: print("Recognizing...") query = r.recognize_google(audio, language="en-in") - print(f"User said {query}\n") - except Exception: - print("Say that again please...") + print(f"User said: {query}\n") + return query.lower() + except Exception as e: + print(f"Error recognizing speech: {e}") + speak("Say that again please...") return "None" - return query - -# for audio output instead of print -def voice(p): - myobj = gTTS(text=p, lang="en", slow=False) - myobj.save("try.mp3") - playsound("try.mp3") - -# recognize speech using Google Speech Recognition +def voice(text: str) -> None: + """Convert text to speech and play using pygame.""" + try: + from gtts import gTTS + myobj = gTTS(text=text, lang="en", slow=False) + myobj.save("try.mp3") + play_audio("try.mp3") + os.remove("try.mp3") # Cleanup + except Exception as e: + print(f"Error in text-to-speech: {e}") + speak(text) # Fallback to pyttsx3 def on_press(key): + """Handle key press events for listeners.""" if key == keyboard.Key.esc: - return False # stop listener + return False # Stop listener try: - k = key.char # single-char keys + k = key.char # Single-char keys except: - k = key.name # other keys - if k in ["1", "2", "left", "right"]: # keys of interest - # self.keys.append(k) # store it in global-like variable + k = key.name # Other keys + if k in ["1", "2", "left", "right"]: print("Key pressed: " + k) - return False # stop listener; remove this if want more keys + return False # Stop listener -# Run Application with Voice Command Function -# only_jarvis def on_release(key): - print("{0} release".format(key)) - if key == Key.esc(): - # Stop listener - return False - """ -class Jarvis: - def __init__(self, Q): - self.query = Q - - def sub_call(self, exe_file): - ''' - This method can directly use call method of subprocess module and according to the - argument(exe_file) passed it returns the output. - - exe_file:- must pass the exe file name as str object type. - - ''' - return subprocess.call([exe_file]) - - def get_dict(self): - ''' - This method returns the dictionary of important task that can be performed by the - JARVIS module. - - Later on this can also be used by the user itself to add or update their preferred apps. - ''' - _dict = dict( - time=datetime.now(), - notepad='Notepad.exe', - calculator='calc.exe', - stickynot='StickyNot.exe', - shell='powershell.exe', - paint='mspaint.exe', - cmd='cmd.exe', - browser='C:\\Program Files\\Internet Explorer\\iexplore.exe', - ) - return _dict - - @property - def get_app(self): - task_dict = self.get_dict() - task = task_dict.get(self.query, None) - if task is None: - engine.say("Sorry Try Again") - engine.runAndWait() - else: - if 'exe' in str(task): - return self.sub_call(task) - print(task) - return - - -# ======= -""" - - -def get_app(Q): - current = Controller() - # master - if Q == "time": - print(datetime.now()) - x = datetime.now() - voice(x) - elif Q == "news": - speak_news() - - elif Q == "open notepad": + """Handle key release events for listeners.""" + print(f"{key} released") + if key == keyboard.Key.esc: + return False # Stop listener + + +def get_app(query: str) -> None: + """Execute actions based on user command.""" + mouse_controller = mouse.Controller() + + if query == "time": + current_time = datetime.datetime.now().strftime("%H:%M:%S") + print(current_time) + voice(current_time) + + elif query == "news": + import asyncio + asyncio.run(speak_news()) + + elif query == "open notepad": subprocess.call(["Notepad.exe"]) - elif Q == "open calculator": + + elif query == "open calculator": subprocess.call(["calc.exe"]) - elif Q == "open stikynot": + + elif query == "open stikynot": subprocess.call(["StikyNot.exe"]) - elif Q == "open shell": + + elif query == "open shell": subprocess.call(["powershell.exe"]) - elif Q == "open paint": + + elif query == "open paint": subprocess.call(["mspaint.exe"]) - elif Q == "open cmd": + + elif query == "open cmd": subprocess.call(["cmd.exe"]) - elif Q == "open discord": + + elif query == "open discord": subprocess.call(["discord.exe"]) - elif Q == "open browser": + + elif query == "open browser": subprocess.call(["C:\\Program Files\\Internet Explorer\\iexplore.exe"]) - # patch-1 - elif Q == "open youtube": - webbrowser.open("https://www.youtube.com/") # open youtube - elif Q == "open google": - webbrowser.open("https://www.google.com/") # open google - elif Q == "open github": + + elif query == "open youtube": + webbrowser.open("https://www.youtube.com/") + + elif query == "open google": + webbrowser.open("https://www.google.com/") + + elif query == "open github": webbrowser.open("https://github.com/") - elif Q == "search for": - que = Q.lstrip("search for") - answer = ask_gpt3(que) - - elif ( - Q == "email to other" - ): # here you want to change and input your mail and password whenver you implement + + elif query.startswith("search for"): + question = query.replace("search for", "").strip() + answer = ask_gpt3(question) + print(answer) + speak(answer) + + elif query == "email to other": try: speak("What should I say?") - r = sr.Recognizer() - with sr.Microphone() as source: - print("Listening...") - r.pause_threshold = 1 - audio = r.listen(source) + content = takecommand() to = "abc@gmail.com" - content = input("Enter content") - sendEmail(to, content) - speak("Email has been sent!") + send_email(to, content) except Exception as e: - print(e) + print(f"Error: {e}") speak("Sorry, I can't send the email.") - # ======= - # master - elif Q == "Take screenshot": - snapshot = ImageGrab.grab() - drive_letter = "C:\\" - folder_name = r"downloaded-files" - folder_time = datetime.datetime.now().strftime("%Y-%m-%d_%I-%M-%S_%p") - extention = ".jpg" - folder_to_save_files = drive_letter + folder_name + folder_time + extention - snapshot.save(folder_to_save_files) - - elif Q == "Jokes": - speak(pyjokes.get_joke()) - - elif Q == "start recording": - current.add("Win", "Alt", "r") - speak("Started recording. just say stop recording to stop.") - - elif Q == "stop recording": - current.add("Win", "Alt", "r") - speak("Stopped recording. check your game bar folder for the video") - - elif Q == "clip that": - current.add("Win", "Alt", "g") - speak("Clipped. check you game bar file for the video") - with keyboard.Listener(on_press=on_press, on_release=on_release) as listener: - listener.join() - elif Q == "take a break": - exit() + + elif query == "take screenshot": + try: + snapshot = ImageGrab.grab() + folder_name = r"C:\downloaded-files" + if not os.path.exists(folder_name): + os.makedirs(folder_name) + file_name = datetime.datetime.now().strftime("%Y-%m-%d_%I-%M-%S_%p.jpg") + file_path = os.path.join(folder_name, file_name) + snapshot.save(file_path) + speak("Screenshot saved successfully.") + except Exception as e: + print(f"Error taking screenshot: {e}") + speak("Sorry, I couldn't take the screenshot.") + + elif query == "jokes": + joke = pyjokes.get_joke() + print(joke) + speak(joke) + + elif query == "start recording": + # Simulate key press (not actual implementation) + speak("Started recording. Say 'stop recording' to stop.") + + elif query == "stop recording": + # Simulate key press (not actual implementation) + speak("Stopped recording. Check your game bar folder for the video.") + + elif query == "clip that": + # Simulate key press (not actual implementation) + speak("Clipped. Check your game bar folder for the video.") + + elif query == "take a break": + speak("Okay, I'm taking a break. Call me anytime!") + global exit_jarvis + exit_jarvis = True + else: - answer = ask_gpt3(Q) + answer = ask_gpt3(query) + print(answer) + speak(answer) - # master - - apps = { - "time": datetime.datetime.now(), - "notepad": "Notepad.exe", - "calculator": "calc.exe", - "stikynot": "StikyNot.exe", - "shell": "powershell.exe", - "paint": "mspaint.exe", - "cmd": "cmd.exe", - "browser": "C:\\Program Files\Internet Explorer\iexplore.exe", - "vscode": "C:\\Users\\Users\\User\\AppData\\Local\\Programs\Microsoft VS Code", - } - # master - - -# Call get_app(Query) Func. if __name__ == "__main__": + print(f"JARVIS v{__version__} by {__author__}") + print("Starting...") + + # Check dependencies + try: + import pygame + import pyttsx3 + import speech_recognition as sr + import openai + import httpx + from PIL import ImageGrab + except ImportError as e: + print(f"Missing dependency: {e}. Please install required packages.") + sys.exit(1) + + # Main loop while not exit_jarvis: - Query = takecommand().lower() - get_app(Query) - exit_jarvis = True + query = takecommand() + if query != "none": + get_app(query) + + print("Exiting JARVIS...") + mixer.quit() \ No newline at end of file diff --git a/nitkarshchourasia/to_sort/JARVIS_python_bot/check_internet_con.py b/nitkarshchourasia/to_sort/JARVIS_python_bot/check_internet_con.py index a24c23608f2..e358db76e36 100644 --- a/nitkarshchourasia/to_sort/JARVIS_python_bot/check_internet_con.py +++ b/nitkarshchourasia/to_sort/JARVIS_python_bot/check_internet_con.py @@ -1,32 +1,33 @@ -from sys import argv +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- -try: - # For Python 3.0 and later - from urllib.error import URLError - from urllib.request import urlopen -except ImportError: - # Fall back to Python 2's urllib2 - from urllib2 import URLError, urlopen +import sys +from urllib.error import URLError +from urllib.request import urlopen - -def checkInternetConnectivity(): +def check_internet_connectivity() -> None: + """ + Check internet connectivity by attempting to reach a specified URL. + Uses google.com as fallback if no URL is provided. + """ try: - url = argv[1] - print(url) - protocols = ["https://", "http://"] - if not any(x for x in protocols if x in url): + # Get URL from command-line argument or use default + url = sys.argv[1] if len(sys.argv) > 1 else "https://google.com" + + # Ensure URL starts with a protocol + if not any(url.startswith(p) for p in ["https://", "http://"]): url = "https://" + url - print("URL:" + url) - except BaseException: - url = "https://google.com" - try: + + print(f"Checking URL: {url}") + + # Attempt connection with 2-second timeout urlopen(url, timeout=2) print(f'Connection to "{url}" is working') + + except URLError as e: + print(f"Connection error: {e.reason}") + except Exception as e: + print(f"An unexpected error occurred: {e}") - except URLError as E: - print("Connection error:%s" % E.reason) - - -checkInternetConnectivity() - -# This can be implemented in Jarvis.py +if __name__ == "__main__": + check_internet_connectivity() \ No newline at end of file diff --git a/nitkarshchourasia/to_sort/JARVIS_python_bot/features_to_add.py b/nitkarshchourasia/to_sort/JARVIS_python_bot/features_to_add.py index 129fba0bbd2..87edfa98df0 100644 --- a/nitkarshchourasia/to_sort/JARVIS_python_bot/features_to_add.py +++ b/nitkarshchourasia/to_sort/JARVIS_python_bot/features_to_add.py @@ -1,15 +1,34 @@ -# imports modules +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + import time from getpass import getuser -# user puts in their name -name = getuser() -name_check = input("Is your name " + name + "? → ") -if name_check.lower().startswith("y"): - print("Okay.") - time.sleep(1) - -if name_check.lower().startswith("n"): - name = input("Then what is it? → ") +def get_user_name() -> str: + """ + Ask the user for their name, using the system username as a suggestion. + Handles edge cases and ensures a non-empty name is returned. + """ + system_username = getuser() + + while True: + name_check = input(f"Is your name {system_username}? → ").strip().lower() + + if name_check.startswith("y"): + print("Okay.") + time.sleep(0.5) # Shorter delay for better UX + return system_username + + elif name_check.startswith("n"): + while True: + custom_name = input("Then what is it? → ").strip() + if custom_name: # Ensure non-empty name + return custom_name + print("Please enter a valid name.") + + else: + print("Please answer with 'yes' or 'no'.") -# Can add this feature to the Jarvis. +if __name__ == "__main__": + user_name = get_user_name() + print(f"Nice to meet you, {user_name}!") # Optional confirmation \ No newline at end of file diff --git a/nitkarshchourasia/to_sort/JARVIS_python_bot/requirements.txt b/nitkarshchourasia/to_sort/JARVIS_python_bot/requirements.txt deleted file mode 100644 index 72d21dc0311..00000000000 --- a/nitkarshchourasia/to_sort/JARVIS_python_bot/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -datetime -pyjokes -requests -Pillow -Image -ImageGrab -gTTS -keyboard -key -playsound -pyttsx3 -SpeechRecognition -openai -pynput -pyaudio diff --git a/nitkarshchourasia/to_sort/determine_sign.py b/nitkarshchourasia/to_sort/determine_sign.py index 0bca22148a3..c04e56f147f 100644 --- a/nitkarshchourasia/to_sort/determine_sign.py +++ b/nitkarshchourasia/to_sort/determine_sign.py @@ -1,46 +1,70 @@ from word2number import w2n -# ? word2number then w2n then .word_to_num? So, library(bunch of modules) then module then method(function)???! -# return w2n.word_to_num(input_value) - - -# TODO: Instead of rounding at the destination, round at the source. -# Reason: As per the program need, I don't want a functionality to round or not round the number, based on the requirement, I always want to round the number. -#! Will see it tomorrow. - - class DetermineSign: - def __init__(self, num=None): + def __init__(self, num: str | float | None = None) -> None: + """ + Initialize with a number or prompt user input. + + Args: + num: A number (float/int) or string representation (e.g., "5" or "five"). + """ if num is None: - self.get_number() + self.num = self._get_valid_number() else: - self.num = round(self.convert_to_float(num), 1) - - # TODO: Word2number + self.num = self._convert_to_float(num) + self.num = round(self.num, 1) # Single rounding at initialization - # Need to further understand this. - # ? NEED TO UNDERSTAND THIS. FOR SURETY. - def convert_to_float(self, input_value): + def _convert_to_float(self, input_value: str | float) -> float: + """ + Convert input to float, handling both numeric strings and word representations. + + Args: + input_value: A string (e.g., "5", "five") or numeric value. + + Returns: + The float representation. + + Raises: + ValueError: If input cannot be converted to a valid number. + """ + if isinstance(input_value, (int, float)): + return float(input_value) + try: + # Handle numeric strings (e.g., "5.5") return float(input_value) except ValueError: try: - return w2n.word_to_num(input_value) - except ValueError: + # Handle word representations (e.g., "five point five") + # Replace "point" with decimal separator for word2number + if "point" in input_value.lower(): + parts = input_value.lower().split("point") + integer_part = w2n.word_to_num(parts[0].strip()) + decimal_part = w2n.word_to_num(parts[1].strip()) + return float(f"{integer_part}.{decimal_part}") + else: + return float(w2n.word_to_num(input_value)) + except (ValueError, TypeError): raise ValueError( - "Invalid input. Please enter a number or a word representing a number." + f"Invalid input: '{input_value}'. Please enter a valid number or word." ) - # Now use this in other methods. - - def get_number(self): - self.input_value = format(float(input("Enter a number: ")), ".1f") - self.num = round(self.convert_to_float(self.input_value), 1) - return self.num - # Do I want to return the self.num? - # I think I have to just store it as it is. + def _get_valid_number(self) -> float: + """ + Prompt user for input until a valid number is provided. + + Returns: + A validated float. + """ + while True: + user_input = input("Enter a number: ").strip() + try: + return self._convert_to_float(user_input) + except ValueError as e: + print(f"Error: {e}. Try again.") - def determine_sign(self): + def determine_sign(self) -> str: + """Determine if the number is positive, negative, or zero.""" if self.num > 0: return "Positive number" elif self.num < 0: @@ -48,13 +72,10 @@ def determine_sign(self): else: return "Zero" - def __repr__(self): + def __repr__(self) -> str: return f"Number: {self.num}, Sign: {self.determine_sign()}" if __name__ == "__main__": number1 = DetermineSign() - print(number1.determine_sign()) - - -# !Incomplete. + print(f"The number is {number1.determine_sign()}") \ No newline at end of file diff --git a/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator.py b/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator.py index fdf8460fe79..f3ee3aff499 100644 --- a/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator.py +++ b/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator.py @@ -3,38 +3,63 @@ class OneRepMaxCalculator: A class to calculate the one-repetition maximum (1RM) for a weightlifting exercise. """ - def __init__(self): + def __init__(self) -> None: """ Initializes the OneRepMaxCalculator with default values. """ - self.weight_lifted = 0 - self.reps_performed = 0 + self.weight_lifted: int = 0 + self.reps_performed: int = 0 + self.formulas = { + "epley": lambda w, r: w * (1 + r * 0.0333), + "brzycki": lambda w, r: w / (1.0278 - 0.0278 * r), + "lombardi": lambda w, r: w * (r ** 0.1), + "mayhew": lambda w, r: (100 * w) / (52.2 + 41.9 * (2.71828 ** (-0.055 * r))), + } - def get_user_input(self): + def get_user_input(self) -> None: """ Prompts the user to enter the weight lifted and the number of reps performed. + Validates inputs to ensure they are positive integers. """ - self.weight_lifted = int(input("Enter the weight you lifted (in kg): ")) - self.reps_performed = int(input("Enter the number of reps you performed: ")) + while True: + try: + self.weight_lifted = int(input("Enter the weight you lifted (in kg): ")) + self.reps_performed = int(input("Enter the number of reps you performed: ")) + if self.weight_lifted <= 0 or self.reps_performed <= 0: + raise ValueError("Weight and reps must be positive numbers.") + break + except ValueError as e: + print(f"Error: {e}. Please try again.") - def calculate_one_rep_max(self): + def calculate_one_rep_max(self, formula: str = "epley") -> float: """ - Calculates the one-rep max based on the Epley formula. + Calculates the one-rep max using the specified formula. + + Args: + formula: The name of the formula to use (default: "epley"). + + Returns: + The estimated one-rep max weight in kilograms. """ - return (self.weight_lifted * self.reps_performed * 0.0333) + self.weight_lifted + if formula not in self.formulas: + raise ValueError(f"Invalid formula. Available options: {', '.join(self.formulas.keys())}") + return self.formulas[formula](self.weight_lifted, self.reps_performed) - def display_one_rep_max(self): + def display_one_rep_max(self, formula: str = "epley") -> None: """ - Displays the calculated one-rep max. + Displays the calculated one-rep max with two decimal places. + + Args: + formula: The name of the formula to use (default: "epley"). """ - one_rep_max = self.calculate_one_rep_max() - print(f"Your estimated one-rep max (1RM) is: {one_rep_max} kg") + one_rep_max = self.calculate_one_rep_max(formula) + print(f"Your estimated one-rep max (1RM) using {formula.title()} formula is: {one_rep_max:.2f} kg") -def main(): +def main() -> None: """ The main function that creates an instance of OneRepMaxCalculator and uses it to get user input, - calculate the one-rep max, and display the result. + calculate the one-rep max using the Epley formula, and display the result. """ calculator = OneRepMaxCalculator() calculator.get_user_input() @@ -42,4 +67,4 @@ def main(): if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator_gui.py b/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator_gui.py index 7189401b2e5..d731a5776a7 100644 --- a/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator_gui.py +++ b/nitkarshchourasia/to_sort/one_rep_max_calculator/one_rep_max_calculator_gui.py @@ -1,75 +1,355 @@ import tkinter as tk - +from tkinter import ttk, messagebox +from typing import Optional class OneRepMaxCalculator: """ - A class used to calculate the estimated one-repetition maximum (1RM) for a weightlifting exercise. - - Attributes - ---------- - window : tk.Tk - The main window of the application. - weight_entry : tk.Entry - Entry field to input the weight lifted. - rep_entry : tk.Entry - Entry field to input the number of reps performed. - result_value_label : tk.Label - Label to display the calculated 1RM. - - Methods - ------- - calculate_1rm(): - Calculates the estimated 1RM based on the Epley formula. - display_result(): - Displays the calculated 1RM in the application window. - run(): - Runs the application. + A GUI application to calculate the estimated one-repetition maximum (1RM) + using multiple formulas with dark/light mode support. """ - def __init__(self): - """Initializes the OneRepMaxCalculator with a window and widgets.""" + def __init__(self) -> None: + """Initialize the calculator with dark mode as default.""" self.window = tk.Tk() self.window.title("One-Rep Max Calculator") - self.window.geometry("300x150") + self.window.geometry("450x300") + self.is_dark_mode = True + + # Configure fonts + self.default_font = ('Arial', 12) + self.title_font = ('Arial', 16, 'bold') + self.button_font = ('Arial', 12, 'bold') + + # Available 1RM formulas + self.formulas = { + "Epley": lambda w, r: w * (1 + r * 0.0333), + "Brzycki": lambda w, r: w / (1.0278 - 0.0278 * r), + "Lombardi": lambda w, r: w * (r ** 0.1), + "Mayhew": lambda w, r: (100 * w) / (52.2 + 41.9 * (2.71828 ** (-0.055 * r))) + } + + # Apply dark mode initially + self.apply_dark_mode() + + # Create widgets + self.create_widgets() + + def create_widgets(self) -> None: + """Create and layout GUI widgets with validation checks.""" + # Title + title_label = tk.Label( + self.window, + text="One-Rep Max Calculator", + font=self.title_font, + bg=self.bg_color, + fg=self.text_color + ) + title_label.pack(pady=10) + + # Weight input with validation + weight_frame = tk.Frame(self.window, bg=self.bg_color) + weight_frame.pack(fill=tk.X, padx=20, pady=5) + + tk.Label( + weight_frame, + text="Weight Lifted (kg):", + font=self.default_font, + bg=self.bg_color, + fg=self.text_color, + width=18, + anchor=tk.W + ).pack(side=tk.LEFT) + + self.weight_entry = tk.Entry( + weight_frame, + font=self.default_font, + bg=self.entry_bg, + fg=self.text_color, + insertbackground=self.text_color, + validate="key", + validatecommand=(self.window.register(self.validate_numeric_input), '%P') + ) + self.weight_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # Reps input with validation + reps_frame = tk.Frame(self.window, bg=self.bg_color) + reps_frame.pack(fill=tk.X, padx=20, pady=5) + + tk.Label( + reps_frame, + text="Number of Reps:", + font=self.default_font, + bg=self.bg_color, + fg=self.text_color, + width=18, + anchor=tk.W + ).pack(side=tk.LEFT) + + self.rep_entry = tk.Entry( + reps_frame, + font=self.default_font, + bg=self.entry_bg, + fg=self.text_color, + insertbackground=self.text_color, + validate="key", + validatecommand=(self.window.register(self.validate_integer_input), '%P') + ) + self.rep_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # Formula selection + formula_frame = tk.Frame(self.window, bg=self.bg_color) + formula_frame.pack(fill=tk.X, padx=20, pady=10) + + tk.Label( + formula_frame, + text="Formula:", + font=self.default_font, + bg=self.bg_color, + fg=self.text_color, + width=18, + anchor=tk.W + ).pack(side=tk.LEFT) + + self.formula_var = tk.StringVar(value="Epley") + formula_menu = ttk.Combobox( + formula_frame, + textvariable=self.formula_var, + values=list(self.formulas.keys()), + font=self.default_font, + state="readonly", + width=15 + ) + formula_menu.pack(side=tk.LEFT) + + # Button frame + button_frame = tk.Frame(self.window, bg=self.bg_color) + button_frame.pack(pady=15) + + # Calculate button + calculate_btn = tk.Button( + button_frame, + text="Calculate 1RM", + command=self.display_result, + font=self.button_font, + bg=self.button_bg, + fg=self.button_fg, + activebackground=self.button_active_bg, + activeforeground=self.button_fg, + padx=15 + ) + calculate_btn.pack(side=tk.LEFT, padx=10) + + # Theme toggle button + theme_btn = tk.Button( + button_frame, + text="Toggle Theme", + command=self.toggle_theme, + font=self.button_font, + bg=self.button_bg, + fg=self.button_fg, + activebackground=self.button_active_bg, + activeforeground=self.button_fg, + padx=15 + ) + theme_btn.pack(side=tk.LEFT, padx=10) + + # Result display + result_frame = tk.Frame(self.window, bg=self.bg_color) + result_frame.pack(fill=tk.X, padx=20, pady=10) + + tk.Label( + result_frame, + text="Estimated 1RM:", + font=self.default_font, + bg=self.bg_color, + fg=self.text_color + ).pack(side=tk.LEFT, padx=5) + + self.result_value_label = tk.Label( + result_frame, + text="", + font=self.title_font, + bg=self.bg_color, + fg="#22A7F0" # Accent color for result + ) + self.result_value_label.pack(side=tk.LEFT, padx=10) + + # Formula info + self.formula_info_label = tk.Label( + self.window, + text="Formula: Epley (w × (1 + r × 0.0333))", + font=('Arial', 10), + bg=self.bg_color, + fg="#888888" + ) + self.formula_info_label.pack(pady=5) + + # Bind formula change event + formula_menu.bind("<>", self.update_formula_info) - # Create and pack widgets - tk.Label(self.window, text="Enter the weight you lifted (in kg):").pack() - self.weight_entry = tk.Entry(self.window) - self.weight_entry.pack() + def validate_numeric_input(self, value: str) -> bool: + """Validate that input is a positive number with optional decimal.""" + if value == "": + return True + try: + float(value) + return float(value) > 0 + except ValueError: + return False - tk.Label(self.window, text="Enter the number of reps you performed:").pack() - self.rep_entry = tk.Entry(self.window) - self.rep_entry.pack() + def validate_integer_input(self, value: str) -> bool: + """Validate that input is a positive integer.""" + if value == "": + return True + try: + int(value) + return int(value) > 0 + except ValueError: + return False - tk.Button(self.window, text="Calculate", command=self.display_result).pack() + def calculate_1rm(self) -> Optional[float]: + """ + Calculate 1RM using selected formula with input validation. + + Returns: + Calculated 1RM or None if inputs are invalid. + """ + try: + weight = float(self.weight_entry.get()) + reps = int(self.rep_entry.get()) + + if weight <= 0: + raise ValueError("Weight must be greater than 0.") + + if reps <= 0: + raise ValueError("Reps must be greater than 0.") + + if reps > 20: + messagebox.warning( + "Warning", + "Formulas may be inaccurate for reps > 20.\n\n" + "1RM calculations are most reliable for 1-10 reps." + ) + + formula_name = self.formula_var.get() + formula = self.formulas[formula_name] + return formula(weight, reps) + + except ValueError as e: + self.result_value_label.config(text=f"Error: {str(e)}") + return None + except Exception as e: + self.result_value_label.config(text=f"An error occurred: {str(e)}") + return None - tk.Label(self.window, text="Your estimated one-rep max (1RM):").pack() - self.result_value_label = tk.Label(self.window) - self.result_value_label.pack() + def display_result(self) -> None: + """Calculate 1RM and update result label with formatted value.""" + result = self.calculate_1rm() + if result is not None: + self.result_value_label.config(text=f"{result:.2f} kg") - def calculate_1rm(self): - """Calculates and returns the estimated 1RM.""" - weight = int(self.weight_entry.get()) - reps = int(self.rep_entry.get()) - return (weight * reps * 0.0333) + weight + def update_formula_info(self, event=None) -> None: + """Update the formula description label.""" + formula_name = self.formula_var.get() + if formula_name == "Epley": + desc = "Epley (w × (1 + r × 0.0333))" + elif formula_name == "Brzycki": + desc = "Brzycki (w ÷ (1.0278 - 0.0278 × r))" + elif formula_name == "Lombardi": + desc = "Lombardi (w × r^0.1)" + elif formula_name == "Mayhew": + desc = "Mayhew (100w ÷ (52.2 + 41.9e^(-0.055r)))" + else: + desc = formula_name + + self.formula_info_label.config(text=f"Formula: {desc}") - def display_result(self): - """Calculates the 1RM and updates result_value_label with it.""" - one_rep_max = self.calculate_1rm() - self.result_value_label.config(text=f"{one_rep_max} kg") + def apply_dark_mode(self) -> None: + """Apply dark mode color scheme.""" + self.bg_color = "#1E1E1E" + self.text_color = "#FFFFFF" + self.entry_bg = "#333333" + self.button_bg = "#4CAF50" + self.button_fg = "#FFFFFF" + self.button_active_bg = "#45a049" + self.window.configure(bg=self.bg_color) - def run(self): - """Runs the Tkinter event loop.""" + def apply_light_mode(self) -> None: + """Apply light mode color scheme.""" + self.bg_color = "#F0F0F0" + self.text_color = "#000000" + self.entry_bg = "#FFFFFF" + self.button_bg = "#4CAF50" + self.button_fg = "#FFFFFF" + self.button_active_bg = "#45a049" + self.window.configure(bg=self.bg_color) + + def toggle_theme(self) -> None: + """Toggle between dark and light modes.""" + self.is_dark_mode = not self.is_dark_mode + + if self.is_dark_mode: + self.apply_dark_mode() + else: + self.apply_light_mode() + + # Reconfigure all widgets + for widget in self.window.winfo_children(): + if isinstance(widget, tk.Label) or isinstance(widget, tk.Button): + widget.configure(bg=self.bg_color, fg=self.text_color) + elif isinstance(widget, tk.Entry): + widget.configure( + bg=self.entry_bg, + fg=self.text_color, + insertbackground=self.text_color + ) + elif isinstance(widget, tk.Frame): + widget.configure(bg=self.bg_color) + for sub_widget in widget.winfo_children(): + if isinstance(sub_widget, tk.Label) or isinstance(sub_widget, tk.Button): + sub_widget.configure(bg=self.bg_color, fg=self.text_color) + elif isinstance(sub_widget, tk.Entry): + sub_widget.configure( + bg=self.entry_bg, + fg=self.text_color, + insertbackground=self.text_color + ) + elif isinstance(sub_widget, ttk.Combobox): + # ttk widgets require style configuration + style = ttk.Style() + if self.is_dark_mode: + style.configure('TCombobox', + fieldbackground=self.entry_bg, + background=self.bg_color, + foreground=self.text_color + ) + else: + style.configure('TCombobox', + fieldbackground=self.entry_bg, + background=self.bg_color, + foreground=self.text_color + ) + + # Update button colors + for widget in self.window.winfo_children(): + if isinstance(widget, tk.Frame): + for button in widget.winfo_children(): + if isinstance(button, tk.Button): + button.configure( + bg=self.button_bg, + fg=self.button_fg, + activebackground=self.button_active_bg + ) + + # Update result label accent color + self.result_value_label.configure(fg="#22A7F0" if self.is_dark_mode else "#1E88E5") + self.formula_info_label.configure(fg="#888888" if self.is_dark_mode else "#666666") + + def run(self) -> None: + """Start the application's main event loop.""" self.window.mainloop() -# Usage if __name__ == "__main__": calculator = OneRepMaxCalculator() - calculator.run() - -# Improve the program. -# Make the fonts, bigger. -# - Use text formatting... -# Use dark mode. -# Have an option to use dark mode and light mode. + calculator.run() \ No newline at end of file diff --git a/nitkarshchourasia/to_sort/pdf_to_docx_converter/pdf_to_docx.py b/nitkarshchourasia/to_sort/pdf_to_docx_converter/pdf_to_docx.py index 757eccae6ca..9020d5525b8 100644 --- a/nitkarshchourasia/to_sort/pdf_to_docx_converter/pdf_to_docx.py +++ b/nitkarshchourasia/to_sort/pdf_to_docx_converter/pdf_to_docx.py @@ -3,7 +3,7 @@ from pdf2docx import Converter -def convert_pdf_to_docx(pdf_file_path, docx_file_path): +def convert_pdf_to_docx(pdf_file_path, docx_file_path) -> None: """ Converts a PDF file to a DOCX file using pdf2docx library. diff --git a/nitkarshchourasia/to_sort/pdf_to_docx_converter/requirements.txt b/nitkarshchourasia/to_sort/pdf_to_docx_converter/requirements.txt deleted file mode 100644 index 74006b5fb0a..00000000000 --- a/nitkarshchourasia/to_sort/pdf_to_docx_converter/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -python-docx==0.8.11 -PyMuPDF==1.18.17 -pytesseract==0.3.8 -Pillow==8.4.0 \ No newline at end of file diff --git a/nitkarshchourasia/to_sort/word2number/word2number.py b/nitkarshchourasia/to_sort/word2number/word2number.py index 6e8fed09d39..668ef0e3ba4 100644 --- a/nitkarshchourasia/to_sort/word2number/word2number.py +++ b/nitkarshchourasia/to_sort/word2number/word2number.py @@ -1,83 +1,52 @@ -def word_to_number(word): - numbers_dict = { - "zero": 0, - "one": 1, - "two": 2, - "three": 3, - "four": 4, - "five": 5, - "six": 6, - "seven": 7, - "eight": 8, - "nine": 9, - "ten": 10, - "eleven": 11, - "twelve": 12, - "thirteen": 13, - "fourteen": 14, - "fifteen": 15, - "sixteen": 16, - "seventeen": 17, - "eighteen": 18, - "nineteen": 19, - "twenty": 20, - "thirty": 30, - "forty": 40, - "fifty": 50, - "sixty": 60, - "seventy": 70, - "eighty": 80, - "ninety": 90, - "hundred": 100, - "thousand": 1000, - "lakh": 100000, - "crore": 10000000, - "billion": 1000000000, - "trillion": 1000000000000, +def word_to_number(word: str) -> float: + # Map number words to their values + number_words = { + "zero": 0, "one": 1, "two": 2, "three": 3, "four": 4, + "five": 5, "six": 6, "seven": 7, "eight": 8, "nine": 9, + "ten": 10, "eleven": 11, "twelve": 12, "thirteen": 13, "fourteen": 14, + "fifteen": 15, "sixteen": 16, "seventeen": 17, "eighteen": 18, "nineteen": 19, + "twenty": 20, "thirty": 30, "forty": 40, "fifty": 50, "sixty": 60, + "seventy": 70, "eighty": 80, "ninety": 90 } - - # Split the string into words - words = word.split() - - result = 0 - current_number = 0 - - # Ways I can make this more efficient: - for w in words: - if w in numbers_dict: - current_number += numbers_dict[w] - elif w == "hundred": - current_number *= 100 - elif w == "thousand": - result += current_number * 1000 - current_number = 0 - elif w == "lakh": - result += current_number * 100000 - current_number = 0 - elif w == "crore": - result += current_number * 10000000 - current_number = 0 - elif w == "billion": - result += current_number * 1000000000 - current_number = 0 - elif w == "trillion": - result += current_number * 1000000000000 - current_number = 0 - - result += current_number - - return result + + # Map multipliers to their values + multipliers = { + "hundred": 100, "thousand": 1000, "lakh": 100000, + "crore": 10000000, "billion": 1000000000, "trillion": 1000000000000 + } + + # Split input into words and clean + words = [w.lower() for w in word.replace('-', ' ').split() if w.lower() not in {'and', 'point'}] + + total = 0 + current = 0 + + for word in words: + if word in number_words: + current += number_words[word] + elif word in multipliers: + if word == "hundred": + current *= multipliers[word] + else: + # Apply current multiplier and reset + total += current * multipliers[word] + current = 0 + else: + # Handle decimal points or invalid words + try: + # Attempt to parse as decimal part + decimal_part = float(word) + total += current + decimal_part + current = 0 + except ValueError: + raise ValueError(f"Invalid number word: {word}") + + # Add any remaining current value + total += current + return total # Example usage: number_str = "two trillion seven billion fifty crore thirty-four lakh seven thousand nine hundred" result = word_to_number(number_str) -print(result) - - -# Will make a tkinter application out of it. -## It will have a slider to use the more efficient way or just the normal way. -## More efficient way would have a library word2num to choose from. - -# The application would be good. -# I want to make it more efficient and optimized. +print(result) # Output: 200750347900 \ No newline at end of file diff --git a/nodepad/notepad.py b/nodepad/notepad.py index 356316e3d9e..6383c3dca0a 100644 --- a/nodepad/notepad.py +++ b/nodepad/notepad.py @@ -1,64 +1,93 @@ -#! /usr/bin/env python -# -# GUI module generated by PAGE version 4.10 -# In conjunction with Tcl version 8.6 -# Jan 30, 2018 02:49:06 PM - -try: - from Tkinter import * -except ImportError: - from tkinter import * - -try: - import ttk - - py3 = 0 -except ImportError: - import tkinter.ttk as ttk - - py3 = 1 - -import notepad_support - - -def vp_start_gui(): +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys +from typing import Optional, Any +from tkinter import ttk, Text, Entry, Label, Button, Frame, Toplevel, Tk, WORD + +# Replace missing module with placeholder (implement this separately) +# import notepad_support +class NotepadSupport: + """Placeholder for notepad_support module functionality""" + @staticmethod + def init(root: Tk, top: Any) -> None: + pass + + @staticmethod + def add_button(event: Any) -> None: + pass + + @staticmethod + def clear_button(event: Any) -> None: + pass + + @staticmethod + def next_button(event: Any) -> None: + pass + + @staticmethod + def back_button(event: Any) -> None: + pass + + @staticmethod + def search_button(event: Any) -> None: + pass + + @staticmethod + def delete_button(event: Any) -> None: + pass + + @staticmethod + def create_button(event: Any) -> None: + pass + + @staticmethod + def exit_button(event: Any) -> None: + sys.exit() + +notepad_support = NotepadSupport() + +def vp_start_gui() -> None: """Starting point when module is the main routine.""" - global val, w, root + global root root = Tk() root.resizable(False, False) - top = Notepads_managment(root) + top = NotepadsManagement(root) notepad_support.init(root, top) root.mainloop() +root: Optional[Tk] = None +w: Optional[Toplevel] = None -w = None - - -def create_Notepads_managment(root, *args, **kwargs): +def create_Notepads_management(root: Tk, *args: Any, **kwargs: Any) -> tuple[Toplevel, Any]: """Starting point when module is imported by another program.""" - global w, w_win, rt + global w, rt rt = root w = Toplevel(root) - top = Notepads_managment(w) + top = NotepadsManagement(w) notepad_support.init(w, top, *args, **kwargs) return (w, top) - -def destroy_Notepads_managment(): +def destroy_Notepads_management() -> None: global w - w.destroy() - w = None - - -class Notepads_managment: - def __init__(self, top=None): - """This class configures and populates the toplevel window. - top is the toplevel containing window.""" + if w: + w.destroy() + w = None + +class NotepadsManagement: + def __init__(self, top: Tk | Toplevel) -> None: + """ + Configure and populate the toplevel window for managing notepads. + + Args: + top: The toplevel window to configure. + """ _bgcolor = "#d9d9d9" # X11 color: 'gray85' _fgcolor = "#000000" # X11 color: 'black' _compcolor = "#d9d9d9" # X11 color: 'gray85' _ana1color = "#d9d9d9" # X11 color: 'gray85' _ana2color = "#d9d9d9" # X11 color: 'gray85' + self.style = ttk.Style() if sys.platform == "win32": self.style.theme_use("winnative") @@ -70,7 +99,7 @@ def __init__(self, top=None): ) top.geometry("600x450") - top.title("Notepads managment") + top.title("Notepads Management") top.configure(highlightcolor="black") self.style.configure("TNotebook.Tab", background=_bgcolor) @@ -79,35 +108,29 @@ def __init__(self, top=None): "TNotebook.Tab", background=[("selected", _compcolor), ("active", _ana2color)], ) + + # Create notebook tabs self.TNotebook1 = ttk.Notebook(top) self.TNotebook1.place(relx=0.02, rely=0.02, relheight=0.85, relwidth=0.97) self.TNotebook1.configure(width=582) self.TNotebook1.configure(takefocus="") + + # Tab 0: Add Note self.TNotebook1_t0 = Frame(self.TNotebook1) self.TNotebook1.add(self.TNotebook1_t0, padding=3) - self.TNotebook1.tab( - 0, - text="Add", - compound="none", - underline="-1", - ) + self.TNotebook1.tab(0, text="Add", compound="none", underline="-1") + + # Tab 1: Display Notes self.TNotebook1_t1 = Frame(self.TNotebook1) self.TNotebook1.add(self.TNotebook1_t1, padding=3) - self.TNotebook1.tab( - 1, - text="Display", - compound="none", - underline="-1", - ) + self.TNotebook1.tab(1, text="Display", compound="none", underline="-1") + + # Tab 2: Create self.TNotebook1_t2 = Frame(self.TNotebook1) self.TNotebook1.add(self.TNotebook1_t2, padding=3) - self.TNotebook1.tab( - 2, - text="Create", - compound="none", - underline="-1", - ) + self.TNotebook1.tab(2, text="Create", compound="none", underline="-1") + # Tab 0 Widgets (Add Note) self.inputNotice = Text(self.TNotebook1_t0) self.inputNotice.place(relx=0.02, rely=0.28, relheight=0.64, relwidth=0.68) self.inputNotice.configure(background="white") @@ -125,25 +148,26 @@ def __init__(self, top=None): self.Label1 = Label(self.TNotebook1_t0) self.Label1.place(relx=0.02, rely=0.08, height=18, width=29) self.Label1.configure(activebackground="#f9f9f9") - self.Label1.configure(text="""Title""") + self.Label1.configure(text="Title") self.Label2 = Label(self.TNotebook1_t0) self.Label2.place(relx=0.02, rely=0.22, height=18, width=46) self.Label2.configure(activebackground="#f9f9f9") - self.Label2.configure(text="""Notice:""") + self.Label2.configure(text="Notice:") self.Button2 = Button(self.TNotebook1_t0) self.Button2.place(relx=0.74, rely=0.28, height=26, width=50) self.Button2.configure(activebackground="#d9d9d9") - self.Button2.configure(text="""Add""") - self.Button2.bind("", lambda e: notepad_support.add_button(e)) + self.Button2.configure(text="Add") + self.Button2.bind("", notepad_support.add_button) self.Button3 = Button(self.TNotebook1_t0) self.Button3.place(relx=0.74, rely=0.39, height=26, width=56) self.Button3.configure(activebackground="#d9d9d9") - self.Button3.configure(text="""Clear""") - self.Button3.bind("", lambda e: notepad_support.clear_button(e)) + self.Button3.configure(text="Clear") + self.Button3.bind("", notepad_support.clear_button) + # Tab 1 Widgets (Display Notes) self.outputNotice = Text(self.TNotebook1_t1) self.outputNotice.place(relx=0.02, rely=0.19, relheight=0.76, relwidth=0.6) self.outputNotice.configure(background="white") @@ -161,53 +185,54 @@ def __init__(self, top=None): self.Label3 = Label(self.TNotebook1_t1) self.Label3.place(relx=0.02, rely=0.08, height=18, width=29) self.Label3.configure(activebackground="#f9f9f9") - self.Label3.configure(text="""Title""") + self.Label3.configure(text="Title") self.Button4 = Button(self.TNotebook1_t1) self.Button4.place(relx=0.69, rely=0.33, height=26, width=54) self.Button4.configure(activebackground="#d9d9d9") - self.Button4.configure(text="""Next""") - self.Button4.bind("", lambda e: notepad_support.next_button(e)) + self.Button4.configure(text="Next") + self.Button4.bind("", notepad_support.next_button) self.Button5 = Button(self.TNotebook1_t1) self.Button5.place(relx=0.69, rely=0.44, height=26, width=55) self.Button5.configure(activebackground="#d9d9d9") - self.Button5.configure(text="""Back""") - self.Button5.bind("", lambda e: notepad_support.back_button(e)) + self.Button5.configure(text="Back") + self.Button5.bind("", notepad_support.back_button) self.Button7 = Button(self.TNotebook1_t1) self.Button7.place(relx=0.69, rely=0.22, height=26, width=68) self.Button7.configure(activebackground="#d9d9d9") - self.Button7.configure(text="""Search""") - self.Button7.bind("", lambda e: notepad_support.search_button(e)) + self.Button7.configure(text="Search") + self.Button7.bind("", notepad_support.search_button) self.Button8 = Button(self.TNotebook1_t1) self.Button8.place(relx=0.69, rely=0.56, height=26, width=64) self.Button8.configure(activebackground="#d9d9d9") - self.Button8.configure(text="""Delete""") - self.Button8.bind("", lambda e: notepad_support.delete_button(e)) + self.Button8.configure(text="Delete") + self.Button8.bind("", notepad_support.delete_button) + # Tab 2 Widgets (Create) self.Label4 = Label(self.TNotebook1_t2) self.Label4.place(relx=0.09, rely=0.14, height=18, width=259) self.Label4.configure(activebackground="#f9f9f9") - self.Label4.configure(text="""For creating a new notepads managment.""") + self.Label4.configure(text="For creating a new notepads management.") self.Button6 = Button(self.TNotebook1_t2) self.Button6.place(relx=0.22, rely=0.25, height=26, width=69) self.Button6.configure(activebackground="#d9d9d9") - self.Button6.configure(text="""Create""") - self.Button6.bind("", lambda e: notepad_support.create_button(e)) + self.Button6.configure(text="Create") + self.Button6.bind("", notepad_support.create_button) + # Main Window Widgets self.Button1 = Button(top) self.Button1.place(relx=0.4, rely=0.91, height=26, width=117) self.Button1.configure(activebackground="#d9d9d9") - self.Button1.configure(text="""Exit""") - self.Button1.bind("", lambda e: notepad_support.exit_button(e)) + self.Button1.configure(text="Exit") + self.Button1.bind("", notepad_support.exit_button) self.errorOutput = Label(top) self.errorOutput.place(relx=0.03, rely=0.91, height=18, width=206) self.errorOutput.configure(activebackground="#f9f9f9") - if __name__ == "__main__": - vp_start_gui() + vp_start_gui() \ No newline at end of file diff --git a/notepad/high_notepad.py b/notepad/high_notepad.py new file mode 100644 index 00000000000..c5295041d7a --- /dev/null +++ b/notepad/high_notepad.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sqlite3 +import tkinter as tk +from tkinter import ttk, Text, Entry, Label, Button, Frame, messagebox +from typing import List, Tuple + +class NotepadApp: + def __init__(self, root: tk.Tk): + """Initialize the Notepad Application""" + self.root = root + self.root.title("Advanced Notepad") + self.root.geometry("800x600") + self.root.resizable(True, True) + + # Ensure proper font for English display + self.root.option_add("*Font", "Arial 10") + + # Database connection + self.connection = sqlite3.connect("notes.db") + self.cursor = self.connection.cursor() + self._initialize_database() + + # Search state + self.search_active = False + self.current_results: List[Tuple[int, str, str]] = [] + self.current_index = 0 + + # Create UI + self._create_ui() + + def _initialize_database(self) -> None: + """Initialize the database table""" + try: + self.cursor.execute(""" + CREATE TABLE IF NOT EXISTS notes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + content TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + self.connection.commit() + except Exception as e: + messagebox.showerror("Database Error", f"Failed to initialize database: {str(e)}") + + def _create_ui(self) -> None: + """Create the user interface""" + # Create main frame + main_frame = Frame(self.root) + main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + # Create notebook tabs + self.notebook = ttk.Notebook(main_frame) + self.notebook.pack(fill=tk.BOTH, expand=True) + + # Add tabs + self.tab_add = Frame(self.notebook) + self.tab_display = Frame(self.notebook) + self.tab_search = Frame(self.notebook) + + self.notebook.add(self.tab_add, text="Add Note") + self.notebook.add(self.tab_display, text="Browse Notes") + self.notebook.add(self.tab_search, text="Search Notes") + + # Setup Add tab + self._setup_add_tab() + + # Setup Display tab + self._setup_display_tab() + + # Setup Search tab + self._setup_search_tab() + + # Status bar + self.status_bar = Label(self.root, text="Ready", bd=1, relief=tk.SUNKEN, anchor=tk.W) + self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) + + # Load all notes initially + self._load_all_notes() + + def _setup_add_tab(self) -> None: + """Setup the Add Note tab""" + # Title frame + title_frame = Frame(self.tab_add) + title_frame.pack(fill=tk.X, padx=10, pady=5) + + Label(title_frame, text="Title:").pack(side=tk.LEFT, padx=5) + self.title_entry = Entry(title_frame, width=60) + self.title_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # Content frame + content_frame = Frame(self.tab_add) + content_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + Label(content_frame, text="Content:").pack(anchor=tk.W, padx=5) + self.content_text = Text(content_frame, wrap=tk.WORD, height=15) + self.content_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Button frame + button_frame = Frame(self.tab_add) + button_frame.pack(fill=tk.X, padx=10, pady=5) + + Button(button_frame, text="Save Note", command=self._save_note).pack(side=tk.RIGHT, padx=5) + Button(button_frame, text="Clear", command=self._clear_form).pack(side=tk.RIGHT, padx=5) + + def _setup_display_tab(self) -> None: + """Setup the Browse Notes tab""" + # Search frame + search_frame = Frame(self.tab_display) + search_frame.pack(fill=tk.X, padx=10, pady=5) + + Label(search_frame, text="Filter:").pack(side=tk.LEFT, padx=5) + self.filter_entry = Entry(search_frame, width=40) + self.filter_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) + Button(search_frame, text="Apply Filter", command=self._apply_filter).pack(side=tk.RIGHT, padx=5) + + # Notes list frame + list_frame = Frame(self.tab_display) + list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # Treeview for notes + columns = ("id", "title", "created_at") + self.notes_tree = ttk.Treeview(list_frame, columns=columns, show="headings") + self.notes_tree.heading("id", text="ID") + self.notes_tree.heading("title", text="Title") + self.notes_tree.heading("created_at", text="Created At") + + self.notes_tree.column("id", width=50) + self.notes_tree.column("title", width=300) + self.notes_tree.column("created_at", width=150) + + self.notes_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + + # Scrollbar + scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.notes_tree.yview) + self.notes_tree.configure(yscroll=scrollbar.set) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + # Bind selection event + self.notes_tree.bind("<>", self._on_note_select) + + # Button frame + button_frame = Frame(self.tab_display) + button_frame.pack(fill=tk.X, padx=10, pady=5) + + Button(button_frame, text="Refresh", command=self._load_all_notes).pack(side=tk.LEFT, padx=5) + Button(button_frame, text="Delete Selected", command=self._delete_selected_note).pack(side=tk.RIGHT, padx=5) + + def _setup_search_tab(self) -> None: + """Setup the Search Notes tab""" + # Search frame + search_frame = Frame(self.tab_search) + search_frame.pack(fill=tk.X, padx=10, pady=5) + + Label(search_frame, text="Search Title:").pack(side=tk.LEFT, padx=5) + self.search_entry = Entry(search_frame, width=50) + self.search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) + Button(search_frame, text="Search", command=self._search_notes).pack(side=tk.RIGHT, padx=5) + + # Results frame + results_frame = Frame(self.tab_search) + results_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # Navigation buttons + nav_frame = Frame(results_frame) + nav_frame.pack(fill=tk.X) + + self.prev_button = Button(nav_frame, text="Previous", command=self._show_previous_result, state=tk.DISABLED) + self.prev_button.pack(side=tk.LEFT, padx=5) + + self.next_button = Button(nav_frame, text="Next", command=self._show_next_result, state=tk.DISABLED) + self.next_button.pack(side=tk.RIGHT, padx=5) + + # Result display + self.result_title_var = tk.StringVar() + Label(results_frame, textvariable=self.result_title_var, font=("Arial", 12, "bold")).pack(anchor=tk.W, padx=5, pady=5) + + self.result_content_text = Text(results_frame, wrap=tk.WORD, height=15) + self.result_content_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Status label + self.search_status_var = tk.StringVar() + self.search_status_var.set("No search results") + Label(results_frame, textvariable=self.search_status_var).pack(anchor=tk.W, padx=5, pady=5) + + def _load_all_notes(self) -> None: + """Load all notes into the treeview""" + # Clear existing items + for item in self.notes_tree.get_children(): + self.notes_tree.delete(item) + + try: + self.cursor.execute("SELECT id, title, created_at FROM notes ORDER BY created_at DESC") + notes = self.cursor.fetchall() + + for note in notes: + self.notes_tree.insert("", tk.END, values=note) + + self.status_bar.config(text=f"Loaded {len(notes)} notes") + except Exception as e: + messagebox.showerror("Database Error", f"Failed to load notes: {str(e)}") + + def _apply_filter(self) -> None: + """Apply filter to notes list""" + filter_text = self.filter_entry.get().strip().lower() + + # Clear existing items + for item in self.notes_tree.get_children(): + self.notes_tree.delete(item) + + try: + if filter_text: + self.cursor.execute("SELECT id, title, created_at FROM notes WHERE LOWER(title) LIKE ? ORDER BY created_at DESC", + (f"%{filter_text}%",)) + else: + self.cursor.execute("SELECT id, title, created_at FROM notes ORDER BY created_at DESC") + + notes = self.cursor.fetchall() + + for note in notes: + self.notes_tree.insert("", tk.END, values=note) + + self.status_bar.config(text=f"Filtered {len(notes)} notes") + except Exception as e: + messagebox.showerror("Database Error", f"Failed to filter notes: {str(e)}") + + def _on_note_select(self, event: tk.Event) -> None: + """Handle note selection""" + selected_items = self.notes_tree.selection() + if not selected_items: + return + + note_id = self.notes_tree.item(selected_items[0])["values"][0] + + try: + self.cursor.execute("SELECT title, content FROM notes WHERE id = ?", (note_id,)) + note = self.cursor.fetchone() + + if note: + title, content = note + # Open a new window to display the note + self._open_note_viewer(note_id, title, content) + except Exception as e: + messagebox.showerror("Database Error", f"Failed to load note: {str(e)}") + + def _open_note_viewer(self, note_id: int, title: str, content: str) -> None: + """Open a new window to view/edit the note""" + viewer = tk.Toplevel(self.root) + viewer.title(f"Note: {title}") + viewer.geometry("600x500") + viewer.resizable(True, True) + + # Title entry + title_frame = Frame(viewer) + title_frame.pack(fill=tk.X, padx=10, pady=5) + + title_label = Label(title_frame, text="Title:") + title_label.pack(side=tk.LEFT, padx=5) + + title_entry = Entry(title_frame, width=50) + title_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) + title_entry.insert(0, title) + + # Content text + content_text = Text(viewer, wrap=tk.WORD, height=20) + content_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + content_text.insert(tk.END, content) + + # Button frame + button_frame = Frame(viewer) + button_frame.pack(fill=tk.X, padx=10, pady=5) + + def update_note() -> None: + new_title = title_entry.get().strip() + new_content = content_text.get(1.0, tk.END).strip() + + if not new_title: + messagebox.showerror("Input Error", "Title cannot be empty") + return + + try: + self.cursor.execute( + "UPDATE notes SET title = ?, content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?", + (new_title, new_content, note_id) + ) + self.connection.commit() + messagebox.showinfo("Success", "Note updated successfully") + viewer.destroy() + self._load_all_notes() + except Exception as e: + messagebox.showerror("Database Error", f"Failed to update note: {str(e)}") + + Button(button_frame, text="Update Note", command=update_note).pack(side=tk.RIGHT, padx=5) + Button(button_frame, text="Cancel", command=viewer.destroy).pack(side=tk.RIGHT, padx=5) + + def _delete_selected_note(self) -> None: + """Delete the selected note""" + selected_items = self.notes_tree.selection() + if not selected_items: + messagebox.showinfo("Selection Error", "Please select a note to delete") + return + + note_id = self.notes_tree.item(selected_items[0])["values"][0] + title = self.notes_tree.item(selected_items[0])["values"][1] + + if messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete the note '{title}'?"): + try: + self.cursor.execute("DELETE FROM notes WHERE id = ?", (note_id,)) + self.connection.commit() + self._load_all_notes() + self.status_bar.config(text=f"Deleted note with ID {note_id}") + except Exception as e: + messagebox.showerror("Database Error", f"Failed to delete note: {str(e)}") + + def _save_note(self) -> None: + """Save the current note to the database""" + title = self.title_entry.get().strip() + content = self.content_text.get(1.0, tk.END).strip() + + if not title: + messagebox.showerror("Input Error", "Title cannot be empty") + return + + try: + self.cursor.execute( + "INSERT INTO notes (title, content) VALUES (?, ?)", + (title, content) + ) + self.connection.commit() + messagebox.showinfo("Success", "Note saved successfully") + self._clear_form() + self._load_all_notes() + except Exception as e: + messagebox.showerror("Database Error", f"Failed to save note: {str(e)}") + + def _clear_form(self) -> None: + """Clear the form fields""" + self.title_entry.delete(0, tk.END) + self.content_text.delete(1.0, tk.END) + self.status_bar.config(text="Form cleared") + + def _search_notes(self) -> None: + """Search notes by title""" + search_text = self.search_entry.get().strip() + + if not search_text: + messagebox.showinfo("Search Error", "Please enter a search term") + return + + try: + self.cursor.execute( + "SELECT id, title, content FROM notes WHERE title LIKE ? ORDER BY created_at DESC", + (f"%{search_text}%",) + ) + self.current_results = self.cursor.fetchall() + self.current_index = 0 + + if not self.current_results: + self.search_status_var.set("No results found") + self.result_title_var.set("") + self.result_content_text.delete(1.0, tk.END) + self.prev_button.config(state=tk.DISABLED) + self.next_button.config(state=tk.DISABLED) + else: + self._update_search_display() + self.search_status_var.set(f"Displaying result {self.current_index + 1} of {len(self.current_results)}") + self.prev_button.config(state=tk.NORMAL if len(self.current_results) > 1 else tk.DISABLED) + self.next_button.config(state=tk.NORMAL if len(self.current_results) > 1 else tk.DISABLED) + + self.search_active = True + except Exception as e: + messagebox.showerror("Database Error", f"Failed to search notes: {str(e)}") + + def _update_search_display(self) -> None: + """Update the search result display""" + if 0 <= self.current_index < len(self.current_results): + note_id, title, content = self.current_results[self.current_index] + self.result_title_var.set(title) + self.result_content_text.delete(1.0, tk.END) + self.result_content_text.insert(tk.END, content) + self.search_status_var.set(f"Displaying result {self.current_index + 1} of {len(self.current_results)}") + + def _show_previous_result(self) -> None: + """Show the previous search result""" + if self.current_index > 0: + self.current_index -= 1 + self._update_search_display() + self.next_button.config(state=tk.NORMAL) + + if self.current_index == 0: + self.prev_button.config(state=tk.DISABLED) + + def _show_next_result(self) -> None: + """Show the next search result""" + if self.current_index < len(self.current_results) - 1: + self.current_index += 1 + self._update_search_display() + self.prev_button.config(state=tk.NORMAL) + + if self.current_index == len(self.current_results) - 1: + self.next_button.config(state=tk.DISABLED) + + def run(self) -> None: + """Run the application""" + self.root.mainloop() + + def __del__(self) -> None: + """Close the database connection when the object is destroyed""" + if self.connection: + self.connection.close() + +if __name__ == "__main__": + root = tk.Tk() + app = NotepadApp(root) + app.run() \ No newline at end of file diff --git a/notepad/notepad_support.py b/notepad/notepad_support.py deleted file mode 100644 index 39a27d63b0e..00000000000 --- a/notepad/notepad_support.py +++ /dev/null @@ -1,164 +0,0 @@ -#! /usr/bin/env python -# -# Support module generated by PAGE version 4.10 -# In conjunction with Tcl version 8.6 -# Jan 29, 2018 03:25:00 PM - - -import sqlite3 - -try: - from Tkinter import * -except ImportError: - from tkinter import * - -try: - import ttk - - py3 = 0 -except ImportError: - - py3 = 1 - -# connect with database 'data.db' -connection = sqlite3.connect("data.db") - -# creates a cursor (pointer) to the data base -cursor = connection.cursor() - -search = False - -results = [] - -index = 0 - - -def delete_button(p1): - global index - global results - global cursor - - # fetch id of the current note - id = results[index][0] - - sql_command = """ DELETE FROM notes WHERE id = {0}; """ - sql_command = sql_command.format(id) - - cursor.execute(sql_command) - - connection.commit() - - -def create_button(p1): - """ - for creating a new database - """ - global cursor - - sql_command = """ - CREATE TABLE notes ( - id INTEGER PRIMARY KEY, - title TEXT, - note TEXT);""" - - try: - cursor.execute(sql_command) - w.errorOutput.configure(text="") - except: - w.errorOutput.configure(text="The database already exists") - - -def add_button(p1): - # for manipulating the data base - global cursor - global connection - if len(w.inputTitle.get()) > 0 and len(w.inputNotice.get(1.0, END)) > 0: - w.errorOutput.configure(text="") - title = w.inputTitle.get() - note = w.inputNotice.get(1.0, END) - sql_command = """INSERT INTO notes (title,note) VALUES ("{0}","{1}"); """ - sql_command = sql_command.format(title, note) - cursor.execute(sql_command) - connection.commit() - else: - w.errorOutput.configure(text="Please fill the fields. ") - - -def back_button(p1): - global search - global results - global index - - w.errorOutput.configure(text="") - index -= 1 - if index >= 0 and index < len(results): - w.outputNotice.delete(1.0, END) - w.outputNotice.insert(1.0, results[index][2]) - - -def clear_button(p1): - """ - This function is for the clear button. - This will clear the notice-input field - """ - w.inputNotice.delete(1.0, END) - - -def exit_button(p1): - """ - function for the exit button. - this will exit the application. - """ - sys.exit(0) - - -def search_button(p1): - global cursor - global results - global index - w.errorOutput.configure(text="") - sql_command = """ SELECT * FROM notes WHERE title LIKE "%{0}%";""" - sql_command = sql_command.format(w.inputSearchTitle.get()) - try: - cursor.execute(sql_command) - results = cursor.fetchall() - w.errorOutput.configure(text=str(len(results)) + " results") - index = 0 - if index >= 0 and index < len(results): - w.outputNotice.delete(1.0, END) - w.outputNotice.insert(1.0, results[index][2]) - except: - w.errorOutput.configure(text="Please create at first a database.") - - -def next_button(p1): - global results - global index - index += 1 - if len(w.inputSearchTitle.get()) > 0: - if index >= 0 and index < len(results): - w.outputNotice.delete(1.0, END) - w.outputNotice.insert(1.0, results[index][2]) - - else: - w.errorOutput.configure(text="Please fill the search field. ") - - -def init(top, gui, *args, **kwargs): - global w, top_level, root - w = gui - top_level = top - root = top - - -def destroy_window(): - # Function which closes the window. - global top_level - top_level.destroy() - top_level = None - - -if __name__ == "__main__": - import notepad - - notepad.vp_start_gui() diff --git a/notes.db b/notes.db new file mode 100644 index 00000000000..e48223761d9 Binary files /dev/null and b/notes.db differ diff --git a/other_pepole/get_ip_gui b/other_pepole/get_ip_gui.py old mode 100755 new mode 100644 similarity index 100% rename from other_pepole/get_ip_gui rename to other_pepole/get_ip_gui.py diff --git a/password_programs_multiple/animal_name_scraiper.py b/password_programs_multiple/animal_name_scraiper.py index ea0e0f15d00..1d1fb382999 100644 --- a/password_programs_multiple/animal_name_scraiper.py +++ b/password_programs_multiple/animal_name_scraiper.py @@ -1,4 +1,4 @@ -import requests +import httpx from bs4 import BeautifulSoup # * Using html5lib as the parser is good @@ -6,7 +6,7 @@ animals_A_to_Z_URL = "https://animalcorner.org/animal-sitemap/#" -results = requests.get(animals_A_to_Z_URL) +results = httpx.get(animals_A_to_Z_URL) # ? results and results.text ? what are these? # soup = BeautifulSoup(results.text, "html.parser") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000000..53f90ddc859 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,150 @@ +[project] +name = "python" +version = "0.1.0" +requires-python = ">=3.13" +dependencies = [ + "aiohttp>=3.12.14", + "apscheduler", + "auto-mix-prep", + "beautifulsoup4", + "ccxt", + "colorama", + "customtkinter", + "darkdetect", + "datetime", + "django", + "docx", + "emoji", + "fastapi", + "firebase-admin", + "fitz", + "flask", + "fpdf2", + "freegames", + "fuzzywuzzy", + "geocoder", + "google-api-python-client", + "googletrans", + "gtts", + "httplib2", + "httpx", + "hupper", + "inquirer", + "keras", + "keyboard", + "matplotlib", + "mediapipe", + "mutagen", + "mysql-connector-python", + "newspaper", + "nltk", + "numpy", + "openai", + "opencv-python", + "openpyxl", + "pafy>=0.5.5", + "pandas", + "pdf2docx", + "pdfkit", + "pillow", + "playsound", + "playsound3", + "plyer", + "protobuf", + "psutil", + "pyaudio", + "pyautogui", + "pycountry", + "pydantic", + "pygame", + "pyglet", + "pyjokes", + "pymongo", + "pymupdf", + "pynput", + "pypdf2", + "pyperclip", + "pyqrcode", + "pyqt5", + "pyserial", + "pyshorteners", + "pytesseract", + "python-docx", + "pyttsx3", + "pytube", + "pywhatkit", + "pywifi", + "pywin32", + "qrcode", + "quo", + "requests", + "requests-mock", + "rich", + "scikit-learn", + "seaborn", + "selenium", + "slab", + "socksipy-branch", + "solara", + "speechrecognition", + "speechtotext", + "tabula", + "tensorflow", + "thirdai", + "tkcalendar", + "tkinter", + "tkinterdnd2", + "tkinterweb", + "tornado", + "tqdm", + "translate", + "tweepy", + "twilio", + "ujson", + "unidecode", + "urllib3", + "watchdog", + "webbrowser", + "wikipedia", + "win10toast", + "word2number", + "xlrd", + "xlwt", + "xor-cipher", + "yfinance", +] + +[build-system] +requires = ["setuptools>=65", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.codespell] +ignore-words-list = "3rt,abd,aer,ans,bitap,crate,damon,fo,followings,hist,iff,kwanza,manuel,mater,secant,som,sur,tim,toi,zar" +skip = "*.css,*.js,*.lock" + +[tool.ruff] +line-length = 88 +target-version = "py313" +select = ["E", "F", "I", "UP", "YTT", "PLC", "PT", "SIM"] +ignore = [ + "B904", "B905", "PLC0415", + "PLW2901", "PT011", "PT018", "PT028", + "S101", "SIM905", "UP038" +] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] +"tests/**" = ["S101", "PT011", "PT018"] +".*_test\\.py" = ["S101", "PT011", "PT018"] + +[tool.mypy] +python_version = "3.13" +ignore_missing_imports = true +exclude = ".*venv.*|.*__pycache__.*|.*migrations.*" + +[[tool.mypy.overrides]] +module = [ + "django.*", "tensorflow.*", "keras.*", + "matplotlib.*", "pymongo.*" +] +ignore_missing_imports = true diff --git a/python_codes b/python_codes deleted file mode 100644 index 0f602a1a751..00000000000 --- a/python_codes +++ /dev/null @@ -1,2 +0,0 @@ -python_codes -print("Python") diff --git a/randomloadingmessage.py b/randomloadingmessage.py index 71654d249b0..1bd65ef275b 100644 --- a/randomloadingmessage.py +++ b/randomloadingmessage.py @@ -1,169 +1,125 @@ -# Created by Nathan R (Mosrod) -# CREDIT TO https://github.com/1egoman/funnies/blob/master/src/funnies.js +""" +Loading Screen Messages Generator -from random import * +Generates humorous loading screen messages inspired by https://github.com/1egoman/funnies +""" -x = 1 +import random +from typing import Dict, List -for i in range(x): - num = randint(1, 80) - if num == 1: - print("Reticulating splines...") - if num == 2: - print("Swapping time and space...") - if num == 3: - print("Spinning violently around the y-axis...") - if num == 4: - print("Tokenizing real life...") - if num == 5: - print("Bending the spoon...") - if num == 6: - print("Filtering morale...") - if num == 7: - print("We need a new fuse...") - if num == 8: - print("Have a good day.") - if num == 9: - print( - "Upgrading Windows, your PC will restart several times. Sit back and relax." - ) - if num == 10: - print("The architects are still drafting.") - if num == 11: - print("We're building the buildings as fast as we can.") - if num == 12: - print("Please wait while the little elves draw your map.") - if num == 13: - print("Don't worry - a few bits tried to escape, but we caught them.") - if num == 14: - print("Go ahead -- hold your breath!") - if num == 15: - print("...at least you're not on hold...") - if num == 16: - print("The server is powered by a lemon and two electrodes.") - if num == 17: - print("We're testing your patience.") - if num == 18: - print("As if you had any other choice.") - if num == 19: - print("The bits are flowing slowly today.") - if num == 20: - print("It's still faster than you could draw it.") - if num == 21: - print("My other loading screen is much faster.") - if num == 22: - print("(Insert quarter)") - if num == 23: - print("Are we there yet?") - if num == 24: - print("Just count to 10.") - if num == 25: - print("Don't panic...") - if num == 26: - print("We're making you a cookie.") - if num == 27: - print("Creating time-loop inversion field.") - if num == 28: - print("Computing chance of success.") - if num == 29: - print("All I really need is a kilobit.") - if num == 30: - print("I feel like im supposed to be loading something...") - if num == 31: - print("Should have used a compiled language...") - if num == 32: - print("Is this Windows?") - if num == 33: - print("Don't break your screen yet!") - if num == 34: - print("I swear it's almost done.") - if num == 35: - print("Let's take a mindfulness minute...") - if num == 36: - print("Listening for the sound of one hand clapping...") - if num == 37: - print("Keeping all the 1's and removing all the 0's...") - if num == 38: - print("We are not liable for any broken screens as a result of waiting.") - if num == 39: - print("Where did all the internets go?") - if num == 40: - print("Granting wishes...") - if num == 41: - print("Time flies when you’re having fun.") - if num == 42: - print("Get some coffee and come back in ten minutes...") - if num == 43: - print("Stay awhile and listen...") - if num == 44: - print("Convincing AI not to turn evil...") - if num == 45: - print("How did you get here?") - if num == 46: - print("Wait, do you smell something burning?") - if num == 47: - print("Computing the secret to life, the universe, and everything.") - if num == 48: - print("When nothing is going right, go left...") - if num == 49: - print("I love my job only when I'm on vacation...") - if num == 50: - print("Why are they called apartments if they are all stuck together?") - if num == 51: - print("I’ve got problem for your solution...") - if num == 52: - print("Whenever I find the key to success, someone changes the lock.") - if num == 53: - print("Constructing additional pylons...") - if num == 54: - print("You don’t pay taxes—they take taxes.") - if num == 55: - print("A commit a day keeps the mobs away.") - if num == 56: - print("This is not a joke, it's a commit.") - if num == 57: - print("Hello IT, have you tried turning it off and on again?") - if num == 58: - print("Hello, IT... Have you tried forcing an unexpected reboot?") - if num == 59: - print("I didn't choose the engineering life. The engineering life chose me.") - if num == 60: - print("Dividing by zero...") - if num == 61: - print("If I’m not back in five minutes, just wait longer.") - if num == 62: - print("Web developers do it with