Esempio n. 1
0
    def _process_row(self,
                     row: Dict[str, Any],
                     filename: str = None,
                     line_num: int = None) -> Dict[str, Any]:
        raw_size = row["Quantity"]
        try:
            row["Quantity"], row["Unit"] = Unit.from_exp(raw_size)
        except RuntimeError as e:
            raise UserFacingException(str(e))

        row["Formula#"] = self.formulas[row["Name"]]
        ingr = Ingredient.objects.filter(number=row["Ingr#"])
        if not ingr.exists():
            raise IntegrityException(
                message="Cannot import Formula",
                line_num=line_num,
                referring_name="Formula",
                referred_name="Ingredient",
                fk_name="Ingr#",
                fk_value=row["Ingr#"],
            )
        ingr = ingr[0]
        if row["Unit"].unit_type != ingr.unit.unit_type:
            raise UserFacingException(
                f"Cannot import Formula.\nUnit '{row['Unit'].symbol}' is incompatible "
                f"with unit '{ingr.unit.symbol}' used in ingredient '{ingr.name}'."
            )
        row["Ingr#"] = ingr
        return row
Esempio n. 2
0
    def _process_row(self,
                     row: Dict[str, Any],
                     filename: str = None,
                     line_num: int = None) -> Dict[str, Any]:
        raw_size = row["Size"]
        try:
            row["Size"], row["Unit"] = Unit.from_exp(raw_size)
        except RuntimeError as e:
            raise UserFacingException(str(e))
        try:
            # The CSV parser is being too "helpful" by converting size to a float...
            row["Size"] = Decimal(str(row["Size"]))
        except InvalidOperation:
            raise UserFacingException(
                f"{row['Size']}is not a valid decimal number")
        try:
            row["Cost"] = Decimal(utils.parse_usd(row["Cost"]))
        except ValueError as e:
            raise UserFacingException(str(e))

        vendor = Vendor.objects.filter(info=row["Vendor Info"])
        if vendor.exists():
            row["Vendor Info"] = vendor[0]
        else:
            row["Vendor Info"] = Vendor.objects.create(info=row["Vendor Info"])
        if row["Comment"] is None:
            row["Comment"] = ""
        return row
Esempio n. 3
0
def auto_schedule(request):
    if not request.user.is_plant_manager:
        raise UserFacingException("You are not authorized to use the auto-scheduler.")
    items = json.loads(request.POST.get("items", "[]"))
    existing = json.loads(request.POST.get("existing", "{}"))
    start = request.POST.get("start", "")
    end = request.POST.get("end", "")
    scheduler_name = request.POST.get("scheduler", "")
    current_timezone = timezone.get_current_timezone()
    try:
        start = datetime.fromtimestamp(int(start), tz=current_timezone)
        end = datetime.fromtimestamp(int(end), tz=current_timezone)
    except ValueError:
        raise UserFacingException("Unable to schedule: invalid start/end time")
    if start >= end:
        raise UserFacingException(
            "Unable to schedule: end time is less than start time"
        )
    if not items:
        # Return empty schedule
        return "[]"
    to_schedule = []
    existing_items = {}
    try:
        owned_lines = request.user.owned_lines
        for item in items:
            to_schedule.append(
                scheduling.Item(
                    GoalItem.objects.get(id=item["id"]),
                    int(item["hours"]),
                    set(item["groups"]).intersection(owned_lines),
                )
            )
        for group, items in existing.items():
            if not group in owned_lines:
                continue
            existing_items[group] = [
                scheduling.ExistingItem(
                    GoalItem.objects.get(id=item["id"]),
                    datetime.fromtimestamp(int(item["start"]), tz=current_timezone),
                    datetime.fromtimestamp(int(item["end"]), tz=current_timezone),
                )
                for item in items
            ]
    except Exception:
        logger.exception("Invalid auto-schedule request")
        raise UserFacingException("Unable to schedule: invalid request.")
    try:
        scheduler = scheduling.get_scheduler(scheduler_name)
        result = scheduler(to_schedule, existing_items, start, end)
    except scheduling.ScheduleException as e:
        raise UserFacingException(str(e))
    return json.dumps(result)
Esempio n. 4
0
 def _to_decimal(row: Dict[str, Any], fields: Iterable[str]) -> None:
     for field in fields:
         try:
             row[field] = Decimal(str(row[field]))
         except InvalidOperation:
             raise UserFacingException(
                 f"'{row[field]}' is not a valid floating point number.")
Esempio n. 5
0
    def commit(self) -> Tuple[int, int, int]:
        """
        Force commits all instances, including collisions.
        :return: a 3-tuple (inserted, updated, ignored) of integers representing the
            numbers of instances in each category.
        """
        if not self.is_bound:
            raise UserFacingException(
                "Data corruption detected. Please try importing "
                "again. Error code: ENOTBOUND")
        for instance in self._instances:
            self.__save(instance.instance)
            self._save_m2m(instance)

        for record in self._collisions:
            ex = record.instance
            ex.old_instance.delete()
            self.__save(ex.new_instance)
            self._save_m2m(record)

        for record in self._ignored:
            # Even though the instance itself is ignored, it doesn't mean that
            # its M2M fields haven't changed. We save those here.
            self._save_m2m(record)

        self._post_process()
        return len(self.instances), len(self.collisions), len(self.ignored)
Esempio n. 6
0
 def _parse_usd(row: Dict[str, Any], fields: Iterable[str]):
     for field in fields:
         try:
             row[field] = Decimal(utils.parse_usd(row[field]))
         except (InvalidOperation, ValueError):
             raise UserFacingException(
                 "Cost must be a USD expression; "
                 f"'{row[field]}' is not a valid USD expression.")
Esempio n. 7
0
def remove_lines(request):
    try:
        line_ids = set(map(int, json.loads(request.GET.get("toRemove", "[]"))))
    except ValueError as e:
        raise UserFacingException(f"Invalid Manufacturing Line IDs: '{e:s}'")
    _, deleted = ManufacturingLine.objects.filter(pk__in=line_ids).delete()
    return (
        f"Successfully deleted {deleted.get('meals.ManufacturingLine', 0)} "
        "Manufacturing Line(s)")
Esempio n. 8
0
def _find_users_by_id(user_ids):
    user_objs = User.objects.filter(pk__in=user_ids)
    found = set(user_objs.values_list("pk", flat=True))
    missing = user_ids - found
    if missing:
        raise UserFacingException(
            f"The following user IDs cannot be found: {', '.join(map(str, missing))}"
        )
    return user_objs
Esempio n. 9
0
def view_pl_skus(request, pk):
    queryset = ProductLine.objects.filter(pk=pk)
    if queryset.exists():
        pl = queryset[0]
        skus = pl.sku_set.all()
        resp = render_to_string(
            template_name="meals/sku/view_sku.html",
            context={"pl_skus": skus},
            request=request,
        )
        return resp
    raise UserFacingException(f"Product Line with ID '{pk}' not found.")
Esempio n. 10
0
def sales_projection(request):
    sku_number = request.GET.get("sku", None)
    if not sku_number:
        raise UserFacingException("Invalid request: no SKU was provided")
    start_date = request.GET.get("start",
                                 None) or datetime.now() - timedelta(days=10)
    end_date = request.GET.get("end", None) or datetime.now()
    sku_qs = Sku.objects.filter(number=sku_number)
    if sku_qs.exists():
        sku = sku_qs[0]
    else:
        raise UserFacingException(
            f"Invalid request: SKU #{sku_number} cannot be found.")

    params = {"start": start_date, "end": end_date}
    form = ProjectionsFilterForm(params)
    if form.is_valid():
        data = form.query(sku_number)
    else:
        data = {}

    if data:
        avg = statistics.mean(data.values())
        std = statistics.stdev(data.values())
    else:
        avg = std = Decimal("0.0")
    form_html = render_to_string(
        request=request,
        template_name="meals/sales/projection.html",
        context={
            "data": data,
            "form": form,
            "sku": sku,
            "avg": avg,
            "std": std
        },
    )
    return form_html
Esempio n. 11
0
def remove_users(request):
    user_ids = set(map(int, json.loads(request.GET.get("u", "[]"))))
    if not user_ids:
        return "No user was selected."
    user_objs = _find_users_by_id(user_ids)
    superusers = user_objs.filter(is_superuser=True)
    if superusers.exists():
        raise UserFacingException(
            f"User '{superusers[0].username}' is reserved and cannot be removed."
        )
    _, deleted = user_objs.delete()
    logger.info("Deleted=%s", deleted)
    num_deleted = deleted.get("meals.User", 1)
    return f"Successfully deleted {num_deleted} user(s)."
Esempio n. 12
0
 def _check_duplicates(
     self,
     raw: Dict[str, str],
     converted: Dict[str, Any],
     filename: str,
     line_num: int,
 ):
     name_value = (raw["Name"], )
     if name_value in self.unique_dict[("name", )]:
         prev_line_num = self.unique_dict[("name", )][name_value]
         if raw["Formula#"]:
             formula_number = (raw["Formula#"], )
             unique_dict = self.unique_dict[("number", )]
             if (formula_number not in unique_dict
                     or unique_dict[formula_number] != prev_line_num):
                 raise UserFacingException(
                     f"Cannot import Formula: formula name '{name_value[0]}' "
                     "and number mismatched. Expected number to match with record "
                     f"in line {prev_line_num}.")
             else:
                 raise Skip
         else:
             raise Skip
     super()._check_duplicates(raw, converted, filename, line_num)
Esempio n. 13
0
    def _process_row(self,
                     row: Dict[str, Any],
                     filename: str = None,
                     line_num: int = None) -> Dict[str, Any]:
        try:
            row["SKU#"] = int(row["SKU#"])
        except ValueError:
            raise UserFacingException(
                f"SKU number must be numeric; '{row['SKU#']}' is not a valid integer."
            )
        row["Count per case"] = int(row["Count per case"])
        raw_formula = row["Formula#"]

        formula_qs = Formula.objects.filter(number=raw_formula)
        if formula_qs.exists():
            row["Formula#"] = formula_qs[0]
        else:
            raise IntegrityException(
                message=f"Cannot import SKU #{row['SKU#']}",
                line_num=line_num,
                referring_name="SKU",
                referred_name="Formula",
                fk_name="Formula",
                fk_value=row["Formula#"],
            )
        for field in ["Case UPC", "Unit UPC"]:
            if utils.is_valid_upc(row[field]):
                if str(row[field])[0] not in ["2", "3", "4", "5"]:
                    row[field] = Upc.objects.get_or_create(
                        upc_number=row[field])[0]
                else:
                    raise ValidationError(
                        "%(field)s must be a valid consumer UPC; '%(upc)s' is not a "
                        "valid consumer UPC number.",
                        params={
                            "field": field,
                            "upc": row[field]
                        },
                    )
            else:
                raise UserFacingException(f"{row[field]} is not a valid UPC.")
        if row["Comment"] is None:
            row["Comment"] = ""
        product_line = ProductLine.objects.filter(name=row["PL Name"])
        if product_line.exists():
            row["PL Name"] = product_line[0]
        else:
            raise IntegrityException(
                message=f"Cannot import SKU #{row['SKU#']}",
                line_num=line_num,
                referring_name="SKU",
                referred_name="Product Line",
                fk_name="PL Name",
                fk_value=row["PL Name"],
            )

        row["ML Shortnames"] = self._check_manufacturing_line(row, line_num)

        self._to_decimal(row, ["Rate", "Formula factor"])
        self._parse_usd(row, ["Mfg setup cost", "Mfg run cost"])

        return row