diff --git a/.gitignore b/.gitignore index 81cc48e521bef9bac816ab4e4b0ddae62594384d..250c32e382d8658d68cb134371e02fba8306885e 100644 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,5 @@ ENV/ # pytest .pytest_cache +# Ignore config file with sensitive information +config.ini diff --git a/README.rst b/README.rst index c930acfddcced0092ef36525c9774a885e2e7d0e..1bd22c09c54388fb08461e31f6f7ad8ef596d651 100644 --- a/README.rst +++ b/README.rst @@ -2,3 +2,37 @@ Formula One Module Repository ======================== This Ptyhon project downloads and prepares formula one data from the API ergast.com. + +# Formula One Module Repository +## Setup + +1. Clone the repository: + + ```bash + git clone https://github.com/safer-lgtm/formulaone.git + cd formulaone + ``` + +2. Create a `config.ini` file in the root directory of the project: + + ```ini + [AWS] + aws_access_key_id = your_access_key_id + aws_secret_access_key = your_secret_access_key + region_name = your_region + + [DYNAMODB] + table_name = your_table_name + ``` + +3. Install the dependencies: + + ```bash + pip install -r requirements.txt + ``` + +4. Run the tests: + + ```bash + pytest + ``` diff --git a/docs/generated/dynamodb_helpers.rst b/docs/generated/dynamodb_helpers.rst new file mode 100644 index 0000000000000000000000000000000000000000..19260166747828d71ed862a54f030c92c46fd9e3 --- /dev/null +++ b/docs/generated/dynamodb_helpers.rst @@ -0,0 +1,19 @@ +sample.helpers +============== + +.. automodule:: sample.helpers + :members: + :undoc-members: + :show-inheritance: + + .. rubric:: Functions + + .. autosummary:: + :toctree: _autosummary + + get_raw_data_path + get_dynamodb_resource + list_dynamodb_tables + get_movies_table + get_movie_item + query_movies_by_year diff --git a/docs/generated/ergast.rst b/docs/generated/ergast.rst new file mode 100644 index 0000000000000000000000000000000000000000..c072d5365b70ff79e2b2fcce6dc5eaacc78297de --- /dev/null +++ b/docs/generated/ergast.rst @@ -0,0 +1,22 @@ +.. _ergast: + +Ergast API Interface +==================== + +.. currentmodule:: + fastf1.ergast + +Introduction +------------ + +This module can be used to interface with the Ergast F1 API +(https://ergast.com/mrd/). All Ergast endpoints are supported. + +The :class:`Ergast` object provides access to all API Endpoints of the +Ergast API. + +The terms of use of Ergast apply (https://ergast.com/mrd/terms/). +Especially take care not to exceed the specified rate limits. +FastF1 will handle caching and it will try to enforce rate limits where +possible. Make sure to know what limits apply. For more information on how +FastF1 handles caching and rate limiting see :ref:`requests-and-caching`. diff --git a/formulaone/dynamodb_helpers.py b/formulaone/dynamodb_helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..822ebf2fc3f264c29516151d8fceec66d33ecfaf --- /dev/null +++ b/formulaone/dynamodb_helpers.py @@ -0,0 +1,93 @@ +import boto3 +import configparser +from boto3.dynamodb.conditions import Key + +def get_config(): + """ + Reads the configuration file and returns the config object. + + Returns: + config: The config object. + """ + config = configparser.ConfigParser() + config.read('config.ini') + return config + +def get_dynamodb_resource(): + """ + Creates a DynamoDB resource using the AWS session. + + Returns: + dynamo_resource: The DynamoDB resource. + """ + config = get_config() + aws_access_key_id = config['AWS']['aws_access_key_id'] + aws_secret_access_key = config['AWS']['aws_secret_access_key'] + region_name = config['AWS']['region_name'] + + session = boto3.Session( + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + region_name=region_name + ) + + dynamo_resource = session.resource('dynamodb') + return dynamo_resource + +def list_dynamodb_tables(dynamo_resource): + """ + Lists all tables in the DynamoDB resource. + + Args: + dynamo_resource: The DynamoDB resource. + + Returns: + list: List of table names. + """ + tables = [table.name for table in dynamo_resource.tables.all()] + return tables + +def get_movies_table(dynamo_resource): + """ + Gets the movies table from DynamoDB. + + Args: + dynamo_resource: The DynamoDB resource. + + Returns: + table: The DynamoDB table resource for movies. + """ + config = get_config() + table_name = config['DYNAMODB']['table_name'] + return dynamo_resource.Table(table_name) + +def get_movie_item(table, year, title): + """ + Gets a movie item from the DynamoDB table. + + Args: + table: The DynamoDB table resource. + year: The year of the movie. + title: The title of the movie. + + Returns: + dict: The movie item. + """ + response = table.get_item(Key={'year': year, 'title': title}) + return response.get('Item') + +def query_movies_by_year(table, year): + """ + Queries movies by year from the DynamoDB table. + + Args: + table: The DynamoDB table resource. + year: The year to query. + + Returns: + list: List of movies for the given year. + """ + response = table.query( + KeyConditionExpression=Key('year').eq(year) + ) + return response.get('Items', []) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000000000000000000000000000000000..1ea3aa010b20a8fc3db6b214cab9dc93581f5666 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,23 @@ +[pytest] +minversion = 6.0 + +testpaths = + formulaone + docs + tests + +norecursedirs = + _build + build + +python_files = test_*.py +addopts = + --doctest-glob="*.rst" + --doctest-plus + +filterwarnings = + error + ignore:.*df.iloc.*will attempt to set the values inplace.*:FutureWarning + ignore:.*Attribute s is deprecated and will be removed in Python 3.14.*:DeprecationWarning + ignore:(?s).*Pyarrow will become a required dependency of pandas.*:DeprecationWarning + ignore:(?s).*Passing 'weights' as positional argument.*:FutureWarning diff --git a/requirements.txt b/requirements.txt index e73e418b1c978f856a21a008a0fd97fdb4f693a9..75bd0a169b9d38e2b153222b219f34684f71e94e 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/setup.py b/setup.py index 859c71ae996b0bad92848fe8c40e5e984bce7e1e..8afcb488ae506f61cf649c8930b121888e12b121 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ with open('LICENSE') as f: setup( name='formulaone', - version='0.1.0', + version='0.1.1', # increase version by 0.0.1 description='Downloads and prepares formula one data', long_description=readme, author='Timo Schuerg', diff --git a/tests/test_basic.py b/tests/test_basic.py index 4d0e88683b32552b42b95b2da17a08f0dfac039f..c28980fc60292e881cacc0df1e5fb53310a710d3 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1,9 +1,22 @@ # content of test_sample.py +import os +import sys -from sample.helpers import get_tidy_data_path -import pandas as pd +# add the formulaone model directory to python path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +#from formulaone.sample.helpers import get_tidy_data_path +from formulaone.helpers import get_tidy_data_path +import pandas as pd + def test_check_dataframe_size(): df = pd.read_parquet(get_tidy_data_path() / 'current_race.parquet') assert df.shape[1] == 26 + +def test_file_format(): + # Assuming get_tidy_data_path returns a Path object + file_path = get_tidy_data_path() / 'current_race.parquet' + # Check if the file extension is .parquet + assert file_path.suffix == '.parquet', "File format should be .parquet" diff --git a/tests/test_dynamodb.py b/tests/test_dynamodb.py new file mode 100644 index 0000000000000000000000000000000000000000..28e6837d464ce934f5d4f962201f677a1f6b0920 --- /dev/null +++ b/tests/test_dynamodb.py @@ -0,0 +1,75 @@ +import os +import sys +import pytest +import configparser +import boto3 +from botocore.exceptions import NoCredentialsError, PartialCredentialsError + +# Add the formulaone model directory to the Python path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from formulaone.dynamodb_helpers import ( + get_dynamodb_resource, + list_dynamodb_tables, + get_movies_table, + get_movie_item, + query_movies_by_year +) + +def test_aws_config_keys(): + """Test the necessary AWS config keys.""" + config = configparser.ConfigParser() + config.read(os.path.join(os.path.dirname(__file__), '../config.ini')) + assert 'AWS' in config, "AWS section is missing" + assert 'aws_access_key_id' in config['AWS'], "aws_access_key_id is missing in AWS" + assert 'aws_secret_access_key' in config['AWS'], "aws_secret_access_key is missing in AWS" + assert 'region_name' in config['AWS'], "region_name is missing in AWS" + +def test_dynamodb_config_keys(): + """Test the necessary DynamoDB config keys.""" + config = configparser.ConfigParser() + config.read(os.path.join(os.path.dirname(__file__), '../config.ini')) + assert 'DYNAMODB' in config, "DYNAMODB section is missing" + assert 'table_name' in config['DYNAMODB'], "table_name is missing in DYNAMODB" + +def test_dynamodb_connection(): + """Test the DynamoDB connection using the provided credentials and configuration.""" + # Read the config file + config = configparser.ConfigParser() + config.read(os.path.join(os.path.dirname(__file__), '../config.ini')) + + # Credentials and region + aws_access_key_id = config['AWS']['aws_access_key_id'] + aws_secret_access_key = config['AWS']['aws_secret_access_key'] + region_name = config['AWS']['region_name'] + table_name = config['DYNAMODB']['table_name'] + + # Create session with credentials + session = boto3.Session( + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + region_name=region_name + ) + + # DynamoDB object resource + dynamo_resource = session.resource('dynamodb') + + # Describe the table + try: + table = dynamo_resource.Table(table_name) + response = table.table_status + assert response in ['ACTIVE', 'CREATING', 'UPDATING', 'DELETING'], "Table status should be valid" + except NoCredentialsError: + assert False, "NO CREDENTIALS FOUND!" + except PartialCredentialsError: + assert False, "INCOMPLETE CREDENTIALS!!" + except Exception as e: + assert False, f"UNEXPECTED ERROR: {str(e)}" + +def test_dynamodb_resource(): + """Test the DynamoDB resource initialization.""" + dynamo_resource = get_dynamodb_resource() + assert dynamo_resource is not None, "DynamoDB resource should be initialized" + +if __name__ == "__main__": + pytest.main()