From 2acdaa24502791dafa596939b276bfb8052e8b5b Mon Sep 17 00:00:00 2001 From: lighting9999 Date: Wed, 16 Jul 2025 11:00:21 +0800 Subject: [PATCH 01/22] fix bugs and ruff --- .gitignore | 2 + .../Deleting record in a binary file.py | 1 - .../input,output and error streams.py | 2 +- .../requirements.txt | 10 - BlackJack_game/blackjack.py | 373 +++-- BlackJack_game/requirements.txt | 2 - BoardGame-CLI/snakeLadder.py | 331 +++-- BoardGame-CLI/uno.py | 461 ++++--- .../{backend.py => backendBrowserHistory.py} | 0 BrowserHistory/tests/test_browser_history.py | 3 +- CliYoutubeDownloader/requirements.txt | 1 - Colors/pixel_sort.py | 379 ++--- Colors/primary_colors.py | 246 ++-- Colors/print_colors.py | 35 +- Compression_Analysis/PSNR.py | 101 +- .../variation1.py | 72 +- HangMan Game.py | 3 +- ImageDownloader/img_downloader.py | 4 +- .../src/hangman/main.py | 6 +- JustDialScrapperGUI/Justdial Scrapper GUI.py | 5 +- Key_Binding/key_binding.py | 2 +- Python Program for factorial of a number.py | 6 +- cicd | 1 - cli_master/database_import_countries.py | 4 +- currency converter/country.txt | 177 --- currency converter/main.py | 330 ++++- depreciated_programs/corona_cases.py | 6 +- fF => fF.py | 0 ...acci_SIMPLIFIED => fibonacci_SIMPLIFIED.py | 0 .../instagram_image_scrapping.ipynb | 62 +- insta_monitering/insta_api.py | 358 +++-- insta_monitering/insta_datafetcher.py | 1228 +++++++++++------ .../local_weighted_learning.py | 22 +- my project => my project.py | 0 python_codes | 2 - randomloadingmessage.py | 288 ++-- 36 files changed, 2790 insertions(+), 1733 deletions(-) delete mode 100644 BlackJack_game/requirements.txt rename BrowserHistory/{backend.py => backendBrowserHistory.py} (100%) delete mode 100644 CliYoutubeDownloader/requirements.txt delete mode 100644 cicd delete mode 100644 currency converter/country.txt rename fF => fF.py (100%) rename fibonacci_SIMPLIFIED => fibonacci_SIMPLIFIED.py (100%) rename my project => my project.py (100%) delete mode 100644 python_codes diff --git a/.gitignore b/.gitignore index 0f3717818e6..994e4dfe7a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .idea +.vscode +_pycache_ *.pyc string=sorted(input()) lower="" 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..0e24012b803 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,6 +1,5 @@ import pickle - def bdelete(): # Opening a file & loading it with open("studrec.dat","rb") as F: 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..377de6c660c 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 @@ -13,4 +13,4 @@ else: sys.stderr.write("End of file reached") break - + \ No newline at end of file diff --git a/Automated Scheduled Call Reminders/requirements.txt b/Automated Scheduled Call Reminders/requirements.txt index f5635170c24..bed361c003a 100644 --- a/Automated Scheduled Call Reminders/requirements.txt +++ b/Automated Scheduled Call Reminders/requirements.txt @@ -1,14 +1,4 @@ -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/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/pixel_sort.py b/Colors/pixel_sort.py index 920e034817f..539c0f57b8d 100644 --- a/Colors/pixel_sort.py +++ b/Colors/pixel_sort.py @@ -1,169 +1,232 @@ -"""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 + +# Configuration - Modify these for different results +AUDIO_SAMPLE_RATE = 44100 # Audio sampling rate (Hz) +AUDIO_DURATION = 10 # Audio duration (seconds) +BASE_FREQUENCY = 220 # Lowest frequency for audio conversion (Hz) +MAX_FREQUENCY = 880 # Highest frequency for audio conversion (Hz) +SORT_REPETITIONS = 8 # Controls color sorting smoothness +OUTPUT_VIDEO_FPS = 16 # Frames per second for output video + +def parse_arguments(): + """Parse command line arguments with default values""" + parser = 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): + """Validate input image exists and return its path""" + image_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): + """Create necessary output directories""" + output_base = Path(args.output_dir) + output_subdir = 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): + """Sort image pixels row by row using HSV color space""" + height, width = img.shape[:2] + sorted_img = 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 = sorted_img[row].copy() + row_pixels_sorted = 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): + """Sort pixels using HSV color space for better visual coherence""" + # Calculate HSV-based sorting key for each pixel + sort_keys = np.array([step_sort_key(pixel) for pixel in pixels]) + # Sort pixels based on the computed keys + sort_indices = np.argsort(sort_keys, axis=0)[:, 0] + return pixels[sort_indices] + +def step_sort_key(bgr, repetitions=SORT_REPETITIONS): + """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() - + # Calculate luminance (weighted for human perception) + luminance = 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(h * repetitions) + v_scaled = 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, sorted_img, output_path, fps=OUTPUT_VIDEO_FPS): + """Generate video showing the pixel sorting process""" + print("\n>>> Generating sorting process video...") + height, width = original_img.shape[:2] + fourcc = cv2.VideoWriter_fourcc(*"mp4v") + video_writer = 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 = 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, output_path): + """Save pixel data to Excel file for analysis""" + try: + df = 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, duration=AUDIO_DURATION, sample_rate=AUDIO_SAMPLE_RATE): + """Convert pixel data to audio signal""" + print("\n>>> Generating audio from pixel data...") + num_samples = int(sample_rate * duration) + + # Resize pixel data to match audio length + if pixels.size > 0: + # Flatten and normalize pixel data + pixels_flat = pixels.reshape(-1, 3) / 255.0 + # Interpolate to match audio sample count + x_old = np.linspace(0, 1, len(pixels_flat)) + x_new = np.linspace(0, 1, num_samples) + pixel_data = 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.random.rand(num_samples, 3) + + # Map pixel channels to audio parameters + frequencies = calculate_frequencies(pixel_data) + amplitudes = calculate_amplitudes(pixel_data) + waveforms = calculate_waveforms(pixel_data) + + # Generate audio signal + t = np.linspace(0, duration, num_samples, endpoint=False) + audio_signal = 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): + """Map pixel red channel to audio frequencies""" + return BASE_FREQUENCY + pixels[:, 2] * (MAX_FREQUENCY - BASE_FREQUENCY) + +def calculate_amplitudes(pixels): + """Map pixel green channel to audio amplitudes""" + return 0.1 + pixels[:, 1] * 0.7 # Range [0.1, 0.8] + +def calculate_waveforms(pixels): + """Map pixel blue channel to waveform types""" + return pixels[:, 0] # Range [0, 1] + +def save_audio(audio_signal, output_path, sample_rate=AUDIO_SAMPLE_RATE): + """Save audio signal to WAV file""" + try: + from scipy.io import wavfile + # Convert to 16-bit integer format + audio_int16 = (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(): - 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() + """Main processing pipeline""" + # Parse command line arguments + args = parse_arguments() + + # Validate input and create output directories + input_path = validate_input_image(args) + output_dir = create_output_directories(args) + + # Load and process image + try: + img = 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 = sort_pixels_by_row(img) + + # Save sorted image + sorted_img_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 = output_dir / f"{args.filename}.mp4" + generate_sorting_video(img, sorted_img, video_path) + + # Save pixel data + pixel_data_path = output_dir / "pixel_data.xlsx" + save_pixel_data(sorted_img, pixel_data_path) + + # Generate and save audio + audio_path = output_dir / f"{args.filename}.wav" + audio_signal = 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..07fd0646648 100644 --- a/Colors/primary_colors.py +++ b/Colors/primary_colors.py @@ -1,180 +1,192 @@ def diff(a, b): """ - 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 + """ + 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, default_g, default_b = 255, 0, 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..af0d2ede1aa 100644 --- a/Colors/print_colors.py +++ b/Colors/print_colors.py @@ -1,15 +1,34 @@ import sys + class colors: + """ANSI color codes for terminal output""" 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]) + ENDC = "\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 main() -> None: + """Main function with default message support""" + # Set default message if no command-line argument is provided + default_message = "Hello, Colorful World!" + message = sys.argv[1] if len(sys.argv) > 1 else default_message + + # 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..4d6dbfa6eb9 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 # Core for cross-platform path handling +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_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) -def calculate(img): - b, g, r = cv2.split(img) - pixelAt = Representational(r, g, b) - return pixelAt +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) +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 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) - - # Getting image height and width - height, width = orignal_image.shape[:2] - - orignalPixelAt = calculate(orignal_image) - compressedPixelAt = calculate(compressed_image) - - diff = orignalPixelAt - compressedPixelAt - error = np.sum(np.abs(diff) ** 2) + """Cross-platform PSNR calculation tool with automatic path handling.""" + # Define default paths using Path (auto-handles separators) + default_original = Path("Compression_Analysis") / "orignal_image.png" + default_compressed = Path("Compression_Analysis") / "compressed_image.png" + + # Set up command-line arguments + parser = 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 = parser.parse_args() - error = error / (height * width) + try: + # Load images with cross-platform path handling + original_image = load_image(args.original) + compressed_image = 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 = calculate_luminance(original_image) + compressed_lum = calculate_luminance(compressed_image) + psnr = 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/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/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/Industrial_developed_hangman/src/hangman/main.py b/Industrial_developed_hangman/src/hangman/main.py index b2a7e780ac3..806521e528b 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 @@ -70,8 +70,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] 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/Python Program for factorial of a number.py b/Python Program for factorial of a number.py index fb75b99de87..7e47519be5f 100644 --- a/Python Program for factorial of a number.py +++ b/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/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/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/main.py b/currency converter/main.py index b656e7bdf3b..4fb581159d9 100644 --- a/currency converter/main.py +++ b/currency converter/main.py @@ -1,57 +1,301 @@ -# 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, Any +from PyQt5.QtGui import QDoubleValidator +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import (QApplication, QMainWindow) +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): + super().__init__() + self.base_dir = Path(__file__).parent # Directory of current script + self.ui = self.load_ui() + self.setup_ui_elements() + self.load_currency_data() # Load from embedded list + + def load_ui(self) -> Any: + """Load UI file with proper path handling""" + ui_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() + unique_currencies = [] + 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 = from_currency.split("-")[-1].strip() + to_code = 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 = "b43a653672c4a94c4c26" # Replace with your API key if needed + url = f"https://free.currconv.com/api/v7/convert?q={from_code}_{to_code}&compact=ultra&apiKey={api_key}" + + response = httpx.get(url, timeout=10) + response.raise_for_status() + data = response.json() + + rate_key = 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 = self.lineEdit.text() + from_currency = self.dropDown1.currentText() + to_currency = 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(amount_text) + rate = self.get_exchange_rate(from_currency, to_currency) + + if rate is not None: + result = 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(sys.argv) + window = 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..aa74c959cbd 100644 --- a/depreciated_programs/corona_cases.py +++ b/depreciated_programs/corona_cases.py @@ -1,14 +1,14 @@ import sys try: - import requests + import httpx except ImportError: - print("Please Install Requests Module With Command 'pip install requests'") + print("Please Install HTTPX Module With Command 'pip install httpx'") sys.exit(1) from time import sleep url = "https://api.covid19api.com/summary" -visit = requests.get(url).json() +visit = httpx.get(url).json() NewConfirmed = visit["Global"]["NewConfirmed"] TotalConfirmed = visit["Global"]["TotalConfirmed"] 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/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/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