From b04cabe0fbe69ba1c8d1f88d0c1c33e1a8226f31 Mon Sep 17 00:00:00 2001 From: Alan Date: Fri, 19 Sep 2025 00:11:51 -0600 Subject: [PATCH 1/3] added processtext endpoint --- main_template.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/main_template.py b/main_template.py index 2b15666..26396a7 100644 --- a/main_template.py +++ b/main_template.py @@ -7,7 +7,6 @@ swagger = Swagger(app) class UppercaseText(Resource): - def get(self): """ This method responds to the GET request for this endpoint and returns the data in uppercase. @@ -36,6 +35,62 @@ def get(self): return jsonify({"text": text.upper()}) +class ProcessText(Resource): + def get(self): + """ + This method responds to the GET request for processing text and returns the processed text. + --- + tags: + - Text Processing + parameters: + - name: text + 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 times to duplicate the text + - name: capitalization + in: query + type: string + required: false + enum: [UPPER, LOWER, None] + description: The capitalization style for the text + responses: + 200: + description: A successful GET request + content: + application/json: + schema: + type: object + properties: + processed_text: + type: string + description: The processed text + """ + text = request.args.get('text') + duplication_factor = int(request.args.get('duplication_factor', 1)) + capitalization = request.args.get('capitalization', 'None') + + # Validate capitalization input + if capitalization not in ['UPPER', 'LOWER', 'None']: + return {"error": "Invalid capitalization value"}, 400 + + # Process the text based on duplication_factor and capitalization + if capitalization == 'UPPER': + text = text.upper() + elif capitalization == 'LOWER': + text = text. lower() + + processed_text = text * duplication_factor + + return {"processed_text": processed_text}, 200 + +api.add_resource(ProcessText, "/process_text") + api.add_resource(UppercaseText, "/uppercase") if __name__ == "__main__": From 80ae6d0f78bd373d6728f1ae7e7af7bd4a75438d Mon Sep 17 00:00:00 2001 From: Alan Date: Fri, 19 Sep 2025 07:21:12 -0600 Subject: [PATCH 2/3] Added processtext endpoint --- api_request_test.py | 12 +++ app.py | 232 +++++++++++++++++++++++++++----------------- 2 files changed, 154 insertions(+), 90 deletions(-) create mode 100644 api_request_test.py diff --git a/api_request_test.py b/api_request_test.py new file mode 100644 index 0000000..4d5ea93 --- /dev/null +++ b/api_request_test.py @@ -0,0 +1,12 @@ +import requests + +base_url = 'https://ac-book-review-api.onrender.com/' + +params = {'text': 'hello world'} + +response = requests.get(base_url, params=params) +try: + print(response.json()) +except ValueError: + print("Non-JSON response:", response.status_code) + print(response.text[:300]) diff --git a/app.py b/app.py index 997d548..b313ff0 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ from flask_restful import Api, Resource from flasgger import Swagger -import book_review +import book_review # Airtable helpers app = Flask(__name__) api = Api(app) @@ -11,127 +11,179 @@ class UppercaseText(Resource): def get(self): """ - This method responds to the GET request for this endpoint and returns the data in uppercase. + Return the text in uppercase. --- tags: - - Text Processing + - Text Processing parameters: - - name: text - in: query - type: string - required: true - description: The text to be converted to uppercase + - name: text + in: query + type: string + required: true + description: The text to convert to uppercase responses: - 200: - description: A successful GET request - content: - application/json: - schema: - type: object - properties: - text: - type: string - description: The text in uppercase + 200: + description: Successful request + schema: + type: object + properties: + text: + type: string """ text = request.args.get('text') - + if not text: + return jsonify({"error": "Missing required parameter: text"}), 400 return jsonify({"text": text.upper()}) - + + +class ProcessText(Resource): + def get(self): + """ + Process text with optional duplication and capitalization. + --- + tags: + - Text Processing + parameters: + - name: text + in: query + type: string + required: true + description: The text to be processed + - name: duplication_factor + in: query + type: integer + required: false + default: 1 + description: Number of times to duplicate the (possibly transformed) text + - name: capitalization + in: query + type: string + required: false + default: NONE + enum: + - UPPER + - LOWER + - NONE + description: Capitalization option (UPPER/LOWER/NONE) + responses: + 200: + description: Successful processing + schema: + type: object + properties: + result: + type: string + 400: + description: Bad request + schema: + type: object + properties: + error: + type: string + """ + text = request.args.get('text') + if not text: + return jsonify({"error": "Missing required parameter: text"}), 400 + + dup_raw = request.args.get('duplication_factor', '1') + try: + duplication_factor = int(dup_raw) + except ValueError: + return jsonify({"error": "duplication_factor must be an integer"}), 400 + if duplication_factor < 0: + return jsonify({"error": "duplication_factor must be >= 0"}), 400 + + cap = (request.args.get('capitalization', 'NONE') or 'NONE').upper() + if cap not in ('UPPER', 'LOWER', 'NONE'): + return jsonify({"error": "capitalization must be one of UPPER, LOWER, NONE"}), 400 + + if cap == 'UPPER': + text = text.upper() + elif cap == 'LOWER': + text = text.lower() + + result = text * duplication_factor + return jsonify({"result": result}) + + class Records(Resource): def get(self): """ - This method responds to the GET request for returning a number of books. + Return a list of books from Airtable. --- tags: - - Records + - Records parameters: - - name: count - in: query - type: integer - required: false - description: The number of books to return - - name: sort - in: query - type: string - enum: ['ASC', 'DESC'] - required: false - description: Sort order for the books + - name: count + in: query + type: integer + required: false + description: Max number of books to return + - name: sort + in: query + type: string + required: false + enum: + - ASC + - DESC + description: Sort by Rating ascending or descending responses: - 200: - description: A successful GET request - schema: + 200: + description: Successful request + schema: + type: object + properties: + books: + type: array + items: 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 + count = request.args.get('count') 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. + Add a new book record to Airtable. --- tags: - - Records + - 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) + - in: body + name: body + required: true + schema: + id: BookReview + required: + - Book + - Rating + properties: + Book: + type: string + Rating: + type: integer responses: - 200: - description: A successful POST request - 400: - description: Bad request, missing 'Book' or 'Rating' in the request body + 200: + description: Record added + 400: + description: Missing fields """ - - data = request.json - print(data) - - # Check if 'Book' and 'Rating' are present in the request body + data = request.get_json(force=True, silent=True) or {} 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) + return ({"message": "Record added successfully"}, 200) if success else ({"message": "Failed to add record"}, 500) - if success: - return {"message": "Record added successfully"}, 200 - else: - return {"message": "Failed to add record"}, 500 - - -api.add_resource(AddRecord, "/add-record") -api.add_resource(Records, "/records") +# ROUTES api.add_resource(UppercaseText, "/uppercase") +api.add_resource(ProcessText, "/process_text") # <-- now available here +api.add_resource(Records, "/records") +api.add_resource(AddRecord, "/add-record") if __name__ == "__main__": - app.run(debug=True) \ No newline at end of file + # For Render, you'll typically use gunicorn; this dev server is fine locally. + app.run(debug=True) From eae47f9472e292574fd17eec8925ab3a73d93a21 Mon Sep 17 00:00:00 2001 From: Alan Date: Fri, 19 Sep 2025 10:24:52 -0600 Subject: [PATCH 3/3] Final code --- api_request_test.py | 13 +- app.py | 317 ++++++++++++++++++++++++-------------------- book_review.py | 69 +++------- main_template.py | 97 -------------- 4 files changed, 202 insertions(+), 294 deletions(-) delete mode 100644 main_template.py diff --git a/api_request_test.py b/api_request_test.py index 4d5ea93..30feee2 100644 --- a/api_request_test.py +++ b/api_request_test.py @@ -1,12 +1,11 @@ import requests +import json -base_url = 'https://ac-book-review-api.onrender.com/' +base_url = 'https://kg-book-review-api.onrender.com/all_reviews' -params = {'text': 'hello world'} +params = {'max_records': 3, 'sort': 'DESC'} +# body = {'book': 'The Alchemist', 'rating': 7.2, 'notes': 'A classic!'} response = requests.get(base_url, params=params) -try: - print(response.json()) -except ValueError: - print("Non-JSON response:", response.status_code) - print(response.text[:300]) + +print(response.json()) \ No newline at end of file diff --git a/app.py b/app.py index b313ff0..9d2e468 100644 --- a/app.py +++ b/app.py @@ -2,188 +2,219 @@ from flask_restful import Api, Resource from flasgger import Swagger -import book_review # Airtable helpers +import book_review app = Flask(__name__) api = Api(app) swagger = Swagger(app) -class UppercaseText(Resource): - def get(self): +br = book_review.BookReview() + +class PostReview(Resource): + def post(self): """ - Return the text in uppercase. + This method responds to the POST request for adding a book review to the database. --- tags: - - Text Processing + - Book Reviews parameters: - - name: text - in: query - type: string - required: true - description: The text to convert to uppercase + - 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) + notes: + type: string + default: "" + description: any additional notes about the book responses: - 200: - description: Successful request - schema: - type: object - properties: - text: - type: string + 201: + description: A successful POST request + content: + application/json: + schema: + type: object + properties: + message: + type: string + description: A success message + 400: + description: Bad request if the required fields are missing """ - text = request.args.get('text') - if not text: - return jsonify({"error": "Missing required parameter: text"}), 400 - return jsonify({"text": text.upper()}) + data = request.json + print(data) -class ProcessText(Resource): + if not data: + return {"error": "Request body must be in JSON format."}, 400 + + book = data.get('book') + review = data.get('rating') + notes = data.get('notes', '') + + # Check if the required fields are provided + if not book or not review: + return {"error": "Both 'book' and 'rating' are required fields."}, 400 + + # Add the new review to the database + br.add_book_rating(book, review, notes) + + return {"message": "Book review added successfully."}, 201 + +class AllReviews(Resource): def get(self): """ - Process text with optional duplication and capitalization. + This method responds to the GET request for retrieving all book reviews. --- tags: - - Text Processing + - Book Reviews parameters: - - name: text - in: query - type: string - required: true - description: The text to be processed - - name: duplication_factor - in: query - type: integer - required: false - default: 1 - description: Number of times to duplicate the (possibly transformed) text - - name: capitalization - in: query - type: string - required: false - default: NONE - enum: - - UPPER - - LOWER - - NONE - description: Capitalization option (UPPER/LOWER/NONE) + - name: sort + in: query + type: string + required: false + enum: [ASC, DESC] + description: Sort order for reviews (ascending or descending) + - name: max_records + in: query + type: integer + required: false + description: Maximum number of records to retrieve responses: - 200: - description: Successful processing - schema: - type: object - properties: - result: - type: string - 400: - description: Bad request - schema: - type: object - properties: - error: - type: string + 200: + description: A successful GET request + content: + application/json: + schema: + type: array + items: + type: object + properties: + book_title: + type: string + description: The book title + book_rating: + type: number + description: The book rating + book_notes: + type: string + description: The book review """ - text = request.args.get('text') - if not text: - return jsonify({"error": "Missing required parameter: text"}), 400 - - dup_raw = request.args.get('duplication_factor', '1') - try: - duplication_factor = int(dup_raw) - except ValueError: - return jsonify({"error": "duplication_factor must be an integer"}), 400 - if duplication_factor < 0: - return jsonify({"error": "duplication_factor must be >= 0"}), 400 - - cap = (request.args.get('capitalization', 'NONE') or 'NONE').upper() - if cap not in ('UPPER', 'LOWER', 'NONE'): - return jsonify({"error": "capitalization must be one of UPPER, LOWER, NONE"}), 400 - - if cap == 'UPPER': - text = text.upper() - elif cap == 'LOWER': - text = text.lower() + sort = request.args.get('sort', default=None) + max_records = int(request.args.get('max_records', default=10)) + + # Validate the sort parameter + if sort and sort not in ['ASC', 'DESC']: + return {"error": "Invalid sort value"}, 400 - result = text * duplication_factor - return jsonify({"result": result}) + # Sort the reviews based on the 'sort' parameter + if sort == 'ASC': + book_reviews = br.get_book_ratings(sort=sort, max_records=max_records) + elif sort == 'DESC': + book_reviews = br.get_book_ratings(sort=sort, max_records=max_records) + else: + book_reviews = br.get_book_ratings(max_records=max_records) + return book_reviews, 200 -class Records(Resource): + +class UppercaseText(Resource): def get(self): """ - Return a list of books from Airtable. + This method responds to the GET request for this endpoint and returns the data in uppercase. --- tags: - - Records + - Text Processing parameters: - - name: count - in: query - type: integer - required: false - description: Max number of books to return - - name: sort - in: query - type: string - required: false - enum: - - ASC - - DESC - description: Sort by Rating ascending or descending + - name: text + in: query + type: string + required: true + description: The text to be converted to uppercase responses: - 200: - description: Successful request - schema: - type: object - properties: - books: - type: array - items: - type: object + 200: + description: A successful GET request + content: + application/json: + schema: + type: object + properties: + text: + type: string + description: The text in uppercase """ - count = request.args.get('count') - sort = request.args.get('sort') - books = book_review.get_all_records(count=count, sort=sort) - return {"books": books}, 200 - + text = request.args.get('text') -class AddRecord(Resource): - def post(self): + return {"text": text.upper()}, 200 + +class ProcessText(Resource): + def get(self): """ - Add a new book record to Airtable. + This method responds to the GET request for processing text and returns the processed text. --- tags: - - Records + - Text Processing parameters: - - in: body - name: body - required: true - schema: - id: BookReview - required: - - Book - - Rating - properties: - Book: - type: string - Rating: - type: integer + - name: text + 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 times to duplicate the text + - name: capitalization + in: query + type: string + required: false + enum: [UPPER, LOWER, None] + description: The capitalization style for the text responses: - 200: - description: Record added - 400: - description: Missing fields + 200: + description: A successful GET request + content: + application/json: + schema: + type: object + properties: + processed_text: + type: string + description: The processed text """ - data = request.get_json(force=True, silent=True) or {} - if 'Book' not in data or 'Rating' not in data: - return {"message": "Bad request, missing 'Book' or 'Rating' in the request body"}, 400 - success = book_review.add_record(data) - return ({"message": "Record added successfully"}, 200) if success else ({"message": "Failed to add record"}, 500) + text = request.args.get('text') + duplication_factor = int(request.args.get('duplication_factor', 1)) + capitalization = request.args.get('capitalization', 'None') + + # Validate capitalization input + if capitalization not in ['UPPER', 'LOWER', 'None']: + return {"error": "Invalid capitalization value"}, 400 + + # Process the text based on duplication_factor and capitalization + if capitalization == 'UPPER': + text = text.upper() + elif capitalization == 'LOWER': + text = text.lower() + + processed_text = text * duplication_factor + return {"processed_text": processed_text}, 200 -# ROUTES +api.add_resource(PostReview, "/review") +api.add_resource(AllReviews, "/all_reviews") +api.add_resource(ProcessText, "/process_text") api.add_resource(UppercaseText, "/uppercase") -api.add_resource(ProcessText, "/process_text") # <-- now available here -api.add_resource(Records, "/records") -api.add_resource(AddRecord, "/add-record") if __name__ == "__main__": - # For Render, you'll typically use gunicorn; this dev server is fine locally. - app.run(debug=True) + app.run(debug=True) \ No newline at end of file diff --git a/book_review.py b/book_review.py index dd0725d..2f5f8a6 100644 --- a/book_review.py +++ b/book_review.py @@ -1,54 +1,29 @@ -import os +import os from pyairtable import Api -API_TOKEN = os.environ.get('AIRTABLE_TOKEN') +class BookReview: + def __init__(self): + self.api = Api(os.environ['AIRTABLE_API_KEY']) + self.table = self.api.table('appWM08dyTvVAHd5e', 'tbl67ZaTNCeuPLGfI') -BASE_ID = 'appi1uzlLKn1TEKSw' -TABLE_ID = 'tblvMMAVHo901m2Ra' + def get_book_ratings(self, sort="ASC", max_records=10): + if not sort: + return self.table.all(max_records=max_records) + elif sort == "ASC": + rating = ["Rating"] + elif sort == "DESC": + rating = ["-Rating"] + + table = self.table.all(sort=rating, max_records=max_records) + return table -api = Api(API_TOKEN) + def add_book_rating(self, book_title, book_rating, notes=None): + fields = {'Book': book_title, 'Rating': book_rating, 'Notes': notes} + self.table.create(fields=fields) -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 + br = BookReview() + # br.add_book_rating('Infinite Jest', 7.0) + get_book_ratings = br.get_book_ratings(sort="DESC", max_records=1) + print(get_book_ratings) \ No newline at end of file diff --git a/main_template.py b/main_template.py deleted file mode 100644 index 26396a7..0000000 --- a/main_template.py +++ /dev/null @@ -1,97 +0,0 @@ -from flask import Flask, jsonify, request -from flask_restful import Api, Resource -from flasgger import Swagger - -app = Flask(__name__) -api = Api(app) -swagger = Swagger(app) - -class UppercaseText(Resource): - def get(self): - """ - This method responds to the GET request for this endpoint and returns the data in uppercase. - --- - tags: - - Text Processing - parameters: - - name: text - in: query - type: string - required: true - description: The text to be converted to uppercase - responses: - 200: - description: A successful GET request - content: - application/json: - schema: - type: object - properties: - text: - type: string - description: The text in uppercase - """ - text = request.args.get('text') - - return jsonify({"text": text.upper()}) - -class ProcessText(Resource): - def get(self): - """ - This method responds to the GET request for processing text and returns the processed text. - --- - tags: - - Text Processing - parameters: - - name: text - 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 times to duplicate the text - - name: capitalization - in: query - type: string - required: false - enum: [UPPER, LOWER, None] - description: The capitalization style for the text - responses: - 200: - description: A successful GET request - content: - application/json: - schema: - type: object - properties: - processed_text: - type: string - description: The processed text - """ - text = request.args.get('text') - duplication_factor = int(request.args.get('duplication_factor', 1)) - capitalization = request.args.get('capitalization', 'None') - - # Validate capitalization input - if capitalization not in ['UPPER', 'LOWER', 'None']: - return {"error": "Invalid capitalization value"}, 400 - - # Process the text based on duplication_factor and capitalization - if capitalization == 'UPPER': - text = text.upper() - elif capitalization == 'LOWER': - text = text. lower() - - processed_text = text * duplication_factor - - return {"processed_text": processed_text}, 200 - -api.add_resource(ProcessText, "/process_text") - -api.add_resource(UppercaseText, "/uppercase") - -if __name__ == "__main__": - app.run(debug=True) \ No newline at end of file