class WorkshopResourcesSerializer(serializers.ModelSerializer): on_date = serializers.DateField(required=False, default=_convert_to_given_timezone( timezone.now(), settings.TIME_ZONE).date()) updated_by = serializers.PrimaryKeyRelatedField( default=serializers.CurrentUserDefault(), queryset=get_user_model().objects.all()) class Meta: model = WorkshopResources fields = ('__all__') def save(self, **kwargs): request = self.context.get('request') kwargs['updated_by'] = request.user super(WorkshopResourcesSerializer, self).save(**kwargs)
def handle(self, *args, **options): current_time = timezone.now() logger.info( "Script:: OPS_ALERTS_DAILY_SUMMARY:: Script started - current_time: %s" % current_time) current_context_time = _convert_to_given_timezone( timezone.now(), settings.TIME_ZONE) start_time = make_datetime_timezone_aware_convert_to_utc( str(current_context_time.date()) + " 00:00:00", "+0530") end_time = make_datetime_timezone_aware_convert_to_utc( str(current_context_time.date()) + " 23:59:59", "+0530") logger.info( "Script:: OPS_ALERTS_DAILY_SUMMARY:: start_time: %s - end_Time: %s" % (start_time, end_time)) try: ops_alerts_raised = BookingAlertTriggerStatus.objects.select_related('alert_type')\ .filter(created_at__range =[start_time, end_time]).exclude(alert_type_id__in=[1, 2]) logger.debug( 'Script:: OPS_ALERTS_DAILY_SUMMARY:: Bookings sent in notification: %s' % ops_alerts_raised) notification = Notifications.objects.get( name='OPS_ALERTS_DAILY_SUMMARY') cc_address_list = notification.get_cc_list() to_address_list = notification.get_to_list() email_service = NewEmailService( to_address_list, cc_address_list, context={'ops_alerts_raised': ops_alerts_raised}, analytic_info={'notification_id': notification.id}) email_service.send( template_folder_name=notification.template_folder_name) except: logger.exception( "Script:: OPS_ALERTS_DAILY_SUMMARY:: Failed to get bookings to process" )
def build_resource_datewise(days_to_plan_for, num_of_working_hrs): today = _convert_to_given_timezone(timezone.now(), settings.TIME_ZONE) for item in BUMPER_WORKSHOP_RESOURCES: daily_avilable_hrs = {} for num in range(0, days_to_plan_for, 1): logger.info( 'SCHEDULING_EOD_WORKSHOP:: Building res for date ->%s' % str( (today + timezone.timedelta(days=num)).date())) daily_avilable_hrs[str( (today + timezone.timedelta(days=num)).date())] = { 'total_minutes_available': num_of_working_hrs[str( (today + timezone.timedelta(days=num)).date())] * item['count'] * 60, 'minutes_remaining': num_of_working_hrs[str( (today + timezone.timedelta(days=num)).date())] * item['count'] * 60, } item['hrs_mapping'] = daily_avilable_hrs
def handle(self, *args, **options): current_time = timezone.now() logger.info( "Script:: SCHEDULING_WORKSHOP:: Script started - current_time: %s" % current_time) # pickup time in next day current_context_time = _convert_to_given_timezone( timezone.now(), settings.TIME_ZONE) date_for_tomorrow_in_current_context = current_context_time + timezone.timedelta( days=1) start_time = make_datetime_timezone_aware_convert_to_utc( str(date_for_tomorrow_in_current_context.date()) + " 00:00:00", "+0530") end_time = start_time + timezone.timedelta(hours=24) logger.info( "Script:: SCHEDULING_WORKSHOP:: Tomorrow's start_time: %s - end_Time: %s" % (start_time, end_time)) try: # Get all panels in all bookings for which work needs to be done, # and that are in workshop in ready to work status. all_bookings_in_workshop = Booking.objects.filter( status__in=[12, 13]) all_booking_all_tasks = [] for booking in all_bookings_in_workshop: groups_of_work = get_groups_of_work_from_booking(booking) # all_panels_in_booking = BookingPackagePanel.objects.select_related('panel') \ # .filter(booking_package__booking_id=booking.id) booking_tasks_remaining = [] groups = [] for type_of_group in groups_of_work: work_list = get_steps_by_type_of_work(type_of_group) for group in groups_of_work[type_of_group]: if group["name"] not in groups: groups.append(group["name"]) for work in work_list: booking_tasks_remaining.append({ 'task_name': "Task(%s:%s)::%s::seq-%s" % (booking.id, group['name'], work['step']['name'], work['step']['seq'] ), # (12435:fender_right)::denting::seq-1 'group_name': group['name'], 'type_of_work': work['step']['name'], 'resource': work['step']['resource_type_req'], 'processing_time': (work['processing_time'] * group['units']), 'seq': work['step']['seq'], }) checkpoint_tasks = [] checkpoints = get_checkpoint_steps(booking) for checkpoint_num, checkpoint in enumerate(checkpoints): if "Full_Car" not in groups: groups.append("Full_Car") t = { 'task_name': "Task(%s:Full_Car)::%s::seq-%s" % (booking.id, checkpoint['step']['name'], checkpoint['step']['seq']), 'group_name': "full_car", 'type_of_work': checkpoint['step']['name'], 'resource': checkpoint['step']['resource_type_req'], 'processing_time': checkpoint['processing_time'], 'seq': checkpoint['step']['seq'], } booking_tasks_remaining.append(t) checkpoint_tasks.append(t) all_booking_all_tasks.append({ 'id': booking.id, 'groups': groups, 'tasks': booking_tasks_remaining, 'checkpoints': checkpoint_tasks, }) logger.info( "Script:: SCHEDULING_WORKSHOP:: Bookings & their tasks To Process: %s" % all_booking_all_tasks) bumper_horizon = 0 for booking in all_booking_all_tasks: for task in booking['tasks']: bumper_horizon += task['processing_time'] logger.info("Script:: SCHEDULING_WORKSHOP::%s" % bumper_horizon) task_to_interval = collections.OrderedDict() resource_to_intervals = {} all_resource_list = [] for r in bumper_resources: i = 1 while i <= r['count']: resource_name = "%s%i" % (r['name'], i) all_resource_list.append(resource_name) resource_to_intervals[resource_name] = list() i += 1 # Create the solver. solver = pywrapcp.Solver('bumper workshop') # Create all interval based tasks for all bookings. for booking in all_booking_all_tasks: for task in booking['tasks']: solver_task = solver.FixedDurationIntervalVar( 0, bumper_horizon - task['processing_time'], task['processing_time'], False, task["task_name"]) task_to_interval[task['task_name']] = solver_task # process multiple resource on same task task_resources_required_both = str( task['resource']).split('&') for req_resource_type in task_resources_required_both: # process optional resources on same task task_resources_required = str(req_resource_type).split( '|') RA_tasks_optional = list() for resource_type in task_resources_required: for resource_name in get_resource_seq_by_type( resource_type): # Adding optional tasks I_ = solver.FixedDurationIntervalVar( 0, bumper_horizon - task['processing_time'], task['processing_time'], True, '%s_%s' % (task['task_name'], resource_name)) resource_to_intervals[resource_name].append(I_) RA_tasks_optional.append(I_) solver.Add(solver_task.StaysInSync(I_)) # one resource needs to get selected solver.Add( solver.Sum([ I_.PerformedExpr() for I_ in RA_tasks_optional ]) == 1) # resources sequences = collections.OrderedDict() for R in all_resource_list: disj = solver.DisjunctiveConstraint(resource_to_intervals[R], R) sequences[R] = disj.SequenceVar() solver.Add(disj) # Precedences inside a job. # for booking in all_bookings_in_workshop: # groups_of_work = get_groups_of_work_from_booking(booking) # for type_of_group in groups_of_work: # work_list = get_steps_by_type_of_work(type_of_group) # for idx, group in enumerate(groups_of_work[type_of_group]): # for task_num, work in enumerate( work_list): # # Do this using SEQ num rather than list index. # if task_num == len(work_list) - 1: # continue # solver.Add(task_to_interval["%s-%s-%s" % (booking.id, idx, task_num + 1)].StartsAfterEnd( # task_to_interval["%s-%s-%s" % (booking.id, idx, task_num)])) for booking in all_booking_all_tasks: # set precedence within booking tasks for group in booking['groups']: # set precedence within group of works all_task_within_group = [ item for item in booking["tasks"] if str(item["task_name"]).split("::")[0] == 'Task(%s:%s)' % (booking["id"], group) ] # is there a task with greater seq num than this task in this group foo = sorted(all_task_within_group, key=lambda x: x['seq']) max_task_to_process = len(foo) - 1 for idx, task in enumerate(foo): if idx == max_task_to_process: continue solver.Add(task_to_interval[foo[ idx + 1]["task_name"]].StartsAfterEnd( task_to_interval[task["task_name"]])) if len(all_task_within_group) > 0 and group != "Full_Car": # set precedence for checkpoints for checkpoint_task in booking["checkpoints"]: seq_num = checkpoint_task["seq"] all_tasks_below_checkpoint = [ item for item in all_task_within_group if item["seq"] < seq_num ] if len(all_tasks_below_checkpoint) > 0: # going through all group to get last task before seq last_task = all_tasks_below_checkpoint[-1] solver.Add(task_to_interval[checkpoint_task[ "task_name"]].StartsAfterEnd( task_to_interval[ last_task["task_name"]])) # Objective # obj_var = solver.Max([((task_to_interval["%s-%s-%s" % (booking['id'], task['group_num'], task['task_num'])].EndExpr() # )for task in booking["tasks"]) for booking in all_booking_all_tasks]) # obj_var = solver.Max([task_to_interval[booking['tasks'][-1]['task_name']].EndExpr() # for booking in all_booking_all_tasks if len(booking['tasks']) > 0]) # Last item as per seq to be done as soon as possible. obj_var = solver.Max([ task_to_interval[sorted( booking["tasks"], key=lambda x: x['seq'])[-1]['task_name']].EndExpr() for booking in all_booking_all_tasks if len(booking['tasks']) > 0 ]) objective_monitor = solver.Minimize(obj_var, 1) # Creates search phases. vars_phase = solver.Phase([obj_var], solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE) sequence_phase = solver.Phase(sequences.values(), solver.SEQUENCE_DEFAULT) main_phase = solver.Compose([sequence_phase, vars_phase]) # Create the solution collector. collector = solver.LastSolutionCollector() # Add the interesting variables to the SolutionCollector. collector.Add(sequences.values()) collector.AddObjective(obj_var) for R in all_resource_list: sequence = sequences[R] sequence_count = sequence.Size() for j in range(0, sequence_count): t = sequence.Interval(j) collector.Add(t.StartExpr().Var()) collector.Add(t.EndExpr().Var()) # Solve the problem. disp_col_width = 10 if solver.Solve(main_phase, [objective_monitor, collector]): print("\nOptimal Schedule Length:", collector.ObjectiveValue(0), "\n") sol_line = "" sol_line_tasks = "" print("Optimal Schedule", "\n") for R in all_resource_list: seq = sequences[R] sol_line += R + ": " sol_line_tasks += R + ": " sequence = collector.ForwardSequence(0, seq) seq_size = len(sequence) for j in range(0, seq_size): t = seq.Interval(sequence[j]) # Add spaces to output to align columns. sol_line_tasks += t.Name() + " " * disp_col_width for j in range(0, seq_size): t = seq.Interval(sequence[j]) sol_tmp = "[" + str( collector.Value(0, t.StartExpr().Var())) + "," sol_tmp += str(collector.Value( 0, t.EndExpr().Var())) + "] " # Add spaces to output to align columns. sol_line += sol_tmp + " " * disp_col_width sol_line += "\n" sol_line_tasks += "\n" print(sol_line_tasks) print("Time Intervals for Tasks\n") print(sol_line) except: logger.exception( "Script:: SCHEDULING_WORKSHOP:: Failed to process to bookings")
def handle(self, *args, **options): current_time = timezone.now() logger.info( "Script:: PICKUP_REMINDER_TODAY:: Script started - current_time: %s" % current_time) subject = None # pickup time in next day current_context_time = _convert_to_given_timezone( timezone.now(), settings.TIME_ZONE) start_time = make_datetime_timezone_aware_convert_to_utc( str(current_context_time.date()) + " 00:00:00", "+0530") end_time = make_datetime_timezone_aware_convert_to_utc( str(current_context_time.date()) + " 23:59:59", "+0530") logger.info( "Script:: PICKUP_REMINDER_TODAY:: start_time: %s - end_Time: %s" % (start_time, end_time)) try: pickup_in_next_day_bookings = Booking.objects.select_related('user')\ .filter(status_id=3, pickup_time__range =[start_time, end_time])\ .exclude(Q(ops_status_id=8) | Q(pickup_driver_id__isnull=True)) if not pickup_in_next_day_bookings: logger.exception( "Script:: PICKUP_REMINDER_TODAY:: No Bookings to send notifications to." ) for booking in pickup_in_next_day_bookings: try: pickup_time = format_datetime_for_grid(booking.pickup_time) if pickup_time and format_datetime_for_grid( booking.pickup_slot_end_time): pickup_time = pickup_time + ' - ' + format_datetime_for_msg( booking.pickup_slot_end_time) booking_packages = BookingPackage.objects.filter( booking=booking) package_taken = [] for item in booking_packages: package_taken.append(item.package.package.name) logger.info( "Script:: PICKUP_REMINDER_TODAY:: Booking Id: %s" % booking.id) template_vars = { 'bookingId': booking.id, 'customer_name': booking.user.name, 'phone': booking.user.phone, 'email': booking.user.email, 'pickup_driver_name': booking.pickup_driver.name if booking.pickup_driver else '', 'pickup_driver_phone': booking.pickup_driver.ops_phone if booking.pickup_driver else '', 'pickup_time_details': pickup_time if pickup_time else '', 'package_details': ', '.join(package_taken), 'app_redirect_url': settings.BUMPER_APP_URL_THROUGH_APP_REDIRECT_URL % str(booking.id) } messages = Messages.objects.filter(booking_id=booking.id) if booking.is_doorstep: sms_notification = Notifications.objects.get( name='USER_SMS_PICKUP_REMINDER_ATDOOR') push_notification = Notifications.objects.get( name='USER_PUSH_PICKUP_REMINDER_ATDOOR') else: sms_notification = Notifications.objects.get( name='USER_SMS_PICKUP_REMINDER') push_notification = Notifications.objects.get( name='USER_EMAIL_PICKUP_REMINDER') logger.info("Script:: PICKUP_REMINDER_TODAY:: Sending SMS") send_custom_notification( sms_notification.name, template_vars, params_dict={'booking_id': booking.id}, user=booking.user) logger.info( "Script:: PICKUP_REMINDER_TODAY:: Sending PUSH") send_custom_notification( push_notification.name, template_vars, params_dict={'booking_id': booking.id}, user=booking.user) logger.info("Script:: PICKUP_REMINDER_TODAY:: Processed") except: logger.exception("Script:: PICKUP_REMINDER_TODAY:: Failed") except: logger.exception( "Script:: PICKUP_REMINDER_TODAY:: Failed to process to bookings" )
def generate_workshop_schedule(days_to_plan_for, avail_resources, remove_list, workshop_id, use_current_status=True, working_hrs_available=9): # TODO: Find delay from expected status. # TODO: when considering next day then priortize untouched. logger.info("SCHEDULING_EOD_WORKSHOP:: Script started - current_time: %s" % timezone.now()) today = _convert_to_given_timezone(timezone.now(), settings.TIME_ZONE) try: for item in BUMPER_WORKSHOP_RESOURCES: if item["name"] in avail_resources: item["count"] = int(avail_resources[item["name"]]) datewise_booking_allocation = {} datewise_available_working_hrs = {} for num in range(0, days_to_plan_for, 1): dt_to_consider = today + timezone.timedelta(days=num) datewise_booking_allocation[str(dt_to_consider.date())] = [] datewise_available_working_hrs[str( dt_to_consider.date())] = working_hrs_available if use_current_status and num == 0: if dt_to_consider.hour >= 19: datewise_available_working_hrs[str( dt_to_consider.date())] = 0 elif dt_to_consider.hour < 10: datewise_available_working_hrs[str( dt_to_consider.date())] = working_hrs_available else: datewise_available_working_hrs[str( dt_to_consider.date())] = 19 - dt_to_consider.hour build_resource_datewise(days_to_plan_for, datewise_available_working_hrs) # Get all panels in all bookings for which work needs to be done, # and that are in workshop in ready to work status. workshop_ids_to_consider = [workshop_id] if workshop_id == 17: workshop_ids_to_consider = [16, 17, 18] all_bookings_in_workshop = Booking.objects.filter( status__in=[12, 13], estimate_complete_time__isnull=False, rework_booking_id__isnull=True, workshop_id__in=workshop_ids_to_consider).order_by('workshop_eta') if remove_list: all_bookings_in_workshop.exclude(id__in=remove_list) all_bookings_with_eod = [] projected_delayed_bookings = set() for booking in all_bookings_in_workshop: has_dent_or_fbb = BookingPackage.objects.filter( booking_id=booking.id, package__package__category__in=[2, 3]).exists() if has_dent_or_fbb: workshop_reached_time = _convert_to_given_timezone( booking.workshop_reached_time, settings.TIME_ZONE) customer_eta = _convert_to_given_timezone( booking.estimate_complete_time, settings.TIME_ZONE) workshop_eta = _convert_to_given_timezone( booking.workshop_eta, settings.TIME_ZONE) panels_breakup = get_panels_breakup(booking) workshop_vendor = booking.workshop.name work_to_done_in_day_datewise = {} date_to_process = today for num in range(0, days_to_plan_for, 1): date_to_process = today + timezone.timedelta(days=num) if date_to_process.isoweekday() == 7: # to remove sunday from scheduling continue if date_to_process == today: # taking status at start of day as, output need resource allocation for complete day. # if current status is taken then full resource time will be available but that is not the # case in realty. if not use_current_status: status_at_start_of_day = get_status_at_start_of_day( booking, today.date()) else: booking_expected_eod = BookingExpectedEOD.objects\ .filter(booking=booking,for_date=today.date())\ .order_by('-created_at').first() if booking_expected_eod: status_at_start_of_day = { 'last_status': booking_expected_eod.status.flow_order_num, 'last_ops_status': booking_expected_eod.ops_status. flow_order_num if booking_expected_eod.ops_status else None, } else: status_at_start_of_day = { 'last_status': booking.status.flow_order_num, 'last_ops_status': booking.ops_status.flow_order_num if booking.ops_status else None, } else: if date_to_process.isoweekday() == 1: if str( (date_to_process - timezone.timedelta(days=2) ).date()) not in work_to_done_in_day_datewise: status_at_start_of_day = get_status_at_start_of_day( booking, date_to_process.date()) else: status_at_start_of_day = \ work_to_done_in_day_datewise[str((date_to_process - timezone.timedelta(days=2)).date())][ 'status_at_end_of_day'] else: if str( (date_to_process - timezone.timedelta(days=1) ).date()) not in work_to_done_in_day_datewise: status_at_start_of_day = get_status_at_start_of_day( booking, date_to_process.date()) else: status_at_start_of_day = \ work_to_done_in_day_datewise[str((date_to_process - timezone.timedelta(days=1)).date())][ 'status_at_end_of_day'] expected_end_of_day = get_expected_end_of_day( booking.id, workshop_reached_time, date_to_process, panels_breakup, status_at_start_of_day, working_hrs_available) if expected_end_of_day['delay']: projected_delayed_bookings.add( "%s_%s" % (booking.id, workshop_vendor)) data = { 'expected_status_as_per_sla': get_ops_desc_from_seq( expected_end_of_day['eod_task_seq']), 'status_at_start_of_day': status_at_start_of_day, 'status_at_end_of_day': expected_end_of_day['status_at_end_of_day'], 'total_time_used_for_date': expected_end_of_day['total_time_used_for_date'], 'delay': expected_end_of_day['delay'], 'days_in_workshop': expected_end_of_day['days_in_workshop'], 'tasks_done_for_date': expected_end_of_day['tasks_done_for_date'], } total_hrs_used = 0 if expected_end_of_day['total_time_used_for_date']: for work_data in expected_end_of_day[ 'total_time_used_for_date'].iteritems(): total_hrs_used += work_data[1] if total_hrs_used > 0: formatted_data = data formatted_data["booking_id"] = booking.id formatted_data[ "car_model"] = booking.usercar.car_model.name formatted_data[ "car_reg_num"] = booking.usercar.registration_number formatted_data["workshop"] = booking.workshop.name formatted_data["panels_breakup"] = panels_breakup formatted_data["workshop_eta"] = workshop_eta formatted_data["customer_eta"] = customer_eta formatted_data[ "current_status"] = booking.ops_status.ops_status_desc if booking.ops_status else "" formatted_data[ "status_at_start_of_day_desc"] = get_ops_desc_from_seq( data['status_at_start_of_day'] ['last_ops_status'] ) if data['status_at_start_of_day'][ 'last_ops_status'] else "Not Started" formatted_data[ "status_at_end_of_day_desc"] = get_ops_desc_from_seq( data['status_at_end_of_day'] ['last_ops_status']) datewise_booking_allocation[str( date_to_process.date())].append(formatted_data) work_to_done_in_day_datewise[str( date_to_process.date())] = data ordered_work_to_done_in_day_datewise = OrderedDict( sorted(work_to_done_in_day_datewise.items(), key=lambda t: t[0])) cannot_meet_workshop_eta = False if len(ordered_work_to_done_in_day_datewise.values()) > 0: last_data = ordered_work_to_done_in_day_datewise.values( )[-1] if workshop_eta.date() > date_to_process.date( ) and last_data['status_at_end_of_day'][ 'last_ops_status'] < 327: cannot_meet_workshop_eta = True booking_detail = { 'booking_id': booking.id, 'workshop_vendor': workshop_vendor, 'workshop_id': booking.workshop.id, 'workshop_reached_time': workshop_reached_time, 'workshop_eta': workshop_eta, 'customer_eta': customer_eta, 'work_to_done_in_day_datewise': ordered_work_to_done_in_day_datewise, 'panels_breakup': panels_breakup, 'cannot_meet_workshop_eta': cannot_meet_workshop_eta # 'is_untouched': is_untouched, } all_bookings_with_eod.append(booking_detail) for item in BUMPER_WORKSHOP_RESOURCES: ordered_datewise = OrderedDict( sorted(item['hrs_mapping'].items(), key=lambda t: t[0])) item['hrs_mapping'] = ordered_datewise return all_bookings_with_eod, projected_delayed_bookings, BUMPER_WORKSHOP_RESOURCES, datewise_booking_allocation except: logger.exception('Failed to create EOD list')
def handle(self, *args, **options): current_time = timezone.now() logger.info( "Script:: CREW_SCHEDULING:: Script started - current_time: %s" % current_time) gmaps = googlemaps.Client(key=GOOGLE_MAP_API_KEY) only_pickup = options['only_pickup'] # pickup time in next day current_context_time = _convert_to_given_timezone( timezone.now(), settings.TIME_ZONE) date_for_tomorrow_in_current_context = current_context_time + timezone.timedelta( days=1) start_time = make_datetime_timezone_aware_convert_to_utc( str(date_for_tomorrow_in_current_context.date()) + " 00:00:00", "+0530") end_time = start_time + timezone.timedelta(hours=24) logger.info( "Script:: CREW_SCHEDULING:: start_time: %s - end_Time: %s" % (start_time, end_time)) try: pickup_in_next_day_bookings = Booking.objects.select_related('user')\ .filter(status_id__lt=9, pickup_time__range =[start_time, end_time])\ .exclude(ops_status_id=8) pickup_data = [] for booking in pickup_in_next_day_bookings: address = booking.booking_address.filter( type=BookingAddress.ADDRESS_TYPE_PICKUP).first() directions_result = None try: origins = [ (DRIVER_1_LOC_LAT, DRIVER_1_LOC_LONG), (DRIVER_2_LOC_LAT, DRIVER_2_LOC_LONG), ] directions_result = gmaps.distance_matrix( origins, (address.address.latitude, address.address.longitude), mode="transit", units="metric", arrival_time=booking.pickup_time) except: logger.exception( "Script:: CREW_SCHEDULING:: Failed To get ETA based on Google map APIS." ) pickup_data.append({ 'bookingId': booking.id, 'name': booking.user.name, 'phone': booking.user.phone, 'time': booking.pickup_time, 'address_lat': address.address.latitude, 'address_long': address.address.longitude, 'driver': booking.pickup_driver, 'directions_result': directions_result }) logger.info("Script:: CREW_SCHEDULING:: pickup data: %s" % (pickup_data)) except: logger.exception( "Script:: CREW_SCHEDULING:: Failed to process to bookings")
def handle(self, *args, **options): current_time = timezone.now() logger.info("Script:: PICKUP_SUMMARY:: Script started - current_time: %s" % current_time) override_subject = None if options['reminder']: override_subject = "Reminder to set Tomorrow's Pickup" # pickup time in next day current_context_time = _convert_to_given_timezone(timezone.now(), settings.TIME_ZONE) date_for_tomorrow_in_current_context = current_context_time + timezone.timedelta(days=1) start_time = make_datetime_timezone_aware_convert_to_utc(str(date_for_tomorrow_in_current_context.date())+" 00:00:00", "+0530") end_time = start_time + timezone.timedelta(hours=24) logger.info("Script:: PICKUP_SUMMARY:: start_time: %s - end_Time: %s" % (start_time, end_time)) try: pickup_in_next_day_bookings = Booking.objects.select_related('user', 'usercar', 'usercar__car_model')\ .filter(status_id__lt=9, pickup_time__range =[start_time, end_time])\ .exclude(ops_status_id=8) pickup_data = [] for booking in pickup_in_next_day_bookings: booking_packages = BookingPackage.objects.filter(booking=booking) package_taken = [] panels = 0 for item in booking_packages: package_taken.append(item.package.package.name) if item.package.package.category == Package.CATEGORY_DENT: for panel_item in BookingPackagePanel.objects.filter(booking_package=item): # Not using count here as full body will be counted as 14 panels. panels += 1 if item.package.package.category == Package.CATEGORY_FULL_BODY: panels += 14 pickup_address = booking.booking_address.filter(type=BookingAddress.ADDRESS_TYPE_PICKUP).first() pickup_data.append({ 'bookingId': booking.id, 'name': booking.user.name, 'phone': booking.user.phone, 'time': format_datetime_for_grid(booking.pickup_time), 'address': "%s, %s" % (pickup_address.address.address1, pickup_address.address.address2), 'packages': ', '.join(package_taken), 'panels': panels, 'car': str(booking.usercar.car_model), 'driver': booking.pickup_driver.name if booking.pickup_driver else '', }) try: logger.info("Script:: PICKUP_SUMMARY:: pickup data: %s" % (pickup_data)) notification = Notifications.objects.get(name='OPS_ALERT_PICKUP_FOR_TOMORROW') cc_address_list = notification.get_cc_list() to_address_list = notification.get_to_list() email_service = NewEmailService(to_address_list, cc_address_list, context={'pickup_data': pickup_data}, analytic_info={'notification_id': notification.id}) email_service.send(template_folder_name=notification.template_folder_name, subject=override_subject) logger.info("Script:: PICKUP_SUMMARY:: Sent") except: logger.exception("Script:: PICKUP_SUMMARY:: Failed") except: logger.exception("Script:: PICKUP_SUMMARY:: Failed to process to bookings")