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)