import os from datetime import datetime from django.http import JsonResponse from googleapiclient.errors import HttpError from rest_framework import generics from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.response import Response from rest_framework.utils import json from rest_framework.views import APIView from django.contrib.auth.models import User from api.models import Accounts, ServiceDays, Stores, ServiceVisits, Projects, AccountStatus from api.serializers import ( UserSerializer, AccountsSerializer, ServiceDaysSerializer, StoresSerializer, ServiceVisitsSerializer, ProjectsSerializer, AccountStatusSerializer ) from api.gcalendar import create_event from api.gdrive import duplicate_punchlist, create_pdf_from_punchlist, store_pdf_as_bytecode from api.gsheets import fill_new_punchlist from api.gmail import GmailClient from api.redis.client import get_rate, get_revenue def get_service_account_key(): """Get service account key from environment or file.""" key = os.environ.get('SERVICE_ACCOUNT_KEY') if not key: key_file_path = os.environ.get('SERVICE_ACCOUNT_KEY_FILE', '/app/service_account_key.json') if os.path.exists(key_file_path): with open(key_file_path, 'r') as f: key = json.load(f) return json.dumps(key) if isinstance(key, dict) else key return key def get_team_emails(): """Get team member email mappings from environment.""" emails_str = os.environ.get('TEAM_EMAILS', '') emails = {} for pair in emails_str.split(','): if ':' in pair: name, email = pair.strip().split(':', 1) emails[name.strip().lower()] = email.strip() return emails class CreateUserView(generics.CreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (AllowAny,) class AccountsView(generics.ListAPIView): queryset = Accounts.objects.all() serializer_class = AccountsSerializer permission_classes = (IsAuthenticated,) class ServiceDaysView(generics.ListAPIView): queryset = ServiceDays.objects.all() serializer_class = ServiceDaysSerializer permission_classes = (IsAuthenticated,) class AccountServiceDaysView(generics.ListAPIView): serializer_class = ServiceDaysSerializer permission_classes = (IsAuthenticated,) def get_queryset(self): return ServiceDays.objects.filter(short_name=self.kwargs['account']) class StoresView(generics.ListAPIView): serializer_class = StoresSerializer permission_classes = (IsAuthenticated,) def get_queryset(self): return Stores.objects.all() class StoreView(generics.ListAPIView): serializer_class = StoresSerializer permission_classes = (IsAuthenticated,) def get_queryset(self): return Stores.objects.filter(store=self.kwargs['store']) class ProjectsView(generics.ListAPIView): serializer_class = ProjectsSerializer permission_classes = (IsAuthenticated,) def get_queryset(self): return Projects.objects.all() class ServiceVisitsView(APIView): serializer_class = ServiceVisitsSerializer permission_classes = (IsAuthenticated,) def get(self, request, *args, **kwargs): username = request.GET.get('username') month = request.GET.get('month') year = request.GET.get('year') account = request.GET.get('account') try: if username and month and year and account: if account == '*': queryset = ServiceVisits.objects.filter( team_member__overlap=[username], date__month=int(month), date__year=int(year), ) else: queryset = ServiceVisits.objects.filter( team_member__overlap=[username], date__month=int(month), date__year=int(year), short_name=Accounts.objects.get(short_name=account), ) serializer = self.serializer_class(queryset, many=True) return Response(serializer.data) else: return Response({"detail": "No username or filter data provided."}, status=404) except ServiceVisits.DoesNotExist: return Response({"detail": "No service visits found."}, status=404) def post(self, request, *args, **kwargs): date = request.data.get('date') username = request.data.get('username') try: queryset = ServiceVisits.objects.filter(date=date, team_member__overlap=[username]) serializer = self.serializer_class(queryset, many=True) return Response(serializer.data) except ServiceVisits.DoesNotExist: return Response({"detail": "No service visits found for this date."}, status=404) class CloseVisitView(APIView): serializer_class = ServiceVisitsSerializer permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): visit_id = request.data.get('id') notes = request.data.get('notes') try: visit = ServiceVisits.objects.get(id=visit_id) if visit.notes: visit.notes += f', {notes}' else: visit.notes = notes visit.status = 'Closed' visit.save() except ServiceVisits.DoesNotExist: return Response({"detail": "Error occurred while fetching service visit!"}, status=404) return Response({"detail": "Service visit closed successfully!"}, status=200) class CreateCalendarEvents(APIView): permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): try: data = json.loads(request.body) store_id = data['id'] summary = data['summary'] description = data['description'] start1 = data['start1'] + ":00" end1 = data['end1'] + ":00" location = data['location'] # Build attendees list from environment-configured emails team_emails = get_team_emails() attendees1 = [] for attendee in data.get('attendees', []): attendee_lower = attendee.lower() if attendee_lower in team_emails: attendees1.append({"email": team_emails[attendee_lower]}) reminders = [ {'method': 'email', 'minutes': 60}, {'method': 'popup', 'minutes': 60}, {'method': 'popup', 'minutes': 30}, ] dispatch_email = os.environ.get('DISPATCH_EMAIL', 'dispatch@example.com') service_account_key = get_service_account_key() create_event( service_account_key, dispatch_email, dispatch_email, summary, description, start1, end1, location, attendees1, reminders ) # Check for second visit requirement if data.get('secondVisit') == 'Yes': summary2 = f"Store #{data['store']}: Return Visit" description2 = "Second visit for follow-up work." start2 = data['start2'] + ":00" end2 = data['end2'] + ":00" create_event( service_account_key, dispatch_email, dispatch_email, summary2, description2, start2, end2, location, attendees1, reminders ) # Create project record parent_store = Stores.objects.get(id=store_id) project_date_str = start1[0:10] project_date = datetime.strptime(project_date_str, '%Y-%m-%d') project = Projects(store=parent_store, date=project_date, status='Open') project.save() return Response({"detail": "Calendar events created successfully!"}, status=200) except HttpError as e: return JsonResponse({'error': f'Google Calendar API Error: {e}'}, status=500) except KeyError as e: return JsonResponse({'error': f'Missing key in request data: {e}'}, status=400) except Exception as e: return JsonResponse({'error': str(e)}, status=500) class CreatePunchlist(APIView): permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): try: data = json.loads(request.body) proj_id = data['id'] store_no = data['store'] proj_date = data['date'] new_sheet_name = f"Punchlist-{store_no}-{proj_date}" new_sheet_object = duplicate_punchlist(new_sheet_name) new_sheet_id = new_sheet_object['id'] fill_new_punchlist(data, new_sheet_id) new_pdf = create_pdf_from_punchlist(new_sheet_id) store_pdf_as_bytecode(new_pdf, proj_id) return Response({ "message": f"Punchlist and PDF created! https://drive.google.com/file/d/{new_pdf}/view" }, status=200) except HttpError as e: return JsonResponse({'error': f'Google API Error: {e}'}, status=500) except KeyError as e: return JsonResponse({'error': f'Missing key in request data: {e}'}, status=400) except Exception as e: return JsonResponse({'error': str(e)}, status=500) class CloseProject(APIView): permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): try: project_id = request.data.get('id') project = Projects.objects.get(id=project_id) project.status = 'Closed' project.save() except Projects.DoesNotExist: return Response({"detail": "Error occurred while fetching project!"}, status=404) return Response({"detail": "Project closed successfully!"}, status=200) class GetAccountStatus(APIView): permission_classes = (IsAuthenticated,) serializer_class = AccountStatusSerializer def get(self, request, *args, **kwargs): query = AccountStatus.objects.all() serializer = self.serializer_class(query, many=True) return Response(serializer.data) class GetAccountRate(APIView): permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): account = request.data.get('account') rate = get_rate(account) return Response({"rate": rate}, status=200) class GetAccountRevenue(APIView): permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): account = request.data.get('account') revenue = get_revenue(account) return Response({"revenue": revenue}, status=200) class ChangePasswordView(APIView): permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def post(self, request, *args, **kwargs): username = request.data.get('username') try: user = User.objects.get(username=username) except User.DoesNotExist: return Response({"error": "User not found"}, status=400) old_password = request.data.get('oldPassword') if not user.check_password(old_password): return Response({"error": "Old password is incorrect"}, status=400) new_password1 = request.data.get('newPassword1') new_password2 = request.data.get('newPassword2') if new_password1 != new_password2: return Response({"error": "Passwords do not match"}, status=400) user.set_password(new_password1) user.save() return Response({"message": "Password changed successfully!"}, status=200) class SupplyRequest(APIView): permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): data = json.loads(request.body) account = data['account'] team = data['team'] supplies = data['supplies'] notes = data['notes'] supplies_dict = { 'multiFoldTowels': 'Multfold Hand Towels', 'cFoldTowels': 'C-Fold Hand Towels', 'jumboPtRoll': 'Jumbo Paper Towel Rolls', 'kitPtRoll': 'Kitchen Paper Towel Rolls', 'jumboTp': 'Jumbo Toilet Paper Rolls', 'standardTp': 'Standard/Individual Toilet Paper Rolls', 'dispSoap': 'Auto/Manual Dispenser Hand Soap', 'indSoap': 'Individual Hand Soap', 'urinal': 'Urinal Screens', 'uMats': 'Urinal Mats', 'fem': 'Feminine Hygiene Waste Bags', 'air': 'Air Fresheners', 'lgBag': 'Large Trash Bags', 'smBag': 'Small Trash Bags', 'mdBag': 'Medium Trash Bags' } service_account_key = get_service_account_key() dispatch_email = os.environ.get('DISPATCH_EMAIL', 'dispatch@example.com') supply_request_email = os.environ.get('SUPPLY_REQUEST_EMAIL', dispatch_email) gmail_client = GmailClient(service_account_key, dispatch_email) sender = dispatch_email to = supply_request_email subject = f'Supply Request for {account}!' # Build supply list supply_items = [ supplies_dict[key] for key, label in supplies_dict.items() if supplies.get(key) ] message_text = f"{team} says {account} needs the following supplies:\n" message_text += "\n".join(f"- {item}" for item in supply_items) message_text += f"\n\nAdditional Notes:\n{notes if notes else 'None'}" message_text += "\n\nThank You!\nDispatch" message_html = f"""
{team.capitalize()} says we need the following at {account}:
Additional Notes:
Thank You!
Dispatch
""" message = gmail_client.create_message(sender, to, subject, message_text, message_html) gmail_client.send_message(message) return Response({"message": "Request sent successfully!"}, status=200)