Skip to content

Record

record

RECORD

Handles record data.

This is about some aspects of query and note records and organization and project records by which they are organized. And it is about word records.

It is called by controllers that fetch sidebar material, see M:RECORD.body.

Source code in modules/record.py
class RECORD:
    """Handles record data.

    This is about some aspects of query and note records and
    organization and project records by which they are organized.
    And it is about word records.

    It is called by controllers that fetch sidebar material,
    see [M:RECORD.body][record.RECORD.body].
    """

    def __init__(self):
        pass

    def authWriteGeneric(self, label):
        auth = current.auth

        authorized = auth.user is not None
        errorMessage = f"You have no access to create/modify a {label}"
        return (authorized, "" if authorized else errorMessage)

    def authReadGeneric(self, label):
        authorized = True
        errorMessage = ""
        return (authorized, errorMessage)

    def authRead(self, mr, qw, iidRep):

        if mr == "m":
            return (True, "")
        if qw == "w":
            return (True, "")
        if qw == "n":
            if not iidRep:
                return (False, f"Not a valid note id: {iidRep}")
            return (True, "")
        if qw == "q":
            if iidRep is not None:
                (iid, keywords) = iDecode(qw, iidRep)
                if iid > 0:
                    Query = self.Query
                    return Query.authRead(iid)
            return (False, f"Not a valid query id: {iidRep}")
        return (None, f"Not a valid id: {iidRep}")

    def body(self):
        """Produce data that will get sidebar material later via AJAX.

        !!! caution "Web2py device"
            We use a
            [web2py device]({{web2pyComponents}})
            here that shields the mechanics of an AJAX call.
            That leads to code that is not very clear.

            These calls are hidden in Web2py javascript, and you will
            not find them in the SHEBANQ client app code.

        This is where the controllers
        [C:hebrew.sidewordbody][controllers.hebrew.sidewordbody],
        [C:hebrew.sidequerybody][controllers.hebrew.sidequerybody] and
        [C:hebrew.sidenotebody][controllers.hebrew.sidenotebody]
        will be used.

        These calls are used when the user requests a **record**
        page directly. The web2py device takes care that when the sidebar
        is editable, the edits will be submitted via AJAX.
        So far so good.

        However, when a user navigates between **material** pages and **record**
        pages, the pages are not served from scratch, and new sidebar material
        has to be fetched through AJAX as well.

        In the SHEBANQ code we do these AJAX calls explicitly
        client code: [{sidecontent.fetch}][sidecontentfetch].

        Since I was not able to use the Web2py approach for this part of the use case,
        it is probably a good idea ditch this usage of the web2py mechanism
        altogehter in favour fo the more explicit way, which is already in our code.
        Now we have two ways of doing the same thing!
        My apologies.
        """  # noqa E501

        Check = current.Check
        LOAD = current.LOAD

        iidRep = Check.field("material", "", "iid")
        vr = Check.field("material", "", "version")
        mr = Check.field("material", "", "mr")
        qw = Check.field("material", "", "qw")

        kind = "word" if qw == "w" else "query" if qw == "q" else "note"

        (authorized, msg) = self.authRead(mr, qw, iidRep)
        if authorized:
            msg = f"fetching {kind}"
        return dict(
            load=LOAD(
                "hebrew",
                f"side{kind}body",
                extension="load",
                vars=dict(mr="r", qw="w", version=vr, iid=iidRep),
                ajax=False,
                ajax_trap=True,
                target=f"{kind}body",
                content=msg,
            )
        )

    def setItem(self):
        """Saves a record to the database.

        Meant for: objects that organize the notes and query overviews:
        organizations and projects.
        Can also save query records.

        !!! hint "Note records and word records"
            Word records are readonly and will nver be saved.

            There are no such things as note records.
            Notes are organized in sets bases on keywords and authoring users,
            but a notes set does not have an embodiment as record in the database.
        """

        Check = current.Check
        auth = current.auth

        msgs = []
        orgRecord = {}
        projectRecord = {}
        good = False
        orgGood = False
        projectGood = False
        obj_id = None
        label = None
        table = None
        fields = None

        myId = auth.user.id if auth.user is not None else None

        requestVars = current.request.vars

        for x in [1]:
            tp = requestVars.tp
            if tp not in TPS:
                msgs.append(("error", f"unknown type {tp}!"))
                break
            (label, table) = TPS[tp]
            obj_id = Check.isId("obj_id", tp, label, msgs)
            upd = requestVars.upd
            if obj_id is None:
                break
            if upd not in {"true", "false"}:
                msgs.append(("error", f"invalid instruction {upd}!"))
                break
            upd = True if upd == "true" else False
            if upd and not myId:
                msgs.append(("error", "for updating you have to be logged in!"))
                break
            fields = ["name"]

            if tp == "q":
                Query = self.Query
                fields.append("organization")
                fields.append("project")
            else:
                fields.append("website")
            if upd:
                (authorized, msg) = (
                    Query.authWrite(obj_id)
                    if tp == "q"
                    else self.authWriteGeneric(label)
                )
            else:
                (authorized, msg) = (
                    Query.authRead(obj_id) if tp == "q" else self.authReadGeneric(label)
                )
            if not authorized:
                msgs.append(("error", msg))
                break
            if upd:
                if tp == "q":
                    subfields = ["name", "website"]
                    fieldValues = [requestVars.name]
                    doNewOrg = requestVars.doNewOrg
                    doNewProject = requestVars.doNewProject
                    if doNewOrg not in {"true", "false"}:
                        msgs.append(
                            (
                                "error",
                                f"invalid instruction for organization {doNewOrg}!",
                            )
                        )
                        break
                    doNewOrg = doNewOrg == "true"
                    if doNewProject not in {"true", "false"}:
                        msgs.append(
                            (
                                "error",
                                f"invalid instruction for project {doNewProject}!",
                            )
                        )
                        break
                    doNewProject = doNewProject == "true"
                    orgGood = True
                    if doNewOrg:
                        (orgGood, org_id) = self.update(
                            "o",
                            0,
                            myId,
                            subfields,
                            [requestVars.org_name, requestVars.org_website],
                            msgs,
                        )
                        if orgGood:
                            orgRecord = dict(
                                id=org_id,
                                name=requestVars.org_name,
                                website=requestVars.org_website,
                            )
                    else:
                        org_id = Check.isId("org_id", "o", TPS["o"][0], msgs)
                    projectGood = True
                    if doNewProject:
                        (projectGood, project_id) = self.update(
                            "p",
                            0,
                            myId,
                            subfields,
                            [requestVars.project_name, requestVars.project_website],
                            msgs,
                        )
                        if projectGood:
                            projectRecord = dict(
                                id=project_id,
                                name=requestVars.project_name,
                                website=requestVars.project_website,
                            )
                    else:
                        project_id = Check.isId("project_id", "p", TPS["o"][0], msgs)
                    if not orgGood or not projectGood:
                        break
                    if org_id is None or project_id is None:
                        break
                    fieldValues.extend([org_id, project_id])
                else:
                    fieldValues = [requestVars[field] for field in fields]

                (good, obj_idNew) = self.update(
                    tp, obj_id, myId, fields, fieldValues, msgs
                )
                if not good:
                    break
                obj_id = obj_idNew
            else:
                good = True

        record = self.make(tp, label, table, fields, obj_id, good, msgs)

        return dict(
            data=json.dumps(
                dict(
                    record=record,
                    orgRecord=orgRecord,
                    projectRecord=projectRecord,
                    msgs=msgs,
                    good=good,
                    orgGood=orgGood,
                    projectGood=projectGood,
                )
            )
        )

    def make(self, tp, label, table, fields, obj_id, good, msgs):
        db = current.db

        record = {}

        if good:
            dbRecord = None
            if tp == "q":
                if obj_id == 0:
                    dbRecord = [0, "", 0, "", "", 0, "", ""]
                else:
                    Query = self.Query
                    dbRecord = Query.getTreeInfo(obj_id)
            else:
                if obj_id == 0:
                    dbRecord = [0, "", ""]
                else:
                    dbRecord = db.executesql(
                        dedent(
                            f"""
                            select {",".join(fields)}
                            from {table}
                            where id = {obj_id}
                            ;
                            """
                        ),
                        as_dict=True,
                    )
            if dbRecord:
                record = dbRecord[0]
            else:
                msgs.append(("error", f"No {label} with id {obj_id}"))

        return record

    def update(self, tp, obj_id, myId, fields, fieldValues, msgs):
        Check = current.Check
        db = current.db

        fieldsUpd = {}
        good = False
        (label, table) = TPS[tp]
        useValues = {}
        for i in range(len(fields)):
            field = fields[i]
            value = fieldValues[i]
            useValues[field] = value

        now = current.request.utcnow

        for x in [1]:
            valSql = Check.isName(
                tp,
                obj_id,
                myId,
                useValues["name"],
                msgs,
            )
            if valSql is None:
                break
            fieldsUpd["name"] = valSql
            if tp == "q":
                val = Check.isId(
                    "org_id",
                    "o",
                    TPS["o"][0],
                    msgs,
                    valrep=str(useValues["organization"]),
                )
                if val is None:
                    break
                valSql = Check.isRel("o", val, msgs)
                if valSql is None:
                    break
                fieldsUpd["organization"] = valSql
                val = Check.isId(
                    "project_id",
                    "p",
                    TPS["p"][0],
                    msgs,
                    valrep=str(useValues["project"]),
                )
                valSql = Check.isRel("p", val, msgs)
                if valSql is None:
                    break
                fieldsUpd["project"] = valSql
                fld = "modified_on"
                fieldsUpd[fld] = now
                fields.append(fld)
                if obj_id == 0:
                    fld = "created_on"
                    fieldsUpd[fld] = now
                    fields.append(fld)
                    fld = "created_by"
                    fieldsUpd[fld] = myId
                    fields.append(fld)
            else:
                valSql = Check.isWebsite(tp, useValues["website"], msgs)
                if valSql is None:
                    break
                fieldsUpd["website"] = valSql
            good = True
        if good:
            if obj_id:
                fieldVals = [f" {f} = '{fieldsUpd[f]}'" for f in fields]
                sql = dedent(
                    f"""
                    update {table}
                    set{",".join(fieldVals)}
                    where id = {obj_id}
                    ;
                    """
                )
                thisMsg = "updated"
            else:
                fieldVals = [f"'{fieldsUpd[f]}'" for f in fields]
                sql = dedent(
                    f"""
                    insert into {table}
                    ({",".join(fields)})
                    values ({",".join(fieldVals)})
                    ;
                    """
                )
                thisMsg = f"{label} added"

            db.executesql(sql)

            db.commit()

            if obj_id == 0:
                obj_id = db.executesql(
                    dedent(
                        """
                    select last_insert_id() as x
                    ;
                    """
                    )
                )[0][0]

            msgs.append(("good", thisMsg))
        return (good, obj_id)

__init__(self) special

Source code in modules/record.py
def __init__(self):
    pass

authWriteGeneric(self, label)

Source code in modules/record.py
def authWriteGeneric(self, label):
    auth = current.auth

    authorized = auth.user is not None
    errorMessage = f"You have no access to create/modify a {label}"
    return (authorized, "" if authorized else errorMessage)

authReadGeneric(self, label)

Source code in modules/record.py
def authReadGeneric(self, label):
    authorized = True
    errorMessage = ""
    return (authorized, errorMessage)

authRead(self, mr, qw, iidRep)

Source code in modules/record.py
def authRead(self, mr, qw, iidRep):

    if mr == "m":
        return (True, "")
    if qw == "w":
        return (True, "")
    if qw == "n":
        if not iidRep:
            return (False, f"Not a valid note id: {iidRep}")
        return (True, "")
    if qw == "q":
        if iidRep is not None:
            (iid, keywords) = iDecode(qw, iidRep)
            if iid > 0:
                Query = self.Query
                return Query.authRead(iid)
        return (False, f"Not a valid query id: {iidRep}")
    return (None, f"Not a valid id: {iidRep}")

body(self)

Produce data that will get sidebar material later via AJAX.

Web2py device

We use a web2py device here that shields the mechanics of an AJAX call. That leads to code that is not very clear.

These calls are hidden in Web2py javascript, and you will not find them in the SHEBANQ client app code.

This is where the controllers C:hebrew.sidewordbody, C:hebrew.sidequerybody and C:hebrew.sidenotebody will be used.

These calls are used when the user requests a record page directly. The web2py device takes care that when the sidebar is editable, the edits will be submitted via AJAX. So far so good.

However, when a user navigates between material pages and record pages, the pages are not served from scratch, and new sidebar material has to be fetched through AJAX as well.

In the SHEBANQ code we do these AJAX calls explicitly client code: {sidecontent.fetch}.

Since I was not able to use the Web2py approach for this part of the use case, it is probably a good idea ditch this usage of the web2py mechanism altogehter in favour fo the more explicit way, which is already in our code. Now we have two ways of doing the same thing! My apologies.

Source code in modules/record.py
def body(self):
    """Produce data that will get sidebar material later via AJAX.

    !!! caution "Web2py device"
        We use a
        [web2py device]({{web2pyComponents}})
        here that shields the mechanics of an AJAX call.
        That leads to code that is not very clear.

        These calls are hidden in Web2py javascript, and you will
        not find them in the SHEBANQ client app code.

    This is where the controllers
    [C:hebrew.sidewordbody][controllers.hebrew.sidewordbody],
    [C:hebrew.sidequerybody][controllers.hebrew.sidequerybody] and
    [C:hebrew.sidenotebody][controllers.hebrew.sidenotebody]
    will be used.

    These calls are used when the user requests a **record**
    page directly. The web2py device takes care that when the sidebar
    is editable, the edits will be submitted via AJAX.
    So far so good.

    However, when a user navigates between **material** pages and **record**
    pages, the pages are not served from scratch, and new sidebar material
    has to be fetched through AJAX as well.

    In the SHEBANQ code we do these AJAX calls explicitly
    client code: [{sidecontent.fetch}][sidecontentfetch].

    Since I was not able to use the Web2py approach for this part of the use case,
    it is probably a good idea ditch this usage of the web2py mechanism
    altogehter in favour fo the more explicit way, which is already in our code.
    Now we have two ways of doing the same thing!
    My apologies.
    """  # noqa E501

    Check = current.Check
    LOAD = current.LOAD

    iidRep = Check.field("material", "", "iid")
    vr = Check.field("material", "", "version")
    mr = Check.field("material", "", "mr")
    qw = Check.field("material", "", "qw")

    kind = "word" if qw == "w" else "query" if qw == "q" else "note"

    (authorized, msg) = self.authRead(mr, qw, iidRep)
    if authorized:
        msg = f"fetching {kind}"
    return dict(
        load=LOAD(
            "hebrew",
            f"side{kind}body",
            extension="load",
            vars=dict(mr="r", qw="w", version=vr, iid=iidRep),
            ajax=False,
            ajax_trap=True,
            target=f"{kind}body",
            content=msg,
        )
    )

setItem(self)

Saves a record to the database.

Meant for: objects that organize the notes and query overviews: organizations and projects. Can also save query records.

Note records and word records

Word records are readonly and will nver be saved.

There are no such things as note records. Notes are organized in sets bases on keywords and authoring users, but a notes set does not have an embodiment as record in the database.

Source code in modules/record.py
def setItem(self):
    """Saves a record to the database.

    Meant for: objects that organize the notes and query overviews:
    organizations and projects.
    Can also save query records.

    !!! hint "Note records and word records"
        Word records are readonly and will nver be saved.

        There are no such things as note records.
        Notes are organized in sets bases on keywords and authoring users,
        but a notes set does not have an embodiment as record in the database.
    """

    Check = current.Check
    auth = current.auth

    msgs = []
    orgRecord = {}
    projectRecord = {}
    good = False
    orgGood = False
    projectGood = False
    obj_id = None
    label = None
    table = None
    fields = None

    myId = auth.user.id if auth.user is not None else None

    requestVars = current.request.vars

    for x in [1]:
        tp = requestVars.tp
        if tp not in TPS:
            msgs.append(("error", f"unknown type {tp}!"))
            break
        (label, table) = TPS[tp]
        obj_id = Check.isId("obj_id", tp, label, msgs)
        upd = requestVars.upd
        if obj_id is None:
            break
        if upd not in {"true", "false"}:
            msgs.append(("error", f"invalid instruction {upd}!"))
            break
        upd = True if upd == "true" else False
        if upd and not myId:
            msgs.append(("error", "for updating you have to be logged in!"))
            break
        fields = ["name"]

        if tp == "q":
            Query = self.Query
            fields.append("organization")
            fields.append("project")
        else:
            fields.append("website")
        if upd:
            (authorized, msg) = (
                Query.authWrite(obj_id)
                if tp == "q"
                else self.authWriteGeneric(label)
            )
        else:
            (authorized, msg) = (
                Query.authRead(obj_id) if tp == "q" else self.authReadGeneric(label)
            )
        if not authorized:
            msgs.append(("error", msg))
            break
        if upd:
            if tp == "q":
                subfields = ["name", "website"]
                fieldValues = [requestVars.name]
                doNewOrg = requestVars.doNewOrg
                doNewProject = requestVars.doNewProject
                if doNewOrg not in {"true", "false"}:
                    msgs.append(
                        (
                            "error",
                            f"invalid instruction for organization {doNewOrg}!",
                        )
                    )
                    break
                doNewOrg = doNewOrg == "true"
                if doNewProject not in {"true", "false"}:
                    msgs.append(
                        (
                            "error",
                            f"invalid instruction for project {doNewProject}!",
                        )
                    )
                    break
                doNewProject = doNewProject == "true"
                orgGood = True
                if doNewOrg:
                    (orgGood, org_id) = self.update(
                        "o",
                        0,
                        myId,
                        subfields,
                        [requestVars.org_name, requestVars.org_website],
                        msgs,
                    )
                    if orgGood:
                        orgRecord = dict(
                            id=org_id,
                            name=requestVars.org_name,
                            website=requestVars.org_website,
                        )
                else:
                    org_id = Check.isId("org_id", "o", TPS["o"][0], msgs)
                projectGood = True
                if doNewProject:
                    (projectGood, project_id) = self.update(
                        "p",
                        0,
                        myId,
                        subfields,
                        [requestVars.project_name, requestVars.project_website],
                        msgs,
                    )
                    if projectGood:
                        projectRecord = dict(
                            id=project_id,
                            name=requestVars.project_name,
                            website=requestVars.project_website,
                        )
                else:
                    project_id = Check.isId("project_id", "p", TPS["o"][0], msgs)
                if not orgGood or not projectGood:
                    break
                if org_id is None or project_id is None:
                    break
                fieldValues.extend([org_id, project_id])
            else:
                fieldValues = [requestVars[field] for field in fields]

            (good, obj_idNew) = self.update(
                tp, obj_id, myId, fields, fieldValues, msgs
            )
            if not good:
                break
            obj_id = obj_idNew
        else:
            good = True

    record = self.make(tp, label, table, fields, obj_id, good, msgs)

    return dict(
        data=json.dumps(
            dict(
                record=record,
                orgRecord=orgRecord,
                projectRecord=projectRecord,
                msgs=msgs,
                good=good,
                orgGood=orgGood,
                projectGood=projectGood,
            )
        )
    )

make(self, tp, label, table, fields, obj_id, good, msgs)

Source code in modules/record.py
def make(self, tp, label, table, fields, obj_id, good, msgs):
    db = current.db

    record = {}

    if good:
        dbRecord = None
        if tp == "q":
            if obj_id == 0:
                dbRecord = [0, "", 0, "", "", 0, "", ""]
            else:
                Query = self.Query
                dbRecord = Query.getTreeInfo(obj_id)
        else:
            if obj_id == 0:
                dbRecord = [0, "", ""]
            else:
                dbRecord = db.executesql(
                    dedent(
                        f"""
                        select {",".join(fields)}
                        from {table}
                        where id = {obj_id}
                        ;
                        """
                    ),
                    as_dict=True,
                )
        if dbRecord:
            record = dbRecord[0]
        else:
            msgs.append(("error", f"No {label} with id {obj_id}"))

    return record

update(self, tp, obj_id, myId, fields, fieldValues, msgs)

Source code in modules/record.py
def update(self, tp, obj_id, myId, fields, fieldValues, msgs):
    Check = current.Check
    db = current.db

    fieldsUpd = {}
    good = False
    (label, table) = TPS[tp]
    useValues = {}
    for i in range(len(fields)):
        field = fields[i]
        value = fieldValues[i]
        useValues[field] = value

    now = current.request.utcnow

    for x in [1]:
        valSql = Check.isName(
            tp,
            obj_id,
            myId,
            useValues["name"],
            msgs,
        )
        if valSql is None:
            break
        fieldsUpd["name"] = valSql
        if tp == "q":
            val = Check.isId(
                "org_id",
                "o",
                TPS["o"][0],
                msgs,
                valrep=str(useValues["organization"]),
            )
            if val is None:
                break
            valSql = Check.isRel("o", val, msgs)
            if valSql is None:
                break
            fieldsUpd["organization"] = valSql
            val = Check.isId(
                "project_id",
                "p",
                TPS["p"][0],
                msgs,
                valrep=str(useValues["project"]),
            )
            valSql = Check.isRel("p", val, msgs)
            if valSql is None:
                break
            fieldsUpd["project"] = valSql
            fld = "modified_on"
            fieldsUpd[fld] = now
            fields.append(fld)
            if obj_id == 0:
                fld = "created_on"
                fieldsUpd[fld] = now
                fields.append(fld)
                fld = "created_by"
                fieldsUpd[fld] = myId
                fields.append(fld)
        else:
            valSql = Check.isWebsite(tp, useValues["website"], msgs)
            if valSql is None:
                break
            fieldsUpd["website"] = valSql
        good = True
    if good:
        if obj_id:
            fieldVals = [f" {f} = '{fieldsUpd[f]}'" for f in fields]
            sql = dedent(
                f"""
                update {table}
                set{",".join(fieldVals)}
                where id = {obj_id}
                ;
                """
            )
            thisMsg = "updated"
        else:
            fieldVals = [f"'{fieldsUpd[f]}'" for f in fields]
            sql = dedent(
                f"""
                insert into {table}
                ({",".join(fields)})
                values ({",".join(fieldVals)})
                ;
                """
            )
            thisMsg = f"{label} added"

        db.executesql(sql)

        db.commit()

        if obj_id == 0:
            obj_id = db.executesql(
                dedent(
                    """
                select last_insert_id() as x
                ;
                """
                )
            )[0][0]

        msgs.append(("good", thisMsg))
    return (good, obj_id)

RECORDQUERY (RECORD)

Source code in modules/record.py
class RECORDQUERY(RECORD):
    def __init__(self, Query):
        super().__init__()
        self.Query = Query

__init__(self, Query) special

Source code in modules/record.py
def __init__(self, Query):
    super().__init__()
    self.Query = Query