386 lines
14 KiB
Python
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)
|