def configure_tasktable_crud(self, task=None, function=None, args=None, vars=None, period = 3600, # seconds, so 1 hour ): """ Configure the task table for interactive CRUD, setting defaults, widgets and hiding unnecessary fields @param task: the task name (will use a UUID if omitted) @param function: the function name (won't hide if omitted) @param args: the function position arguments @param vars: the function named arguments """ if args is None: args = [] if vars is None: vars = {} T = current.T NONE = current.messages["NONE"] UNLIMITED = T("unlimited") tablename = self.TASK_TABLENAME table = current.db[tablename] table.uuid.readable = table.uuid.writable = False table.prevent_drift.readable = table.prevent_drift.writable = False table.sync_output.readable = table.sync_output.writable = False table.times_failed.readable = False # Configure start/stop time fields for fn in ("start_time", "stop_time"): field = table[fn] field.represent = lambda dt: \ S3DateTime.datetime_represent(dt, utc=True) field.requires = IS_UTC_DATETIME() set_min = set_max = None if fn == "start_time": set_min = "#scheduler_task_stop_time" elif fn == "stop_time": set_max = "#scheduler_task_start_time" field.widget = S3CalendarWidget(past = 0, set_min = set_min, set_max = set_max, timepicker = True, ) if not task: import uuid task = str(uuid.uuid4()) field = table.task_name field.default = task field.readable = False field.writable = False if function: field = table.function_name field.default = function field.readable = False field.writable = False field = table.args field.default = json.dumps(args) field.readable = False field.writable = False field = table.repeats field.label = T("Repeat") field.comment = T("times (0 = unlimited)") field.default = 0 field.represent = lambda opt: \ opt and "%s %s" % (opt, T("times")) or \ opt == 0 and UNLIMITED or \ NONE field = table.period field.label = T("Run every") field.default = period field.widget = S3TimeIntervalWidget.widget field.requires = IS_TIME_INTERVAL_WIDGET(table.period) field.represent = S3TimeIntervalWidget.represent field.comment = T("seconds") table.timeout.default = 600 table.timeout.represent = lambda opt: \ opt and "%s %s" % (opt, T("seconds")) or \ opt == 0 and UNLIMITED or \ NONE field = table.vars field.default = json.dumps(vars) field.readable = field.writable = False # Always use "default" controller (web2py uses current controller), # otherwise the anonymous worker does not pass the controller # permission check and gets redirected to login before it reaches # the task function which does the s3_impersonate field = table.application_name field.default = "%s/default" % current.request.application field.readable = field.writable = False table.group_name.readable = table.group_name.writable = False table.status.readable = table.status.writable = False table.next_run_time.readable = table.next_run_time.writable = False table.times_run.readable = table.times_run.writable = False table.assigned_worker_name.readable = \ table.assigned_worker_name.writable = False current.s3db.configure(tablename, list_fields = ["id", "enabled", "start_time", "repeats", "period", (T("Last run"), "last_run_time"), (T("Last status"), "status"), (T("Next run"), "next_run_time"), "stop_time" ], ) response = current.response if response: response.s3.crud_strings[tablename] = Storage( label_create = T("Create Job"), title_display = T("Scheduled Jobs"), title_list = T("Job Schedule"), title_update = T("Edit Job"), label_list_button = T("List Jobs"), msg_record_created = T("Job added"), msg_record_modified = T("Job updated"), msg_record_deleted = T("Job deleted"), msg_list_empty = T("No jobs configured yet"), msg_no_match = T("No jobs configured")) return
def s3_datetime(name="date", **attr): """ Return a standard datetime field @param name: the field name @keyword default: the field default, use "now" for current datetime @keyword represent: the field representation method, use "date" for S3DateTime.date_represent (default is S3DateTime.datetime_represent) @keyword widget: the form widget, can use "date" to configure an S3DateWidget (default is S3DateTimeWidget) @keyword past: limit selection to x hours before now @keyword future: limit selection to x hours after now @keyword min: earliest selectable datetime.datetime (overrides past) @keyword max: latest selectable datetime.datetime (overrides future) @ToDo: Different default field name in case we need to start supporting Oracle, where 'date' is a reserved word """ now = current.request.utcnow limits = {} for keyword in ("past", "future", "min", "max"): if keyword in attr: limits[keyword] = attr[keyword] del attr[keyword] # Default and label if "default" in attr and attr["default"] == "now": attr["default"] = now if "label" not in attr: attr["label"] = current.T("Date") # Representation option if "represent" not in attr: attr["represent"] = lambda dt: S3DateTime.datetime_represent(dt, utc=True) elif attr["represent"] == "date": attr["represent"] = lambda dt: S3DateTime.date_represent(dt, utc=True) # Helper functions to convert min/max dates into past/future hours def past_hours(earliest): diff = now - earliest return divmod(diff.days * 86400 + diff.seconds, 3600)[0] def future_hours(latest): diff = latest - now return divmod(diff.days * 86400 + diff.seconds, 3600)[0] requires = None widget = attr.get("widget") if widget == "date": # Helper function to convert past/future hours into # earliest/latest datetime, retaining day of month and # time of day def limit(delta): current_month = now.month years, hours = divmod(-delta, 8760) months = divmod(hours, 744)[0] if months > current_month: years += 1 month = divmod((current_month - months) + 12, 12)[1] year = now.year - years return now.replace(month=month, year=year) # Compute limits earliest = limits.get("min") if not earliest: past = limits.get("past") if past is not None: earliest = limit(-past) else: past = past_hours(earliest) latest = attr.get("max") if not latest: future = attr.get("future") if future is not None: latest = limit(future) else: future = future_hours(latest) # Widget widget_opts = {} if past is not None: widget_opts["past"] = int(round(past / 744.0, 0)) if future is not None: widget_opts["future"] = int(round(future / 744.0, 0)) attr["widget"] = S3DateWidget(**widget_opts) # Validator if "requires" not in attr: dateformat = current.deployment_settings.get_L10n_date_format() if past is None and future is None: requires = IS_UTC_DATE() elif past is None: requires = IS_UTC_DATE(maximum=latest.date()) elif future is None: requires = IS_UTC_DATE(minimum=earliest.date()) else: attr["widget"] = S3DateWidget(past=past, future=future) requires = IS_UTC_DATE(maximum=latest.date(), minimum=earliest.date()) elif widget is None or widget == "datetime": # Widget attr["widget"] = S3DateTimeWidget(**limits) # Validator if "requires" not in attr: earliest = limits.get("min") if not earliest: past = limits.get("past") if past is not None: earliest = now - datetime.timedelta(hours=past) latest = limits.get("max") if not latest: future = limits.get("future") if future is not None: latest = now + datetime.timedelta(hours=future) if earliest and latest: requires = IS_UTC_DATETIME(minimum=earliest, maximum=latest) elif earliest: requires = IS_UTC_DATETIME(minimum=earliest) elif latest: requires = IS_UTC_DATETIME(maximum=latest) else: requires = IS_UTC_DATETIME() if "requires" not in attr and requires is not None: empty = attr.pop("empty", None) if empty is False: attr["requires"] = requires else: attr["requires"] = IS_EMPTY_OR(requires) return S3ReusableField(name, "datetime", **attr)()
def configure_tasktable_crud( self, task=None, function=None, args=None, vars=None, period=3600, # seconds, so 1 hour ): """ Configure the task table for interactive CRUD, setting defaults, widgets and hiding unnecessary fields @param task: the task name (will use a UUID if omitted) @param function: the function name (won't hide if omitted) @param args: the function position arguments @param vars: the function named arguments """ if args is None: args = [] if vars is None: vars = {} T = current.T NONE = current.messages["NONE"] UNLIMITED = T("unlimited") tablename = self.TASK_TABLENAME table = current.db[tablename] table.uuid.readable = table.uuid.writable = False table.prevent_drift.readable = table.prevent_drift.writable = False table.sync_output.readable = table.sync_output.writable = False table.times_failed.readable = False field = table.start_time field.represent = lambda dt: \ S3DateTime.datetime_represent(dt, utc=True) field.widget = S3DateTimeWidget(past=0) field.requires = IS_UTC_DATETIME( format=current.deployment_settings.get_L10n_datetime_format()) field = table.stop_time field.represent = lambda dt: \ S3DateTime.datetime_represent(dt, utc=True) field.widget = S3DateTimeWidget(past=0) field.requires = IS_EMPTY_OR( IS_UTC_DATETIME( format=current.deployment_settings.get_L10n_datetime_format())) if not task: import uuid task = str(uuid.uuid4()) field = table.task_name field.default = task field.readable = False field.writable = False if function: field = table.function_name field.default = function field.readable = False field.writable = False field = table.args field.default = json.dumps(args) field.readable = False field.writable = False field = table.repeats field.label = T("Repeat") field.comment = T("times (0 = unlimited)") field.default = 0 field.represent = lambda opt: \ opt and "%s %s" % (opt, T("times")) or \ opt == 0 and UNLIMITED or \ NONE field = table.period field.label = T("Run every") field.default = period field.widget = S3TimeIntervalWidget.widget field.requires = IS_TIME_INTERVAL_WIDGET(table.period) field.represent = S3TimeIntervalWidget.represent field.comment = None table.timeout.default = 600 table.timeout.represent = lambda opt: \ opt and "%s %s" % (opt, T("seconds")) or \ opt == 0 and UNLIMITED or \ NONE field = table.vars field.default = json.dumps(vars) field.readable = field.writable = False # Always use "default" controller (web2py uses current controller), # otherwise the anonymous worker does not pass the controller # permission check and gets redirected to login before it reaches # the task function which does the s3_impersonate field = table.application_name field.default = "%s/default" % current.request.application field.readable = field.writable = False table.group_name.readable = table.group_name.writable = False table.status.readable = table.status.writable = False table.next_run_time.readable = table.next_run_time.writable = False table.times_run.readable = table.times_run.writable = False table.assigned_worker_name.readable = \ table.assigned_worker_name.writable = False current.s3db.configure( tablename, list_fields=[ "id", "enabled", "start_time", "repeats", "period", (T("Last run"), "last_run_time"), (T("Last status"), "status"), (T("Next run"), "next_run_time"), "stop_time" ], ) response = current.response if response: response.s3.crud_strings[tablename] = Storage( label_create=T("Create Job"), title_display=T("Scheduled Jobs"), title_list=T("Job Schedule"), title_update=T("Edit Job"), label_list_button=T("List Jobs"), msg_record_created=T("Job added"), msg_record_modified=T("Job updated"), msg_record_deleted=T("Job deleted"), msg_list_empty=T("No jobs configured yet"), msg_no_match=T("No jobs configured")) return
def s3_date(name="date", **attr): """ Return a standard Date field Additional options to normal S3ReusableField: default == "now" (in addition to usual meanings) past = x months future = x months start_field = "selector" for start field default_interval = x months from start date default_explicit = Bool for explicit default start_field and default_interval should be given together @ToDo: Different default field name in case we need to start supporting Oracle, where 'date' is a reserved word """ if "past" in attr: past = attr["past"] del attr["past"] else: past = None if "future" in attr: future = attr["future"] del attr["future"] else: future = None T = current.T now = current.request.utcnow.date() if "default" in attr and attr["default"] == "now": attr["default"] = now if "label" not in attr: attr["label"] = T("Date") if "represent" not in attr: attr["represent"] = lambda d: S3DateTime.date_represent(d, utc=True) if "requires" not in attr: if past is None and future is None: requires = IS_UTC_DATE() else: current_month = now.month if past is None: future_month = now.month + future if future_month <= 12: maximum = now.replace(month=future_month) else: current_year = now.year years = int(future_month / 12) future_year = current_year + years future_month = future_month - (years * 12) if future_month: maximum = now.replace(year=future_year, month=future_month) else: maximum = now.replace(year=future_year) requires = IS_UTC_DATE(maximum=maximum) elif future is None: if past < current_month: minimum = now.replace(month=current_month - past) else: current_year = now.year past_years = int(past / 12) past_months = past - (past_years * 12) past_month = current_month - past_months if past_month: minimum = now.replace(year=current_year - past_years, month=past_month) else: minimum = now.replace(year=current_year - past_years) requires = IS_UTC_DATE(minimum=minimum) else: future_month = now.month + future if future_month <= 12: maximum = now.replace(month=future_month) else: current_year = now.year years = int(future_month / 12) future_year = now.year + years future_month = future_month - (years * 12) if future_month: maximum = now.replace(year=future_year, month=future_month) else: maximum = now.replace(year=future_year) if past < current_month: minimum = now.replace(month=current_month - past) else: current_year = now.year past_years = int(past / 12) past_months = past - (past_years * 12) past_month = current_month - past_months if past_month: minimum = now.replace(year=current_year - past_years, month=past_month) else: minimum = now.replace(year=current_year - past_years) requires = IS_UTC_DATE(minimum=minimum, maximum=maximum) if "empty" in attr: if attr["empty"] is False: attr["requires"] = requires else: attr["requires"] = IS_EMPTY_OR(requires) del attr["empty"] else: # Default attr["requires"] = IS_EMPTY_OR(requires) if "widget" not in attr: # Widget Options widget_option = {} if "start_field" in attr: widget_option["start_field"] = attr["start_field"] del attr["start_field"] if "default_interval" in attr: widget_option["default_interval"] = attr["default_interval"] del attr["default_interval"] if "default_explicit" in attr: widget_option["default_explicit"] = attr["default_explicit"] del attr["default_explicit"] if future is not None: widget_option["future"] = future if past is not None: widget_option["past"] = past attr["widget"] = S3DateWidget(**widget_option) f = S3ReusableField(name, "date", **attr) return f()