Esempio n. 1
0
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)()
Esempio n. 2
0
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()