nexus-1/api/views.py
2026-01-26 09:45:31 -05:00

386 lines
14 KiB
Python

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"""
<html>
<body>
<p>{team.capitalize()} says we need the following at {account}:</p>
<ul>
{''.join(f'<li>{item}</li>' for item in supply_items)}
</ul>
<p>Additional Notes:</p>
<ul><li>{notes if notes else 'None'}</li></ul>
<p>Thank You!</p>
<p>Dispatch</p>
</body>
</html>
"""
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)