diff --git a/GhostBallBot/Dockerfile b/Dockerfile similarity index 100% rename from GhostBallBot/Dockerfile rename to Dockerfile diff --git a/GhostBallBot/Pipfile b/Pipfile similarity index 100% rename from GhostBallBot/Pipfile rename to Pipfile diff --git a/GhostBallBot/Pipfile.lock b/Pipfile.lock similarity index 100% rename from GhostBallBot/Pipfile.lock rename to Pipfile.lock diff --git a/GhostBallBot/database/__init__.py b/database/__init__.py similarity index 100% rename from GhostBallBot/database/__init__.py rename to database/__init__.py diff --git a/GhostBallBot/database/models.py b/database/models.py similarity index 100% rename from GhostBallBot/database/models.py rename to database/models.py diff --git a/GhostBallBot/discord_client/__init__.py b/discord_client/__init__.py similarity index 100% rename from GhostBallBot/discord_client/__init__.py rename to discord_client/__init__.py diff --git a/GhostBallBot/discord_client/client.py b/discord_client/client.py similarity index 100% rename from GhostBallBot/discord_client/client.py rename to discord_client/client.py diff --git a/GhostBallBot/game/__init__.py b/game/__init__.py similarity index 100% rename from GhostBallBot/game/__init__.py rename to game/__init__.py diff --git a/GhostBallBot/game/base.py b/game/base.py similarity index 100% rename from GhostBallBot/game/base.py rename to game/base.py diff --git a/GhostBallBot/game/end_game.py b/game/end_game.py similarity index 100% rename from GhostBallBot/game/end_game.py rename to game/end_game.py diff --git a/GhostBallBot/game/guess.py b/game/guess.py similarity index 100% rename from GhostBallBot/game/guess.py rename to game/guess.py diff --git a/GhostBallBot/game/help.py b/game/help.py similarity index 100% rename from GhostBallBot/game/help.py rename to game/help.py diff --git a/GhostBallBot/game/manager.py b/game/manager.py similarity index 100% rename from GhostBallBot/game/manager.py rename to game/manager.py diff --git a/GhostBallBot/game/new_game.py b/game/new_game.py similarity index 100% rename from GhostBallBot/game/new_game.py rename to game/new_game.py diff --git a/GhostBallBot/game/points.py b/game/points.py similarity index 100% rename from GhostBallBot/game/points.py rename to game/points.py diff --git a/GhostBallBot/game/process_guess.py b/game/process_guess.py similarity index 100% rename from GhostBallBot/game/process_guess.py rename to game/process_guess.py diff --git a/GhostBallBot/lint.sh b/lint.sh similarity index 100% rename from GhostBallBot/lint.sh rename to lint.sh diff --git a/GhostBallBot/main.py b/main.py similarity index 100% rename from GhostBallBot/main.py rename to main.py diff --git a/GhostBallBot/run.sh b/run.sh similarity index 100% rename from GhostBallBot/run.sh rename to run.sh diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/__init__.py b/src/main/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/configs.py b/src/main/configs.py deleted file mode 100644 index 3cc008a..0000000 --- a/src/main/configs.py +++ /dev/null @@ -1,51 +0,0 @@ -# Names of Configurations -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - -DATABASE_USERNAME = 'ghost_user' -DATABASE_PASSWORD = 'root' -DATABASE_HOST = '192.168.0.11' -DATABASE_PORT = '5432' -DATABASE_NAME = 'ghostball' -SEASON_1_SPREADSHEET_ID = 's1_spreadsheet_id' -SEASON_2_SPREADSHEET_ID = 's2_spreadsheet_id' -PLAYER_SPREADSHEET = 'player_spreadsheet' - -''' -Main source for configurations fetched from a startup configuration file. Includes the ability to fetch all, or fetch -one configuration once the file is loaded. - -You'll find the names of these configs above as constants that can be used throughout the rest of this repository -''' -class Configs(): - configs = {} - - def __init__(self, config_file_path): - self.config_file_path = config_file_path - self.__load_configs__() - - ''' - Fetches a single configuration by the name of that configuration. - Returns None if that configuration does not exist - ''' - def get_config_by_name(self, name): - try: - return Configs.configs[name] - except KeyError: - return None - - ''' - Fetches all configurations and returns them as a dictionary of config_key -> config_value - ''' - def get_all_configs(self): - return Configs.configs - - ''' - Performs the initial load of configurations from a startup configuration file - ''' - def __load_configs__(self): - Configs.configs = {} - config_file = open(self.config_file_path, 'r') - for line in config_file: - split_line = line.split('=') - Configs.configs[split_line[0]] = split_line[1].strip('\n') \ No newline at end of file diff --git a/src/main/database_module/__init__.py b/src/main/database_module/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/database_module/database_classes/__init__.py b/src/main/database_module/database_classes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/database_module/database_classes/db_classes.py b/src/main/database_module/database_classes/db_classes.py deleted file mode 100644 index 46ac1b7..0000000 --- a/src/main/database_module/database_classes/db_classes.py +++ /dev/null @@ -1,28 +0,0 @@ -from sqlalchemy import Column, String, Integer, ForeignKey, Date -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relationship - -Base = declarative_base() - -class Play(Base): - __tablename__ = 'play' - - play_id = Column(String, nullable=False, primary_key=True) - pitch_value = Column(Integer, nullable=True) - creation_date = Column(Date, nullable=False) - server_id = Column(String, nullable=False) - - guesses = relationship(lambda : Guess) - -class Guess(Base): - __FAKE_VALUE__ = -5000 - - __tablename__ = 'guess' - member_id = Column(String, nullable=False, primary_key=True) - play_id = Column(UUID, ForeignKey(Play.play_id), nullable=False, primary_key=True) - guessed_number = Column(Integer, nullable=False) - member_name = Column(String, nullable=False) - difference = Column(Integer) - - play = relationship("Play", back_populates="guesses") diff --git a/src/main/database_module/guess_dao.py b/src/main/database_module/guess_dao.py deleted file mode 100644 index 4ac9b60..0000000 --- a/src/main/database_module/guess_dao.py +++ /dev/null @@ -1,123 +0,0 @@ -from copy import deepcopy - -from src.main.database_module.database_classes.db_classes import Guess -from src.main.db_session import DatabaseSession - - -MEMBER_ID = 'member_id' -PLAY_ID = 'play_id' -GUESSED_NUMBER = 'guessed_number' -DIFFERENCE = 'difference' -MEMBER_NAME = 'member_name' - -class GuessDAO(): - db_string = None - session = None - Session = None - engine = None - - _database_session = None - - def __init__(self): - self._database_session = DatabaseSession() - - def insert(self, guess_info, allow_update=False): - session = self._database_session.get_or_create_session() - - guess = Guess( - member_id=guess_info[MEMBER_ID], - play_id = guess_info[PLAY_ID], - guessed_number = guess_info[GUESSED_NUMBER], - member_name = guess_info[MEMBER_NAME] - ) - - existing_guess = self.__convert_all__(session\ - .query(Guess)\ - .filter(Guess.member_id == guess_info[MEMBER_ID], Guess.play_id == guess_info[PLAY_ID])) - - if len(existing_guess) == 0: - session.add(guess) - session.commit() - return True - elif allow_update: - session\ - .query(Guess)\ - .filter(Guess.member_id == guess_info[MEMBER_ID], Guess.play_id == guess_info[PLAY_ID], Guess.member_name == guess_info[MEMBER_NAME])\ - .update({Guess.guessed_number: guess_info[GUESSED_NUMBER]}) - return True - else: - return False - - ''' - Converts the database object into a Dictionary, so that the database object is not passed out of the - datastore layer. - ''' - def __convert_all__(self, games): - converted_games = [] - for game in games: - game_dict = {} - for column in game.__dict__: - game_dict[column] = str(getattr(game, column)) - - converted_games.append(deepcopy(game_dict)) - - return converted_games - - def set_differences(self, pitch_value, play_id): - session = self._database_session.get_or_create_session() - games_to_update = self.__convert_all__(session.query(Guess).filter(Guess.play_id == play_id)) - - for game in games_to_update: - difference = self.calculate_difference(pitch_value, game[GUESSED_NUMBER]) - session.query(Guess).filter(Guess.member_id == game[MEMBER_ID], Guess.play_id == game[PLAY_ID]).update({Guess.difference: difference}) - - session.commit() - - def calculate_difference(self, pitch_value, guess_value): - pitched_number = int(pitch_value) - possible_value = abs(int(guess_value) - pitched_number) - - if possible_value > 500: - return 1000 - possible_value - else: - return possible_value - - def fetch_closest(self, num_to_fetch): - session = self._database_session.get_or_create_session() - - return self.__convert_all__( - session\ - .query(Guess)\ - .order_by(Guess.difference)\ - .limit(num_to_fetch) - ) - - def refresh(self): - self._database_session.__create_new_session__() # I know, I know. It's fine. - - def get_all_guesses_for_plays(self, play_ids): - session = self._database_session.get_or_create_session() - return self.__convert_all__( - session - .query(Guess) - .filter(Guess.play_id.in_(play_ids)) - ) - - def get_closest_on_play(self, play): - session = self._database_session.get_or_create_session() - - # TODO: Make this a MAX query for ties - converted_guesses = self.__convert_all__( - session - .query(Guess) - .filter(Guess.play_id == play) - .order_by(Guess.difference) - .limit(1) - ) - - if len(converted_guesses) > 1: - raise AssertionError("More than one best guess! Can't continue!") - elif len(converted_guesses) == 0: - return None - else: - return converted_guesses[0] \ No newline at end of file diff --git a/src/main/database_module/play_dao.py b/src/main/database_module/play_dao.py deleted file mode 100644 index 2bcaf2a..0000000 --- a/src/main/database_module/play_dao.py +++ /dev/null @@ -1,96 +0,0 @@ -from copy import deepcopy -from sqlalchemy.sql.expression import and_ - -from src.main.db_session import DatabaseSession -from src.main.database_module.database_classes.db_classes import Play - -import datetime - -PLAY_ID = 'play_id' -PITCH_VALUE = 'pitch_value' -CREATION_DATE = 'creation_date' -SERVER_ID = 'server_id' - -class PlayDAO(): - db_string = None - session = None - Session = None - engine = None - _database_session = None - - def __init__(self): - self._database_session = DatabaseSession() - - def insert(self, play_info): - session = self._database_session.get_or_create_session() - - play = Play( - play_id = play_info[PLAY_ID], - pitch_value = play_info[PITCH_VALUE] if PITCH_VALUE in play_info else None, - creation_date = play_info[CREATION_DATE], - server_id = play_info[SERVER_ID] - ) - - session.add(play) - session.commit() - - def get_play_by_id(self, input_id): - session = self._database_session.get_or_create_session() - return self.__convert_all__(session.query(Play).filter(Play.play_id == input_id)) - - def get_all_plays_after(self, timestamp, input_server_id): - session = self._database_session.get_or_create_session() - return self.__convert_all__(session.query(Play).filter(and_(Play.server_id == str(input_server_id), Play.creation_date > timestamp))) - - def get_all_plays_on_server(self, input_server_id, earliest_timestamp): - session = self._database_session.get_or_create_session() - converted_datetime = datetime.datetime.fromtimestamp(earliest_timestamp / 1000.0) - - return self.__convert_all__(session.query(Play).filter(and_(Play.server_id == str(input_server_id), Play.creation_date > converted_datetime))) - - ''' - Checks to see if there is a play that is currently active or not - ''' - def is_active_play(self, server_id): - return self.get_active_play(server_id) != None - - def get_active_play(self, input_server_id): - session = self._database_session.get_or_create_session() - plays = self.__convert_all__(session.query(Play).filter(and_(Play.pitch_value == None, Play.server_id == str(input_server_id)))) - - if len(plays) > 1: - raise AssertionError("More than one active play! Can't continue!") - elif len(plays) == 0: - return None - else: - return plays[0] - - def resolve_play(self, input_pitch, input_server_id): - session = self._database_session.get_or_create_session() - active_id = self.get_active_play(input_server_id) - - session\ - .query(Play)\ - .filter(and_(Play.pitch_value == None, Play.server_id == str(input_server_id)))\ - .update({Play.pitch_value: input_pitch}) - session.commit() - - return active_id - - def refresh(self): - self._database_session.__create_new_session__() # I know, I know. It's fine. - - ''' - Converts the database object into a Dictionary, so that the database object is not passed out of the - datastore layer. - ''' - def __convert_all__(self, plays): - converted_plays = [] - for play in plays: - play_dict = {} - for column in play.__dict__: - play_dict[column] = str(getattr(play, column)) - - converted_plays.append(deepcopy(play_dict)) - - return converted_plays diff --git a/src/main/db_session.py b/src/main/db_session.py deleted file mode 100644 index e0c65b6..0000000 --- a/src/main/db_session.py +++ /dev/null @@ -1,49 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - -import sys -import sqlite3 -sys.path.append('../../../../../../src') - -from src.main.configs import Configs, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_HOST, DATABASE_NAME - -''' -Stores a database session for use throughout the application. Must be initialized at startup before any database calls -are made and AFTER the Configurations are setup. - -This shouldn't need to be touched after startup. To use, see the sqlalchemy docs...or just start by calling Session() -and then use it to handle the necessary CRUD operations. - -You should NOT instantiate this in any method except the main application runner -''' -class DatabaseSession(): - _session = None - - def __init__(self): - self.__create_new_session__() - - def __create_new_session__(self): - if self._session is not None: - self._session.close() - - config_map = Configs.configs - db_string = self._pgsql_conn_string_(config_map) - Session = sessionmaker(create_engine(db_string)) - self._session = Session() - - return self._session - - def get_or_create_session(self): - try: - self._session.connection() - return self._session - except: # The linter can scream all it wants, this makes sense. If it's this broke, we want a new one anyway. - return self.__create_new_session__() - - # Look, this kinda sucks. But it's for fun and friends and I'm doing it quick and dirty. - def _pgsql_conn_string_(self, config_map): - return 'postgresql://%s:%s@%s/%s' % \ - (config_map[DATABASE_USERNAME], config_map[DATABASE_PASSWORD], config_map[DATABASE_HOST], config_map[DATABASE_NAME]) - - def _sqlite_conn_string(self, config_map): - return "sqlite:///ghostball.db" \ No newline at end of file diff --git a/src/main/discord_module/__init__.py b/src/main/discord_module/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/discord_module/bot_runner.py b/src/main/discord_module/bot_runner.py deleted file mode 100644 index 7471a2c..0000000 --- a/src/main/discord_module/bot_runner.py +++ /dev/null @@ -1,220 +0,0 @@ -import sys - -import discord -from discord.utils import get - -import uuid -import datetime -import dateparser - -from src.main.configs import Configs -from src.main.database_module.guess_dao import GuessDAO, GUESSED_NUMBER, MEMBER_ID, MEMBER_NAME, DIFFERENCE -from src.main.services.points_service import PointsService -from src.main.database_module.play_dao import PlayDAO, PLAY_ID, CREATION_DATE, SERVER_ID -from src.main.db_session import DatabaseSession -from src.main.discord_module.leaderboard_config import LeaderboardConfig - -play_dao = None -guess_dao = None -points_service = PointsService() -bot = discord.Client() - - -@bot.event -async def on_ready(): - print('Logged in as') - print(bot.user.name) - print(bot.user.id) - print('------') - - -@bot.event -async def on_message(message): - if message.author == bot.user: - return - - content = message.content - server_id = message.guild.id - - ''' - Sets up the next set of guesses. - ''' - if content.startswith('!ghostball'): - if play_dao.is_active_play(server_id): - await message.channel.send("There's already an active play. Could you close that one first, please?") - else: - generated_play_id = uuid.uuid4() - play_object = {PLAY_ID: generated_play_id, CREATION_DATE: datetime.datetime.now(), SERVER_ID: server_id} - play_dao.insert(play_object) - - await message.channel.send("@flappy ball, pitch is in! Send me your guesses with a !guess command.") - - if content.startswith("!guess"): - guess_value = None - try: - guess_value = __parse_guess__(content) - except ValueError: - await message.channel.send("That number is not between 1 and 1000. We're still in MLN so don't try to cheat.") - return - - if guess_value is None: - await message.channel.send("I don't know what you did but I'm pretty sure you're tyring to break the bot so please stop.") - return - - if not play_dao.is_active_play(server_id): - await message.channel.send("Hey, there's no active play! Start one up first with !ghostball.") - else: - play = play_dao.get_active_play(server_id) - guess_object = {PLAY_ID: play['play_id'], - MEMBER_ID: str(message.author.id), - GUESSED_NUMBER: guess_value, - MEMBER_NAME: str(message.author.name)} - - if guess_dao.insert(guess_object, allow_update=True): - await message.add_reaction(emoji="\N{THUMBS UP SIGN}") - - # Closes off the active play to be ready for the next set - if content.startswith('!resolve'): - # try: - pitch_number, batter_id, batter_guess, has_batter = __parse_resolve_play__(content) - if args is None: - await message.channel.send("Hey " + "<@" + str(message.author.id) + ">, I'm not sure what you meant. " - "You need real, numeric, values for this command to work. " - "Use !resolve " - " and try again.") - - # Check if we have an active play - if not play_dao.is_active_play(server_id): - await message.channel.send("You confused me. There's no active play so I have nothing to close!") - else: - if has_batter: - referenced_member_id = batter_id[3:-1] - play = play_dao.get_active_play(server_id) - guess_object = {PLAY_ID: play['play_id'], - MEMBER_ID: str(referenced_member_id), - GUESSED_NUMBER: batter_guess, - MEMBER_NAME: bot.get_user(int(referenced_member_id)).name} - - guess_dao.insert(guess_object, True) - - play = play_dao.resolve_play(pitch_number, server_id) - guess_dao.set_differences(pitch_number, play['play_id']) - guesses = points_service.fetch_sorted_guesses_by_play(guess_dao, play['play_id']) - - response_message = "Closed this play! Here are the results:\n" - response_message += "PLAYER --- DIFFERENCE --- POINTS GAINED\n" - for guess in guesses: - response_message += guess[1] + " --- " + str(guess[2]) + " --- " + str(guess[3]) + "\n" - - if len(guesses) < 2: - response_message += "Not enough people participated to give best and worst awards. Stop being lazy." - - else: - response_message += "\nCongrats to <@" + str(guesses[0][0]) + "> for being the closest! \n" - response_message += "And tell <@" + str(guesses[-1][0]) + "> they suck." - - await message.channel.send(response_message) - - if content.startswith("!points"): - try: - timestamp = __parse_points_message__(content) - except: - await message.channel.send("You gave me a timestamp that was so bad, the best date handling library in the" - " world of software couldn't figure out what you meant. That's...impressive. Now" - " fix your shit and try again.") - return - - points_by_user = points_service.fetch_points(timestamp, server_id, play_dao, guess_dao) - response = "Here are the top guessers by points as per your request..." - for user in points_by_user: - if str(user[2]) != '0': - response += "\n" + str(user[1]) + " : " + str(user[2]) - - await message.channel.send(response) - - # Refresh Postgres connection - if content.startswith('!restart'): - play_dao.refresh() - guess_dao.refresh() - - if content.startswith('!help'): - help_message = __get_help_message__() - recipient = await bot.fetch_user(message.author.id) - await recipient.send(help_message) - - -def __get_help_message__(): - # Start message with person who asked for help - help_message = "Hey! I can be instructed to do any number of things! Use the following commands: \n" \ - "!guess --- This will add your guess to the currently active play. " \ - "I will give you a thumbs up if everything worked!\n" \ - "!ghostball --- Starts a new play. I'll let you know if this didn't work for some reason!\n" \ - "!help --- You just asked for this. If you ask for it again, I'll repeat myself.\n" \ - "!resolve --- " \ - "Uses the pitch number and real swing number to figure out who was closest and ends the active play." \ - "If you include the batter and their swing number, they will get credit for how well they did!\n" \ - "!points Fetches all plays since your requested time, or the beginning of the unvierse " \ - "if none given. Will currently always dump all players - top X coming soon...\n" \ - "!restart --- If the bot looks broken, this will take a shot at fixing it. It won't answer your commands " \ - "for about 3 seconds after you do this! BE CAREFUL! ONLY USE IN AN EMERGENCY!\n" \ - "\n" - - return help_message - - -def __parse_leaderboard_message__(message_content): - return LeaderboardConfig(message_content) - - -def __parse_points_message__(message_content): - pieces = message_content.split(' ') - - if len(pieces) > 1: - try: - timestamp = dateparser.parse(pieces[1]) - except: - raise RuntimeError("Unable to parse timestamp!") - else: - timestamp = dateparser.parse("1970-01-01") - - return timestamp - - -def __parse_guess__(message_content): - pieces = message_content.split(' ') - try: - guess_value = pieces[1] - guess_as_int = int(guess_value) - if guess_as_int > 1000 or guess_as_int < 1: - raise ValueError("Number not between 1 and 1000 inclusive") - else: - return guess_value - except TypeError: - return None - - -def __parse_resolve_play__(message_content): - pieces = message_content.split() - try: - if len(pieces) == 2: - return pieces[1], None, None, False - elif len(pieces) == 4: - return pieces[1], pieces[2], pieces[3], True - else: - print("Illegal resolution command") - return None, None - except TypeError: - return None, None - - -if __name__ == '__main__': - args = sys.argv - token = args[1] - file_path = args[2] - - configs = Configs(file_path) - databaseSession = DatabaseSession() - - play_dao = PlayDAO() - guess_dao = GuessDAO() - bot.run(token) diff --git a/src/main/discord_module/leaderboard_config.py b/src/main/discord_module/leaderboard_config.py deleted file mode 100644 index ff38fd0..0000000 --- a/src/main/discord_module/leaderboard_config.py +++ /dev/null @@ -1,15 +0,0 @@ -class LeaderboardConfig(): - closest = True - def __init__(self, message_content): - pieces = message_content.split(' ') - if len(pieces) == 1: - return - - if pieces[1] == 'average': - self.closest = False - - def should_sort_by_pure_closest(self): - return self.closest - - def should_sort_by_best_average(self): - return not self.closest diff --git a/src/main/services/__init__.py b/src/main/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/services/points_service.py b/src/main/services/points_service.py deleted file mode 100644 index 9093a8f..0000000 --- a/src/main/services/points_service.py +++ /dev/null @@ -1,48 +0,0 @@ - - -class PointsService(): - _point_table = [(5,25),(25, 75), (75, 50), (150, 25)] - - def fetch_points(self, timestamp, server_id, play_dao, guess_dao): - plays = play_dao.get_all_plays_after(timestamp, server_id) - all_guesses = guess_dao.get_all_guesses_for_plays(x['play_id'] for x in plays) - - # Build a dictionary of each member and their total points - totals_by_player = {} - for guess in all_guesses: - if guess['member_id'] in totals_by_player: - totals_by_player[guess['member_id']]['points'] += self.__get_points_for_diff__(guess['difference']) - else: - totals_by_player[guess['member_id']] = {} - totals_by_player[guess['member_id']]['points'] = self.__get_points_for_diff__(guess['difference']) - totals_by_player[guess['member_id']]['member_name'] = guess['member_name'] - - # And now pull those numbers out into a list and sort them - sorted_players = [] - for player in totals_by_player: - sorted_players.append([player, - totals_by_player[player]['member_name'], - totals_by_player[player]['points']]) - - sorted_players.sort(key=lambda x: x[2], reverse=True) - return sorted_players - - def fetch_sorted_guesses_by_play(self, guess_dao, play_id): - all_guesses = guess_dao.get_all_guesses_for_plays([play_id]) - player_list = [] - for guess in all_guesses: - player_list.append([guess['member_id'], guess['member_name'], int(guess['difference']), self.__get_points_for_diff__(guess['difference'])]) - - player_list.sort(key=lambda x: x[2]) - return player_list - - # Iterates through the point table, which we assume is sorted, and gets the points - def __get_points_for_diff__(self, diff): - if diff == 'None': - return 0 - - for i in range(0, len(self._point_table)): - if int(diff) < self._point_table[i][0]: - return self._point_table[i][1] - - return 0 diff --git a/src/test/__init__.py b/src/test/__init__.py deleted file mode 100644 index e69de29..0000000