-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add GitHub Actions CI workflow with pytest
- Loading branch information
Showing
14 changed files
with
1,471 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import sqlite3 | ||
import random | ||
|
||
def create_database_tables(conn): | ||
cursor = conn.cursor() | ||
cursor.execute(''' | ||
CREATE TABLE IF NOT EXISTS animals ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
name TEXT NOT NULL, | ||
species TEXT NOT NULL, | ||
breed TEXT, | ||
age INTEGER, | ||
personality TEXT, | ||
image_path TEXT, | ||
adoption_status TEXT DEFAULT 'Available' | ||
) | ||
''') | ||
cursor.execute(''' | ||
CREATE TABLE IF NOT EXISTS adopters ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
username TEXT UNIQUE NOT NULL, | ||
password TEXT NOT NULL, | ||
email TEXT UNIQUE, | ||
join_date TEXT DEFAULT CURRENT_TIMESTAMP | ||
) | ||
''') | ||
conn.commit() | ||
|
||
def populate_initial_data(conn): | ||
cursor = conn.cursor() | ||
|
||
# Sample animals data | ||
animals = [ | ||
('Luna', 'Dog', 'Labrador Mix', 2, 'Playful and energetic', '/images/luna.jpg', 'Available'), | ||
('Oliver', 'Cat', 'Tabby', 4, 'Independent but affectionate', '/images/oliver.jpg', 'Available'), | ||
('Max', 'Dog', 'German Shepherd', 3, 'Loyal and intelligent', '/images/max.jpg', 'Available') | ||
] | ||
|
||
for animal in animals: | ||
cursor.execute(''' | ||
INSERT INTO animals (name, species, breed, age, personality, image_path, adoption_status) | ||
VALUES (?, ?, ?, ?, ?, ?, ?) | ||
''', animal) | ||
|
||
conn.commit() | ||
|
||
if __name__ == "__main__": | ||
print("Initializing PawsAndWhiskers adoption database...") | ||
conn = sqlite3.connect('animal_shelter.db') | ||
create_database_tables(conn) | ||
populate_initial_data(conn) | ||
print("Database setup complete!") | ||
conn.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,342 @@ | ||
from flask import Flask, jsonify, request, send_from_directory | ||
from flask_cors import CORS | ||
from flasgger import Swagger | ||
import os | ||
from pets import add_pet, update_pet, delete_pet, search_pets, reset_pets | ||
|
||
app = Flask(__name__) | ||
app.secret_key = "test_secret_key" | ||
CORS(app) | ||
|
||
# Set up Swagger documentation | ||
swagger_config = { | ||
"title": "Animal Shelter API", | ||
"version": "1.0.0", | ||
"description": "API for animal adoption management" | ||
} | ||
swagger = Swagger(app, config=swagger_config) | ||
|
||
# Mock data for testing | ||
ANIMALS = [ | ||
{ | ||
"id": 1, | ||
"name": "Luna", | ||
"species": "Dog", | ||
"breed": "Labrador Mix", | ||
"age": 2, | ||
"personality": "Playful and energetic", | ||
"image_path": "/images/luna.jpg", | ||
"adoption_status": "Available" | ||
}, | ||
{ | ||
"id": 2, | ||
"name": "Oliver", | ||
"species": "Cat", | ||
"breed": "Tabby", | ||
"age": 4, | ||
"personality": "Independent but affectionate", | ||
"image_path": "/images/oliver.jpg", | ||
"adoption_status": "Available" | ||
}, | ||
{ | ||
"id": 3, | ||
"name": "Max", | ||
"species": "Dog", | ||
"breed": "German Shepherd", | ||
"age": 3, | ||
"personality": "Loyal and intelligent", | ||
"image_path": "/images/max.jpg", | ||
"adoption_status": "Available" | ||
} | ||
] | ||
|
||
USERS = [ | ||
{ | ||
"id": 1, | ||
"username": "admin", | ||
# In a real app, this would be hashed, but for testing it's plain text | ||
"password": "admin123", | ||
"email": "admin@example.com" | ||
} | ||
] | ||
|
||
# User registration endpoint | ||
@app.route('/api/register', methods=['POST']) | ||
def register_user(): | ||
""" | ||
Register a new adopter | ||
--- | ||
parameters: | ||
- in: body | ||
name: user | ||
description: User registration details | ||
schema: | ||
type: object | ||
required: | ||
- username | ||
- password | ||
properties: | ||
username: | ||
type: string | ||
password: | ||
type: string | ||
email: | ||
type: string | ||
responses: | ||
201: | ||
description: User registered successfully | ||
400: | ||
description: Missing required fields | ||
409: | ||
description: Username already exists | ||
""" | ||
data = request.get_json() | ||
username = data.get('username') | ||
password = data.get('password') | ||
email = data.get('email') | ||
|
||
if not username or not password or not email: | ||
return jsonify({'error': 'All fields are required'}), 400 | ||
|
||
# Check if username already exists | ||
if any(user['username'] == username for user in USERS): | ||
return jsonify({'error': 'Username already exists'}), 409 | ||
|
||
# Check if email already exists | ||
if any(user['email'] == email for user in USERS): | ||
return jsonify({'error': 'Email already registered'}), 409 | ||
|
||
# Create new user | ||
new_user = { | ||
"id": len(USERS) + 1, | ||
"username": username, | ||
"password": password, # In real app, would be hashed | ||
"email": email | ||
} | ||
USERS.append(new_user) | ||
|
||
return jsonify({'message': 'Registration successful!', 'user_id': new_user['id']}), 201 | ||
|
||
# User login endpoint | ||
@app.route('/api/login', methods=['POST']) | ||
def authenticate_user(): | ||
""" | ||
User login | ||
--- | ||
parameters: | ||
- in: body | ||
name: credentials | ||
description: Login credentials | ||
schema: | ||
type: object | ||
required: | ||
- username | ||
- password | ||
properties: | ||
username: | ||
type: string | ||
password: | ||
type: string | ||
responses: | ||
200: | ||
description: Login successful | ||
401: | ||
description: Invalid credentials | ||
""" | ||
data = request.get_json() | ||
username = data.get('username') | ||
password = data.get('password') | ||
|
||
if not username or not password: | ||
return jsonify({'error': 'Username and password required'}), 400 | ||
|
||
# Find user by username | ||
user = next((user for user in USERS if user['username'] == username), None) | ||
|
||
if not user or user['password'] != password: | ||
return jsonify({'error': 'Invalid username or password'}), 401 | ||
|
||
return jsonify({ | ||
'message': 'Login successful', | ||
'user_id': user['id'], | ||
'username': user['username'], | ||
'email': user['email'] | ||
}), 200 | ||
|
||
# Animal routes | ||
@app.route('/api/animals', methods=['GET']) | ||
def list_animals(): | ||
""" | ||
Get all available animals | ||
--- | ||
responses: | ||
200: | ||
description: List of available animals | ||
""" | ||
# Filter only available animals | ||
available_animals = [animal for animal in ANIMALS if animal['adoption_status'] == 'Available'] | ||
return jsonify(available_animals), 200 | ||
|
||
@app.route('/api/animals/<int:animal_id>', methods=['GET']) | ||
def animal_details(animal_id): | ||
""" | ||
Get details for a specific animal | ||
--- | ||
parameters: | ||
- name: animal_id | ||
in: path | ||
type: integer | ||
required: true | ||
responses: | ||
200: | ||
description: Animal details | ||
404: | ||
description: Animal not found | ||
""" | ||
animal = next((animal for animal in ANIMALS if animal['id'] == animal_id), None) | ||
|
||
if not animal: | ||
return jsonify({'error': 'Animal not found'}), 404 | ||
|
||
return jsonify(animal), 200 | ||
|
||
@app.route('/api/animals/<int:animal_id>/adopt', methods=['POST']) | ||
def adopt_animal(animal_id): | ||
""" | ||
Adopt an animal | ||
--- | ||
parameters: | ||
- name: animal_id | ||
in: path | ||
type: integer | ||
required: true | ||
- in: body | ||
name: adoption | ||
schema: | ||
type: object | ||
required: | ||
- user_id | ||
properties: | ||
user_id: | ||
type: integer | ||
responses: | ||
200: | ||
description: Adoption successful | ||
404: | ||
description: Animal not found | ||
400: | ||
description: Animal not available for adoption | ||
""" | ||
data = request.get_json() | ||
user_id = data.get('user_id') | ||
|
||
if not user_id: | ||
return jsonify({'error': 'User ID required'}), 400 | ||
|
||
# Find the animal | ||
animal_index = next((i for i, animal in enumerate(ANIMALS) if animal['id'] == animal_id), None) | ||
|
||
if animal_index is None: | ||
return jsonify({'error': 'Animal not found'}), 404 | ||
|
||
# Check if animal is available | ||
if ANIMALS[animal_index]['adoption_status'] != 'Available': | ||
return jsonify({'error': 'Animal not available for adoption'}), 400 | ||
|
||
# Update status to adopted | ||
ANIMALS[animal_index]['adoption_status'] = 'Adopted' | ||
|
||
return jsonify({ | ||
'message': 'Adoption successful', | ||
'animal': ANIMALS[animal_index] | ||
}), 200 | ||
|
||
@app.route('/api/animals/search', methods=['GET']) | ||
def search_animals(): | ||
""" | ||
Search for animals with filters | ||
--- | ||
parameters: | ||
- name: species | ||
in: query | ||
type: string | ||
required: false | ||
- name: min_age | ||
in: query | ||
type: integer | ||
required: false | ||
- name: max_age | ||
in: query | ||
type: integer | ||
required: false | ||
responses: | ||
200: | ||
description: Search results | ||
""" | ||
species = request.args.get('species') | ||
min_age = request.args.get('min_age') | ||
max_age = request.args.get('max_age') | ||
|
||
results = ANIMALS.copy() | ||
|
||
# Apply filters | ||
if species: | ||
results = [animal for animal in results if animal['species'].lower() == species.lower()] | ||
|
||
if min_age: | ||
results = [animal for animal in results if animal['age'] >= int(min_age)] | ||
|
||
if max_age: | ||
results = [animal for animal in results if animal['age'] <= int(max_age)] | ||
|
||
return jsonify(results), 200 | ||
|
||
# Serve images of animals | ||
@app.route('/images/<path:filename>') | ||
def serve_image(filename): | ||
return send_from_directory(os.path.join(app.root_path, 'static/images'), filename) | ||
|
||
@app.route('/') | ||
def homepage(): | ||
return """ | ||
<h1>Animal Shelter API</h1> | ||
<p>Welcome to our animal adoption platform!</p> | ||
<a href="/apidocs">View API Documentation</a> | ||
""" | ||
# Add a new pet | ||
@app.route('/api/pets', methods=['POST']) | ||
def add_new_pet(): | ||
pet_data = request.get_json() | ||
return add_pet(pet_data) | ||
|
||
# Update an existing pet | ||
@app.route('/api/pets/<int:pet_id>', methods=['PUT']) | ||
def update_existing_pet(pet_id): | ||
pet_data = request.get_json() | ||
return update_pet(pet_id, pet_data) | ||
|
||
# Delete a pet | ||
@app.route('/api/pets/<int:pet_id>', methods=['DELETE']) | ||
def remove_pet(pet_id): | ||
return delete_pet(pet_id) | ||
|
||
# Search for pets | ||
@app.route('/api/pets/search', methods=['GET']) | ||
def search_for_pets(): | ||
return search_pets(request.args) | ||
|
||
# Reset pets data (for testing) | ||
@app.route('/api/pets/reset', methods=['POST']) | ||
def reset_pets_data(): | ||
""" | ||
Reset the pets data to initial state. | ||
--- | ||
responses: | ||
200: | ||
description: Pets data reset successfully | ||
""" | ||
reset_pets() | ||
return jsonify({'message': 'Pets data reset successfully'}), 200 | ||
if __name__ == '__main__': | ||
app.run(debug=True) |
Oops, something went wrong.