예제 #1
0
    def handle(self, **options):
        # Make sure the debug flag is not set!
        # When it is set, the django database wrapper collects a list of all sql
        # statements executed and their timings. This consumes plenty of memory
        # and cpu time.
        tmp_debug = settings.DEBUG
        settings.DEBUG = False

        # Pick up the options
        if 'verbosity' in options:
            verbosity = int(options['verbosity'])
        else:
            verbosity = 1
        if 'cluster' in options:
            cluster = int(options['cluster'])
        else:
            cluster = 100
        if 'demand' in options:
            demand = int(options['demand'])
        else:
            demand = 30
        if 'forecast_per_item' in options:
            forecast_per_item = int(options['forecast_per_item'])
        else:
            forecast_per_item = 50
        if 'level' in options:
            level = int(options['level'])
        else:
            level = 5
        if 'resource' in options:
            resource = int(options['resource'])
        else:
            resource = 60
        if 'resource_size' in options:
            resource_size = int(options['resource_size'])
        else:
            resource_size = 5
        if 'components' in options:
            components = int(options['components'])
        else:
            components = 200
        if 'components_per' in options:
            components_per = int(options['components_per'])
        else:
            components_per = 5
        if components == 0:
            components_per = 0
        if 'deliver_lt' in options:
            deliver_lt = int(options['deliver_lt'])
        else:
            deliver_lt = 30
        if 'procure_lt' in options:
            procure_lt = int(options['procure_lt'])
        else:
            procure_lt = 40
        if 'currentdate' in options:
            currentdate = options['currentdate'] or datetime.strftime(
                date.today(), '%Y-%m-%d')
        else:
            currentdate = datetime.strftime(date.today(), '%Y-%m-%d')
        if 'database' in options:
            database = options['database'] or DEFAULT_DB_ALIAS
        else:
            database = DEFAULT_DB_ALIAS
        if database not in settings.DATABASES:
            raise CommandError("No database settings known for '%s'" %
                               database)
        if 'user' in options and options['user']:
            try:
                user = User.objects.all().using(database).get(
                    username=options['user'])
            except:
                raise CommandError("User '%s' not found" % options['user'])
        else:
            user = None

        random.seed(100)  # Initialize random seed to get reproducible results

        now = datetime.now()
        task = None
        try:
            # Initialize the task
            if 'task' in options and options['task']:
                try:
                    task = Task.objects.all().using(database).get(
                        pk=options['task'])
                except:
                    raise CommandError("Task identifier not found")
                if task.started or task.finished or task.status != "Waiting" or task.name != 'generate model':
                    raise CommandError("Invalid task identifier")
                task.status = '0%'
                task.started = now
            else:
                task = Task(name='generate model',
                            submitted=now,
                            started=now,
                            status='0%',
                            user=user)
            task.arguments = "--cluster=%s --demand=%s --forecast_per_item=%s --level=%s --resource=%s " \
              "--resource_size=%s --components=%s --components_per=%s --deliver_lt=%s --procure_lt=%s" % (
                cluster, demand, forecast_per_item, level, resource,
                resource_size, components, components_per, deliver_lt, procure_lt
              )
            task.save(using=database)

            # Pick up the startdate
            try:
                startdate = datetime.strptime(currentdate, '%Y-%m-%d')
            except:
                raise CommandError(
                    "current date is not matching format YYYY-MM-DD")

            # Check whether the database is empty
            if Buffer.objects.using(database).count(
            ) > 0 or Item.objects.using(database).count() > 0:
                raise CommandError(
                    "Database must be empty before creating a model")

            # Plan start date
            if verbosity > 0:
                print("Updating current date...")
            Parameter.objects.using(database).create(name="currentdate",
                                                     value=datetime.strftime(
                                                         startdate,
                                                         "%Y-%m-%d %H:%M:%S"))
            Parameter.objects.using(database).create(name="plan.loglevel",
                                                     value="3")

            # Planning horizon
            # minimum 10 daily buckets, weekly buckets till 40 days after current
            if verbosity > 0:
                print("Updating buckets...")
            management.call_command('frepple_createbuckets',
                                    user=user,
                                    database=database)
            task.status = '2%'
            task.save(using=database)

            # Weeks calendar
            if verbosity > 0:
                print("Creating weeks calendar...")
            with transaction.atomic(using=database):
                weeks = Calendar.objects.using(database).create(name="Weeks",
                                                                defaultvalue=0)
                for i in BucketDetail.objects.using(database).filter(
                        bucket="week").all():
                    CalendarBucket(startdate=i.startdate,
                                   enddate=i.enddate,
                                   value=1,
                                   calendar=weeks).save(using=database)
                task.status = '4%'
                task.save(using=database)

            # Working days calendar
            if verbosity > 0:
                print("Creating working days...")
            with transaction.atomic(using=database):
                workingdays = Calendar.objects.using(database).create(
                    name="Working Days", defaultvalue=0)
                minmax = BucketDetail.objects.using(database).filter(
                    bucket="week").aggregate(Min('startdate'),
                                             Max('startdate'))
                CalendarBucket(startdate=minmax['startdate__min'],
                               enddate=minmax['startdate__max'],
                               value=1,
                               calendar=workingdays,
                               priority=1,
                               saturday=False,
                               sunday=False).save(using=database)
                task.status = '6%'
                task.save(using=database)

            # Parent location
            loc = Location.objects.using(database).create(
                name="Factory", available=workingdays)

            # Create a random list of categories to choose from
            categories = [
                'cat A', 'cat B', 'cat C', 'cat D', 'cat E', 'cat F', 'cat G'
            ]

            # Create customers
            if verbosity > 0:
                print("Creating customers...")
            with transaction.atomic(using=database):
                cust = []
                for i in range(100):
                    c = Customer.objects.using(database).create(
                        name='Cust %03d' % i)
                    cust.append(c)
                task.status = '8%'
                task.save(using=database)

            # Create resources and their calendars
            if verbosity > 0:
                print("Creating resources and calendars...")
            with transaction.atomic(using=database):
                res = []
                for i in range(resource):
                    cal = Calendar.objects.using(database).create(
                        name='capacity for res %03d' % i,
                        category='capacity',
                        defaultvalue=0)
                    CalendarBucket.objects.using(database).create(
                        startdate=startdate, value=resource_size, calendar=cal)
                    r = Resource.objects.using(database).create(
                        name='Res %03d' % i,
                        maximum_calendar=cal,
                        location=loc)
                    res.append(r)
                task.status = '10%'
                task.save(using=database)
                random.shuffle(res)

            # Create the components
            if verbosity > 0:
                print("Creating raw materials...")
            with transaction.atomic(using=database):
                comps = []
                compsupplier = Supplier.objects.using(database).create(
                    name='component supplier')
                for i in range(components):
                    it = Item.objects.using(database).create(
                        name='Component %04d' % i,
                        category='Procured',
                        price=str(round(random.uniform(0, 100))))
                    ld = abs(
                        round(random.normalvariate(procure_lt,
                                                   procure_lt / 3)))
                    Buffer.objects.using(database).create(
                        name='%s @ %s' % (it.name, loc.name),
                        location=loc,
                        category='Procured',
                        item=it,
                        minimum=20,
                        onhand=str(
                            round(forecast_per_item * random.uniform(1, 3) *
                                  ld / 30)),
                    )
                    ItemSupplier.objects.using(database).create(
                        item=it,
                        location=loc,
                        supplier=compsupplier,
                        leadtime=timedelta(days=ld),
                        sizeminimum=80,
                        sizemultiple=10,
                        priority=1,
                        cost=it.price)
                    comps.append(it)
                task.status = '12%'
                task.save(using=database)

            # Loop over all clusters
            durations = [timedelta(days=i) for i in range(1, 6)]
            progress = 88.0 / cluster
            for i in range(cluster):
                with transaction.atomic(using=database):
                    if verbosity > 0:
                        print("Creating supply chain for end item %d..." % i)

                    # Item
                    it = Item.objects.using(database).create(
                        name='Itm %05d' % i,
                        category=random.choice(categories),
                        price=str(round(random.uniform(100, 200))))

                    # Level 0 buffer
                    buf = Buffer.objects.using(database).create(
                        name='%s @ %s' % (it.name, loc.name),
                        item=it,
                        location=loc,
                        category='00')

                    # Demand
                    for j in range(demand):
                        Demand.objects.using(database).create(
                            name='Dmd %05d %05d' % (i, j),
                            item=it,
                            location=loc,
                            quantity=int(random.uniform(1, 6)),
                            # Exponential distribution of due dates, with an average of deliver_lt days.
                            due=startdate + timedelta(days=round(
                                random.expovariate(float(1) / deliver_lt /
                                                   24)) / 24),
                            # Orders have higher priority than forecast
                            priority=random.choice([1, 2]),
                            customer=random.choice(cust),
                            category=random.choice(categories))

                    # Create upstream operations and buffers
                    ops = []
                    previtem = it
                    for k in range(level):
                        if k == 1 and res:
                            # Create a resource load for operations on level 1
                            oper = Operation.objects.using(database).create(
                                name='Oper %05d L%02d' % (i, k),
                                type='time_per',
                                location=loc,
                                duration_per=timedelta(days=1),
                                sizemultiple=1,
                                item=previtem)
                            if resource < cluster and i < resource:
                                # When there are more cluster than resources, we try to assure
                                # that each resource is loaded by at least 1 operation.
                                OperationResource.objects.using(
                                    database).create(resource=res[i],
                                                     operation=oper)
                            else:
                                OperationResource.objects.using(
                                    database).create(
                                        resource=random.choice(res),
                                        operation=oper)
                        else:
                            oper = Operation.objects.using(database).create(
                                name='Oper %05d L%02d' % (i, k),
                                duration=random.choice(durations),
                                sizemultiple=1,
                                location=loc,
                                item=previtem)
                        ops.append(oper)
                        # Some inventory in random buffers
                        if random.uniform(0, 1) > 0.8:
                            buf.onhand = int(random.uniform(5, 20))
                        buf.save(using=database)
                        OperationMaterial.objects.using(database).create(
                            operation=oper,
                            item=previtem,
                            quantity=1,
                            type="end")
                        if k != level - 1:
                            # Consume from the next level in the bill of material
                            it_tmp = Item.objects.using(database).create(
                                name='Itm %05d L%02d' % (i, k + 1),
                                category=random.choice(categories),
                                price=str(round(random.uniform(100, 200))))
                            buf = Buffer.objects.using(database).create(
                                name='%s @ %s' % (it_tmp.name, loc.name),
                                item=it_tmp,
                                location=loc,
                                category='%02d' % (k + 1))
                            OperationMaterial.objects.using(database).create(
                                operation=oper, item=it_tmp, quantity=-1)
                        previtem = it_tmp

                    # Consume raw materials / components
                    c = []
                    for j in range(components_per):
                        o = random.choice(ops)
                        b = random.choice(comps)
                        while (o, b) in c:
                            # A flow with the same operation and buffer already exists
                            o = random.choice(ops)
                            b = random.choice(comps)
                        c.append((o, b))
                        OperationMaterial.objects.using(database).create(
                            operation=o,
                            item=b,
                            quantity=random.choice([-1, -1, -1, -2, -3]))

                    # Commit the current cluster
                    task.status = '%d%%' % (12 + progress * (i + 1))
                    task.save(using=database)

            # Task update
            task.status = 'Done'
            task.finished = datetime.now()

        except Exception as e:
            if task:
                task.status = 'Failed'
                task.message = '%s' % e
                task.finished = datetime.now()
                task.save(using=database)
            raise e

        finally:
            if task:
                task.save(using=database)
            settings.DEBUG = tmp_debug
예제 #2
0
    def handle(self, **options):
        # Make sure the debug flag is not set!
        # When it is set, the django database wrapper collects a list of all sql
        # statements executed and their timings. This consumes plenty of memory
        # and cpu time.
        tmp_debug = settings.DEBUG
        settings.DEBUG = False

        # Pick up the options
        verbosity = int(options["verbosity"])
        cluster = int(options["cluster"])
        demand = int(options["demand"])
        forecast_per_item = int(options["forecast_per_item"])
        level = int(options["level"])
        resource = int(options["resource"])
        resource_size = int(options["resource_size"])
        components = int(options["components"])
        components_per = int(options["components_per"])
        if components <= 0:
            components_per = 0
        deliver_lt = int(options["deliver_lt"])
        procure_lt = int(options["procure_lt"])
        if options["currentdate"]:
            currentdate = options["currentdate"]
        else:
            currentdate = datetime.strftime(date.today(), "%Y-%m-%d")
        database = options["database"]
        if database not in settings.DATABASES:
            raise CommandError("No database settings known for '%s'" %
                               database)
        if options["user"]:
            try:
                user = User.objects.all().using(database).get(
                    username=options["user"])
            except Exception:
                raise CommandError("User '%s' not found" % options["user"])
        else:
            user = None

        random.seed(100)  # Initialize random seed to get reproducible results

        now = datetime.now()
        task = None
        try:
            # Initialize the task
            if options["task"]:
                try:
                    task = Task.objects.all().using(database).get(
                        pk=options["task"])
                except Exception:
                    raise CommandError("Task identifier not found")
                if (task.started or task.finished or task.status != "Waiting"
                        or task.name
                        not in ("frepple_createmodel", "createmodel")):
                    raise CommandError("Invalid task identifier")
                task.status = "0%"
                task.started = now
            else:
                task = Task(
                    name="createmodel",
                    submitted=now,
                    started=now,
                    status="0%",
                    user=user,
                )
            task.arguments = (
                "--cluster=%s --demand=%s --forecast_per_item=%s --level=%s --resource=%s "
                "--resource_size=%s --components=%s --components_per=%s --deliver_lt=%s --procure_lt=%s"
                % (
                    cluster,
                    demand,
                    forecast_per_item,
                    level,
                    resource,
                    resource_size,
                    components,
                    components_per,
                    deliver_lt,
                    procure_lt,
                ))
            task.save(using=database)

            # Pick up the startdate
            try:
                startdate = datetime.strptime(currentdate, "%Y-%m-%d")
            except Exception:
                raise CommandError(
                    "current date is not matching format YYYY-MM-DD")

            # Check whether the database is empty
            if (Buffer.objects.using(database).count() > 0
                    or Item.objects.using(database).count() > 0):
                raise CommandError(
                    "Database must be empty before creating a model")

            # Plan start date
            if verbosity > 0:
                print("Updating current date...")
            param = Parameter.objects.using(database).get_or_create(
                name="currentdate")[0]
            param.value = datetime.strftime(startdate, "%Y-%m-%d %H:%M:%S")
            param.save(using=database)

            # Planning horizon
            # minimum 10 daily buckets, weekly buckets till 40 days after current
            if verbosity > 0:
                print("Updating buckets...")
            management.call_command("createbuckets",
                                    user=user,
                                    database=database)
            task.status = "2%"
            task.save(using=database)

            # Weeks calendar
            if verbosity > 0:
                print("Creating weeks calendar...")
            with transaction.atomic(using=database):
                weeks = Calendar.objects.using(database).create(name="Weeks",
                                                                defaultvalue=0)
                for i in (BucketDetail.objects.using(database).filter(
                        bucket="week").all()):
                    CalendarBucket(
                        startdate=i.startdate,
                        enddate=i.enddate,
                        value=1,
                        calendar=weeks,
                    ).save(using=database)
                task.status = "4%"
                task.save(using=database)

            # Working days calendar
            if verbosity > 0:
                print("Creating working days...")
            with transaction.atomic(using=database):
                workingdays = Calendar.objects.using(database).create(
                    name="Working Days", defaultvalue=0)
                minmax = (BucketDetail.objects.using(database).filter(
                    bucket="week").aggregate(Min("startdate"),
                                             Max("startdate")))
                CalendarBucket(
                    startdate=minmax["startdate__min"],
                    enddate=minmax["startdate__max"],
                    value=1,
                    calendar=workingdays,
                    priority=1,
                    saturday=False,
                    sunday=False,
                ).save(using=database)
                task.status = "6%"
                task.save(using=database)

            # Parent location
            loc = Location.objects.using(database).create(
                name="Factory", available=workingdays)

            # Create a random list of categories to choose from
            categories = [
                "cat A", "cat B", "cat C", "cat D", "cat E", "cat F", "cat G"
            ]

            # Create customers
            if verbosity > 0:
                print("Creating customers...")
            with transaction.atomic(using=database):
                cust = []
                for i in range(100):
                    c = Customer.objects.using(database).create(
                        name="Cust %03d" % i)
                    cust.append(c)
                task.status = "8%"
                task.save(using=database)

            # Create resources and their calendars
            if verbosity > 0:
                print("Creating resources and calendars...")
            with transaction.atomic(using=database):
                res = []
                for i in range(resource):
                    cal = Calendar.objects.using(database).create(
                        name="capacity for res %03d" % i,
                        category="capacity",
                        defaultvalue=0,
                    )
                    CalendarBucket.objects.using(database).create(
                        startdate=startdate, value=resource_size, calendar=cal)
                    r = Resource.objects.using(database).create(
                        name="Res %03d" % i,
                        maximum_calendar=cal,
                        location=loc)
                    res.append(r)
                task.status = "10%"
                task.save(using=database)
                random.shuffle(res)

            # Create the components
            if verbosity > 0:
                print("Creating raw materials...")
            with transaction.atomic(using=database):
                comps = []
                compsupplier = Supplier.objects.using(database).create(
                    name="component supplier")
                for i in range(components):
                    it = Item.objects.using(database).create(
                        name="Component %04d" % i,
                        category="Procured",
                        cost=str(round(random.uniform(0, 100))),
                    )
                    ld = abs(
                        round(random.normalvariate(procure_lt,
                                                   procure_lt / 3)))
                    Buffer.objects.using(database).create(
                        location=loc,
                        category="Procured",
                        item=it,
                        minimum=20,
                        onhand=str(
                            round(forecast_per_item * random.uniform(1, 3) *
                                  ld / 30)),
                    )
                    ItemSupplier.objects.using(database).create(
                        item=it,
                        location=loc,
                        supplier=compsupplier,
                        leadtime=timedelta(days=ld),
                        sizeminimum=80,
                        sizemultiple=10,
                        priority=1,
                        cost=it.cost,
                    )
                    comps.append(it)
                task.status = "12%"
                task.save(using=database)

            # Loop over all clusters
            durations = [timedelta(days=i) for i in range(1, 6)]
            progress = 88.0 / cluster
            for i in range(cluster):
                with transaction.atomic(using=database):
                    if verbosity > 0:
                        print("Creating supply chain for end item %d..." % i)

                    # Item
                    it = Item.objects.using(database).create(
                        name="Itm %05d" % i,
                        category=random.choice(categories),
                        cost=str(round(random.uniform(100, 200))),
                    )

                    # Level 0 buffer
                    buf = Buffer.objects.using(database).create(item=it,
                                                                location=loc,
                                                                category="00")

                    # Demand
                    for j in range(demand):
                        Demand.objects.using(database).create(
                            name="Dmd %05d %05d" % (i, j),
                            item=it,
                            location=loc,
                            quantity=int(random.uniform(1, 6)),
                            # Exponential distribution of due dates, with an average of deliver_lt days.
                            due=startdate + timedelta(days=round(
                                random.expovariate(float(1) / deliver_lt /
                                                   24)) / 24),
                            # Orders have higher priority than forecast
                            priority=random.choice([1, 2]),
                            customer=random.choice(cust),
                            category=random.choice(categories),
                        )

                    # Create upstream operations and buffers
                    ops = []
                    previtem = it
                    for k in range(level):
                        if k == 1 and res:
                            # Create a resource load for operations on level 1
                            oper = Operation.objects.using(database).create(
                                name="Oper %05d L%02d" % (i, k),
                                type="time_per",
                                location=loc,
                                duration_per=timedelta(days=1),
                                sizemultiple=1,
                                item=previtem,
                            )
                            if resource < cluster and i < resource:
                                # When there are more cluster than resources, we try to assure
                                # that each resource is loaded by at least 1 operation.
                                OperationResource.objects.using(
                                    database).create(resource=res[i],
                                                     operation=oper)
                            else:
                                OperationResource.objects.using(
                                    database).create(
                                        resource=random.choice(res),
                                        operation=oper)
                        else:
                            oper = Operation.objects.using(database).create(
                                name="Oper %05d L%02d" % (i, k),
                                duration=random.choice(durations),
                                sizemultiple=1,
                                location=loc,
                                item=previtem,
                            )
                        ops.append(oper)
                        # Some inventory in random buffers
                        if random.uniform(0, 1) > 0.8:
                            buf.onhand = int(random.uniform(5, 20))
                        buf.save(using=database)
                        OperationMaterial.objects.using(database).create(
                            operation=oper,
                            item=previtem,
                            quantity=1,
                            type="end")
                        if k != level - 1:
                            # Consume from the next level in the bill of material
                            it_tmp = Item.objects.using(database).create(
                                name="Itm %05d L%02d" % (i, k + 1),
                                category=random.choice(categories),
                                cost=str(round(random.uniform(100, 200))),
                            )
                            buf = Buffer.objects.using(database).create(
                                item=it_tmp,
                                location=loc,
                                category="%02d" % (k + 1))
                            OperationMaterial.objects.using(database).create(
                                operation=oper, item=it_tmp, quantity=-1)
                        previtem = it_tmp

                    # Consume raw materials / components
                    c = []
                    for j in range(components_per):
                        o = random.choice(ops)
                        b = random.choice(comps)
                        while (o, b) in c:
                            # A flow with the same operation and buffer already exists
                            o = random.choice(ops)
                            b = random.choice(comps)
                        c.append((o, b))
                        OperationMaterial.objects.using(database).create(
                            operation=o,
                            item=b,
                            quantity=random.choice([-1, -1, -1, -2, -3]),
                        )

                    # Commit the current cluster
                    task.status = "%d%%" % (12 + progress * (i + 1))
                    task.save(using=database)

            # Task update
            task.status = "Done"
            task.finished = datetime.now()

        except Exception as e:
            if task:
                task.status = "Failed"
                task.message = "%s" % e
                task.finished = datetime.now()
                task.save(using=database)
            raise e

        finally:
            if task:
                task.save(using=database)
            settings.DEBUG = tmp_debug
예제 #3
0
  def handle(self, **options):
    # Make sure the debug flag is not set!
    # When it is set, the django database wrapper collects a list of all sql
    # statements executed and their timings. This consumes plenty of memory
    # and cpu time.
    tmp_debug = settings.DEBUG
    settings.DEBUG = False

    # Pick up the options
    if 'verbosity' in options:
      verbosity = int(options['verbosity'])
    else:
      verbosity = 1
    if 'cluster' in options:
      cluster = int(options['cluster'])
    else:
      cluster = 100
    if 'demand' in options:
      demand = int(options['demand'])
    else:
      demand = 30
    if 'forecast_per_item' in options:
      forecast_per_item = int(options['forecast_per_item'])
    else:
      forecast_per_item = 50
    if 'level' in options:
      level = int(options['level'])
    else:
      level = 5
    if 'resource' in options:
      resource = int(options['resource'])
    else:
      resource = 60
    if 'resource_size' in options:
      resource_size = int(options['resource_size'])
    else:
      resource_size = 5
    if 'components' in options:
      components = int(options['components'])
    else:
      components = 200
    if 'components_per' in options:
      components_per = int(options['components_per'])
    else:
      components_per = 5
    if components == 0:
      components_per = 0
    if 'deliver_lt' in options:
      deliver_lt = int(options['deliver_lt'])
    else:
      deliver_lt = 30
    if 'procure_lt' in options:
      procure_lt = int(options['procure_lt'])
    else:
      procure_lt = 40
    if 'currentdate' in options:
      currentdate = options['currentdate'] or datetime.strftime(date.today(), '%Y-%m-%d')
    else:
      currentdate = datetime.strftime(date.today(), '%Y-%m-%d')
    if 'database' in options:
      database = options['database'] or DEFAULT_DB_ALIAS
    else:
      database = DEFAULT_DB_ALIAS
    if not database in settings.DATABASES:
      raise CommandError("No database settings known for '%s'" % database )
    if 'user' in options and options['user']:
      try:
        user = User.objects.all().using(database).get(username=options['user'])
      except:
        raise CommandError("User '%s' not found" % options['user'] )
    else:
      user = None

    random.seed(100)  # Initialize random seed to get reproducible results

    now = datetime.now()
    task = None
    try:
      # Initialize the task
      if 'task' in options and options['task']:
        try:
          task = Task.objects.all().using(database).get(pk=options['task'])
        except:
          raise CommandError("Task identifier not found")
        if task.started or task.finished or task.status != "Waiting" or task.name != 'generate model':
          raise CommandError("Invalid task identifier")
        task.status = '0%'
        task.started = now
      else:
        task = Task(name='generate model', submitted=now, started=now, status='0%', user=user)
      task.arguments = "--cluster=%s --demand=%s --forecast_per_item=%s --level=%s --resource=%s " \
        "--resource_size=%s --components=%s --components_per=%s --deliver_lt=%s --procure_lt=%s" % (
          cluster, demand, forecast_per_item, level, resource,
          resource_size, components, components_per, deliver_lt, procure_lt
        )
      task.save(using=database)
      transaction.commit(using=database)

      # Pick up the startdate
      try:
        startdate = datetime.strptime(currentdate, '%Y-%m-%d')
      except:
        raise CommandError("current date is not matching format YYYY-MM-DD")

      # Check whether the database is empty
      if Buffer.objects.using(database).count() > 0 or Item.objects.using(database).count() > 0:
        raise CommandError("Database must be empty before creating a model")

      # Plan start date
      if verbosity > 0:
        print("Updating current date...")
      param = Parameter.objects.using(database).create(name="currentdate")
      param.value = datetime.strftime(startdate, "%Y-%m-%d %H:%M:%S")
      param.save(using=database)

      # Planning horizon
      # minimum 10 daily buckets, weekly buckets till 40 days after current
      if verbosity > 0:
        print("Updating buckets...")
      management.call_command('frepple_createbuckets', user=user, database=database)
      if verbosity > 0:
        print("Updating horizon telescope...")
      updateTelescope(10, 40, 730, database)
      task.status = '2%'
      task.save(using=database)

      # Weeks calendar
      if verbosity > 0:
        print("Creating weeks calendar...")
      with transaction.atomic(using=database):
        weeks = Calendar.objects.using(database).create(name="Weeks", defaultvalue=0)
        for i in BucketDetail.objects.using(database).filter(bucket="week").all():
          CalendarBucket(
            startdate=i.startdate, enddate=i.enddate, value=1, calendar=weeks
            ).save(using=database)
        task.status = '4%'
        task.save(using=database)

      # Working days calendar
      if verbosity > 0:
        print("Creating working days...")
      with transaction.atomic(using=database):
        workingdays = Calendar.objects.using(database).create(name="Working Days", defaultvalue=0)
        minmax = BucketDetail.objects.using(database).filter(bucket="week").aggregate(Min('startdate'), Max('startdate'))
        CalendarBucket(
          startdate=minmax['startdate__min'], enddate=minmax['startdate__max'],
          value=1, calendar=workingdays, priority=1, saturday=False, sunday=False
          ).save(using=database)
        task.status = '6%'
        task.save(using=database)

      # Create a random list of categories to choose from
      categories = [
        'cat A', 'cat B', 'cat C', 'cat D', 'cat E', 'cat F', 'cat G'
        ]

      # Create customers
      if verbosity > 0:
        print("Creating customers...")
      with transaction.atomic(using=database):
        cust = []
        for i in range(100):
          c = Customer.objects.using(database).create(name='Cust %03d' % i)
          cust.append(c)
        task.status = '8%'
        task.save(using=database)

      # Create resources and their calendars
      if verbosity > 0:
        print("Creating resources and calendars...")
      with transaction.atomic(using=database):
        res = []
        for i in range(resource):
          loc = Location(name='Loc %05d' % int(random.uniform(1, cluster)))
          loc.save(using=database)
          cal = Calendar(name='capacity for res %03d' % i, category='capacity', defaultvalue=0)
          bkt = CalendarBucket(startdate=startdate, value=resource_size, calendar=cal)
          cal.save(using=database)
          bkt.save(using=database)
          r = Resource.objects.using(database).create(
            name='Res %03d' % i, maximum_calendar=cal, location=loc
            )
          res.append(r)
        task.status = '10%'
        task.save(using=database)
        random.shuffle(res)

      # Create the components
      if verbosity > 0:
        print("Creating raw materials...")
      with transaction.atomic(using=database):
        comps = []
        comploc = Location.objects.using(database).create(name='Procured materials')
        for i in range(components):
          it = Item.objects.using(database).create(
            name='Component %04d' % i,
            category='Procured',
            price=str(round(random.uniform(0, 100)))
            )
          ld = abs(round(random.normalvariate(procure_lt, procure_lt / 3)))
          c = Buffer.objects.using(database).create(
            name='Component %04d' % i,
            location=comploc,
            category='Procured',
            item=it,
            type='procure',
            min_inventory=20,
            max_inventory=100,
            size_multiple=10,
            leadtime=str(ld * 86400),
            onhand=str(round(forecast_per_item * random.uniform(1, 3) * ld / 30)),
            )
          comps.append(c)
        task.status = '12%'
        task.save(using=database)

      # Loop over all clusters
      durations = [ 86400, 86400 * 2, 86400 * 3, 86400 * 5, 86400 * 6 ]
      progress = 88.0 / cluster
      for i in range(cluster):
        with transaction.atomic(using=database):
          if verbosity > 0:
            print("Creating supply chain for end item %d..." % i)

          # location
          loc = Location.objects.using(database).get_or_create(name='Loc %05d' % i)[0]
          loc.available = workingdays
          loc.save(using=database)

          # Item and delivery operation
          oper = Operation.objects.using(database).create(name='Del %05d' % i, sizemultiple=1, location=loc)
          it = Item.objects.using(database).create(
            name='Itm %05d' % i,
            operation=oper,
            category=random.choice(categories),
            price=str(round(random.uniform(100, 200)))
            )

          # Level 0 buffer
          buf = Buffer.objects.using(database).create(
            name='Buf %05d L00' % i,
            item=it,
            location=loc,
            category='00'
            )
          Flow.objects.using(database).create(operation=oper, thebuffer=buf, quantity=-1)

          # Demand
          for j in range(demand):
            Demand.objects.using(database).create(
              name='Dmd %05d %05d' % (i, j),
              item=it,
              quantity=int(random.uniform(1, 6)),
              # Exponential distribution of due dates, with an average of deliver_lt days.
              due=startdate + timedelta(days=round(random.expovariate(float(1) / deliver_lt / 24)) / 24),
              # Orders have higher priority than forecast
              priority=random.choice([1, 2]),
              customer=random.choice(cust),
              category=random.choice(categories)
              )

          # Create upstream operations and buffers
          ops = []
          for k in range(level):
            if k == 1 and res:
              # Create a resource load for operations on level 1
              oper = Operation.objects.using(database).create(
                name='Oper %05d L%02d' % (i, k),
                type='time_per',
                location=loc,
                duration_per=86400,
                sizemultiple=1,
                )
              if resource < cluster and i < resource:
                # When there are more cluster than resources, we try to assure
                # that each resource is loaded by at least 1 operation.
                Load.objects.using(database).create(resource=res[i], operation=oper)
              else:
                Load.objects.using(database).create(resource=random.choice(res), operation=oper)
            else:
              oper = Operation.objects.using(database).create(
                name='Oper %05d L%02d' % (i, k),
                duration=random.choice(durations),
                sizemultiple=1,
                location=loc,
                )
            ops.append(oper)
            buf.producing = oper
            # Some inventory in random buffers
            if random.uniform(0, 1) > 0.8:
              buf.onhand = int(random.uniform(5, 20))
            buf.save(using=database)
            Flow(operation=oper, thebuffer=buf, quantity=1, type="end").save(using=database)
            if k != level - 1:
              # Consume from the next level in the bill of material
              buf = Buffer.objects.using(database).create(
                name='Buf %05d L%02d' % (i, k + 1),
                item=it,
                location=loc,
                category='%02d' % (k + 1)
                )
              Flow.objects.using(database).create(operation=oper, thebuffer=buf, quantity=-1)

          # Consume raw materials / components
          c = []
          for j in range(components_per):
            o = random.choice(ops)
            b = random.choice(comps)
            while (o, b) in c:
              # A flow with the same operation and buffer already exists
              o = random.choice(ops)
              b = random.choice(comps)
            c.append( (o, b) )
            Flow.objects.using(database).create(
              operation=o, thebuffer=b,
              quantity=random.choice([-1, -1, -1, -2, -3])
              )

          # Commit the current cluster
          task.status = '%d%%' % (12 + progress * (i + 1))
          task.save(using=database)

      # Task update
      task.status = 'Done'
      task.finished = datetime.now()

    except Exception as e:
      if task:
        task.status = 'Failed'
        task.message = '%s' % e
        task.finished = datetime.now()
        task.save(using=database)
      raise e

    finally:
      if task:
        task.save(using=database)
      settings.DEBUG = tmp_debug