Module control.typ.value

Types of values in value tables.

Expand source code
"""Types of values in value tables."""

from control.utils import pick as G, E, NBSP
from control.html import HtmlElements as H
from config import Config as C, Names as N
from control.typ.related import Related, castObjectId

CW = C.web

MESSAGES = CW.messages

FILTER_THRESHOLD = CW.filterThreshold

QQ = H.icon(CW.unknown[N.generic])
Qq = H.icon(CW.unknown[N.generic], asChar=True)


class ConversionError(Exception):
    pass


class Value(Related):
    """Type class for types with values in value tables."""

    widgetType = N.related

    def __init__(self, context):
        """## Initialization

        Store a handle to the Context singleton.

        Parameters
        ----------
        context: object
            See below.
        """

        self.context = context
        """*object* A `control.context.Context` singleton.
        """

    def fromStr(self, editVal, constrain=None, uid=None, eppn=None, extensible=False):
        """Convert a value to an object id by looking it up in a value table.

        If the value is a singleton list, and if the value list is extensible,
        we insert the new value into the value table, and return the new id.

        Otherwise, the value must be either:

        *   the string representation of id of a value
        *   or a value in the relevant value table.

        In all cases the MongoDb object id of that value is returned.

        If a `constrain` is given only values in a subset defined by a constraint
        qualify. This happens when we look for score records that are bound to a
        criterion.

        Parameters
        ----------
        editVal: string
            The value as it has been sent from the client
        constrain:
            An additional constraint for the value.
            See `control.db.Db.getValueRecords`.
        """

        if not editVal:
            return None

        context = self.context
        db = context.db
        table = self.name

        valType = type(editVal)
        valuesInv = (
            getattr(db, f"""{table}Inv""", {})
            if constrain is None
            else db.getValueInv(table, constrain)
        )
        if valType is list or valType is tuple:
            if extensible and editVal:
                editVal = editVal[0]
                if editVal in valuesInv:
                    return valuesInv[editVal]
                fieldName = N.rep if extensible is True else extensible
                field = {fieldName: editVal}
                return db.insertItem(table, uid, eppn, True, **field)
            raise ConversionError

        values = (
            getattr(db, table, {})
            if constrain is None
            else db.getValueIds(table, constrain)
        )
        valId = castObjectId(editVal)
        if valId is None:
            valId = G(valuesInv, editVal)
        if valId not in values:
            raise ConversionError
        return valId

    def toEdit(self, val):
        return val

    def toOrig(self, val):
        return val if val is None else str(val)

    def widget(self, val, multiple, extensible, constrain):
        context = self.context
        db = context.db
        table = self.name

        valueRecords = db.getValueRecords(table, constrain=constrain)

        filterControl = (
            [
                H.input(
                    E,
                    type=N.text,
                    placeholder=G(MESSAGES, N.filter, default=E),
                    cls="wfilter",
                ),
                H.iconx(N.add, cls="small wfilter add", title="add value")
                if extensible
                else E,
                H.iconx(N.clear, cls="small wfilter clear", title="clear filter"),
            ]
            if len(valueRecords) > FILTER_THRESHOLD
            else []
        )
        atts = dict(
            markup=True,
            clickable=True,
            multiple=multiple,
            active=val,
            hideInActual=True,
            hideBlockedUsers=True,
        )
        return H.div(
            filterControl
            + [
                formatted
                for (text, formatted) in (
                    ([] if multiple else [self.title(record={}, **atts)])
                    + sorted(
                        (self.title(record=record, **atts) for record in valueRecords),
                        key=lambda x: x[0].lower(),
                    )
                )
            ],
            cls="wvalue",
        )

    def title(
        self,
        eid=None,
        record=None,
        markup=False,
        clickable=False,
        multiple=False,
        active=None,
        hideInActual=False,
        hideBlockedUsers=False,
    ):
        if record is None and eid is None:
            return None if markup is None else (QQ, QQ) if markup else Qq

        table = self.name

        if record is None:
            context = self.context
            record = context.getItem(table, eid)

        titleStr = self.titleStr(record, markup=markup)
        titleHint = self.titleHint(record)

        if markup:
            if eid is None:
                eid = G(record, N._id)

            isActive = eid in (active or []) if multiple else eid == active
            baseCls = (
                ("button " if multiple or not isActive else "label ")
                if clickable
                else "tag "
            )
            activeCls = "active " if isActive else E
            inActualCls = self.inActualCls(record=record)
            hidden = (
                hideInActual
                and inActualCls
                and not isActive
                or hideBlockedUsers
                and table == N.user
                and not G(record, N.mayLogin)
            )
            if hidden:
                return (E, E)
            atts = dict(cls=f"{baseCls}{activeCls}medium {inActualCls}")
            if clickable and eid is not None:
                atts[N.eid] = str(eid)

            if titleHint:
                atts[N.title] = titleHint

            titleIcon = (
                (NBSP + H.icon(N.cross if isActive else N.add, cls="small"))
                if multiple
                else E
            )

            titleFormatted = H.span(
                [titleStr, titleIcon], lab=titleStr.lower(), **atts,
            )
            return (titleStr, titleFormatted)
        else:
            return titleStr

Classes

class ConversionError (*args, **kwargs)

Common base class for all non-exit exceptions.

Expand source code
class ConversionError(Exception):
    pass

Ancestors

  • builtins.Exception
  • builtins.BaseException
class Value (context)

Type class for types with values in value tables.

Initialization

Store a handle to the Context singleton.

Parameters

context : object
See below.
Expand source code
class Value(Related):
    """Type class for types with values in value tables."""

    widgetType = N.related

    def __init__(self, context):
        """## Initialization

        Store a handle to the Context singleton.

        Parameters
        ----------
        context: object
            See below.
        """

        self.context = context
        """*object* A `control.context.Context` singleton.
        """

    def fromStr(self, editVal, constrain=None, uid=None, eppn=None, extensible=False):
        """Convert a value to an object id by looking it up in a value table.

        If the value is a singleton list, and if the value list is extensible,
        we insert the new value into the value table, and return the new id.

        Otherwise, the value must be either:

        *   the string representation of id of a value
        *   or a value in the relevant value table.

        In all cases the MongoDb object id of that value is returned.

        If a `constrain` is given only values in a subset defined by a constraint
        qualify. This happens when we look for score records that are bound to a
        criterion.

        Parameters
        ----------
        editVal: string
            The value as it has been sent from the client
        constrain:
            An additional constraint for the value.
            See `control.db.Db.getValueRecords`.
        """

        if not editVal:
            return None

        context = self.context
        db = context.db
        table = self.name

        valType = type(editVal)
        valuesInv = (
            getattr(db, f"""{table}Inv""", {})
            if constrain is None
            else db.getValueInv(table, constrain)
        )
        if valType is list or valType is tuple:
            if extensible and editVal:
                editVal = editVal[0]
                if editVal in valuesInv:
                    return valuesInv[editVal]
                fieldName = N.rep if extensible is True else extensible
                field = {fieldName: editVal}
                return db.insertItem(table, uid, eppn, True, **field)
            raise ConversionError

        values = (
            getattr(db, table, {})
            if constrain is None
            else db.getValueIds(table, constrain)
        )
        valId = castObjectId(editVal)
        if valId is None:
            valId = G(valuesInv, editVal)
        if valId not in values:
            raise ConversionError
        return valId

    def toEdit(self, val):
        return val

    def toOrig(self, val):
        return val if val is None else str(val)

    def widget(self, val, multiple, extensible, constrain):
        context = self.context
        db = context.db
        table = self.name

        valueRecords = db.getValueRecords(table, constrain=constrain)

        filterControl = (
            [
                H.input(
                    E,
                    type=N.text,
                    placeholder=G(MESSAGES, N.filter, default=E),
                    cls="wfilter",
                ),
                H.iconx(N.add, cls="small wfilter add", title="add value")
                if extensible
                else E,
                H.iconx(N.clear, cls="small wfilter clear", title="clear filter"),
            ]
            if len(valueRecords) > FILTER_THRESHOLD
            else []
        )
        atts = dict(
            markup=True,
            clickable=True,
            multiple=multiple,
            active=val,
            hideInActual=True,
            hideBlockedUsers=True,
        )
        return H.div(
            filterControl
            + [
                formatted
                for (text, formatted) in (
                    ([] if multiple else [self.title(record={}, **atts)])
                    + sorted(
                        (self.title(record=record, **atts) for record in valueRecords),
                        key=lambda x: x[0].lower(),
                    )
                )
            ],
            cls="wvalue",
        )

    def title(
        self,
        eid=None,
        record=None,
        markup=False,
        clickable=False,
        multiple=False,
        active=None,
        hideInActual=False,
        hideBlockedUsers=False,
    ):
        if record is None and eid is None:
            return None if markup is None else (QQ, QQ) if markup else Qq

        table = self.name

        if record is None:
            context = self.context
            record = context.getItem(table, eid)

        titleStr = self.titleStr(record, markup=markup)
        titleHint = self.titleHint(record)

        if markup:
            if eid is None:
                eid = G(record, N._id)

            isActive = eid in (active or []) if multiple else eid == active
            baseCls = (
                ("button " if multiple or not isActive else "label ")
                if clickable
                else "tag "
            )
            activeCls = "active " if isActive else E
            inActualCls = self.inActualCls(record=record)
            hidden = (
                hideInActual
                and inActualCls
                and not isActive
                or hideBlockedUsers
                and table == N.user
                and not G(record, N.mayLogin)
            )
            if hidden:
                return (E, E)
            atts = dict(cls=f"{baseCls}{activeCls}medium {inActualCls}")
            if clickable and eid is not None:
                atts[N.eid] = str(eid)

            if titleHint:
                atts[N.title] = titleHint

            titleIcon = (
                (NBSP + H.icon(N.cross if isActive else N.add, cls="small"))
                if multiple
                else E
            )

            titleFormatted = H.span(
                [titleStr, titleIcon], lab=titleStr.lower(), **atts,
            )
            return (titleStr, titleFormatted)
        else:
            return titleStr

Ancestors

Subclasses

Class variables

var widgetType

Methods

def fromStr(self, editVal, constrain=None, uid=None, eppn=None, extensible=False)

Convert a value to an object id by looking it up in a value table.

If the value is a singleton list, and if the value list is extensible, we insert the new value into the value table, and return the new id.

Otherwise, the value must be either:

  • the string representation of id of a value
  • or a value in the relevant value table.

In all cases the MongoDb object id of that value is returned.

If a constrain is given only values in a subset defined by a constraint qualify. This happens when we look for score records that are bound to a criterion.

Parameters

editVal : string
The value as it has been sent from the client

constrain: An additional constraint for the value. See Db.getValueRecords().

Expand source code
def fromStr(self, editVal, constrain=None, uid=None, eppn=None, extensible=False):
    """Convert a value to an object id by looking it up in a value table.

    If the value is a singleton list, and if the value list is extensible,
    we insert the new value into the value table, and return the new id.

    Otherwise, the value must be either:

    *   the string representation of id of a value
    *   or a value in the relevant value table.

    In all cases the MongoDb object id of that value is returned.

    If a `constrain` is given only values in a subset defined by a constraint
    qualify. This happens when we look for score records that are bound to a
    criterion.

    Parameters
    ----------
    editVal: string
        The value as it has been sent from the client
    constrain:
        An additional constraint for the value.
        See `control.db.Db.getValueRecords`.
    """

    if not editVal:
        return None

    context = self.context
    db = context.db
    table = self.name

    valType = type(editVal)
    valuesInv = (
        getattr(db, f"""{table}Inv""", {})
        if constrain is None
        else db.getValueInv(table, constrain)
    )
    if valType is list or valType is tuple:
        if extensible and editVal:
            editVal = editVal[0]
            if editVal in valuesInv:
                return valuesInv[editVal]
            fieldName = N.rep if extensible is True else extensible
            field = {fieldName: editVal}
            return db.insertItem(table, uid, eppn, True, **field)
        raise ConversionError

    values = (
        getattr(db, table, {})
        if constrain is None
        else db.getValueIds(table, constrain)
    )
    valId = castObjectId(editVal)
    if valId is None:
        valId = G(valuesInv, editVal)
    if valId not in values:
        raise ConversionError
    return valId

Inherited members