diff --git a/README.md b/README.md index 2bdcf71..97dc57f 100644 --- a/README.md +++ b/README.md @@ -1 +1,47 @@ -# Python API Example +# Building and deploying a Python API + +## About + +In this session we will build some API endpoints, documentation for those endpoints, and the ability to test on a live site deploying on render.com. This is a simple example to get started, but hopefully it will provide the foundation for you all to build more complex APIs in the future. If you have any questions, I recommend leaving them on the YouTube recording of the lunch & learn on Tina's channel. + +## Setup +1. **Fork the repository**: + Click on the 'Fork' button at the top right corner of the repository's GitHub page. This will create a copy of the repository in your own GitHub account. + +2. **Clone your forked repository**: + Make sure to replace YOUR_GITHUB_USERNAME with your username in this command + ```bash + git clone -b lunch-and-learn https://github.com/YOUR_GITHUB_USERNAME/python-api-example.git + +3. **Navigate to directory** + ```bash + cd python-api-example + ``` +4. **Install Python libraries** + ```bash + pip install -r requirements.txt + +5. **Run Flask Endpoint** + ```bash + python3 app.py + ``` + +6. **Navigate to server**
+ Open up URL app is running on (should say when you run above command -- http://127.0.0.1:5000 for example). Navigate to http://127.0.0.1:5000/apidocs to see Swagger endpoint specs. + +7. **Making changes and pushing to your fork**
+ After making your changes, you can push them to your forked GitHub repository. + ```bash + git add -u + git commit -m "Your commit message" + git push origin lunch-and-learn + ``` + + +## Render.com + +We will be using [render.com](https://render.com) to deploy our app. Create an account there and connect your Github using the [following instructions](https://render.com/docs/github) + +## Airtable + +As a very simple and easy to set-up DB, we will be using airtable. I'll show in the live stream how to create an API token to use in Python API requests. diff --git a/app.py b/app.py index 997d548..cbacaff 100644 --- a/app.py +++ b/app.py @@ -2,8 +2,6 @@ from flask_restful import Api, Resource from flasgger import Swagger -import book_review - app = Flask(__name__) api = Api(app) swagger = Swagger(app) @@ -16,7 +14,7 @@ def get(self): tags: - Text Processing parameters: - - name: text + - name: message in: query type: string required: true @@ -35,103 +33,60 @@ def get(self): """ text = request.args.get('text') - return jsonify({"text": text.upper()}) + return {"text": text.upper()}, 200 -class Records(Resource): +class StringGenerator(Resource): def get(self): """ - This method responds to the GET request for returning a number of books. + This method responds to the GET request for this endpoint and generates a modified string. --- tags: - - Records + - Text Processing parameters: - - name: count + - name: message + in: query + type: string + required: true + description: The text to be processed + - name: duplication_factor in: query type: integer required: false - description: The number of books to return - - name: sort + description: Number of times to duplicate the text (default is 1) + - name: capitalization in: query type: string - enum: ['ASC', 'DESC'] required: false - description: Sort order for the books + enum: [UPPER, LOWER] + description: Capitalization of the text (default is no change) responses: 200: description: A successful GET request - schema: - type: object - properties: - books: - type: array - items: - type: object - properties: - title: - type: string - description: The title of the book - author: - type: string - description: The author of the book - """ - - count = request.args.get('count') # Default to returning 10 books if count is not provided - sort = request.args.get('sort') - - # Get all the books - books = book_review.get_all_records(count=count, sort=sort) - - return {"books": books}, 200 - -class AddRecord(Resource): - def post(self): - """ - This method responds to the POST request for adding a new record to the DB table. - --- - tags: - - Records - parameters: - - in: body - name: body - required: true - schema: - id: BookReview - required: - - Book - - Rating - properties: - Book: - type: string - description: the name of the book - Rating: - type: integer - description: the rating of the book (1-10) - responses: - 200: - description: A successful POST request - 400: - description: Bad request, missing 'Book' or 'Rating' in the request body + content: + application/json: + schema: + type: object + properties: + generated_text: + type: string + description: The generated text """ + args = request.args + message = args['message'] + duplication_factor = int(args.get('duplication_factor', 1)) + capitalization = args.get('capitalization', None) - data = request.json - print(data) - - # Check if 'Book' and 'Rating' are present in the request body - if 'Book' not in data or 'Rating' not in data: - return {"message": "Bad request, missing 'Book' or 'Rating' in the request body"}, 400 - # Call the add_record function to add the record to the DB table - success = book_review.add_record(data) + if capitalization == 'UPPER': + message = message.upper() + elif capitalization == 'LOWER': + message = message.lower() - if success: - return {"message": "Record added successfully"}, 200 - else: - return {"message": "Failed to add record"}, 500 - + generated_text = message * duplication_factor + return {"generated_text": generated_text}, 200 -api.add_resource(AddRecord, "/add-record") -api.add_resource(Records, "/records") +api.add_resource(StringGenerator, "/generate") api.add_resource(UppercaseText, "/uppercase") if __name__ == "__main__": - app.run(debug=True) \ No newline at end of file + app.run(debug=True) diff --git a/book_review.py b/book_review.py deleted file mode 100644 index dd0725d..0000000 --- a/book_review.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -from pyairtable import Api - -API_TOKEN = os.environ.get('AIRTABLE_TOKEN') - -BASE_ID = 'appi1uzlLKn1TEKSw' -TABLE_ID = 'tblvMMAVHo901m2Ra' - -api = Api(API_TOKEN) - -table = api.table(BASE_ID, TABLE_ID) - -def get_all_records(count=None, sort=None): - sort_param = [] - if sort and sort.upper()=='DESC': - sort_param = ['-Rating'] - elif sort and sort.upper()=='ASC': - sort_param = ['Rating'] - - return table.all(max_records=count, sort=sort_param) - -def get_record_id(name): - return table.first(formula=f"Book='{name}'")['id'] - -def update_record(record_id, data): - table.update(record_id, data) - - return True - -def add_record(data): - # require data contains a "Book" key and a "Rating" key (data is a dict) - if 'Book' not in data or 'Rating' not in data: - return False - - table.create(data) - return True - -if __name__ == '__main__': - ## Show getting certain records - print("Show getting certain records") - print(table.all(formula="Rating < 5", sort=['-Rating'])) - - ## Show getting a single record - print("Show getting a single record") - - # Replace a record - print("Replace a record") - name = "Test Message" - record_id = table.first(formula=f"Book='{name}'")['id'] - table.update(record_id, {"Rating": 5}) - - ## Show all records - print("All records!") - print(table.all()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index edd09fa..61a6683 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ flask flasgger flask_restful pyairtable -gunicorn \ No newline at end of file +gunicorn +