Skip to content

Commit

Permalink
Merge pull request #85 from WSU-4110/Nawal
Browse files Browse the repository at this point in the history
Nawal
  • Loading branch information
nawalragih authored Nov 17, 2024
2 parents 0b50518 + fba1cdc commit 9d9bc0a
Show file tree
Hide file tree
Showing 20 changed files with 1,487 additions and 229 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,7 @@ workspace.xml
venv/
__pycache__/

keys/*.json
keys/*.json

# Ignore the keys directory
src/app/api/keys/
50 changes: 38 additions & 12 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file added Backend/__init__.py
Empty file.
13 changes: 12 additions & 1 deletion Backend/accounts/accounts/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
'django.contrib.messages',
'django.contrib.staticfiles',
'profiles',
'appointments'
'django.contrib.sites',
'allauth',
'allauth.account',
Expand Down Expand Up @@ -165,4 +166,14 @@
ACCOUNT_USERNAME_REQUIRED = True

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# Email backend configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'noreplystyle@gmail.com' # email address where emails sent from
EMAIL_HOST_PASSWORD = 'nsbrese!!24' # password
# Default 'From' email for outgoing emails
DEFAULT_FROM_EMAIL = 'noreply@gmail.com'
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Appointment(models.Model):
date = models.DateField()
time = models.TimeField()
address = models.CharField(max_length=255)
email = models.EmailField(max_length=255)

def __str__(self):
return f"{self.business_name} - {self.service}"
File renamed without changes.
129 changes: 129 additions & 0 deletions Backend/accounts/appointments/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import json
import os
import logging
import firebase_admin
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.core.exceptions import ObjectDoesNotExist
from django.core.mail import send_mail
from firebase_admin import auth, credentials, firestore
from django.conf import settings
from decouple import config
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent
cred_path = config('FIREBASE_SERVICE_ACCOUNT_KEY')
cred = credentials.Certificate(os.path.join(BASE_DIR, 'keys', cred_path))
firebase_admin.initialize_app(cred)

# Initialize Firestore
db = firestore.client()

# Set up logging
logger = logging.getLogger(__name__)

# Fetch all appointments
@require_http_methods(["GET"])
def get_appointments(request):
appointments = Appointment.objects.all().values()
return JsonResponse(list(appointments), safe=False)

# Create a new appointment
@csrf_exempt
@require_http_methods(["POST"])
def create_appointment(request):
try:
# Verify the Firebase token
token = request.headers.get('Authorization').split('Bearer ')[-1]
decoded_token = auth.verify_id_token(token)
user_email = decoded_token.get('email')

if not user_email:
return JsonResponse({"error": "Email not found in Firebase user data"}, status=400)

data = json.loads(request.body)

# Check for missing keys
required_keys = ['business_name', 'service', 'amount_due', 'date', 'time', 'address']
for key in required_keys:
if key not in data:
return JsonResponse({"error": f"Missing key: {key}"}, status=400)

appointment = Appointment.objects.create(
business_name=data['business_name'],
service=data['service'],
amount_due=data['amount_due'],
date=data['date'],
time=data['time'],
address=data['address']
)

# email confirmation
send_mail(
subject='Appointment Confirmation',
message=f"Hello, your appointment for {appointment.service} on {appointment.date} at {appointment.time} has been booked.",
from_email='noreplystyle@gmail.com',
recipient_list=[user_email], e
fail_silently=False,
)

return JsonResponse({"id": appointment.id, "success": "Appointment created"}, status=201)

except KeyError as e:
logger.error(f"Missing key: {str(e)}")
return JsonResponse({"error": f"Missing key: {str(e)}"}, status=400)
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
return JsonResponse({"error": "An unexpected error occurred"}, status=500)

# Retrieve a specific appointment
@require_http_methods(["GET"])
def get_appointment(request, id):
try:
appointment = Appointment.objects.get(id=id)
return JsonResponse(appointment.__dict__)
except ObjectDoesNotExist:
return JsonResponse({"error": "Appointment not found"}, status=404)

# Update an appointment
@csrf_exempt
@require_http_methods(["PUT"])
def update_appointment(request, id):
try:
data = json.loads(request.body)

# Update appointment in the database
updated_count = Appointment.objects.filter(id=id).update(
business_name=data['business_name'],
service=data['service'],
amount_due=data['amount_due'],
date=data['date'],
time=data['time'],
address=data['address']
)

if updated_count == 0:
return JsonResponse({"error": "Appointment not found"}, status=404)

return JsonResponse({"success": "Appointment updated"}, status=200)

except KeyError as e:
logger.error(f"Missing key: {str(e)}")
return JsonResponse({"error": f"Missing key: {str(e)}"}, status=400)
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
return JsonResponse({"error": "An unexpected error occurred"}, status=500)

# Delete an appointment
@csrf_exempt
@require_http_methods(["DELETE"])
def delete_appointment(request, id):
try:
deleted_count, _ = Appointment.objects.filter(id=id).delete()
if deleted_count == 0:
return JsonResponse({"error": "Appointment not found"}, status=404)
return JsonResponse({"success": "Appointment deleted"}, status=204)
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
return JsonResponse({"error": str(e)}, status=500)
12 changes: 5 additions & 7 deletions Backend/accounts/saving/models.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
from django.db import models
from django.contrib.auth.models import User

class Customer(models.Model):
firebase_user_id = models.CharField(max_length=128) # Store Firebase UID for user authentication fullname = models.CharField(max_length=100)
firebase_user_id = models.CharField(max_length=128, unique=True) # Ensure Firebase UID is unique
fullname = models.CharField(max_length=250)
email = models.EmailField()
city = models.CharField(max_length=100)
phone_number = models.CharField(max_length=15, blank=True, null=True) # Optional field
phone_number = models.CharField(max_length=15, blank=True, null=True)
profile_picture = models.ImageField(upload_to='profile_pics/', blank=True, null=True)

def __str__(self):
return self.fullname


class Business(models.Model):
user_profile = models.OneToOneField(User, on_delete=models.CASCADE)
class ArtistPortfolio(models.Model):
user_profile = models.OneToOneField(Customer, on_delete=models.CASCADE)
business_name = models.CharField(max_length=250)
bio = models.TextField()
profile_picture = models.ImageField(upload_to='artist_pics/', null=True, blank=True)
photos = models.JSONField() # Store photo URLs
services = models.JSONField() #Store service info
services = models.JSONField() # Store service info

def __str__(self):
return self.business_name
8 changes: 6 additions & 2 deletions Backend/accounts/saving/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ class Meta:
model = Customer
fields = ['firebase_user_id', 'fullname', 'email', 'city', 'phone_number', 'profile_picture']


class ArtistPortfolioSerializer(serializers.ModelSerializer):
user_profile = UserProfileSerializer()

Expand All @@ -15,7 +14,12 @@ class Meta:
fields = ['user_profile', 'business_name', 'bio', 'profile_picture', 'photos', 'services']

def create(self, validated_data):
# Extract the user_profile data and create or get the related customer
user_profile_data = validated_data.pop('user_profile')
user_profile = UserProfile.objects.create(**user_profile_data)
user_profile, created = Customer.objects.get_or_create(
firebase_user_id=user_profile_data['firebase_user_id'],
defaults=user_profile_data # Create the user profile if it doesn't exist
)
# Now create the artist portfolio with the related user_profile
portfolio = ArtistPortfolio.objects.create(user_profile=user_profile, **validated_data)
return portfolio
2 changes: 1 addition & 1 deletion Backend/accounts/saving/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

urlpatterns = [
path('api/', include(router.urls)),
]
]
19 changes: 14 additions & 5 deletions Backend/accounts/saving/views.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from rest_framework import status
from rest_framework import status, viewsets
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .models import UserProfile, ArtistPortfolio
from .models import Customer, ArtistPortfolio
from .serializers import UserProfileSerializer, ArtistPortfolioSerializer

class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
queryset = Customer.objects.all()
serializer_class = UserProfileSerializer
permission_classes = [IsAuthenticated]

def perform_create(self, serializer):
firebase_user = self.request.user # Get Firebase user from request
# Ensure that only one profile is created per user
serializer.save(firebase_user_id=firebase_user.uid)

class ArtistPortfolioViewSet(viewsets.ModelViewSet):
Expand All @@ -19,5 +20,13 @@ class ArtistPortfolioViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]

def perform_create(self, serializer):
user_profile = UserProfile.objects.get(firebase_user_id=self.request.user.uid)
serializer.save(user_profile=user_profile)
# Attempt to get the existing user profile
user_profile = Customer.objects.filter(firebase_user_id=self.request.user.uid).first()

if not user_profile:
return Response(
{"detail": "User profile not found. Please create a profile first."},
status=status.HTTP_400_BAD_REQUEST
)

serializer.save(user_profile=user_profile)
Loading

0 comments on commit 9d9bc0a

Please sign in to comment.