def generate_header(self) -> List[Tuple[int, str, str, str]]:
     return [
         ((Date.next_lock_in() - dt.timedelta(days=day)).day,
          "d-none d-lg-table-cell" if day > 2 else str(),
          (Date.next_lock_in() - dt.timedelta(days=day)).strftime("%a")[:2],
          "bg-warning text-dark" if Date.next_lock_in() -
          dt.timedelta(days=day) == Date.today() else str())
         for day in range(self.PAST_DAYS, -1, -1)
     ]
 def generate_hotel_status(self):
     hotels = Hotel.objects.filter_by(city=current_user.city).get()
     self.hotels = [(hotel.name, list()) for hotel in hotels]
     for index, hotel in enumerate(hotels):
         last_date = Date(hotel.last_date).date
         if last_date and (last_date < hotel.contract[0]
                           or last_date > Date.next_lock_in()):
             last_date = None
         for day in range(self.PAST_DAYS, -1, -1):
             date = Date.next_lock_in() - dt.timedelta(days=day)
             if not last_date:
                 self.add_status(
                     index, day, self.NOT_DONE
                     if hotel.is_contract_valid(date) else self.NA)
                 continue
             if date < last_date:
                 self.add_status(
                     index, day, self.DONE
                     if hotel.is_contract_valid(date) else self.NA)
             elif date > last_date:
                 self.add_status(
                     index, day, self.NOT_DONE
                     if hotel.is_contract_valid(date) else self.NA)
             else:
                 self.add_status(
                     index, day, self.DONE if hotel.last_timing
                     == Config.EVENING else self.PARTIAL)
         if current_user.hotel != hotel.name:
             continue
         end_date = min(Date.yesterday(), hotel.contract[1])
         last_date = last_date or hotel.contract[0]
         if hotel.contract[0] > Date.today():
             self.status = self.STATUS_NO_CONTRACT
             continue
         if last_date >= end_date:
             self.status = self.STATUS_ALL_DONE if last_date > end_date or hotel.last_timing == Config.EVENING \
                 else self.STATUS_PARTIAL
             continue
         days = (end_date - last_date).days
         self.status = (f"{days} days entry remaining",
                        "list-group-item-danger")
     self.hotels.sort(key=itemgetter(0))
     hotel = next(
         (hotel for hotel in self.hotels if hotel[0] == current_user.hotel))
     if hotel:
         self.hotels.remove(hotel)
         self.hotels.insert(0, hotel)
     return
Exemple #3
0
 def get_data_entry_date(cls, hotel: Hotel) -> Tuple[Optional[dt.date], str]:
     start_date, end_date = hotel.contract
     today = Date.next_lock_in()
     data_entry_date = Date(hotel.last_date).date
     if end_date < start_date:
         return None, "Invalid Contract"
     if today < start_date:
         return None, "Cannot enter events for future contract"
     if not data_entry_date or data_entry_date < start_date:
         return start_date, Config.MORNING
     if today > end_date and data_entry_date > end_date:
         return end_date, str()
     if hotel.last_timing == Config.EVENING and (data_entry_date == end_date or data_entry_date == today):
         return data_entry_date, str()
     if hotel.last_timing == Config.MORNING and data_entry_date <= today:
         return data_entry_date, Config.EVENING
     if hotel.last_timing == Config.EVENING and data_entry_date < today:
         return data_entry_date + dt.timedelta(days=1), Config.MORNING
     return start_date, Config.MORNING
Exemple #4
0
 def display_next(self) -> str:
     if not self.usages:
         return "disabled"
     end_date = min(self.hotel.contract[1], Date.next_lock_in())
     return "disabled" if (self.date == end_date and self.timing == Config.EVENING) or self.date > end_date \
         else str()
Exemple #5
0
 def validate_filename(self, filename: FileField):
     if self.form_type.data != self.UPLOAD:
         return
     file: FileStorage = filename.data
     if not secure_filename(file.filename):
         raise ValidationError("No file selected for upload")
     file_path = File(current_user.id, "csv").local_path
     file.save(file_path)
     with open(file_path) as csv_file:
         csv_reader = csv.DictReader(csv_file)
         columns = set(csv_reader.fieldnames)
         csv_reader = [row for row in csv_reader]
     if columns != {HDR.DATE, HDR.TIMING, HDR.NO_EVENT, HDR.CLIENT, HDR.MEAL, HDR.TYPE, HDR.BALLROOM, HDR.EVENT}:
         raise ValidationError("Invalid column names in the csv file")
     for row in csv_reader:
         usage = Usage()
         usage.hotel = self.hotel.name
         usage.city = self.hotel.city
         date = Date.from_dd_mmm_yyyy(row[HDR.DATE]).date
         if not date:
             self.add_errors(row, HDR.DATE)
             continue
         usage.set_date(date)
         if row[HDR.TIMING] not in Config.TIMINGS:
             self.add_errors(row, HDR.TIMING)
             continue
         usage.timing = row[HDR.TIMING]
         if row[HDR.NO_EVENT] == HDR.NO_EVENT:
             usage.no_event = True
             self.upload_data.append(usage)
             continue
         if not row[HDR.CLIENT]:
             self.add_errors(row, HDR.CLIENT)
             continue
         usage.client = row[HDR.CLIENT]
         if usage.timing == Config.MORNING:
             if row[HDR.MEAL] not in Config.MORNING_MEALS:
                 self.add_errors(row, HDR.MEAL)
                 continue
             usage.meals = [Config.BREAKFAST, Config.LUNCH] if row[HDR.MEAL] == Config.BREAKFAST_LUNCH \
                 else [row[HDR.MEAL]]
         else:
             if row[HDR.MEAL] not in Config.EVENING_MEALS:
                 self.add_errors(row, HDR.MEAL)
                 continue
             usage.meals = [Config.HI_TEA, Config.DINNER] if row[HDR.MEAL] == Config.HI_TEA_DINNER \
                 else [row[HDR.MEAL]]
         if row[HDR.TYPE] not in Config.EVENTS:
             self.add_errors(row, HDR.TYPE)
             continue
         usage.event_type = row[HDR.TYPE]
         ballrooms = [room.strip() for room in row[HDR.BALLROOM].split(",")]
         if any(room not in self.hotel.ballrooms for room in ballrooms):
             self.add_errors(row, HDR.BALLROOM)
             continue
         usage.ballrooms = ballrooms
         self.hotel.set_ballroom_used(ballrooms)
         usage.event_description = row[HDR.EVENT]
         self.upload_data.append(usage)
     if self.upload_errors:
         raise ValidationError("Field specific errors. Fields with red highlight have errors.")
     if not self.upload_data:
         raise ValidationError("There are no events in the csv file")
     self.upload_data.sort(key=lambda usage_item: usage_item.timing, reverse=True)
     self.upload_data.sort(key=lambda usage_item: usage_item.date)
     if current_user.role != Config.ADMIN:
         self._check_start_period_of_uploaded_file()
     last_date, lock_in, end_date = Date(self.upload_data[-1].date).date, Date.next_lock_in(), self.hotel.contract[1]
     if last_date > lock_in:
         raise ValidationError(f"End period ({Date(last_date).format_date}) cannot be greater than the "
                               f"next lock in period ({Date(lock_in).format_date})")
     if last_date > end_date:
         raise ValidationError(f"End period ({Date(last_date).format_date}) cannot be greater than the "
                               f"contract end date ({Date(end_date).format_date})")
     previous_date = None
     for date, date_usages in itertools.groupby(self.upload_data, key=lambda usage_item: usage_item.date):
         date = Date(date).date
         date_usages = list(date_usages)
         if not any(usage.timing == Config.MORNING for usage in date_usages):
             raise ValidationError(f"Date {Date(date).format_date} does not have {Config.MORNING} events")
         if not any(usage.timing == Config.EVENING for usage in date_usages):
             raise ValidationError(f"Date {Date(date).format_date} does not have {Config.EVENING} events")
         for timing, timing_usages in itertools.groupby(date_usages, key=lambda usage_item: usage_item.timing):
             seen = set()
             for usage in timing_usages:
                 if usage.client in seen:
                     raise ValidationError(f"Duplicate client name {usage.client} for the period "
                                           f"{Date(date).format_date} - {timing}")
                 seen.add(usage.client)
         if previous_date and (date - previous_date).days != 1:
             raise ValidationError(f"There are missing events between {Date(previous_date).format_date} and "
                                   f"{Date(date).format_date}")
         previous_date = date
     return