def getWeight(cls, database=DEFAULT_DB_ALIAS, **kwargs): for i in range(5): if ("odoo_read_%s" % i) in os.environ: cls.mode = i PlanTaskRegistry.addArguments( exportstatic=True, source="odoo_%s" % i, skipLoad=True ) return 1 return -1
def getWeight(cls, database=DEFAULT_DB_ALIAS, **kwargs): for i in range(5): if ("odoo_read_%s" % i) in os.environ: cls.mode = i for stdLoad in PlanTaskRegistry.reg.steps: if isinstance(stdLoad, (PlanTaskParallel, PlanTaskSequence)): continue if issubclass(stdLoad, LoadTask): stdLoad.filter = ( "(source is null or source<>'odoo_%s')" % cls.mode ) stdLoad.description += " - non-odoo source" PlanTaskRegistry.addArguments(exportstatic=True, source="odoo_%s" % i) return 1 return -1
def getWeight(cls, database=DEFAULT_DB_ALIAS, **kwargs): for i in range(5): if ("odoo_read_%s" % i) in os.environ: cls.mode = i for idx in (100, 101, 110): stdLoad = PlanTaskRegistry.getTask(sequence=idx) if stdLoad: stdLoad.filter = "source is null or source<>'odoo_%s'" % cls.mode stdLoad.description = "Load non-Odoo data" return 1 else: return -1
def getWeight(cls, database=DEFAULT_DB_ALIAS, **kwargs): for i in range(5): if ("odoo_read_%s" % i) in os.environ: cls.mode = i for idx in (100, 101, 110): stdLoad = PlanTaskRegistry.getTask(sequence=idx) if stdLoad: stdLoad.filter = "(source is null or source<>'odoo_%s')" % cls.mode stdLoad.description = "Load non-Odoo data" return 1 else: return -1
def extra_context(reportclass, request, *args, **kwargs): try: constraint = int(request.session['constraint']) except: constraint = 15 # Synchronize the scenario table with the settings Scenario.syncWithSettings() # Collect optional tasks PlanTaskRegistry.autodiscover() planning_options = PlanTaskRegistry.getLabels() # Loop over all accordion of all apps and directories accordions = set() accord = '' for commandname, appname in get_commands().items(): try: accord = getattr( import_module('%s.management.commands.%s' % (appname, commandname)), 'Command') if accord.index >= 0: accordions.add(accord) except Exception as e: pass # Silently ignore failures accordions = sorted(accordions, key=operator.attrgetter('index')) # Send to template return { 'commandlist': accordions, 'logfileslist': json.dumps( list(x for x in os.listdir(settings.FREPPLE_LOGDIR) if x.endswith('.log'))) }
def getHTML(request): if request.user.has_perm('auth.generate_plan'): # Collect optional tasks PlanTaskRegistry.autodiscover() planning_options = PlanTaskRegistry.getLabels() current_options = request.session.get( 'env', [i[0] for i in planning_options]) try: constraint = int(request.session['constraint']) except: constraint = 15 context = RequestContext( request, { 'planning_options': planning_options, 'current_options': current_options, 'capacityconstrained': constraint & 4, 'materialconstrained': constraint & 2, 'leadtimeconstrained': constraint & 1, 'fenceconstrained': constraint & 8 }) template = Template(''' {% load i18n %} <form role="form" method="post" action="{{request.prefix}}/execute/launch/frepple_run/">{% csrf_token %} <table> <tr> <td style="vertical-align:top; padding: 15px"> <button type="submit" class="btn btn-primary">{% trans "launch"|capfirst %}</button> </td> <td style="padding: 15px;"> {% blocktrans %} Load frePPLe from the database and live data sources...<br> and create a plan in frePPLe...<br> and export results.{% endblocktrans %}<br><br> {% if planning_options %} <p {% if planning_options|length <= 1 %}style="display: none"{% endif %}><b>{% trans "optional planning steps"|capfirst %}</b><br> {% for b in planning_options %} <label for="option_{{b.0}}"><input type="checkbox" name="env" {% if b.0 in current_options %}checked {% endif %}value="{{b.0}}" id="option_{{b.0}}"/> {{b.1}}</label><br> {% endfor %} </p> {% endif %} <p><b>Plan type</b><br> <input type="radio" id="plantype1" name="plantype" {% ifnotequal request.session.plantype '2' %}checked {% endifnotequal %}value="1"/> <label for="plantype1">{% blocktrans %}<span data-toggle="tooltip" data-placement="top" data-html="true" data-original-title="Generate a supply plan that respects all constraints.<br>In case of shortages the demand is planned late or short.">Constrained plan</span>{% endblocktrans %} <span class="fa fa-question-circle" style="display:inline-block;"></span></label><br> <input type="radio" id="plantype2" name="plantype" {% ifequal request.session.plantype '2' %}checked {% endifequal %}value="2"/> <label for="plantype2">{% blocktrans %}<span data-toggle="tooltip" data-placement="top" data-html="true" data-original-title="Generate a supply plan that shows material, capacity and operation problems that prevent the demand from being planned in time.<br>The demand is always met completely and on time.">Unconstrained plan</span>{% endblocktrans %} <span class="fa fa-question-circle" style="display:inline-block;"></span></label><br> </p> <p> <b>{% trans "constraints"|capfirst %}</b><br> <label for="cb4"><input type="checkbox" name="constraint" {% if capacityconstrained %}checked {% endif %}value="4" id="cb4"/> {% trans "Capacity: respect capacity limits" %}</label><br> <label for="cb2"><input type="checkbox" name="constraint" {% if materialconstrained %}checked {% endif %}value="2" id="cb2"/> {% trans "Material: respect procurement limits" %}</label><br> <label for="cb1"><input type="checkbox" name="constraint" {% if leadtimeconstrained %}checked {% endif %}value="1" id="cb1"/> {% trans "Lead time: do not plan in the past" %}</label><br> <label for="cb8"><input type="checkbox" name="constraint" {% if fenceconstrained %}checked {% endif %}value="8" id="cb8"/> {% trans "Release fence: do not plan within the release time window" %}</label><br> </p> </td> </tr> </table> </form> ''') return template.render(context) else: return None
def extra_context(reportclass, request, *args, **kwargs): try: constraint = int(request.session['constraint']) except: constraint = 15 # Synchronize the scenario table with the settings Scenario.syncWithSettings() # Collect optional tasks PlanTaskRegistry.autodiscover() planning_options = PlanTaskRegistry.getLabels() # Loop over all fixtures of all apps and directories fixtures = set() folders = list(settings.FIXTURE_DIRS) for app in get_apps(): if app.__name__.startswith('django'): continue folders.append( os.path.join(os.path.dirname(app.__file__), 'fixtures')) for f in folders: try: for root, dirs, files in os.walk(f): for i in files: if i.endswith('.json'): fixtures.add(i.split('.')[0]) except: pass # Silently ignore failures fixtures = sorted(fixtures) # Function to convert from bytes to human readabl format def sizeof_fmt(num): for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: if abs(num) < 1024.0: return "%3.1f%sB" % (num, unit) num /= 1024.0 return "%.1f%sB" % (num, 'Yi') # List available data files filestoupload = [] if 'FILEUPLOADFOLDER' in settings.DATABASES[request.database]: uploadfolder = settings.DATABASES[ request.database]['FILEUPLOADFOLDER'] if os.path.isdir(uploadfolder): for file in os.listdir(uploadfolder): if file.endswith(('.csv', '.csv.gz', '.log')): filestoupload.append([ file, strftime( "%Y-%m-%d %H:%M:%S", localtime( os.stat(os.path.join(uploadfolder, file)).st_mtime)), sizeof_fmt( os.stat(os.path.join(uploadfolder, file)).st_size) ]) # Send to template return { 'capacityconstrained': constraint & 4, 'materialconstrained': constraint & 2, 'leadtimeconstrained': constraint & 1, 'fenceconstrained': constraint & 8, 'scenarios': Scenario.objects.all(), 'fixtures': fixtures, 'openbravo': 'freppledb.openbravo' in settings.INSTALLED_APPS, 'planning_options': planning_options, 'current_options': request.session.get('env', [i[0] for i in planning_options]), 'filestoupload': filestoupload, 'datafolderconfigured': 'FILEUPLOADFOLDER' in settings.DATABASES[request.database] }
def handle(self, **options): # Pick up the options now = datetime.now() 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 timestamp = now.strftime("%Y%m%d%H%M%S") if database == DEFAULT_DB_ALIAS: logfile = "frepple-%s.log" % timestamp else: logfile = "frepple_%s-%s.log" % (database, timestamp) 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 not in ("runplan", "frepple_run")): raise CommandError("Invalid task identifier") task.status = "0%" task.started = now task.logfile = logfile else: task = Task( name="runplan", submitted=now, started=now, status="0%", user=user, logfile=logfile, ) # Validate options if "constraint" in options: constraint = int(options["constraint"]) if constraint < 0 or constraint > 15: raise ValueError("Invalid constraint: %s" % options["constraint"]) else: constraint = 15 if "plantype" in options: plantype = int(options["plantype"]) else: plantype = 1 # Reset environment variables # TODO avoid having to delete the environment variables. Use options directly? PlanTaskRegistry.autodiscover() for i in PlanTaskRegistry.reg: if "env" in options: # Options specified if i.label and i.label[0] in os.environ: del os.environ[i.label[0]] elif i.label: # No options specified - default to activate them all os.environ[i.label[0]] = "1" # Set environment variables if options["env"]: task.arguments = "--constraint=%d --plantype=%d --env=%s" % ( constraint, plantype, options["env"], ) for i in options["env"].split(","): j = i.split("=") if len(j) == 1: os.environ[j[0]] = "1" else: os.environ[j[0]] = j[1] else: task.arguments = "--constraint=%d --plantype=%d" % ( constraint, plantype, ) if options["background"]: task.arguments += " --background" # Log task # Different from the other tasks the frepple engine will write the processid task.save(using=database) # Locate commands.py import freppledb.common.commands cmd = freppledb.common.commands.__file__ def setlimits(): import resource if settings.MAXMEMORYSIZE: resource.setrlimit( resource.RLIMIT_AS, ( settings.MAXMEMORYSIZE * 1024 * 1024, (settings.MAXMEMORYSIZE + 10) * 1024 * 1024, ), ) if settings.MAXCPUTIME: resource.setrlimit( resource.RLIMIT_CPU, (settings.MAXCPUTIME, settings.MAXCPUTIME + 5), ) # Limiting the file size is a bit tricky as this limit not only applies to the log # file, but also to temp files during the export # if settings.MAXTOTALLOGFILESIZE: # resource.setrlimit( # resource.RLIMIT_FSIZE, # (settings.MAXTOTALLOGFILESIZE * 1024 * 1024, (settings.MAXTOTALLOGFILESIZE + 1) * 1024 * 1024) # ) # Prepare environment os.environ["FREPPLE_PLANTYPE"] = str(plantype) os.environ["FREPPLE_CONSTRAINT"] = str(constraint) os.environ["FREPPLE_TASKID"] = str(task.id) os.environ["FREPPLE_DATABASE"] = database os.environ["FREPPLE_LOGFILE"] = logfile os.environ["FREPPLE_PROCESSNAME"] = settings.DATABASES[database][ "NAME"].replace("demo", "") os.environ["PATH"] = (settings.FREPPLE_HOME + os.pathsep + os.environ["PATH"] + os.pathsep + settings.FREPPLE_APP) if os.path.isfile( os.path.join(settings.FREPPLE_HOME, "libfrepple.so")): os.environ["LD_LIBRARY_PATH"] = settings.FREPPLE_HOME if "DJANGO_SETTINGS_MODULE" not in os.environ: os.environ["DJANGO_SETTINGS_MODULE"] = "freppledb.settings" os.environ["PYTHONPATH"] = os.path.normpath(settings.FREPPLE_APP) libdir = os.path.join(os.path.normpath(settings.FREPPLE_HOME), "lib") if os.path.isdir(libdir): # Folders used by the Windows version os.environ["PYTHONPATH"] += os.pathsep + libdir if os.path.isfile(os.path.join(libdir, "library.zip")): os.environ["PYTHONPATH"] += os.pathsep + os.path.join( libdir, "library.zip") if options["background"]: # Execute as background process on Windows if os.name == "nt": subprocess.Popen(["frepple", cmd], creationflags=0x08000000) else: # Execute as background process on Linux subprocess.Popen(["frepple", cmd], preexec_fn=setlimits) else: if os.name == "nt": # Execute in foreground on Windows ret = subprocess.call(["frepple", cmd]) else: # Execute in foreground on Linux ret = subprocess.call(["frepple", cmd], preexec_fn=setlimits) if ret != 0 and ret != 2: # Return code 0 is a successful run # Return code is 2 is a run cancelled by a user. That's shown in the status field. raise Exception("Failed with exit code %d" % ret) # Reread the task from the database and update it if not options["background"]: task = Task.objects.all().using(database).get(pk=task.id) task.processid = None task.status = "Done" task.finished = datetime.now() task.save(using=database) except Exception as e: if task: task = Task.objects.all().using(database).get(pk=task.id) task.status = "Failed" task.message = "%s" % e task.finished = datetime.now() task.processid = None task.save(using=database) raise e
def getHTML(request): if request.user.has_perm("auth.generate_plan"): # Collect optional tasks PlanTaskRegistry.autodiscover() planning_options = PlanTaskRegistry.getLabels() plantype = "2" constraint = 15 current_options = [i[0] for i in planning_options] lastrun = (Task.objects.all().using(request.database).filter( name="runplan").order_by("-id").only("arguments").first()) if lastrun and lastrun.arguments: # Copy all settings from the previous run by this user for i in shlex.split(lastrun.arguments): if "=" in i: key, val = i.split("=") key = key.strip("--") if key == "constraint": try: constraint = int(val) except: pass elif key == "plantype": plantype = val elif key == "env": try: current_options = val.split(",") except: pass context = RequestContext( request, { "planning_options": planning_options, "current_options": current_options, "capacityconstrained": constraint & 4, "materialconstrained": constraint & 2, "leadtimeconstrained": constraint & 1, "fenceconstrained": constraint & 8, "plantype": plantype, }, ) template = Template(""" {%% load i18n %%} <form role="form" method="post" action="{{request.prefix}}/execute/launch/runplan/">{%% csrf_token %%} <table> <tr> <td style="vertical-align:top; padding: 15px"> <button type="submit" class="btn btn-primary">{%% trans "launch"|capfirst %%}</button> </td> <td style="padding: 15px;">%s<br><br> {%% if planning_options %%} <p {%% if planning_options|length <= 1 %%}style="display: none"{%% endif %%}><b>{%% filter capfirst %%}%s{%% endfilter %%}</b><br> {%% for b in planning_options %%} <label for="option_{{b.0}}"><input type="checkbox" name="env" {%% if b.0 in current_options %%}checked {%% endif %%}value="{{b.0}}" id="option_{{b.0}}"/> {{b.1}}</label><br> {%% endfor %%} </p> {%% endif %%} <p><b>%s</b><br> <input type="radio" id="plantype1" name="plantype" {%% ifnotequal plantype '2' %%}checked {%% endifnotequal %%}value="1"/> <label for="plantype1">%s <span class="fa fa-question-circle" style="display:inline-block;"></span></label><br> <input type="radio" id="plantype2" name="plantype" {%% ifequal plantype '2' %%}checked {%% endifequal %%}value="2"/> <label for="plantype2">%s <span class="fa fa-question-circle" style="display:inline-block;"></span></label><br> </p> <p> <b>{%% filter capfirst %%}%s{%% endfilter %%}</b><br> <label for="cb4"><input type="checkbox" name="constraint" {%% if capacityconstrained %%}checked {%% endif %%}value="4" id="cb4"/> %s</label><br> <label for="cb1"><input type="checkbox" name="constraint" {%% if leadtimeconstrained %%}checked {%% endif %%}value="1" id="cb1"/> %s</label><br> <label for="cb8"><input type="checkbox" name="constraint" {%% if fenceconstrained %%}checked {%% endif %%}value="8" id="cb8"/> %s</label><br> </p> </td> </tr> </table> </form> """ % ( force_text( _("Load all input data, run the planning algorithm, and export the results." )), force_text(_("optional planning steps")), force_text(_("Plan type")), force_text( _('<span data-toggle="tooltip" data-placement="top" data-html="true" data-original-title="Generate a supply plan that respects all constraints.<br>In case of shortages the demand is planned late or short.">Constrained plan</span>' )), force_text( _('<span data-toggle="tooltip" data-placement="top" data-html="true" data-original-title="Generate a supply plan that shows material, capacity and operation problems that prevent the demand from being planned in time.<br>The demand is always met completely and on time.">Unconstrained plan</span>' )), force_text(_("constraints")), force_text(_("Capacity: respect capacity limits")), force_text(_("Lead time: do not plan in the past")), force_text( _("Release fence: do not plan within the release time window" )), )) return template.render(context) else: return None
if "FREPPLE_TASKID" in os.environ: try: task = ( Task.objects.all().using(database).get(pk=os.environ["FREPPLE_TASKID"]) ) task.processid = os.getpid() task.save(update_fields=["processid"], using=database) except Task.DoesNotExist: task = None else: task = None # Find all planning steps and execute them from freppledb.common.commands import PlanTaskRegistry as register register.autodiscover() newstatus = "Done" try: register.run(database=database) except Exception as e: print("Error during planning: %s" % e) newstatus = "Failed" raise finally: # Clear the processid if task: task = Task.objects.all().using(database).get(pk=task.id) task.processid = None task.status = newstatus task.save(update_fields=["processid", "status"], using=database)
def extra_context(reportclass, request, *args, **kwargs): try: constraint = int(request.session['constraint']) except: constraint = 15 # Synchronize the scenario table with the settings Scenario.syncWithSettings() # Collect optional tasks PlanTaskRegistry.autodiscover() planning_options = PlanTaskRegistry.getLabels() # Loop over all fixtures of all apps and directories fixtures = set() folders = list(settings.FIXTURE_DIRS) for app in get_apps(): if app.__name__.startswith('django'): continue folders.append(os.path.join(os.path.dirname(app.__file__), 'fixtures')) for f in folders: try: for root, dirs, files in os.walk(f): for i in files: if i.endswith('.json'): fixtures.add(i.split('.')[0]) except: pass # Silently ignore failures fixtures = sorted(fixtures) # Function to convert from bytes to human readabl format def sizeof_fmt(num): for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']: if abs(num) < 1024.0: return "%3.1f%sB" % (num, unit) num /= 1024.0 return "%.1f%sB" % (num, 'Yi') # List available data files filestoupload = [] if 'FILEUPLOADFOLDER' in settings.DATABASES[request.database]: uploadfolder = settings.DATABASES[request.database]['FILEUPLOADFOLDER'] if os.path.isdir(uploadfolder): for file in os.listdir(uploadfolder): if file.endswith('.csv') or file.endswith('.log'): filestoupload.append([ file, strftime("%Y-%m-%d %H:%M:%S",localtime(os.stat(os.path.join(uploadfolder, file)).st_mtime)), sizeof_fmt(os.stat(os.path.join(uploadfolder, file)).st_size) ]) # Send to template return { 'capacityconstrained': constraint & 4, 'materialconstrained': constraint & 2, 'leadtimeconstrained': constraint & 1, 'fenceconstrained': constraint & 8, 'scenarios': Scenario.objects.all(), 'fixtures': fixtures, 'openbravo': 'freppledb.openbravo' in settings.INSTALLED_APPS, 'planning_options': planning_options, 'current_options': request.session.get('env', [ i[0] for i in planning_options ]), 'filestoupload': filestoupload, 'datafolderconfigured': 'FILEUPLOADFOLDER' in settings.DATABASES[request.database] }
def handle(self, **options): # Pick up the options now = datetime.now() 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 timestamp = now.strftime("%Y%m%d%H%M%S") if database == DEFAULT_DB_ALIAS: logfile = 'frepple-%s.log' % timestamp else: logfile = 'frepple_%s-%s.log' % (database, timestamp) 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 not in ('runplan', 'frepple_run'): raise CommandError("Invalid task identifier") task.status = '0%' task.started = now task.logfile = logfile else: task = Task(name='runplan', submitted=now, started=now, status='0%', user=user, logfile=logfile) # Validate options if 'constraint' in options: constraint = int(options['constraint']) if constraint < 0 or constraint > 15: raise ValueError("Invalid constraint: %s" % options['constraint']) else: constraint = 15 if 'plantype' in options: plantype = int(options['plantype']) else: plantype = 1 # Reset environment variables # TODO avoid having to delete the environment variables. Use options directly? PlanTaskRegistry.autodiscover() for i in PlanTaskRegistry.reg: if 'env' in options: # Options specified if i.label and i.label[0] in os.environ: del os.environ[i.label[0]] elif i.label: # No options specified - default to activate them all os.environ[i.label[0]] = '1' # Set environment variables if options['env']: task.arguments = "--constraint=%d --plantype=%d --env=%s" % (constraint, plantype, options['env']) for i in options['env'].split(','): j = i.split('=') if len(j) == 1: os.environ[j[0]] = '1' else: os.environ[j[0]] = j[1] else: task.arguments = "--constraint=%d --plantype=%d" % (constraint, plantype) if options['background']: task.arguments += " --background" # Log task # Different from the other tasks the frepple engine will write the processid task.save(using=database) # Locate commands.py import freppledb.common.commands cmd = freppledb.common.commands.__file__ def setlimits(): import resource if settings.MAXMEMORYSIZE: resource.setrlimit( resource.RLIMIT_AS, (settings.MAXMEMORYSIZE * 1024 * 1024, (settings.MAXMEMORYSIZE + 10) * 1024 * 1024) ) if settings.MAXCPUTIME: resource.setrlimit( resource.RLIMIT_CPU, (settings.MAXCPUTIME, settings.MAXCPUTIME + 5) ) # Limiting the file size is a bit tricky as this limit not only applies to the log # file, but also to temp files during the export # if settings.MAXTOTALLOGFILESIZE: # resource.setrlimit( # resource.RLIMIT_FSIZE, # (settings.MAXTOTALLOGFILESIZE * 1024 * 1024, (settings.MAXTOTALLOGFILESIZE + 1) * 1024 * 1024) # ) # Prepare environment os.environ['FREPPLE_PLANTYPE'] = str(plantype) os.environ['FREPPLE_CONSTRAINT'] = str(constraint) os.environ['FREPPLE_TASKID'] = str(task.id) os.environ['FREPPLE_DATABASE'] = database os.environ['FREPPLE_LOGFILE'] = logfile os.environ['FREPPLE_PROCESSNAME'] = settings.DATABASES[database]['NAME'].replace('demo', '') os.environ['PATH'] = settings.FREPPLE_HOME + os.pathsep + os.environ['PATH'] + os.pathsep + settings.FREPPLE_APP if os.path.isfile(os.path.join(settings.FREPPLE_HOME, 'libfrepple.so')): os.environ['LD_LIBRARY_PATH'] = settings.FREPPLE_HOME if 'DJANGO_SETTINGS_MODULE' not in os.environ: os.environ['DJANGO_SETTINGS_MODULE'] = 'freppledb.settings' os.environ['PYTHONPATH'] = os.path.normpath(settings.FREPPLE_APP) libdir = os.path.join(os.path.normpath(settings.FREPPLE_HOME), 'lib') if os.path.isdir(libdir): # Folders used by the Windows version os.environ['PYTHONPATH'] += os.pathsep + libdir if os.path.isfile(os.path.join(libdir, 'library.zip')): os.environ['PYTHONPATH'] += os.pathsep + os.path.join(libdir, 'library.zip') if options['background']: # Execute as background process on Windows if os.name == 'nt': subprocess.Popen(['frepple', cmd], creationflags=0x08000000) else: # Execute as background process on Linux subprocess.Popen(['frepple', cmd], preexec_fn=setlimits) else: if os.name == 'nt': # Execute in foreground on Windows ret = subprocess.call(['frepple', cmd]) else: # Execute in foreground on Linux ret = subprocess.call(['frepple', cmd], preexec_fn=setlimits) if ret != 0 and ret != 2: # Return code 0 is a successful run # Return code is 2 is a run cancelled by a user. That's shown in the status field. raise Exception('Failed with exit code %d' % ret) # Reread the task from the database and update it if not options['background']: task = Task.objects.all().using(database).get(pk=task.id) task.processid = None task.status = 'Done' task.finished = datetime.now() task.save(using=database) except Exception as e: if task: task = Task.objects.all().using(database).get(pk=task.id) task.status = 'Failed' task.message = '%s' % e task.finished = datetime.now() task.processid = None task.save(using=database) raise e
def getHTML(request): if request.user.has_perm('auth.generate_plan'): # Collect optional tasks PlanTaskRegistry.autodiscover() planning_options = PlanTaskRegistry.getLabels() plantype = '2' constraint = 15 current_options = [ i[0] for i in planning_options ] lastrun = Task.objects.all().using(request.database) \ .filter(name="runplan") \ .order_by("-id").only("arguments").first() if lastrun and lastrun.arguments: # Copy all settings from the previous run by this user for i in shlex.split(lastrun.arguments): if '=' in i: key, val = i.split('=') key = key.strip('--') if key == 'constraint': try: constraint = int(val) except: pass elif key == 'plantype': plantype = val elif key == 'env': try: current_options = val.split(',') except: pass context = RequestContext(request, { 'planning_options': planning_options, 'current_options': current_options, 'capacityconstrained': constraint & 4, 'materialconstrained': constraint & 2, 'leadtimeconstrained': constraint & 1, 'fenceconstrained': constraint & 8, 'plantype': plantype }) template = Template(''' {%% load i18n %%} <form role="form" method="post" action="{{request.prefix}}/execute/launch/runplan/">{%% csrf_token %%} <table> <tr> <td style="vertical-align:top; padding: 15px"> <button type="submit" class="btn btn-primary">{%% trans "launch"|capfirst %%}</button> </td> <td style="padding: 15px;">%s<br><br> {%% if planning_options %%} <p {%% if planning_options|length <= 1 %%}style="display: none"{%% endif %%}><b>{%% filter capfirst %%}%s{%% endfilter %%}</b><br> {%% for b in planning_options %%} <label for="option_{{b.0}}"><input type="checkbox" name="env" {%% if b.0 in current_options %%}checked {%% endif %%}value="{{b.0}}" id="option_{{b.0}}"/> {{b.1}}</label><br> {%% endfor %%} </p> {%% endif %%} <p><b>%s</b><br> <input type="radio" id="plantype1" name="plantype" {%% ifnotequal plantype '2' %%}checked {%% endifnotequal %%}value="1"/> <label for="plantype1">%s <span class="fa fa-question-circle" style="display:inline-block;"></span></label><br> <input type="radio" id="plantype2" name="plantype" {%% ifequal plantype '2' %%}checked {%% endifequal %%}value="2"/> <label for="plantype2">%s <span class="fa fa-question-circle" style="display:inline-block;"></span></label><br> </p> <p> <b>{%% filter capfirst %%}%s{%% endfilter %%}</b><br> <label for="cb4"><input type="checkbox" name="constraint" {%% if capacityconstrained %%}checked {%% endif %%}value="4" id="cb4"/> %s</label><br> <label for="cb2"><input type="checkbox" name="constraint" {%% if materialconstrained %%}checked {%% endif %%}value="2" id="cb2"/> %s</label><br> <label for="cb1"><input type="checkbox" name="constraint" {%% if leadtimeconstrained %%}checked {%% endif %%}value="1" id="cb1"/> %s</label><br> <label for="cb8"><input type="checkbox" name="constraint" {%% if fenceconstrained %%}checked {%% endif %%}value="8" id="cb8"/> %s</label><br> </p> </td> </tr> </table> </form> ''' % ( force_text(_('Load all input data, run the planning algorithm, and export the results.')), force_text(_("optional planning steps")), force_text(_("Plan type")), force_text(_('<span data-toggle="tooltip" data-placement="top" data-html="true" data-original-title="Generate a supply plan that respects all constraints.<br>In case of shortages the demand is planned late or short.">Constrained plan</span>')), force_text(_('<span data-toggle="tooltip" data-placement="top" data-html="true" data-original-title="Generate a supply plan that shows material, capacity and operation problems that prevent the demand from being planned in time.<br>The demand is always met completely and on time.">Unconstrained plan</span>')), force_text(_("constraints")), force_text(_("Capacity: respect capacity limits")), force_text(_("Material: respect procurement limits")), force_text(_("Lead time: do not plan in the past")), force_text(_("Release fence: do not plan within the release time window")), )) return template.render(context) else: return None
def handle(self, **options): # Pick up the options now = datetime.now() 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 timestamp = now.strftime("%Y%m%d%H%M%S") if database == DEFAULT_DB_ALIAS: logfile = 'frepple-%s.log' % timestamp else: logfile = 'frepple_%s-%s.log' % (database, timestamp) 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 != 'frepple_run': raise CommandError("Invalid task identifier") task.status = '0%' task.started = now task.logfile = logfile else: task = Task(name='frepple_run', submitted=now, started=now, status='0%', user=user, logfile=logfile) # Validate options if 'constraint' in options: constraint = int(options['constraint']) if constraint < 0 or constraint > 15: raise ValueError("Invalid constraint: %s" % options['constraint']) else: constraint = 15 if 'plantype' in options: plantype = int(options['plantype']) else: plantype = 1 # Reset environment variables # TODO avoid having to delete the environment variables. Use options directly? PlanTaskRegistry.autodiscover() for i in PlanTaskRegistry.reg: if 'env' in options: # Options specified if i.label and i.label[0] in os.environ: del os.environ[i.label[0]] elif i.label: # No options specified - default to activate them all os.environ[i.label[0]] = '1' # Set environment variables if options['env']: task.arguments = "--constraint=%d --plantype=%d --env=%s" % ( constraint, plantype, options['env']) for i in options['env'].split(','): j = i.split('=') if len(j) == 1: os.environ[j[0]] = '1' else: os.environ[j[0]] = j[1] else: task.arguments = "--constraint=%d --plantype=%d" % (constraint, plantype) if options['background']: task.arguments += " --background" # Log task task.save(using=database) # Locate commands.py import freppledb.common.commands cmd = freppledb.common.commands.__file__ # Prepare environment os.environ['FREPPLE_PLANTYPE'] = str(plantype) os.environ['FREPPLE_CONSTRAINT'] = str(constraint) os.environ['FREPPLE_TASKID'] = str(task.id) os.environ['FREPPLE_DATABASE'] = database os.environ['FREPPLE_LOGFILE'] = logfile os.environ[ 'PATH'] = settings.FREPPLE_HOME + os.pathsep + os.environ[ 'PATH'] + os.pathsep + settings.FREPPLE_APP if os.path.isfile( os.path.join(settings.FREPPLE_HOME, 'libfrepple.so')): os.environ['LD_LIBRARY_PATH'] = settings.FREPPLE_HOME if 'DJANGO_SETTINGS_MODULE' not in os.environ: os.environ['DJANGO_SETTINGS_MODULE'] = 'freppledb.settings' os.environ['PYTHONPATH'] = os.path.normpath(settings.FREPPLE_APP) if options['background']: # Execute as background process on Windows if os.name == 'nt': subprocess.Popen(['frepple', cmd], creationflags=0x08000000) else: # Execute as background process on Linux subprocess.Popen(['frepple', cmd]) else: # Execute in foreground ret = subprocess.call(['frepple', cmd]) if ret != 0 and ret != 2: # Return code 0 is a successful run # Return code is 2 is a run cancelled by a user. That's shown in the status field. raise Exception('Failed with exit code %d' % ret) # 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() raise e finally: if task: task.save(using=database)
if 'FREPPLE_TEST' in os.environ: settings.DATABASES[database]['NAME'] = settings.DATABASES[database]['TEST']['NAME'] # 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. settings.DEBUG = False # Send the output to a logfile if database == DEFAULT_DB_ALIAS: frepple.settings.logfile = os.path.join(settings.FREPPLE_LOGDIR, 'frepple.log') else: frepple.settings.logfile = os.path.join(settings.FREPPLE_LOGDIR, 'frepple_%s.log' % database) # Welcome message print("FrePPLe with processid %s on %s using database '%s'" % ( os.getpid(), sys.platform, database )) # Find all planning steps and execute them from freppledb.common.commands import PlanTaskRegistry as register register.autodiscover() try: register.run(database=database) except Exception as e: print("Error during planning: ", e) raise
# Update the task with my processid if "FREPPLE_TASKID" in os.environ: try: task = (Task.objects.all().using(database).get( pk=os.environ["FREPPLE_TASKID"])) task.processid = os.getpid() task.save(update_fields=["processid"], using=database) except Task.DoesNotExist: task = None else: task = None # Find all planning steps and execute them from freppledb.common.commands import PlanTaskRegistry as register newstatus = "Done" try: register.run(database=database) except Exception as e: logger.error("Error during planning: %s" % e) newstatus = "Failed" raise finally: # Clear the processid if task: task = Task.objects.all().using(database).get(pk=task.id) task.processid = None task.status = newstatus task.save(update_fields=["processid", "status"], using=database)
def handle(self, **options): # Pick up the options 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 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 plan': raise CommandError("Invalid task identifier") task.status = '0%' task.started = now else: task = Task(name='generate plan', submitted=now, started=now, status='0%', user=user) # Validate options if 'constraint' in options: constraint = int(options['constraint']) if constraint < 0 or constraint > 15: raise ValueError("Invalid constraint: %s" % options['constraint']) else: constraint = 15 if 'plantype' in options: plantype = int(options['plantype']) if plantype < 1 or plantype > 2: raise ValueError("Invalid plan type: %s" % options['plantype']) else: plantype = 1 # Reset environment variables # TODO avoid having to delete the environment variables. Use options directly? PlanTaskRegistry.autodiscover() for i in PlanTaskRegistry.reg: if options['env']: # Options specified if i.label and i.label[0] in os.environ: del os.environ[i.label[0]] elif i.label: # No options specified - default to activate them all os.environ[i.label[0]] = '1' # Set environment variables if options['env']: task.arguments = "--constraint=%d --plantype=%d --env=%s" % (constraint, plantype, options['env']) for i in options['env'].split(','): j = i.split('=') if len(j) == 1: os.environ[j[0]] = '1' else: os.environ[j[0]] = j[1] else: task.arguments = "--constraint=%d --plantype=%d" % (constraint, plantype) if options['background']: task.arguments += " --background" # Log task task.save(using=database) # Locate commands.py import freppledb.common.commands cmd = freppledb.common.commands.__file__ # Prepare environment os.environ['FREPPLE_PLANTYPE'] = str(plantype) os.environ['FREPPLE_CONSTRAINT'] = str(constraint) os.environ['FREPPLE_TASKID'] = str(task.id) os.environ['FREPPLE_DATABASE'] = database os.environ['PATH'] = settings.FREPPLE_HOME + os.pathsep + os.environ['PATH'] + os.pathsep + settings.FREPPLE_APP if os.path.isfile(os.path.join(settings.FREPPLE_HOME, 'libfrepple.so')): os.environ['LD_LIBRARY_PATH'] = settings.FREPPLE_HOME if 'DJANGO_SETTINGS_MODULE' not in os.environ: os.environ['DJANGO_SETTINGS_MODULE'] = 'freppledb.settings' os.environ['PYTHONPATH'] = os.path.normpath(settings.FREPPLE_APP) if options['background']: # Execute as background process on Windows if os.name == 'nt': subprocess.Popen(['frepple', cmd], creationflags=0x08000000) else: # Execute as background process on Linux subprocess.Popen(['frepple', cmd]) else: # Execute in foreground ret = subprocess.call(['frepple', cmd]) if ret != 0 and ret != 2: # Return code 0 is a successful run # Return code is 2 is a run cancelled by a user. That's shown in the status field. raise Exception('Failed with exit code %d' % ret) # 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() raise e finally: if task: task.save(using=database)