diff --git a/.github/data/contributors-log.json b/.github/data/contributors-log.json index 62f9014..3bba434 100644 --- a/.github/data/contributors-log.json +++ b/.github/data/contributors-log.json @@ -329,10 +329,12 @@ }, "Encryption_Project": { "contributor-name": [ - "moonabys" + "moonabys", + "yumicce" ], "pull-request-number": [ - "75" + "75", + "81" ], "demo-path": "Encryption_Project" }, @@ -344,5 +346,77 @@ "72" ], "demo-path": "securepass" + }, + "Biosimilars_Finder": { + "contributor-name": [ + "cmodevcodes" + ], + "pull-request-number": [ + "83" + ], + "demo-path": "Biosimilars_Finder" + }, + "Spell-Sense": { + "contributor-name": [ + "princechaudhary007" + ], + "pull-request-number": [ + "82" + ], + "demo-path": "Spell-Sense" + }, + "Birthday-Paradox": { + "contributor-name": [ + "AdyaTech" + ], + "pull-request-number": [ + "86" + ], + "demo-path": "https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Birthday-Paradox" + }, + "BitMap-Message": { + "contributor-name": [ + "AdyaTech" + ], + "pull-request-number": [ + "87" + ], + "demo-path": "https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/BitMap-Message" + }, + "Bouncing-DVD": { + "contributor-name": [ + "AdyaTech" + ], + "pull-request-number": [ + "89" + ], + "demo-path": "https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Bouncing-DVD" + }, + "Dog-Age-Calculator": { + "contributor-name": [ + "AdyaTech" + ], + "pull-request-number": [ + "90" + ], + "demo-path": "https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Dog-Age-Calculator" + }, + "Story-Generator": { + "contributor-name": [ + "AdyaTech" + ], + "pull-request-number": [ + "91" + ], + "demo-path": "https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Story-Generator" + }, + "slot-machine": { + "contributor-name": [ + "appiokstella" + ], + "pull-request-number": [ + "93" + ], + "demo-path": "slot-machine" } } \ No newline at end of file diff --git a/Biosimilars_Finder/README.md b/Biosimilars_Finder/README.md new file mode 100644 index 0000000..80ea0ca --- /dev/null +++ b/Biosimilars_Finder/README.md @@ -0,0 +1,48 @@ +# Biosimilars Finder +#### Description: + +Query FDA open API database as well as the most recent csv file posted on FDA's +Purple book website for available biologics and biosimilars information + +Class Drug is used to store particular information of a drug and available biosimilars info" + +get_brand() queries the FDA API for the openfda data associated with a drug and store info into the drug instance + +As the FDA API doesn't take RE query or provide detailed functionalities for drug brand name check, rf"^{name}(?![\w-])" is used to do a second round check if a drug is a valid drug given the FDA database. This helps to filter out cases like "name-xxxx" or "XX name" or "xxxnamexxx" + + Parameters: + drug (Class Drug): Drug class instance that is used to store FDA query results + name (str): User input drug name to be checked with FDA database + + Raises: + KeyError: If a drug's brand name exist FDA's database but info such as generic/molecule name, route, or moa is not available (e.g. Humira) + ValueError : if the FDA API cannot be accessed + +get_biologics () finds the most recent PurpleBook CSV file and identify if a drug is biologics and if it has biosimilar. Retrieve biosimilars info and store in the Drug class instance if available" + +It automatically checks 24 months starting from the current month to find the most recent Purple Book csv. The function should be called only after get_brand () is called. + +It determines if a drug is biologics based on PurpleBook's cvs field - Proprietary Name. + +It determines if a drug is biologics based on PurpleBook's cvs field - Ref. Product Proprietary Name + +Class Drug property self._biosimilars stores all fields of the FDA PurpleBook csv file of a drug's biosimilars as Pandas DataFrame, not just the ones that prn_biosim() prints + + Parameters: + drug (Class Drug): Drug class instance that is used to store FDA query results + name (str): User input drug name to be checked with the PurpleBook csv fil + + +prn_biosim() print a Drug instance's selected biosimilars information. Class Drug property self._biosimilars stores all fields of the FDA PurpleBook csv file of a drug's biosimilars as Pandas DataFrame, not just the ones that prn_biosim() prints so more info can be easily added to the output table if needed + + Parameters: + drug (Class Drug): Drug class instance that is used to store FDA query results + + ValueError : if the drug is not biologics or if it doesn't have biosimilars info + + + +TODO +1. The three key functions - get_brand(), get_biologics(), prn_biosim() can be easily changed to instance method as they were designed as method for the class Drug +2. One can sometime access a PurpleBook CSV when it is not even officially posted on FDA's PurpleBook website. For example, in March, 2026. The most updated file should be February, 2026 based on PurpleBook's website but March, 2026 csv can already be accessed. This may/may not be the intended behavior of the program +3. The reason that the program has to automatically download the PurpleBook csv, hold it in memory for query etc is because FDA doesn't currently have an API for PurpleBook. It would be great to change the PurpleBook query to an APL query once an API is available diff --git a/Biosimilars_Finder/biosimilars.py b/Biosimilars_Finder/biosimilars.py new file mode 100644 index 0000000..7a78f1f --- /dev/null +++ b/Biosimilars_Finder/biosimilars.py @@ -0,0 +1,190 @@ +import requests +import pandas as pnds +import re +import time +from tabulate import tabulate +from datetime import datetime +from dateutil.relativedelta import relativedelta +from io import StringIO + + +def main () : + + """ + Query FDA open API database as well as the most recent csv data file on FDA's + Purple book website for available biologics and biosimilars information + + """ + + name= input("Brand Name: ").strip() + drug=Drug(name) + get_brand(drug,name) + if drug.is_drug : + get_biologics(drug,name) + print (drug) + + if drug.is_biologics and drug.has_biosimilar: + biosim= input ("Do you want more biosimilars info?[Y/N] ").lower().strip() + if biosim == "y" or biosim == "yes" : + prn_biosim(drug) + + +class Drug: + + """ + Class Drug is used to store particular information of a drug and available biosimilars info" + """ + + def __init__(self, brand_name=[]): + self.brand_name=brand_name + self.is_drug = False + self._generic_name="" + self._route="" + self._moa="" + self.is_biologics= False + self.has_biosimilar = False + self._biosimilars = [] + + def __str__(self): + if not self.is_drug : + s= f"{self.brand_name} is not a brand drug based on the FDA database" + else : + s= f"\nBrand Name: {self.brand_name}\n" + s += f"Molecule Name: {self._generic_name}\n" + s += f"Route: {self._route}\n" + s += f"Mechnism of Action: {self._moa}\n" + s += "Biologics: Yes\n" if self.is_biologics else "Biologics: N/A\n" + s += "Biosimilars: Yes" if self.has_biosimilar else "Biosimilars: N/A" + return s + + +def get_brand (drug,name): + + """ + Query the FDA API for the openfda data associated with a drug and store info into the drug instance + + Parameters: + drug (Class Drug): Drug class instance that is used to store FDA query results + name (str): User input drug name to be checked with FDA database + + Raises: + KeyError: If a drug's brand name exist FDA's database but info such as generic/molecule + name, route, or moa is not available (e.g. Humira) + ValueError : if the FDA API cannot be accessed + """ + + url= f'https://api.fda.gov/drug/label.json?search=openfda.brand_name:"{name}"&limit=1' + + try : + r = requests.get(url, timeout=10) + except requests.exceptions.RequestException as e: + print("FDA API not avaialble") + + if r.status_code ==200 : + response = r.json() + results = response.get("results", []) + openfda = results[0].get("openfda",{}) + + fda_name = openfda["brand_name"][0].strip().capitalize () + pattern=rf"^{name}(?![\w-])" + + if match :=re.search(pattern,fda_name,re.I) : + drug.brand_name = fda_name.capitalize() + drug.is_drug=True + + try : + drug._generic_name=openfda["generic_name"][0].capitalize() + except KeyError : + drug._generic_name ="N/A" + try : + drug._route=openfda["route"][0].capitalize() + except KeyError : + drug._route ="N/A" + try : + drug._moa=openfda["pharm_class_moa"][0].capitalize()[:-6] # removing " [moa]" at the end of the return string + except KeyError : + drug._moa ="N/A" + else : + drug.is_drug=False + else : + drug.is_drug=False + + +def get_biologics (drug,name): + + """ + Finds the most recent PurpleBook CSV file and identify if a drug is biologics + and if it has biosimilar. Retrieve biosimilars info and store in the Drug class + instance if available" + + Automatically check 24 months starting from the current month to find the most + recent Purple Book csv + + Parameters: + drug (Class Drug): Drug class instance that is used to store FDA query results + name (str): User input drug name to be checked with the PurpleBook csv file + """ + + now = datetime.now() + # number of months back from current to search purplebook data + num_months= 24 + base_url = "https://www.accessdata.fda.gov/drugsatfda_docs/PurpleBook" + headers = {"user-Agent": "Mozilla/5.0"} + + for i in range (num_months) : + past = now - relativedelta(months=i) + month_name = past.strftime("%B").capitalize() + year=str(past.year) + filename = f"purplebook-search-{month_name}-data-download.csv" + url = f"{base_url}/{year}/{filename}" + pb_read_success = False + # + try: + r= requests.get(url, headers=headers) + if r.status_code ==200 : + pb_read_success = True + pb = pnds.read_csv(StringIO(r.text), skiprows=3) + break + else : + time.sleep(2) + except Exception as e: + time.sleep(2) + + if pb_read_success : + biologics_matches = pb[pb["Proprietary Name"].str.contains(name, case=False, na=False)] + drug.is_biologics = not biologics_matches.empty + + if drug.is_biologics : + biosimilars_matches = pb[pb["Ref. Product Proprietary Name"].str.contains(name, case=False, na=False)] + drug.has_biosimilar = not biosimilars_matches.empty + if drug.has_biosimilar : + drug._biosimilars=biosimilars_matches + + +def prn_biosim(drug) : + + """ + Print a Drug instance's selected biosimilars information + + Parameters: + drug (Class Drug): Drug class instance that is used to store FDA query results + + ValueError : if the drug is not biologics or if it doesn't have biosimilars info + + """ + + if drug.has_biosimilar == True and drug.is_biologics == True : + print(tabulate( + drug._biosimilars[['Proprietary Name','Proper Name','Strength','Applicant','Approval Date']], + headers=['Brand Name','Molecule Name','Strength','Applicant','Approval Date'], + tablefmt="grid", + showindex=False, + maxcolwidths=[None,None,None,15,None] + )) + else : + raise ValueError ("The drug does not have biosimlar") + + +if __name__ == "__main__" : + main () + diff --git a/Biosimilars_Finder/requirements.txt b/Biosimilars_Finder/requirements.txt new file mode 100644 index 0000000..67890c1 --- /dev/null +++ b/Biosimilars_Finder/requirements.txt @@ -0,0 +1,5 @@ +requests +pandas +python-dateutil +tabulate +pytest diff --git a/Biosimilars_Finder/test_biosimilars.py b/Biosimilars_Finder/test_biosimilars.py new file mode 100644 index 0000000..28c21a7 --- /dev/null +++ b/Biosimilars_Finder/test_biosimilars.py @@ -0,0 +1,103 @@ +import pytest +import pandas as pnds +from biosimilars import Drug, get_brand, get_biologics, prn_biosim + + +def test_init () : + drug = Drug ("remicade") + assert drug.brand_name == "remicade" + assert drug.is_drug == False + assert drug.is_biologics == False + assert drug.has_biosimilar == False + with pytest.raises(TypeError) : + jar= Drug ("remicade", "rituximab") + + +def test_get_brand () : + drug = Drug () + get_brand (drug, "rituxan") + assert drug.is_drug == True + assert drug._generic_name == "Rituximab and hyaluronidase" + + drug = Drug () + get_brand (drug, "remicade") + assert drug.is_drug == True + + drug = Drug () + get_brand (drug, "HumiRa") + assert drug.is_drug == True + + drug = Drug () + get_brand (drug, "KeyTruda") + assert drug.is_drug == True + + drug = Drug () + get_brand (drug, "r") + assert drug.is_drug == False + + drug = Drug () + get_brand (drug, "re") + assert drug.is_drug == False + + drug = Drug () + get_brand (drug, "Not a Drug") + assert drug.is_drug == False + + drug = Drug () + get_brand (drug, "") + assert drug.is_drug == False + + with pytest.raises(TypeError) : + get_brand (drug) + with pytest.raises(TypeError) : + get_brand (drug,"xxx","XXX") + + +def test_get_biologics () : + drug = Drug () + get_brand (drug, "rituxan") + get_biologics (drug, "rituxan") + assert drug.is_biologics == True + assert drug.has_biosimilar == True + + drug = Drug () + get_brand (drug, "HuMira") + get_biologics (drug, "Humira") + assert drug.is_biologics == True + assert drug.has_biosimilar == True + assert isinstance(drug._biosimilars, pnds.DataFrame) + + drug = Drug () + get_brand (drug,"Remicade") + get_biologics (drug,"reMIcade") + assert drug.is_biologics == True + assert drug.has_biosimilar == True + assert isinstance(drug._biosimilars, pnds.DataFrame) + + drug = Drug () + get_brand (drug,"KeyTruda") + get_biologics (drug, "keytruda") + assert drug.is_biologics == True + assert drug.has_biosimilar == False + assert not isinstance(drug._biosimilars, pnds.DataFrame) + + drug = Drug () + get_brand (drug, "lipitor") + get_biologics (drug, "lipitor") + assert drug.is_biologics == False + assert drug.has_biosimilar == False + assert not isinstance(drug._biosimilars, pnds.DataFrame) + + with pytest.raises(TypeError) : + get_biologics (drug) + with pytest.raises(TypeError) : + get_biologics (drug,"xxx","XXX") + +def test_prn_biosim() : + with pytest.raises(TypeError) : + prn_biosim() + drug = Drug () + get_brand (drug, "lipitor") + get_biologics (drug, "lipitor") + with pytest.raises(ValueError) : + prn_biosim(drug) diff --git a/Birthday-Paradox/birthdayparadox.py b/Birthday-Paradox/birthdayparadox.py new file mode 100644 index 0000000..2ecf9e2 --- /dev/null +++ b/Birthday-Paradox/birthdayparadox.py @@ -0,0 +1,102 @@ +import datetime, random + + +def getBirthdays(numberOfBirthdays): + """Returns a list of number random date objects for birthdays.""" + birthdays = [] + for i in range(numberOfBirthdays): + # The year is unimportant for our simulation, as long as all + # birthdays have the same year. + startOfYear = datetime.date(2001, 1, 1) + + # Get a random day into the year: + randomNumberOfDays = datetime.timedelta(random.randint(0, 364)) + birthday = startOfYear + randomNumberOfDays + birthdays.append(birthday) + return birthdays + + +def getMatch(birthdays): + """Returns the date object of a birthday that occurs more than once + in the birthdays list.""" + if len(birthdays) == len(set(birthdays)): + return None # All birthdays are unique, so return None. + + # Compare each birthday to every other birthday: + for a, birthdayA in enumerate(birthdays): + for b, birthdayB in enumerate(birthdays[a + 1 :]): + if birthdayA == birthdayB: + return birthdayA # Return the matching birthday. + + +# Display the intro: +print('''Birthday Paradox, by Al Sweigart al@inventwithpython.com + +The birthday paradox shows us that in a group of N people, the odds +that two of them have matching birthdays is surprisingly large. +This program does a Monte Carlo simulation (that is, repeated random +simulations) to explore this concept. + +(It's not actually a paradox, it's just a surprising result.) +''') + +# Set up a tuple of month names in order: +MONTHS = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec') + +while True: # Keep asking until the user enters a valid amount. + print('How many birthdays shall I generate? (Max 100)') + response = input('> ') + if response.isdecimal() and (0 < int(response) <= 100): + numBDays = int(response) + break # User has entered a valid amount. +print() + +# Generate and display the birthdays: +print('Here are', numBDays, 'birthdays:') +birthdays = getBirthdays(numBDays) +for i, birthday in enumerate(birthdays): + if i != 0: + # Display a comma for each birthday after the first birthday. + print(', ', end='') + monthName = MONTHS[birthday.month - 1] + dateText = '{} {}'.format(monthName, birthday.day) + print(dateText, end='') +print() +print() + +# Determine if there are two birthdays that match. +match = getMatch(birthdays) + +# Display the results: +print('In this simulation, ', end='') +if match != None: + monthName = MONTHS[match.month - 1] + dateText = '{} {}'.format(monthName, match.day) + print('multiple people have a birthday on', dateText) +else: + print('there are no matching birthdays.') +print() + +# Run through 100,000 simulations: +print('Generating', numBDays, 'random birthdays 100,000 times...') +input('Press Enter to begin...') + +print('Let\'s run another 100,000 simulations.') +simMatch = 0 # How many simulations had matching birthdays in them. +for i in range(100000): + # Report on the progress every 10,000 simulations: + if i % 10000 == 0: + print(i, 'simulations run...') + birthdays = getBirthdays(numBDays) + if getMatch(birthdays) != None: + simMatch = simMatch + 1 +print('100,000 simulations run.') + +# Display simulation results: +probability = round(simMatch / 100000 * 100, 2) +print('Out of 100,000 simulations of', numBDays, 'people, there was a') +print('matching birthday in that group', simMatch, 'times. This means') +print('that', numBDays, 'people have a', probability, '% chance of') +print('having a matching birthday in their group.') +print('That\'s probably more than you would think!') diff --git a/BitMap-Message/bitmapmessage.py b/BitMap-Message/bitmapmessage.py new file mode 100644 index 0000000..59961fa --- /dev/null +++ b/BitMap-Message/bitmapmessage.py @@ -0,0 +1,47 @@ +import sys + +# (!) Try changing this multiline string to any image you like: + +# There are 68 periods along the top and bottom of this string: +# (You can also copy and paste this string from +# https://inventwithpython.com/bitmapworld.txt) +bitmap = """ +.................................................................... + ************** * *** ** * ****************************** + ********************* ** ** * * ****************************** * + ** ***************** ****************************** + ************* ** * **** ** ************** * + ********* ******* **************** * * + ******** *************************** * + * * **** *** *************** ****** ** * + **** * *************** *** *** * + ****** ************* ** ** * + ******** ************* * ** *** + ******** ******** * *** **** + ********* ****** * **** ** * ** + ********* ****** * * *** * * + ****** ***** ** ***** * + ***** **** * ******** + ***** **** ********* + **** ** ******* * + *** * * + ** * * +....................................................................""" + +print('Bitmap Message') +print('Enter the message to display with the bitmap.') +message = input('> ') +if message == '': + sys.exit() + +# Loop over each line in the bitmap: +for line in bitmap.splitlines(): + # Loop over each character in the line: + for i, bit in enumerate(line): + if bit == ' ': + # Print an empty space since there's a space in the bitmap: + print(' ', end='') + else: + # Print a character from the message: + print(message[i % len(message)], end='') + print() # Print a newline. diff --git a/Bouncing-DVD/bouncingdvd.py b/Bouncing-DVD/bouncingdvd.py new file mode 100644 index 0000000..4e82ce8 --- /dev/null +++ b/Bouncing-DVD/bouncingdvd.py @@ -0,0 +1,140 @@ +import sys, random, time + +try: + import bext +except ImportError: + print('This program requires the bext module, which you') + print('can install by following the instructions at') + print('https://pypi.org/project/Bext/') + sys.exit() + +# Set up the constants: +WIDTH, HEIGHT = bext.size() +# We can't print to the last column on Windows without it adding a +# newline automatically, so reduce the width by one: +WIDTH -= 1 + +NUMBER_OF_LOGOS = 5 # (!) Try changing this to 1 or 100. +PAUSE_AMOUNT = 0.2 # (!) Try changing this to 1.0 or 0.0. +# (!) Try changing this list to fewer colors: +COLORS = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'] + +UP_RIGHT = 'ur' +UP_LEFT = 'ul' +DOWN_RIGHT = 'dr' +DOWN_LEFT = 'dl' +DIRECTIONS = (UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT) + +# Key names for logo dictionaries: +COLOR = 'color' +X = 'x' +Y = 'y' +DIR = 'direction' + + +def main(): + bext.clear() + + # Generate some logos. + logos = [] + for i in range(NUMBER_OF_LOGOS): + logos.append({COLOR: random.choice(COLORS), + X: random.randint(1, WIDTH - 4), + Y: random.randint(1, HEIGHT - 4), + DIR: random.choice(DIRECTIONS)}) + if logos[-1][X] % 2 == 1: + # Make sure X is even so it can hit the corner. + logos[-1][X] -= 1 + + cornerBounces = 0 # Count how many times a logo hits a corner. + while True: # Main program loop. + for logo in logos: # Handle each logo in the logos list. + # Erase the logo's current location: + bext.goto(logo[X], logo[Y]) + print(' ', end='') # (!) Try commenting this line out. + + originalDirection = logo[DIR] + + # See if the logo bounces off the corners: + if logo[X] == 0 and logo[Y] == 0: + logo[DIR] = DOWN_RIGHT + cornerBounces += 1 + elif logo[X] == 0 and logo[Y] == HEIGHT - 1: + logo[DIR] = UP_RIGHT + cornerBounces += 1 + elif logo[X] == WIDTH - 3 and logo[Y] == 0: + logo[DIR] = DOWN_LEFT + cornerBounces += 1 + elif logo[X] == WIDTH - 3 and logo[Y] == HEIGHT - 1: + logo[DIR] = UP_LEFT + cornerBounces += 1 + + # See if the logo bounces off the left edge: + elif logo[X] == 0 and logo[DIR] == UP_LEFT: + logo[DIR] = UP_RIGHT + elif logo[X] == 0 and logo[DIR] == DOWN_LEFT: + logo[DIR] = DOWN_RIGHT + + # See if the logo bounces off the right edge: + # (WIDTH - 3 because 'DVD' has 3 letters.) + elif logo[X] == WIDTH - 3 and logo[DIR] == UP_RIGHT: + logo[DIR] = UP_LEFT + elif logo[X] == WIDTH - 3 and logo[DIR] == DOWN_RIGHT: + logo[DIR] = DOWN_LEFT + + # See if the logo bounces off the top edge: + elif logo[Y] == 0 and logo[DIR] == UP_LEFT: + logo[DIR] = DOWN_LEFT + elif logo[Y] == 0 and logo[DIR] == UP_RIGHT: + logo[DIR] = DOWN_RIGHT + + # See if the logo bounces off the bottom edge: + elif logo[Y] == HEIGHT - 1 and logo[DIR] == DOWN_LEFT: + logo[DIR] = UP_LEFT + elif logo[Y] == HEIGHT - 1 and logo[DIR] == DOWN_RIGHT: + logo[DIR] = UP_RIGHT + + if logo[DIR] != originalDirection: + # Change color when the logo bounces: + logo[COLOR] = random.choice(COLORS) + + # Move the logo. (X moves by 2 because the terminal + # characters are twice as tall as they are wide.) + if logo[DIR] == UP_RIGHT: + logo[X] += 2 + logo[Y] -= 1 + elif logo[DIR] == UP_LEFT: + logo[X] -= 2 + logo[Y] -= 1 + elif logo[DIR] == DOWN_RIGHT: + logo[X] += 2 + logo[Y] += 1 + elif logo[DIR] == DOWN_LEFT: + logo[X] -= 2 + logo[Y] += 1 + + # Display number of corner bounces: + bext.goto(5, 0) + bext.fg('white') + print('Corner bounces:', cornerBounces, end='') + + for logo in logos: + # Draw the logos at their new location: + bext.goto(logo[X], logo[Y]) + bext.fg(logo[COLOR]) + print('DVD', end='') + + bext.goto(0, 0) + + sys.stdout.flush() # (Required for bext-using programs.) + time.sleep(PAUSE_AMOUNT) + + +# If this program was run (instead of imported), run the game: +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print() + print('Bouncing DVD Logo, by Al Sweigart') + sys.exit() # When Ctrl-C is pressed, end the program. diff --git a/Dog-Age-Calculator/dogage.py b/Dog-Age-Calculator/dogage.py new file mode 100644 index 0000000..19026bf --- /dev/null +++ b/Dog-Age-Calculator/dogage.py @@ -0,0 +1,15 @@ +def calculate_dog_age(human_age): + if human_age < 0: + return None + if human_age <= 2: + return human_age * 10.5 + return 21 + (human_age - 2) * 4 + + +human_age = int(input("Enter a dog's age in human years: ")) +dog_age = calculate_dog_age(human_age) + +if dog_age is None: + print("Age must be a positive number.") +else: + print(f"The dog's age in dog years is {dog_age}.") \ No newline at end of file diff --git a/Encryption_Project/encryption/sha.py b/Encryption_Project/encryption/sha.py index 1892060..d91a0b6 100644 --- a/Encryption_Project/encryption/sha.py +++ b/Encryption_Project/encryption/sha.py @@ -7,4 +7,5 @@ import hashlib def encryption_sha(message: str): - return hashlib.sha256().digest() # This makes the encryption in SHA-256. This is the usually how your passkeys are encrypted + encoded_message = message.encode() + return hashlib.sha256(encoded_message).digest() # This makes the encryption in SHA-256. This is the usually how your passkeys are encrypted diff --git a/Spell-Sense/README.md b/Spell-Sense/README.md new file mode 100644 index 0000000..2716c58 --- /dev/null +++ b/Spell-Sense/README.md @@ -0,0 +1,49 @@ +# Spell-Sense: Advanced Spell Checker + +Spell-Sense is a professional, modular Python application that provides real-time spelling suggestions. Built with `Tkinter` and `TextBlob`, it improves upon basic implementations by adding robust error handling and a clean, user-friendly interface. + +## Features +- Smart Validation: Detects empty inputs and prevents unnecessary processing. +- Color-Coded Feedback: Uses visual cues (Green for success, Blue for suggestions, Red for errors). +- Modular Design: Separation of concerns between UI logic and processing logic. +- Keyboard Friendly: Press `Enter` to check spelling instantly without clicking. +- Auto-Focus: The cursor starts in the input box for immediate typing. + +## Project Structure +/Spell-Sense/ +├── main.py # Entry point and UI (Tkinter) +├── logic.py # Core NLP logic (TextBlob) +├── requirements.txt # Dependency list +└── README.md # Documentation + +🚀 Installation & Setup +Prerequisites +Python 3.6 or higher + +Setup +Navigate to your project directory: + +Bash +cd Spell-Sense +Install the required dependencies: + +Bash +pip install -r requirements.txt +🛠️ Usage +Run the script from your terminal: + +Bash +python main.py +Type a word into the input box. + +Press Enter or click Check. + +View the suggestion or success message below. + +Click Reset to clear the fields. + +📝 Credits +This project was developed as an enhanced alternative to basic scripts in this repository. It focuses on modularity, error handling, and improved User Experience (UX). + +License +This project is open-source and intended for educational use. \ No newline at end of file diff --git a/Spell-Sense/logic.py b/Spell-Sense/logic.py new file mode 100644 index 0000000..38bd71c --- /dev/null +++ b/Spell-Sense/logic.py @@ -0,0 +1,12 @@ +from textblob import TextBlob + +def get_correction(text): + if not text.strip(): + return None, False + + blob = TextBlob(text) + corrected = str(blob.correct()) + + # Check if the original matches the corrected version + is_correct = text.lower().strip() == corrected.lower().strip() + return corrected, is_correct \ No newline at end of file diff --git a/Spell-Sense/main.py b/Spell-Sense/main.py new file mode 100644 index 0000000..0ab991f --- /dev/null +++ b/Spell-Sense/main.py @@ -0,0 +1,55 @@ +import tkinter as tk +from logic import get_correction + +class SpellCheckerApp: + def __init__(self, root): + self.root = root + self.root.title("Spell Checker Pro") + self.root.geometry("400x300") + self.root.configure(bg='#f7f7f7') + + self.setup_ui() + self.root.bind('', lambda e: self.process_text()) + + def setup_ui(self): + # Label + tk.Label(self.root, text="Pro Spell Checker", font=("Arial", 14, "bold"), bg='#f7f7f7').pack(pady=10) + + # Entry + self.input_entry = tk.Entry(self.root, font=("Arial", 12), width=30) + self.input_entry.pack(pady=10) + self.input_entry.focus_set() + + # Buttons + btn_frame = tk.Frame(self.root, bg='#f7f7f7') + btn_frame.pack(pady=10) + + self.check_btn = tk.Button(btn_frame, text="Check", command=self.process_text, bg='#4CAF50', fg='white', width=10) + self.check_btn.pack(side=tk.LEFT, padx=5) + + self.reset_btn = tk.Button(btn_frame, text="Reset", command=self.reset_fields, bg='#f44336', fg='white', width=10) + self.reset_btn.pack(side=tk.LEFT, padx=5) + + # Result display + self.result_label = tk.Label(self.root, text="", font=("Arial", 11), bg='#f7f7f7', wraplength=350) + self.result_label.pack(pady=20) + + def process_text(self): + text = self.input_entry.get() + corrected, is_correct = get_correction(text) + + if corrected is None: + self.result_label.config(text="Please enter a word!", fg="orange") + elif is_correct: + self.result_label.config(text=f"✔ '{text}' is spelled correctly!", fg="green") + else: + self.result_label.config(text=f"✖ Suggestion: {corrected}", fg="blue") + + def reset_fields(self): + self.input_entry.delete(0, tk.END) + self.result_label.config(text="") + +if __name__ == "__main__": + root = tk.Tk() + app = SpellCheckerApp(root) + root.mainloop() \ No newline at end of file diff --git a/Spell-Sense/requirements.txt b/Spell-Sense/requirements.txt new file mode 100644 index 0000000..3f42dc3 --- /dev/null +++ b/Spell-Sense/requirements.txt @@ -0,0 +1 @@ +textblob \ No newline at end of file diff --git a/Story-Generator/story.py b/Story-Generator/story.py new file mode 100644 index 0000000..627ff7c --- /dev/null +++ b/Story-Generator/story.py @@ -0,0 +1,51 @@ +import random +when = [ + "A few years ago", + "Yesterday", + "Last night", + "A long time ago", + "On 20th January" +] +who = [ + "a rabbit", + "an elephant", + "a mouse", + "a turtle", + "a cat" +] +names = [ + "Ali", + "Miriam", + "Daniel", + "Houuk", + "Starwalker" +] +places = [ + "Barcelona", + "India", + "Germany", + "Venice", + "England" +] +went_to = [ + "cinema", + "university", + "seminar", + "school", + "laundry" +] +happened = [ + "made a lot of friends", + "ate a burger", + "found a secret key", + "solved a mystery", + "wrote a book" +] +story = ( + f"{random.choice(when)}, " + f"{random.choice(names)} the {random.choice(who)} " + f"from {random.choice(places)} went to the " + f"{random.choice(went_to)} and " + f"{random.choice(happened)}." +) +print(story) \ No newline at end of file diff --git a/index.md b/index.md index cea0fd5..4b27d11 100644 --- a/index.md +++ b/index.md @@ -42,8 +42,16 @@ Welcome to **Python-Projects**, your friendly initiation into the world of open- | Number-Plate-Detection | [iamdevdhanush](https://github.com/iamdevdhanush "goto iamdevdhanush profile") | [#58](https://github.com/Grow-with-Open-Source/Python-Projects/pull/58 "visit pr #58") | [/Grow-with-Open-Source/Python-Projects/Number-Plate-Detection/](Number-Plate-Detection "view the result of Number-Plate-Detection") | | Motion-Detection | [iamdevdhanush](https://github.com/iamdevdhanush "goto iamdevdhanush profile") | [#62](https://github.com/Grow-with-Open-Source/Python-Projects/pull/62 "visit pr #62") | [/Grow-with-Open-Source/Python-Projects/Motion-Detection/](Motion-Detection "view the result of Motion-Detection") | | Bowling-Action-Tracking | [musharrafhamraz](https://github.com/musharrafhamraz "goto musharrafhamraz profile") | [#67](https://github.com/Grow-with-Open-Source/Python-Projects/pull/67 "visit pr #67") | [/Grow-with-Open-Source/Python-Projects/Bowling-Action-Tracking/](https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Bowling-Action-Tracking "view the result of Bowling-Action-Tracking") | -| Encryption_Project | [moonabys](https://github.com/moonabys "goto moonabys profile") | [#75](https://github.com/Grow-with-Open-Source/Python-Projects/pull/75 "visit pr #75") | [/Grow-with-Open-Source/Python-Projects/Encryption_Project/](Encryption_Project "view the result of Encryption_Project") | +| Encryption_Project | [moonabys](https://github.com/moonabys "goto moonabys profile"), [yumicce](https://github.com/yumicce "goto yumicce profile") | [#75](https://github.com/Grow-with-Open-Source/Python-Projects/pull/75 "visit pr #75"), [#81](https://github.com/Grow-with-Open-Source/Python-Projects/pull/81 "visit pr #81") | [/Grow-with-Open-Source/Python-Projects/Encryption_Project/](Encryption_Project "view the result of Encryption_Project") | | securepass | [Lampard7crypt](https://github.com/Lampard7crypt "goto Lampard7crypt profile") | [#72](https://github.com/Grow-with-Open-Source/Python-Projects/pull/72 "visit pr #72") | [/Grow-with-Open-Source/Python-Projects/securepass/](securepass "view the result of securepass") | +| Biosimilars_Finder | [cmodevcodes](https://github.com/cmodevcodes "goto cmodevcodes profile") | [#83](https://github.com/Grow-with-Open-Source/Python-Projects/pull/83 "visit pr #83") | [/Grow-with-Open-Source/Python-Projects/Biosimilars_Finder/](Biosimilars_Finder "view the result of Biosimilars_Finder") | +| Spell-Sense | [princechaudhary007](https://github.com/princechaudhary007 "goto princechaudhary007 profile") | [#82](https://github.com/Grow-with-Open-Source/Python-Projects/pull/82 "visit pr #82") | [/Grow-with-Open-Source/Python-Projects/Spell-Sense/](Spell-Sense "view the result of Spell-Sense") | +| Birthday-Paradox | [AdyaTech](https://github.com/AdyaTech "goto AdyaTech profile") | [#86](https://github.com/Grow-with-Open-Source/Python-Projects/pull/86 "visit pr #86") | [/Grow-with-Open-Source/Python-Projects/Birthday-Paradox/](https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Birthday-Paradox "view the result of Birthday-Paradox") | +| BitMap-Message | [AdyaTech](https://github.com/AdyaTech "goto AdyaTech profile") | [#87](https://github.com/Grow-with-Open-Source/Python-Projects/pull/87 "visit pr #87") | [/Grow-with-Open-Source/Python-Projects/BitMap-Message/](https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/BitMap-Message "view the result of BitMap-Message") | +| Bouncing-DVD | [AdyaTech](https://github.com/AdyaTech "goto AdyaTech profile") | [#89](https://github.com/Grow-with-Open-Source/Python-Projects/pull/89 "visit pr #89") | [/Grow-with-Open-Source/Python-Projects/Bouncing-DVD/](https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Bouncing-DVD "view the result of Bouncing-DVD") | +| Dog-Age-Calculator | [AdyaTech](https://github.com/AdyaTech "goto AdyaTech profile") | [#90](https://github.com/Grow-with-Open-Source/Python-Projects/pull/90 "visit pr #90") | [/Grow-with-Open-Source/Python-Projects/Dog-Age-Calculator/](https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Dog-Age-Calculator "view the result of Dog-Age-Calculator") | +| Story-Generator | [AdyaTech](https://github.com/AdyaTech "goto AdyaTech profile") | [#91](https://github.com/Grow-with-Open-Source/Python-Projects/pull/91 "visit pr #91") | [/Grow-with-Open-Source/Python-Projects/Story-Generator/](https://github.com/Grow-with-Open-Source/Python-Projects/tree/main/Story-Generator "view the result of Story-Generator") | +| slot-machine | [appiokstella](https://github.com/appiokstella "goto appiokstella profile") | [#93](https://github.com/Grow-with-Open-Source/Python-Projects/pull/93 "visit pr #93") | [/Grow-with-Open-Source/Python-Projects/slot-machine/](slot-machine "view the result of slot-machine") | diff --git a/slot-machine/README.md b/slot-machine/README.md new file mode 100644 index 0000000..5ec4360 --- /dev/null +++ b/slot-machine/README.md @@ -0,0 +1,11 @@ +## What it does + +A fully functional terminal-based slot machine game. The player deposits +money, chooses how many lines to bet on (up to 3), sets a bet amount per +line, and spins. The machine generates a random 3x3 grid of symbols and +pays out based on matching symbols across each line. The game tracks +balance across multiple spins until the player quits. + +## Requirements + +None — uses only built-in random module diff --git a/slot-machine/slot_machine.py b/slot-machine/slot_machine.py new file mode 100644 index 0000000..69bd93b --- /dev/null +++ b/slot-machine/slot_machine.py @@ -0,0 +1,162 @@ +import random + +MAX_LINES = 3 +MAX_BET = 10 +MIN_BET = 1 + +ROWS = 3 +COLS = 3 + +symbol_count = { + "A": 2, + "B": 4, + "C": 6, + "D": 8 +} + +symbol_value = { + "A": 5, + "B": 4, + "C": 3, + "D": 2 +} + + +def deposit(): + while True: + amount = input("What would you like to deposit? $") + + if amount.isdigit(): + amount = int(amount) + + if amount > 0: + return amount + + print("Please enter a valid amount.") + + +def get_number_of_lines(): + while True: + lines = input(f"Enter the number of lines to bet on (1-{MAX_LINES}): ") + + if lines.isdigit(): + lines = int(lines) + + if 1 <= lines <= MAX_LINES: + return lines + + print("Enter a valid number.") + + +def get_bet(): + while True: + amount = input("What would you like to bet on each line? $") + + if amount.isdigit(): + amount = int(amount) + + if MIN_BET <= amount <= MAX_BET: + return amount + + print(f"Amount must be between ${MIN_BET} and ${MAX_BET}.") + + +def get_slot_machine_spin(rows, cols, symbols): + all_symbols = [] + + for symbol, symbol_count in symbols.items(): + for _ in range(symbol_count): + all_symbols.append(symbol) + + columns = [] + + for _ in range(cols): + current_symbols = all_symbols[:] + column = [] + + for _ in range(rows): + value = random.choice(current_symbols) + current_symbols.remove(value) + column.append(value) + + columns.append(column) + + return columns + + +def print_slot_machine(columns): + for row in range(len(columns[0])): + for i, column in enumerate(columns): + if i != len(columns) - 1: + print(column[row], end=" | ") + else: + print(column[row], end="") + + print() + + +def check_winnings(columns, lines, bet, values): + winnings = 0 + winning_lines = [] + + for line in range(lines): + symbol = columns[0][line] + + for column in columns: + symbol_to_check = column[line] + + if symbol != symbol_to_check: + break + else: + winnings += values[symbol] * bet + winning_lines.append(line + 1) + + return winnings, winning_lines + + +def spin(balance): + lines = get_number_of_lines() + + while True: + bet = get_bet() + total_bet = bet * lines + + if total_bet > balance: + print( + f"You do not have enough balance. Current balance: ${balance}") + else: + break + + print(f"\nYou are betting ${bet} on {lines} lines.") + print(f"Total bet: ${total_bet}\n") + + slots = get_slot_machine_spin(ROWS, COLS, symbol_count) + + print_slot_machine(slots) + + winnings, winning_lines = check_winnings( + slots, lines, bet, symbol_value) + + print(f"\nYou won ${winnings}.") + print(f"Winning lines: {winning_lines}") + + return winnings - total_bet + + +def main(): + balance = deposit() + + while True: + print(f"\nCurrent balance: ${balance}") + + answer = input("Press Enter to spin (q to quit): ") + + if answer.lower() == "q": + break + + balance += spin(balance) + + print(f"\nYou left with ${balance}") + + +main()