Example #1
0
    def makeCaption(self, label, entries, rule=False):
        """Produce the caption for a section of navigation items.

        Parameters
        ----------
        label: string
            Points to a key in web.yaml, under `captions`,
            where the full text of the caption can be founnd.
        entries: iterable of (path, string(html))
            The path is used to determine whether this entry is active;
            the string is the formatted html of the entry.
        rule: boolean
            Whether there should be a rule before the first entry.

        Returns
        -------
        string(html)
        """

        if not entries:
            return E

        refPath = self.path
        paths = {path for (path, rep) in entries}
        reps = [rep for (path, rep) in entries]
        active = any(refPath.startswith(f"""/{p}/""") for p in paths)
        navClass = " active" if active else E
        atts = dict(cls=f"nav {navClass}")
        if rule:
            atts[N.addClass] = " ruleabove"

        entriesRep = H.div(reps, cls="sidebarsec")
        return H.details(label, entriesRep, label, **atts)
Example #2
0
    def status(self, table, kind=None):
        """Present all workflow info and controls relevant to the record.

        Parameters
        ----------
        table: string
            We must specify the kind of record for which we want to see the status:
            contrib, assessment, or review.
        kind: string {`expert`, `final`}, optional `None`
            Only if we want review attributes

        Returns
        -------
        string(html)
        """

        eid = list(self.info(table, N._id, kind=kind))[0]
        itemKey = f"""{table}/{eid}"""
        rButton = H.iconr(itemKey, "#workflow", msg=N.status)

        return H.div(
            [
                rButton,
                self.statusOverview(table, kind=kind),
                self.tasks(table, kind=kind),
            ],
            cls="workflow",
        )
Example #3
0
    def widget(self, val):
        """Constructs and edit widget around for this type.

        Parameters
        ----------
        val: string
            The initial value for the widget.

        Returns
        -------
        string(html)
            Dependent on a batch of Javascript in `index.js`, look for `const widgets`.
        """

        atts = {}
        if self.pattern:
            atts[N.pattern] = self.pattern
        validationMsg = TypeBase.validationMsg(self.name)

        widgetElem = H.input(self.toEdit(val),
                             type=N.text,
                             cls="wvalue",
                             **atts)
        validationElem = H.span(E,
                                valmsg=validationMsg) if validationMsg else E
        return H.join([widgetElem, validationElem])
Example #4
0
    def wrapTestUsers(self):
        """Present a widget to select a test user for login.

        !!! caution
            In production this will do nothing.
            Only in development mode one can select a test user.
        """
        if not self.isDevel:
            return E

        db = self.db

        testUsers = {
            record[N.eppn]: record
            for record in db.user.values()
            if N.eppn in record and G(record, N.authority) == N.local
        }
        return H.join(
            [
                H.div(H.a(u, href=f"/login?eppn={u}", cls="button small"))
                for u in testUsers
            ]
            + [
                H.div(
                    H.input(
                        E,
                        placeholder="email",
                        onchange="window.location.href=`/login?email=${this.value}`",
                    )
                )
            ]
        )
Example #5
0
    def titleStr(self, record):
        """Put the score and the level in the title."""

        score = G(record, N.score)
        if score is None:
            return Qq
        score = H.he(score)
        level = H.he(G(record, N.level)) or Qq
        return f"""{score} - {level}"""
    def bodyCompact(self, **kwargs):
        perm = self.perm

        theTitle = self.title()
        comments = ("""<!-- begin review comment -->""" + H.div(
            self.field(N.comments).wrap(withLabel=False,
                                        asEdit=G(perm, N.isEdit)), ) +
                    """<!-- end review comment -->""")

        return H.div([theTitle, comments], cls=f"reviewentry")
Example #7
0
    def tasks(self, table, kind=None):
        """Present the currently available tasks as buttons on the interface.

        !!! hint "easy comments"
            We also include a comment `<!-- task~!taskName:eid -->
            for the ease of testing.

        Parameters
        ----------
        table: string
            We must specify the table for which we want to present the
            tasks: contrib, assessment, or review.
        kind: string {`expert`, `final`}, optional `None`
            Only if we want review attributes

        Returns
        -------
        string(html)
        """

        uid = self.uid

        if not uid or table not in USER_TABLES:
            return E

        eid = list(self.info(table, N._id, kind=kind))[0]
        taskParts = []

        allowedTasks = sorted((task, taskInfo)
                              for (task, taskInfo) in TASKS.items()
                              if G(taskInfo, N.table) == table)
        justNow = now()

        for (task, taskInfo) in allowedTasks:
            permitted = self.permission(task, kind=kind)
            if not permitted:
                continue

            remaining = type(permitted) is timedelta and permitted
            taskUntil = E
            if remaining:
                remainingRep = datetime.toDisplay(justNow + remaining)
                taskUntil = H.span(f""" before {remainingRep}""", cls="datex")
            taskMsg = G(taskInfo, N.msg)
            taskCls = G(taskInfo, N.cls)

            taskPart = (H.a(
                [taskMsg, taskUntil],
                f"""/api/task/{task}/{eid}""",
                cls=f"large task {taskCls}",
            ) + f"""<!-- task!{task}:{eid} -->""")
            taskParts.append(taskPart)

        return H.join(taskParts)
Example #8
0
    def wrapWidget(self, editable, cls=E):
        """Wrap the field value.

        A widget shows the value and may have additional controls to

        *   edit the value
        *   refresh the value

        Refresh fields are those fields that change if other fields are updated,
        typically fields that record the moment on which something happened.
        These fields will get a refresh button automatically.

        Fields may have three conditions relevant for rendering:

        condition | rendering
        --- | ---
        not editable | readonly
        editable in readonly view | readonly with button for editable view
        editable in edit view | editable with button for readonly view

        Parameters
        ----------
        editable: boolean
            Whether the field should be presented in editable form
        cls
            See `Field.wrap()`

        Returns
        -------
        button: string(html)
        representation: string(html)

        They are packaged as a tuple.
        """

        atts = self.atts
        mayEdit = self.mayEdit
        withRefresh = self.withRefresh

        button = (H.iconx(N.ok, cls="small", action=N.view, **atts) if editable
                  else (H.iconx(N.edit, cls="small", action=N.edit, **atts)
                        if mayEdit else H.iconx(
                            N.refresh,
                            cls="small",
                            action=N.view,
                            title=REFRESH,
                            **atts,
                        ) if withRefresh else E))

        return (button, self.wrapValue(editable, cls=cls))
Example #9
0
    def body(self, myMasters=None, hideMasters=False):
        """Wrap the body of the record in HTML.

        This is the part without the provenance information and without
        the detail records.

        This method can be overridden by `body` methods in derived classes.

        Parameters
        ----------
        myMasters: iterable of string, optional `None`
            A declaration of which fields must be treated as master fields.
        hideMaster: boolean, optional `False`
            If `True`, all master fields as declared in `myMasters` will be left out.

        Returns
        -------
        string(html)
        """

        fieldSpecs = self.fields
        provSpecs = self.prov

        return H.join(
            self.field(field, asMaster=field in myMasters).wrap()
            for field in fieldSpecs
            if (field not in provSpecs
                and not (hideMasters and field in myMasters)))
Example #10
0
    def wrap(self, readonly=False, showTable=None, showEid=None):
        """Wrap the details of all tables for this record into HTML.

        Parameters
        ----------
        readonly: boolean, optional `False`
            Whether the records should be presented in readonly form.
        showTable: string
            Name of the detail table of which record `showEid` should be opened.
        showEid: ObjectId
            Id of the detail record that should be initially opened.

        Returns
        -------
        string(html)
        """

        table = self.table

        for detailTable in G(DETAILS, table, default=[]):
            self.fetchDetails(detailTable)

        details = self.details

        return H.join(
            self.wrapDetail(
                detailTable,
                readonly=readonly,
                showEid=showEid if detailTable == showTable else None,
            ) for detailTable in details)
Example #11
0
    def widget(self, val):
        values = G(BOOLEAN_TYPES, self.name)
        noneValue = False if len(values) == 2 else None
        refV = G(values, val, default=G(values, noneValue))

        return H.div(
            [
                H.iconx(
                    values[w],
                    bool=str(w).lower(),
                    cls=(("active" if values[w] is refV else E) + " medium"),
                )
                for w in values
            ],
            cls="wvalue",
        )
Example #12
0
    def titleRaw(obj, record, cls=E):
        """Generate a title for a different record.

        This is fast title generation.
        No record object will be created.

        The title will be based on the fields in the record,
        and its formatting is assisted by the appropriate
        type class in `control.typ.types`.

        !!! hint
            If the record is  not "actual", its title will get a warning
            background color.
            See `control.db.Db.collect`.

        Parameters
        ----------
        obj: object
            Any object that has a table and context attribute, e.g. a table object
            or a record object
        record: dict
        cls: string, optional, `''`
            A CSS class to add to the outer element of the result
        """

        table = obj.table
        context = obj.context

        types = context.types
        typesObj = getattr(types, table, None)

        inActualCls = Record.inActualCls(obj, record)
        atts = dict(cls=f"{cls} {inActualCls}")

        return H.span(typesObj.title(record=record), **atts)
Example #13
0
    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",
        )
Example #14
0
    def toDisplay(self, val, markup=True):
        """Turns a real value into a HTML code for readonly display.

        Parameters
        ----------
        val: mixed
            A value of this type.

        Returns
        -------
        string(html)
            Possibly with nice formatting depending on the nature of the value.
        """

        if val is None:
            return QQ if markup else Qq
        valBare = H.he(self.normalize(str(val)))
        return H.span(valBare) if markup else valBare
    def wrapHelp(self):
        info = H.join(
            self.field(field, readonly=True).wrap(action=N.view)
            for field in [N.typeContribution, N.remarks]
            if field != N.typeContribution
        )

        detailsObj = self.DetailsClass(self)
        detailsObj.fetchDetails(N.score)
        details = detailsObj.wrapDetail(
            N.score,
            expanded=True,
            readonly=True,
            wrapMethod=N.wrapHelp,
            combineMethod=lambda x: [H.dl(x)],
        )

        return H.div(info + details, cls="criteriahelp")
Example #16
0
    def statusOverview(self, table, kind=None):
        """Present the current status of a record on the interface.

        Parameters
        ----------
        table: string
            We must specify the kind of record for which we want to present the stage:
            contrib, assessment, or review.
        kind: string {`expert`, `final`}, optional `None`
            Only if we want review attributes

        Returns
        -------
        string(html)
        """

        (stage, stageDate, locked, done, frozen, score, eid) = self.info(
            table,
            N.stage,
            N.stageDate,
            N.locked,
            N.done,
            N.frozen,
            N.score,
            N._id,
            kind=kind,
        )
        stageInfo = G(STAGE_ATTS, stage)
        statusCls = G(stageInfo, N.cls)
        stageOn = (H.span(f""" on {datetime.toDisplay(stageDate)}""",
                          cls="date") if stageDate else E)
        statusMsg = H.span([G(stageInfo, N.msg) or E, stageOn],
                           cls=f"large status {statusCls}")
        lockedCls = N.locked if locked else E
        lockedMsg = (H.span(G(STATUS_REP, N.locked),
                            cls=f"large status {lockedCls}") if locked else E)
        doneCls = N.done if done else E
        doneMsg = (H.span(G(STATUS_REP, N.done), cls=f"large status {doneCls}")
                   if done else E)
        frozenCls = N.frozen if frozen else E
        frozenMsg = (H.span(G(STATUS_REP, N.frozen), cls="large status info")
                     if frozen else E)

        statusRep = f"<!-- stage:{stage} -->" + H.div(
            [statusMsg, lockedMsg, doneMsg, frozenMsg], cls=frozenCls)

        scorePart = E
        if table == N.assessment:
            scoreParts = presentScore(score, eid)
            scorePart = (H.span(scoreParts) if table == N.assessment else
                         (scoreParts[0] if scoreParts else E)
                         if table == N.contrib else E)

        return H.div([statusRep, scorePart], cls="workflow-line")
    def wrap(self, *args, **kwargs):
        wfitem = self.wfitem
        if not wfitem:
            return super().wrap(*args, **kwargs)

        details = self.details

        (reviewer, ) = wfitem.info(N.assessment, N.reviewer)

        self.fetchDetails(
            N.reviewEntry,
            sortKey=lambda r: G(r, N.dateCreated, default=0),
        )
        (tableObj, records) = G(details, N.reviewEntry, default=(None, []))
        if not tableObj:
            return E

        byReviewer = {N.expert: E, N.final: E}

        for dest in (N.expert, N.final):
            byReviewer[dest] = self.wrapDetail(
                N.reviewEntry,
                filterFunc=lambda r: G(r, N.creator) == G(reviewer, dest),
                bodyMethod=N.compact,
                expanded=True,
                withProv=False,
                withN=False,
                inner=False,
            )

            if not byReviewer[dest]:
                byReviewer[dest] = H.span("""No review comment yet""",
                                          cls="info small")

        return H.div(
            [
                f"""<!-- begin reviewer {dest} -->""" + H.div(
                    [H.div(cap1(dest), cls="head"),
                     G(byReviewer, dest)],
                    cls=f"reviewentries {dest}",
                ) + f"""<!-- end reviewer {dest} -->""" for dest in reviewer
            ],
            cls="reviewers",
        )
Example #18
0
    def titleStr(self, record):
        """The title is a sequence number plus the short criterion text."""

        context = self.context
        types = context.types

        seq = H.he(G(record, N.seq)) or Qn
        eid = G(record, N.criteria)
        title = Qq if eid is None else types.criteria.title(eid=eid)
        return f"""{seq}. {title}"""
Example #19
0
    def titleStr(self, record):
        """Put the main type and the sub type in the title."""

        if not record:
            return Qq

        mainType = G(record, N.mainType) or E
        subType = G(record, N.subType) or E
        sep = WHYPHEN if mainType and subType else E
        return H.he(f"""{mainType}{sep}{subType}""")
Example #20
0
    def wrap(self, *args, **kwargs):
        wfitem = self.wfitem
        if not wfitem:
            return super().wrap(*args, **kwargs)

        kind = self.kind

        statusRep = wfitem.status(N.review, kind=kind)

        return H.div(statusRep)
    def title(self, *args, **kwargs):
        record = self.record
        critRecord = self.critRecord

        withEvidence = H.icon(
            N.missing if self.field(N.evidence).isBlank() else N.check)
        status = H.span(f"""evidence{NBSP}{withEvidence}""", cls="right small")
        seq = G(record, N.seq, default=Q)
        scoreRep = self.field(N.score).wrapBare()

        return H.span(
            [
                H.span([f"""{seq}{DOT}{NBSP}""",
                        critRecord.title()],
                       cls="col1"),
                H.span(scoreRep, cls="col2"),
                status,
            ],
            cls=f"centrytitle criteria",
        )
Example #22
0
    def makeOptions(self):
        """Produce an options widget.

        The options are defined in web.yaml, under the key `options`.
        """

        options = self.options

        filterRep = [
            H.input(E, type=N.text, id="cfilter", placeholder="match title"),
        ]
        optionsRep = [
            H.span(
                [H.checkbox(name, trival=value),
                 G(G(OPTIONS, name), N.label)],
                cls=N.option,
            ) for (name, value) in options.items()
        ]

        return [("XXX", rep) for rep in filterRep + optionsRep]
    def title(self, *args, **kwargs):
        uid = self.uid
        record = self.record

        creatorId = G(record, N.creator)

        youRep = f""" ({N.you})""" if creatorId == uid else E
        lastModified = G(record, N.modified)
        lastModifiedRep = (self.field(N.modified).value[-1].rsplit(
            DOT, maxsplit=1)[0] if lastModified else Qu)

        return H.span(f"""{lastModifiedRep}{youRep}""", cls=f"rentrytitle")
Example #24
0
    def title(
        self,
        record=None,
        eid=None,
        markup=False,
        active=None,
    ):
        """Generate a title for a rlated record.

        Parameters
        ----------
        record: dict, optional `None`
            The record for which to generate a title.
        eid: ObjectId, optional `None`
            If `record` is not passed, use this to retrieve the full record.
        markup: boolean
            If true, generate the title in HTML markup, otherwise as a plain string.
        active: ObjectId, optional `None`
            If passed, is is the id of the currently *active* record, the one
            that the current user is interacting with.

        Returns
        -------
        string(html)
        """

        if record is None and eid is None:
            return (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)
        titleHint = self.titleHint(record)

        if markup:

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

            inActualCls = self.inActualCls(record)
            atts = dict(cls=f"tag medium {inActualCls}")
            if titleHint:
                atts[N.title] = titleHint

            href = f"""/{table}/item/{eid}"""
            titleFormatted = H.a(titleStr, href, target=N._blank, **atts)
            return (titleStr, titleFormatted)
        else:
            return titleStr
    def bodyCompact(self, **kwargs):
        critId = self.critId
        critRecord = self.critRecord
        perm = self.perm

        critData = critRecord.record
        actual = G(critData, N.actual, default=False)
        msg = E if actual else G(MESSAGES, N.legacyCriterion)

        critKey = f"""{N.criteria}/{critId}/help"""
        (infoShow, infoHide, infoBody) = H.detailx(
            (N.info, N.dismiss),
            critRecord.wrapHelp(),
            critKey,
            openAtts=dict(cls="button small",
                          title="Explanation and scoring guide"),
            closeAtts=dict(cls="button small",
                           title="Hide criteria explanation"),
        )

        score = H.div(self.field(N.score).wrap(asEdit=G(perm, N.isEdit)))
        evidence = H.div(self.field(N.evidence).wrap(asEdit=G(perm, N.isEdit)))
        entry = H.div([
            H.div(H.he(msg), cls="heavy") if msg else E,
            infoShow,
            infoHide,
            infoBody,
            score,
            evidence,
        ], )

        return entry
Example #26
0
 def wrapHelp(self):
     term = self.title()
     definition = H.div(
         [
             self.field(field).wrap(
                 empty=True,
                 action=N.view,
                 cls="scoredesc" if field == N.description else E,
             )
             for field in [N.description, N.remarks]
         ]
     )
     return (term, definition)
Example #27
0
    def makeEntry(self, label, path, withOptions=False, asTask=False):
        """Produce an entry.

        !!! hint "easy comments"
            We also include a comment `<!-- caption^label -->
            for the ease of testing.

        Parameters
        ----------
        label: string
            The text of the entry
        path: url
            The destination after the entry is clicked.
        withOptions: boolean, optional `False`
            Whether to include the options widget.
        asTask: boolean, optional `False`
            Display the entry as a big workflow task button or as a modest
            hyperlink.

        Returns
        -------
        path: url
            The url that corresponds to this entry
        string(html)
            The wrapped entry
        """

        options = self.options
        active = path == self.path

        task = "task info" if asTask else "button"
        navClass = f"{task} small nav" + (" active" if active else E)

        optionsRep = (AMP.join(
            f"""{name}={value}"""
            for (name, value) in options.items()) if withOptions else E)
        if optionsRep:
            optionSep = AMP if Q in path else Q
            optionsRep = optionSep + optionsRep

        atts = dict(cls=navClass, )
        if withOptions:
            atts[N.hrefbase] = path

        comment = f"""<!-- caption^{label} -->"""
        return (
            path,
            comment + H.a(label, path + optionsRep, **atts),
        )
    def wrap(self, *args, **kwargs):
        wfitem = self.wfitem
        if not wfitem:
            return super().wrap(*args, **kwargs)

        self.fetchDetails(
            N.assessment,
            sortKey=lambda r: G(r, N.dateCreated, default=0),
        )

        statusRep = wfitem.status(N.contrib)
        showEid = self.mustShow(N.assessment, kwargs)

        return H.div(
            [statusRep,
             self.wrapDetail(N.assessment, showEid=showEid)])
Example #29
0
    def insertButton(self):
        """Present an insert button on the interface.

        Only if the user has rights to insert new items in this table.
        """

        mayInsert = self.mayInsert

        if not mayInsert:
            return E

        table = self.table
        itemSingle = self.itemLabels[0]

        return H.a(
            f"""New {itemSingle}""",
            f"""/api/{table}/{N.insert}""",
            cls="small task info",
        )
Example #30
0
    def titleStr(self, record):
        context = self.context
        auth = context.auth

        return H.he(auth.identity(record))