Updated Files
هذا الالتزام موجود في:
158
fruit_api.py
158
fruit_api.py
@@ -1,9 +1,21 @@
|
||||
# fruit_api.py
|
||||
from flask import Flask, jsonify, request
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# -----------------------
|
||||
# Logging Configuration
|
||||
# -----------------------
|
||||
LOG_FILE = "fruit_api.log"
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_FILE,
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
|
||||
# In-memory database
|
||||
fruits = [
|
||||
{"id": 1, "name": "Apple", "color": "Red", "price": 1.50, "quantity": 100, "category": "Tropical"},
|
||||
@@ -11,33 +23,36 @@ fruits = [
|
||||
{"id": 3, "name": "Orange", "color": "Orange", "price": 1.20, "quantity": 80, "category": "Citrus"}
|
||||
]
|
||||
|
||||
# 1. GET / - Home endpoint
|
||||
# -----------------------
|
||||
# Logging Hooks
|
||||
# -----------------------
|
||||
@app.before_request
|
||||
def log_request():
|
||||
logging.info(
|
||||
f"REQUEST: method={request.method} path={request.path} "
|
||||
f"args={dict(request.args)} body={request.get_json(silent=True)} "
|
||||
f"ip={request.remote_addr}"
|
||||
)
|
||||
|
||||
@app.after_request
|
||||
def log_response(response):
|
||||
logging.info(
|
||||
f"RESPONSE: method={request.method} path={request.path} "
|
||||
f"status={response.status_code} ip={request.remote_addr}"
|
||||
)
|
||||
return response
|
||||
|
||||
# -----------------------
|
||||
# API Endpoints
|
||||
# -----------------------
|
||||
@app.route('/')
|
||||
def home():
|
||||
return jsonify({
|
||||
"message": "Fruit Store API",
|
||||
"version": "1.0",
|
||||
"endpoints": [
|
||||
"GET /fruits - Get all fruits",
|
||||
"GET /fruits/<id> - Get fruit by ID",
|
||||
"POST /fruits - Add new fruit",
|
||||
"PUT /fruits/<id> - Update fruit",
|
||||
"DELETE /fruits/<id> - Delete fruit",
|
||||
"GET /fruits/category/<category> - Get fruits by category",
|
||||
"GET /fruits/search?name=<name> - Search fruits by name"
|
||||
]
|
||||
})
|
||||
return jsonify({"message": "Fruit Store API", "version": "1.0"})
|
||||
|
||||
# 2. GET /fruits - Get all fruits
|
||||
@app.route('/fruits', methods=['GET'])
|
||||
def get_all_fruits():
|
||||
return jsonify({
|
||||
"fruits": fruits,
|
||||
"total": len(fruits),
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
return jsonify({"fruits": fruits, "total": len(fruits)})
|
||||
|
||||
# 3. GET /fruits/<id> - Get specific fruit
|
||||
@app.route('/fruits/<int:fruit_id>', methods=['GET'])
|
||||
def get_fruit(fruit_id):
|
||||
fruit = next((f for f in fruits if f['id'] == fruit_id), None)
|
||||
@@ -45,17 +60,13 @@ def get_fruit(fruit_id):
|
||||
return jsonify({"fruit": fruit})
|
||||
return jsonify({"error": "Fruit not found"}), 404
|
||||
|
||||
# 4. POST /fruits - Create new fruit
|
||||
@app.route('/fruits', methods=['POST'])
|
||||
def create_fruit():
|
||||
data = request.get_json()
|
||||
|
||||
if not data or 'name' not in data:
|
||||
return jsonify({"error": "Name is required"}), 400
|
||||
|
||||
# Generate new ID
|
||||
|
||||
new_id = max([f['id'] for f in fruits]) + 1 if fruits else 1
|
||||
|
||||
new_fruit = {
|
||||
"id": new_id,
|
||||
"name": data['name'],
|
||||
@@ -65,99 +76,16 @@ def create_fruit():
|
||||
"category": data.get('category', 'General'),
|
||||
"created_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
fruits.append(new_fruit)
|
||||
return jsonify({
|
||||
"message": "Fruit created successfully",
|
||||
"fruit": new_fruit
|
||||
}), 201
|
||||
return jsonify({"message": "Fruit created successfully", "fruit": new_fruit}), 201
|
||||
|
||||
# 5. PUT /fruits/<id> - Update fruit
|
||||
@app.route('/fruits/<int:fruit_id>', methods=['PUT'])
|
||||
def update_fruit(fruit_id):
|
||||
fruit = next((f for f in fruits if f['id'] == fruit_id), None)
|
||||
if not fruit:
|
||||
return jsonify({"error": "Fruit not found"}), 404
|
||||
|
||||
data = request.get_json()
|
||||
|
||||
# Update allowed fields
|
||||
if 'name' in data:
|
||||
fruit['name'] = data['name']
|
||||
if 'color' in data:
|
||||
fruit['color'] = data['color']
|
||||
if 'price' in data:
|
||||
fruit['price'] = data['price']
|
||||
if 'quantity' in data:
|
||||
fruit['quantity'] = data['quantity']
|
||||
if 'category' in data:
|
||||
fruit['category'] = data['category']
|
||||
|
||||
fruit['updated_at'] = datetime.now().isoformat()
|
||||
|
||||
return jsonify({
|
||||
"message": "Fruit updated successfully",
|
||||
"fruit": fruit
|
||||
})
|
||||
|
||||
# 6. DELETE /fruits/<id> - Delete fruit
|
||||
@app.route('/fruits/<int:fruit_id>', methods=['DELETE'])
|
||||
def delete_fruit(fruit_id):
|
||||
global fruits
|
||||
fruit = next((f for f in fruits if f['id'] == fruit_id), None)
|
||||
if not fruit:
|
||||
return jsonify({"error": "Fruit not found"}), 404
|
||||
|
||||
fruits = [f for f in fruits if f['id'] != fruit_id]
|
||||
return jsonify({
|
||||
"message": "Fruit deleted successfully",
|
||||
"deleted_fruit": fruit
|
||||
})
|
||||
|
||||
# 7. GET /fruits/category/<category> - Get fruits by category
|
||||
@app.route('/fruits/category/<category>', methods=['GET'])
|
||||
def get_fruits_by_category(category):
|
||||
category_fruits = [f for f in fruits if f['category'].lower() == category.lower()]
|
||||
return jsonify({
|
||||
"category": category,
|
||||
"fruits": category_fruits,
|
||||
"count": len(category_fruits)
|
||||
})
|
||||
|
||||
# Search endpoint
|
||||
@app.route('/fruits/search', methods=['GET'])
|
||||
def search_fruits():
|
||||
name_query = request.args.get('name', '').lower()
|
||||
if not name_query:
|
||||
return jsonify({"error": "Name parameter is required"}), 400
|
||||
|
||||
matching_fruits = [f for f in fruits if name_query in f['name'].lower()]
|
||||
return jsonify({
|
||||
"search_term": name_query,
|
||||
"results": matching_fruits,
|
||||
"count": len(matching_fruits)
|
||||
})
|
||||
|
||||
# Health check endpoint
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
return jsonify({
|
||||
"status": "healthy",
|
||||
"service": "fruit-api",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
return jsonify({"status": "healthy", "service": "fruit-api"})
|
||||
|
||||
# -----------------------
|
||||
# Run API
|
||||
# -----------------------
|
||||
if __name__ == '__main__':
|
||||
print("Fruit API starting on http://localhost:5000")
|
||||
print("Available endpoints:")
|
||||
print("1. GET / - Home")
|
||||
print("2. GET /fruits - All fruits")
|
||||
print("3. GET /fruits/<id> - Fruit by ID")
|
||||
print("4. POST /fruits - Create fruit")
|
||||
print("5. PUT /fruits/<id> - Update fruit")
|
||||
print("6. DELETE /fruits/<id> - Delete fruit")
|
||||
print("7. GET /fruits/category/<category> - Fruits by category")
|
||||
print("8. GET /fruits/search?name=X - Search fruits")
|
||||
print("9. GET /health - Health check")
|
||||
|
||||
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||
app.run(debug=False, host='0.0.0.0', port=5000)
|
||||
|
المرجع في مشكلة جديدة
حظر مستخدم