25from pprint
import pprint
29from flask
import Response, send_file
86 """Handles card management including setting child cards and problems.
93 d = json.loads(g.request.get_data())
95 command =
StrV(d,
'command')
97 if command ==
'search':
98 g.db.RequireGroup([
'teachers',
'students'], g.T.cantview)
100 keyword =
StrV(d,
'keyword')
101 title =
StrV(d,
'title')
102 content =
StrV(d,
'content')
103 flags =
IntV(d,
'flags')
104 cardtype =
StrV(d,
'type')
105 schoolid =
StrV(d,
'schoolid')
106 classid =
StrV(d,
'classid')
107 start =
IntV(d,
'start', 0, 99999999, 0)
108 limit =
IntV(d,
'limit', 10, 1000, 100)
114 escapedkeyword =
EscapeSQL(
'%' + keyword +
'%')
116 conds.append(
"title LIKE ? OR description LIKE ? OR content LIKE ?")
117 params.append(escapedkeyword)
118 params.append(escapedkeyword)
119 params.append(escapedkeyword)
122 conds.append(
"title LIKE ?")
123 params.append(
EscapeSQL(
'%' + title +
'%'))
126 conds.append(
"content LIKE ?")
127 params.append(
EscapeSQL(
'%' + content +
'%'))
130 conds.append(
"flags & ?")
134 if cardtype ==
'course':
135 conds.append(
"flags & ?")
136 params.append(CARD_COURSE)
139 conds =
"WHERE " + (
" AND ").join(conds)
145 if schoolid
or classid:
147 c = g.db.Load(learn_schoolclass,
FromShortCode(classid), fields =
'rid')
149 c = g.db.Load(learn_school,
FromShortCode(schoolid), fields =
'rid')
151 for r
in g.db.Linked(c,
153 fields =
'rid, image, sound, video, title, description',
157 chosencards.append(r.ToJSON(
'rid, image, sound, video, title, description, content'))
167 orderby =
'title, content',
168 fields =
'rid, image, sound, video, title, description, content'
172 cardobjs.append(r.ToJSON(
'rid, image, sound, video, title, description, content'))
174 results[
'data'] = cardobjs
175 results[
'count'] = len(cards)
176 results[
'chosen'] = chosencards
177 elif command ==
'linked':
178 g.db.RequireGroup([
'teachers',
'students'], g.T.cantview)
180 cardidcode =
StrV(d,
'cardidcode')
181 schoolid =
StrV(d,
'schoolid')
182 classid =
StrV(d,
'classid')
183 studentid =
StrV(d,
'studentid')
184 linktype =
IntV(d,
'linktype')
191 if (g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid)
192 or g.db.HasLink(learn_schoolclass, classid, learn_student, g.session.userid)
194 c = g.db.Load(learn_schoolclass, classid,
'rid')
196 raise Exception(g.T.cantview)
200 if g.db.HasLink(learn_school, schoolid, learn_schooladministrator, g.session.userid):
201 c = g.db.Load(learn_school, schoolid,
'rid')
203 raise Exception(g.T.cantview)
206 c = g.db.Load(learn_student, g.session.userid,
'userid')
208 raise Exception(g.T.cantview)
212 linktype = CARD_CHILD
214 c = g.db.Load(learn_teacher, g.session.userid,
'userid')
218 for r
in g.db.Linked(c, learn_card, linktype):
219 ls.append(r.ToJSON())
222 results[
'count'] = len(ls)
223 elif command ==
'save':
224 g.db.RequireGroup([
'teachers'], g.T.cantchange)
226 data =
DictV(d,
'data')
227 idcode =
StrV(data,
'idcode')
230 raise Exception(g.T.missingparams)
232 data[
'cardtype'] =
IntV(data,
'cardtype')
233 data[
'title'] =
StrV(data,
'title')
234 data[
'description'] =
StrV(data,
'description')
235 data[
'image'] =
StrV(data,
'image')
236 data[
'sound'] =
StrV(data,
'sound')
237 data[
'video'] =
StrV(data,
'video')
238 data[
'markdown'] =
StrV(data,
'markdown')
241 data[
'html'] = markdown.markdown(data[
'markdown'])
243 data[
'html'] =
StrV(data,
'html')
253 if IntV(data,
'flags') & CARD_PROBLEM
and not data[
'markdown']
and not data[
'html']
and not data[
'image']
and not data[
'sound']
and not data[
'video']:
268 data[
'ownerid'] = card.ownerid
if card.ownerid
else g.session.userid
269 data[
'editors'] = card.editors
if card.editors
else []
278 if g.session.userid != card.ownerid
and currentusercode
not in card.editors:
279 card.ownerid = g.session.userid
285 results[
'data'] = card.ToJSON()
286 elif command ==
'setchildcards':
287 teacher = g.db.Load(learn_teacher, g.session.userid, fields =
'userid, allowedcards')
289 cardid =
StrV(d,
'cardid')
290 childcardids =
ArrayV(d,
'childcardids')
293 raise Exception(g.T.missingparams)
295 if cardid
not in teacher.allowedcards:
296 raise Exception(g.T.cantchange)
298 card = g.db.Load(learn_card,
FromShortCode(cardid),
'rid, flags')
300 if card.flags & CARD_LESSON:
301 raise Exception(g.T.lessonscanthavechildcards)
306 for cid
in childcardids:
307 childcard = g.db.Load(learn_card,
FromShortCode(cid),
'rid, image, title, description')
308 cardobjs.append(childcard)
310 o = childcard.ToJSON(
'image, title, description')
315 g.db.LinkArray(card, cardobjs, CARD_CHILD)
318 allowedcards = teacher.allowedcards + childcardids
321 g.db.Unlink(card, learn_card, CARD_CHILD)
324 UpdateAllowedCards(g, teacher)
326 for c
in g.db.Linked(teacher, learn_schoolclass,
'userid'):
327 for student
in g.db.Linked(c, learn_student,
'userid, allowedcards'):
328 if cardid
in student.allowedcards:
329 UpdateAllowedCards(g, student)
333 results[
'data'] = jsonobjs
334 elif command ==
'setproblems':
335 teacher = g.db.Load(learn_teacher, g.session.userid, fields =
'userid, allowedcards')
337 cardid =
StrV(d,
'cardid')
338 problemids =
ArrayV(d,
'problemids')
341 raise Exception(g.T.missingparams)
343 if cardid
not in teacher.allowedcards:
344 raise Exception(g.T.cantchange)
346 card = g.db.Load(learn_card,
FromShortCode(cardid),
'rid, flags')
348 if not (card.flags & CARD_LESSON):
349 raise Exception(g.T.onlylessonscancontainproblems)
353 for cid
in problemids:
354 problem = g.db.Load(learn_problem,
FromShortCode(cid),
'rid')
355 problemobjs.append(problem)
358 g.db.LinkArray(card, problemobjs, CARD_LINK_PROBLEM)
360 g.db.Unlink(card, learn_problem, CARD_LINK_PROBLEM)
364 results[
'data'] = GetAllProblems(g, card.rid, CARD_LINK_PROBLEM)
365 elif command ==
'delete':
366 idcodes =
ArrayV(d,
'ids')
368 cards = g.db.LoadMany(learn_card, [
FromShortCode(x)
for x
in idcodes],
'rid, ownerid, editors')
371 if 'admins' in g.session[
'groups']
or g.session.userid == c.ownerid
or ToShortCode(g.session.userid)
in c.editors:
376 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
377 except Exception
as e:
378 traceback.print_exc()
379 results[
'error'] = str(e)
385 """Manages problems, which are essentially a collection of cards.
387 Note that you need to be a teacher in order to create
or change
388 problems. Normal students can only view problems.
393 d = json.loads(g.request.get_data())
395 command =
StrV(d,
'command')
397 if command ==
'search':
398 g.db.RequireGroup([
'teachers'], g.T.cantview)
400 keyword =
StrV(d,
'keyword')
401 flags =
IntV(d,
'flags')
402 start =
IntV(d,
'start', 0, 99999999, 0)
403 limit =
IntV(d,
'limit', 10, 1000, 100)
409 escapedkeyword =
EscapeSQL(
'%' + keyword +
'%')
411 conds.append(
"content LIKE ?")
412 params.append(escapedkeyword)
415 conds.append(
"flags & ?")
419 conds =
"WHERE " + (
" AND ").join(conds)
433 orderby =
'title, content',
434 fields =
'rid, image, sound, video, content',
436 cards[r.rid] = r.ToJSON(
'rid, image, sound, video, content')
439 cardids = list(cards.keys())
441 questionmarks = [
'?' for x
in cardids]
445 questionmarks =
', '.join(questionmarks)
447 problems = g.db.Find(
449 'WHERE problemid IN (' + questionmarks
450 +
') OR answerid IN (' + questionmarks +
')',
456 problemcount = len(problems)
459 additionalcardids = []
462 if r.problemid
and not cards.get(r.problemid):
463 additionalcardids.append(r.problemid)
465 if r.answerid
and not cards.get(r.answerid):
466 additionalcardids.append(r.answerid)
468 for r
in g.db.LoadMany(
471 'rid, image, sound, video, content'
473 cards[r.rid] = r.ToJSON(
'rid, image, sound, video, content')
478 if not p.problemid
in cards
or not p.answerid
in cards:
481 problemobj = cards[p.problemid]
484 answerobj = cards[p.answerid]
487 o[
'problem'] = problemobj
488 o[
'answer'] = answerobj
491 problemobjs.append(o)
493 results[
'data'] = problemobjs
494 results[
'count'] = problemcount
495 elif command ==
'save':
496 g.db.RequireGroup([
'teachers'], g.T.cantchange)
498 data =
DictV(d,
'data')
499 idcode =
StrV(data,
'idcode')
502 raise Exception(g.T.missingparams)
508 p.Set(ownerid = g.session.userid)
511 for r
in PROBLEM_CARDFIELDS:
519 data[
'timelimit'] =
IntV(data,
'timelimit')
if IntV(data,
'timelimit')
else 0
520 data[
'points'] =
IntV(data,
'points')
if IntV(data,
'points')
else 0
521 data[
'flags'] =
IntV(data,
'flags')
if IntV(data,
'flags')
else 0
523 if not data[
'problemid']
or not data[
'answerid']:
524 raise Exception(g.T.missingparams)
531 if g.session.userid != p.ownerid
and currentusercode
not in p.editors
and not p.HasSameValues(data):
532 data[
'ownerid'] = g.session.userid
544 for r
in PROBLEM_CARDFIELDS:
545 v = getattr(p, r,
None)
552 results[
'data'] = objjson
553 elif command ==
'delete':
554 idcodes =
ArrayV(d,
'ids')
556 problems = g.db.LoadMany(learn_problem, [
FromShortCode(x)
for x
in idcodes],
'rid, ownerid, editors')
559 if 'admins' in g.session[
'groups']
or g.session.userid == c.ownerid
or ToShortCode(g.session.userid)
in c.editors:
562 raise Exception(g.T.cantdelete)
565 elif command ==
'cardproblems':
570 allowedcards = g.db.Load(learn_teacher, g.session.userid, fields =
'allowedcards').allowedcards
572 allowedcards.extend(g.db.Load(learn_student, g.session.userid, fields =
'allowedcards').allowedcards)
573 except Exception
as e:
574 allowedcards = g.db.Load(learn_student, g.session.userid, fields =
'allowedcards').allowedcards
576 cardidcodes =
ArrayV(d,
'cardidcodes')
578 for r
in cardidcodes:
579 if r
not in allowedcards:
580 raise Exception(g.T.cantview)
582 problemtype =
IntV(d,
'problemtype', 0, 99999999, CARD_LINK_PROBLEM)
586 results[
'data'] = GetAllProblems(g, cardids, problemtype)
588 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
589 except Exception
as e:
590 traceback.print_exc()
591 results[
'error'] = str(e)
597 """Used to view and submit answers for a class, which can be viewed
598 in the teacher
's classroom web interface. Functionality for putting
599 students into groups, purchasing items, or setting prompts
for the
600 class are also included.
608 d = json.loads(g.request.get_data())
610 command =
StrV(d,
'command')
612 if command ==
'changescores':
613 classid =
StrV(d,
'classid')
614 students =
ArrayV(d,
'students')
616 if not classid
or not students:
617 raise Exception(g.T.missingparams)
619 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
620 raise Exception(g.T.cantchange)
624 'WHERE userid IN (' +
', '.join([
'?' for x
in students]) +
')',
629 student = [x
for x
in students
if x[
'idcode'] == idcode][0]
631 right =
IntV(student,
'right')
632 wrong =
IntV(student,
'wrong')
633 strikes =
IntV(student,
'strikes')
634 bonus =
IntV(student,
'bonus')
636 if not right
and not wrong
and not strikes
and not bonus:
637 raise Exception(T.missingparams)
640 r.Set(numright = r.numright + right)
643 r.Set(numwrong = r.numwrong + wrong)
646 r.Set(numstrikes = r.numstrikes + strikes)
649 r.Set(bonusscore = r.bonusscore + bonus)
655 elif command ==
'clearscoreadded':
656 classid =
StrV(d,
'classid')
659 raise Exception(g.T.missingparams)
661 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
662 raise Exception(g.T.cantchange)
664 g.db.Execute(
'UPDATE learn_answer SET scoreadded = 0 WHERE classid = ?',
FromShortCode(classid))
666 elif command ==
'save':
667 classid =
StrV(d,
'classid')
670 raise Exception(g.T.missingparams)
674 if (
not g.db.HasLink(learn_schoolclass, classid, learn_student, g.session.userid)
675 and not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid)
677 raise Exception(g.T.cantchange)
679 answer =
StrV(d,
'answer').strip()[:4096]
683 'WHERE userid = ? and classid = ?',
684 [g.session.userid, classid],
692 u = g.db.Load(learn_student, g.session.userid)
694 flags = ANSWER_ENABLE_COPY_PASTE
696 if 'teachers' in g.session[
'groups']:
697 flags |= ANSWER_TEACHER
700 displayname = u.displayname,
703 userid = g.session.userid,
705 o.AddHomeworkScores(g)
707 if not o.flags & ANSWER_LOCKED:
710 userid = g.session.userid,
716 elif command ==
'saveimage':
717 classid =
StrV(d,
'classid')
718 dataurl =
StrV(d,
'dataurl')
720 if not classid
or not dataurl:
721 raise Exception(g.T.missingparams)
725 if (
not g.db.HasLink(learn_schoolclass, classid, learn_student, g.session.userid)
726 and not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid)
728 raise Exception(g.T.cantchange)
732 imageformat, imagedata = dataurl.split(
';base64,')
734 imagedata = base64.b64decode(imagedata)
736 imagefilebase =
'static/learn/drawings/' +
CodeDir(useridcode)
737 jpgfile = imagefilebase +
'.jpg'
738 webpfile = imagefilebase +
'.webp'
740 os.makedirs(os.path.split(imagefilebase)[0], exist_ok =
True)
742 with open(jpgfile,
'wb')
as f:
745 subprocess.call([
'convert', jpgfile,
'-quality',
'70',
'-resize',
'640x640>', webpfile])
748 elif command ==
'get':
749 classid =
StrV(d,
'classid')
752 raise Exception(g.T.missingparams)
756 'WHERE userid = ? and classid = ?',
761 results[
'data'] = o[0].answer
764 elif command ==
'list':
765 classid =
StrV(d,
'classid')
768 raise Exception(g.T.missingparams)
770 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
771 raise Exception(g.T.cantchange)
775 for r
in g.db.Find(learn_answer,
'WHERE classid = ?',
FromShortCode(classid)):
776 ls.append(r.ToJSON())
779 elif command ==
'newclass':
780 classid =
StrV(d,
'classid')
783 raise Exception(g.T.missingparams)
785 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
786 raise Exception(g.T.cantchange)
788 g.db.Delete(learn_answer,
'WHERE classid = ?',
FromShortCode(classid))
790 elif command ==
'setgroup':
791 classid =
StrV(d,
'classid')
794 raise Exception(g.T.missingparams)
796 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
797 raise Exception(g.T.cantchange)
799 idcode =
StrV(d,
'id')
800 groupnum =
IntV(d,
'groupnum')
804 'WHERE userid = ? AND classid = ?',
811 o.Set(groupnum = groupnum)
816 elif command ==
'setgroups':
817 classid =
StrV(d,
'classid')
820 raise Exception(g.T.missingparams)
822 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
823 raise Exception(g.T.cantchange)
825 students =
DictV(d,
'students')
829 'WHERE classid = ? AND userid IN (' +
', '.join([
'?' for x
in students.keys()]) +
')',
838 elif command ==
'cleargroups':
839 classid =
StrV(d,
'classid')
842 raise Exception(g.T.missingparams)
844 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
845 raise Exception(g.T.cantchange)
847 g.db.Execute(
'UPDATE learn_answer SET groupnum = 0 WHERE classid = ?',
FromShortCode(classid))
849 elif command ==
'resetscores':
850 classid =
StrV(d,
'classid')
853 raise Exception(g.T.missingparams)
855 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
856 raise Exception(g.T.cantchange)
872 elif command ==
'getprompt':
873 classid =
StrV(d,
'classid')
876 raise Exception(g.T.missingparams)
880 if (
not g.db.HasLink(learn_schoolclass, classid, learn_student, g.session.userid)
881 and not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid)
883 raise Exception(g.T.cantview)
887 'WHERE classid = ? AND userid = ?',
888 [classid, g.session.userid],
894 results[
'prompt'] = o.prompt
895 results[
'promptdata'] = o.promptdata
896 results[
'coderesult'] = o.coderesult
897 results[
'items'] = o.items
898 results[
'score'] = o.score
899 results[
'flags'] = o.flags
905 if g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
907 displayname = g.db.Load(learn_teacher, g.session.userid,
'displayname').displayname,
908 flags = ANSWER_TEACHER | ANSWER_ENABLE_COPY_PASTE,
912 displayname = g.db.Load(learn_student, g.session.userid,
'displayname').displayname
917 userid = g.session.userid,
920 o.AddHomeworkScores(g)
925 results[
'prompt'] =
''
926 results[
'promptdata'] = {}
927 results[
'coderesult'] =
''
928 results[
'items'] = {}
932 results[
'challenges'] = GetCurrentChallenges(g, classid, CHALLENGE_CLASSWORK)
933 elif command ==
'buyitem':
934 classid =
StrV(d,
'classid')
937 raise Exception(g.T.missingparams)
939 if (
not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_student, g.session.userid)
940 and not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid)
942 raise Exception(g.T.cantview)
944 itemname =
StrV(d,
'data')
945 quantity =
IntV(d,
'quantity')
947 if itemname
not in PRICES.keys():
948 raise Exception(g.T.nothingfound)
950 price = PRICES[itemname]
954 'WHERE classid = ? AND userid = ?',
963 total = price * quantity
966 raise Exception(g.T.notenoughmoney)
971 if itemname
not in o.items:
972 o.items[itemname] = quantity
974 o.items[itemname] += quantity
976 if o.bonusscore > total:
978 bonusscore = o.bonusscore - total,
983 numright = o.numright - total,
987 o.Set(score = o.score - total)
991 g.db.RequireGroup(
'teachers', g.T.cantchange)
993 o = g.db.Find(learn_answer,
'WHERE userid = ?',
FromShortCode(
StrV(d,
'useridcode')))
996 raise Exception(g.T.nothingfound)
1000 if itemname
not in o.items
or o.items[itemname] + quantity < 0:
1001 raise Exception(g.T.nothingfound)
1003 o.items[itemname] += quantity
1004 o.Set(items = o.items)
1009 results[
'prompt'] = o.prompt
1010 results[
'promptdata'] = o.promptdata
1011 results[
'coderesult'] = o.coderesult
1012 results[
'items'] = o.items
1013 results[
'score'] = o.score
1015 results[
'prompt'] =
''
1016 results[
'promptdata'] = {}
1017 results[
'coderesult'] =
''
1018 results[
'items'] = {}
1019 results[
'score'] = 0
1020 elif command ==
'setprompt':
1021 classid =
StrV(d,
'classid')
1024 raise Exception(g.T.missingparams)
1026 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
1027 raise Exception(g.T.cantchange)
1029 prompt =
StrV(d,
'prompt')
1030 promptdata =
DictV(d,
'promptdata')
1050 elif command ==
'setcoderesult':
1051 classid =
StrV(d,
'classid')
1054 raise Exception(g.T.missingparams)
1056 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
1057 raise Exception(g.T.cantchange)
1059 studentid =
StrV(d,
'studentid')
1060 coderesult =
DictV(d,
'coderesult')
1063 raise Exception(T.missingparams)
1068 WHERE userid = ? AND classid = ?
1071 json.dumps(coderesult),
1078 elif command ==
'setflags':
1079 classid =
StrV(d,
'classid')
1082 raise Exception(g.T.missingparams)
1084 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
1085 raise Exception(g.T.cantchange)
1087 studentid =
StrV(d,
'studentid')
1088 flags =
IntV(d,
'flags')
1091 raise Exception(T.missingparams)
1096 WHERE userid = ? AND classid = ?
1106 elif command ==
'setpoints':
1107 challengeid =
StrV(d,
'challengeid')
1108 studentid =
StrV(d,
'studentid')
1109 points =
IntV(d,
'points')
1112 raise Exception(g.T.missingparams)
1116 challengeobj = g.db.Load(learn_challenge, challengeid)
1118 if not g.db.HasLink(learn_schoolclass, challengeobj.classid, learn_teacher, g.session.userid):
1119 raise Exception(g.T.cantchange)
1121 if studentid
not in challengeobj.results:
1122 studentobj = g.db.Load(learn_student,
FromShortCode(studentid))
1123 challengeobj.results[studentid] = [studentobj.displayname, 0, 0, 0, 0]
1125 challengeobj.results[studentid][4] = points
1127 challengeobj.UpdateFields(
'results')
1129 g.db.Update(challengeobj)
1131 cls = g.db.Load(learn_schoolclass, challengeobj.classid)
1136 elif command ==
'delete':
1137 classid =
StrV(d,
'classid')
1138 studentid =
StrV(d,
'studentid')
1140 if not classid
or not studentid:
1141 raise Exception(g.T.missingparams)
1146 if not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
1147 raise Exception(g.T.cantchange)
1150 DELETE FROM learn_answer
1151 WHERE userid = ? AND classid = ?
1161 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
1162 except Exception
as e:
1163 traceback.print_exc()
1164 results[
'error'] = str(e)
1170 """Used to create and save challenge results. Note that only teachers
1171 can create challenges, and students can only save challenges which
1172 have already been started
in /learn/challenges.html.
1174 Also allows the user to view activity logs
and statistics.
1182 d = json.loads(g.request.get_data())
1184 command =
StrV(d,
'command')
1186 if command ==
'getgrades':
1187 classid =
StrV(d,
'classid')
1190 raise Exception(g.T.missingparams)
1194 isstudent = g.db.HasLink(learn_schoolclass, classid, learn_student, g.session.userid)
1197 if not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
1198 raise Exception(g.T.cantview)
1204 'WHERE classid = ?',
1206 orderby =
'finalscore DESC, classpercent DESC, reviewpercent DESC, persistencepercent DESC'
1209 elif command ==
'getactivitylog' or command ==
'getchallengeresults':
1210 classid =
StrV(d,
'classid')
1211 studentid =
StrV(d,
'studentid')
1212 challengetype =
IntV(d,
'challengetype')
1213 starttime = dateutil.parser.parse(
StrV(d,
'starttime')).timestamp()
1214 endtime = dateutil.parser.parse(
StrV(d,
'endtime')).timestamp()
1216 if not studentid
and not classid:
1217 raise Exception(g.T.missingparams)
1222 isstudent = g.db.HasLink(learn_schoolclass, classid, learn_student, g.session.userid)
1225 if not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
1226 raise Exception(g.T.cantview)
1228 if not challengetype:
1229 challengetype = CHALLENGE_HOMEWORK
1231 if command ==
'getchallengeresults':
1236 'WHERE classid = ? AND starttime >= ? AND starttime <= ? AND challengetype = ?',
1243 orderby =
'endtime DESC',
1244 fields =
'rid, title, results',
1246 homeworks.append(r.ToJSON(
'rid, title, results'))
1248 results[
'data'] = homeworks
1251 conds = [
'learn_challengeresult.classid = ?',
'learn_challengeresult.starttime >= ?',
'learn_challengeresult.starttime <= ?']
1252 params = [classid, starttime, endtime]
1255 conds.append(
'userid = ?')
1256 params.append(studentid)
1259 conds.append(
'challengetype = ?')
1260 params.append(challengetype)
1262 entries = g.db.Find(
1263 learn_challengeresult,
1264 '''JOIN learn_challenge ON learn_challengeresult.challengeid = learn_challenge.rid
1265 WHERE ''' + ' AND '.join(conds),
1267 orderby = 'starttime DESC',
1268 fields =
'learn_challengeresult.rid AS rid, learn_challengeresult.userid AS userid, learn_challengeresult.starttime AS starttime, learn_challenge.challenge AS challenge, learn_challenge.lessonids AS lessonids, score, percentright'
1276 'starttime': r.starttime,
1277 'challenge': r.challenge,
1279 'lessonids': r.lessonids,
1281 'percentright': r.percentright,
1286 for r
in logresults:
1288 for s
in r[
'lessonids']:
1292 lessontitles = GetCardTitles(g, list(lessonids))
1294 for r
in logresults:
1298 for s
in r[
'lessonids']:
1301 r[
'lessons'] =
', '.join(lessonnames)
1303 results[
'data'] = logresults
1304 elif command ==
'search':
1305 keyword =
StrV(d,
'keyword')
1306 challenge =
StrV(d,
'challenge', keyword)
1307 title =
StrV(d,
'title', keyword)
1308 lessons =
StrV(d,
'lessons', keyword)
1309 classid =
StrV(d,
'classid')
1310 start =
IntV(d,
'start', 0, 99999999, 0)
1311 limit =
IntV(d,
'limit', 10, 1000, 100)
1314 raise Exception(g.T.missingparams)
1318 if not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
1319 raise Exception(g.T.cantview)
1325 conds.append(
"challenge LIKE ?")
1326 params.append(
EscapeSQL(
'%' + challenge +
'%'))
1329 conds.append(
"title LIKE ?")
1330 params.append(
EscapeSQL(
'%' + json.dumps(title) +
'%'))
1333 conds.append(
"lessontitles LIKE ?")
1334 params.append(
EscapeSQL(
'%' + lessons+
'%'))
1337 conds =
"WHERE classid = ? AND (" + (
" OR ").join(conds) +
')'
1339 conds =
"WHERE classid = ? "
1341 challenges = g.db.Find(
1347 orderby =
'starttime DESC',
1348 fields =
'rid, starttime, endtime, challenge, title, instructions, question, image, sound, video, files, lessonids, problemids, averagescore, averageright, averagepercent, percenttried, percentcomplete, minscore, minpercentage, numpoints, didnttrypenalty, numtries, numproblems, timelimit, challengetype, flags'
1353 timeoffset = int(g.request.cookies.get(
'timeoffset'))
1355 for r
in challenges:
1356 r.OffsetTime(-timeoffset * 60)
1366 for k, v
in GetCardTitles(g, [
FromShortCode(x)
for x
in o[
'lessonids']]).items()
1372 o[
'problems'] = GetAllProblems(g, [], CARD_LINK_PROBLEM, [
FromShortCode(x)
for x
in o[
'problemids']])
1376 challengeobjs.append(o)
1378 results[
'data'] = challengeobjs
1379 results[
'count'] = len(challenges)
1380 elif command ==
'save':
1381 data =
DictV(d,
'data')
1383 challengetype =
IntV(data,
'challengetype')
1384 idcode =
StrV(data,
'idcode')
1385 classid =
StrV(data,
'classid')
1386 title =
StrV(data,
'title')
1387 instructions =
StrV(data,
'instructions')
1388 question =
StrV(data,
'question')
1389 image =
StrV(data,
'image')
1390 sound =
StrV(data,
'sound')
1391 video =
StrV(data,
'video')
1392 files =
ArrayV(data,
'files')
1393 challenge =
StrV(data,
'challenge')
1394 starttime =
StrV(data,
'starttime')
1395 endtime =
StrV(data,
'endtime')
1396 minscore =
IntV(data,
'minscore')
1397 minpercentage =
IntV(data,
'minpercentage')
1398 minpoints =
IntV(data,
'minpoints')
1399 lessonids =
ArrayV(data,
'lessonids')
1400 problemids =
ArrayV(data,
'problemids')
1401 numtries =
IntV(data,
'numtries', 1, 9999, 3)
1402 numpoints =
IntV(data,
'numpoints', 0, 100, 50)
1403 didnttrypenalty =
IntV(data,
'didnttrypenalty', 0, 100, 50)
1404 numproblems =
IntV(data,
'numproblems', 1, 20, 6)
1405 timelimit =
IntV(data,
'timelimit')
1406 flags =
IntV(data,
'flags')
1408 if not classid
or not challenge
or (starttime > endtime):
1409 raise Exception(g.T.missingparams)
1413 if not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
1414 raise Exception(g.T.cantchange)
1419 obj = learn_challenge()
1421 cls = g.db.Load(learn_schoolclass, classid)
1424 ToShortCode(x.userid): [x.displayname, CHALLENGE_DIDNT_TRY, 0, 0, didnttrypenalty]
for x
in g.db.Linked(cls, learn_student, fields =
'userid, displayname')
1427 obj.Set(results = studentresults)
1429 currenttime = time.time()
1430 endtimestamp = dateutil.parser.parse(endtime).timestamp()
1432 flags |= CHALLENGE_NEEDS_ANALYSIS
1434 if not challengetype:
1435 challengetype = CHALLENGE_HOMEWORK
1437 if challengetype == CHALLENGE_CLASSPARTICIPATION:
1438 challenge =
'Class Participation'
1443 if flags & CHALLENGE_USE_STUDYLIST:
1444 title[g.session.lang] = g.T.studylist
1446 title[g.session.lang] =
', '.join(GetCardTitles(g, lessonids).keys())
1448 title[g.session.lang] = getattr(g.T, challenge.tolower().replace(
' ',
''), g.T.challenge)
1452 challengetype = challengetype,
1454 instructions = instructions,
1455 question = question,
1460 challenge = challenge,
1461 starttime = starttime,
1463 minscore = minscore,
1464 minpercentage = minpercentage,
1465 minpoints = minpoints,
1466 lessonids = lessonids,
1467 problemids = problemids,
1468 numtries = numtries,
1469 numpoints = numpoints,
1470 didnttrypenalty = didnttrypenalty,
1471 numproblems = numproblems,
1472 timelimit = timelimit,
1476 timeoffset = int(g.request.cookies.get(
'timeoffset'))
1478 obj.OffsetTime(timeoffset * 60)
1487 results[
'data'] = obj.ToJSON()
1488 elif command ==
'saveresult' or command ==
'saveunfinishedresult':
1489 data =
DictV(d,
'data')
1491 challengeresultid =
StrV(data,
'challengeresultid')
1492 problemresults =
ArrayV(data,
'problemresults')
1493 score =
IntV(data,
'score')
1494 maximumscore =
IntV(data,
'maximumscore')
1495 percentright =
IntV(data,
'percentright')
1497 if not challengeresultid
or not problemresults
or not maximumscore:
1498 raise Exception(g.T.missingparams)
1500 challengeresult = g.db.Load(learn_challengeresult,
FromShortCode(challengeresultid))
1502 if challengeresult.userid != g.session.userid:
1503 raise Exception(g.T.missingparams)
1505 isteacher = g.db.HasLink(learn_schoolclass, challengeresult.classid, learn_teacher, g.session.userid)
1509 previousresults = []
1511 if len(challengeresult.lessonids) == 1:
1512 previousresults = g.db.Find(
1513 learn_challengeresult,
1514 'WHERE userid = ? AND challenge = ? AND lessonids = ?',
1516 challengeresult.userid,
1517 challengeresult.challenge,
1518 challengeresult.lessonids[0],
1522 elif challengeresult.challengeid:
1523 previousresults = g.db.Find(
1524 learn_challengeresult,
1525 'WHERE userid = ? AND challengeid = ?',
1528 challengeresult.challengeid
1534 previoushighscore = 0
1537 for r
in previousresults:
1538 if r.score >= score:
1541 if r.score > previoushighscore:
1542 previoushighscore = r.score
1544 results[
'isnewhighscore'] = data[
'score'] != 0
and isnewhighscore
1545 results[
'previoushighscore'] = previoushighscore
1547 flags = challengeresult.flags
1549 if command ==
'saveunfinishedresult':
1550 flags &= CHALLENGERESULT_UNFINISHED
1552 flags = (flags & (~CHALLENGERESULT_UNFINISHED)) | CHALLENGERESULT_FINISHED
1554 currenttime = datetime.datetime.now()
1556 results[
'passed'] = 0
1557 results[
'remainingattempts'] = 0
1559 if challengeresult.challengeid:
1560 challengeobj = g.db.Load(
1562 challengeresult.challengeid,
1563 'rid, classid, challenge, challengetype, lessonids, starttime, endtime, minscore, minpercentage, numtries, numpoints, results, flags'
1566 if currenttime < challengeobj.starttime:
1567 raise Exception(g.T.challengenotstarted)
1569 if currenttime > challengeobj.endtime:
1570 raise Exception(g.T.challengealreadyover)
1572 results[
'remainingattempts'] = challengeobj.GetNumRemainingAttempts(g)
1576 if challengeobj.challenge ==
'Write Answer':
1577 challengeresult.Set(
1578 problemresults = problemresults,
1579 endtime = currenttime,
1581 g.db.Update(challengeresult)
1585 results[
'bonuspoints'] = 0
1589 if challengeobj.challengetype == CHALLENGE_CLASSWORK
and challengeobj.challenge ==
'Typing':
1590 answerobj = g.db.Find(
1592 'WHERE classid = ? AND userid = ?',
1593 [challengeobj.classid, g.session.userid]
1597 answerobj = answerobj[0]
1599 answerobj.Set(bonusscore = answerobj.bonusscore + score)
1601 answerobj.UpdateScore()
1603 g.db.Update(answerobj)
1607 if currentid
in challengeobj.results:
1608 currentname = challengeobj.results[currentid][0]
1611 currentname = g.db.Load(learn_teacher, g.session.userid,
'displayname').displayname
1613 currentname = g.db.Load(learn_student, g.session.userid,
'displayname').displayname
1615 if ((challengeobj.minscore
and score >= challengeobj.minscore)
1616 or (challengeobj.minpercentage
and percentright >= challengeobj.minpercentage)
1618 flags = (flags & (~CHALLENGERESULT_UNFINISHED)) | CHALLENGERESULT_FINISHED | CHALLENGERESULT_PASSED
1620 results[
'bonuspoints'] = challengeobj.numpoints
1624 for r
in challengeobj.previousresults:
1625 if ((challengeobj.minscore
and r.score > challengeobj.minscore)
1626 or (challengeobj.minpercentage
and r.percentright > challengeobj.minpercentage)
1631 if not alreadypassed
and answerobj:
1633 bonusscore = answerobj.bonusscore + challengeobj.numpoints
1636 answerobj.UpdateScore()
1638 g.db.Update(answerobj)
1640 results[
'passed'] = 1
1642 challengeresult.Set(scoreadded = challengeobj.numpoints)
1644 challengeobj.results[currentid] = [currentname, CHALLENGE_COMPLETED, score, percentright, challengeobj.numpoints]
1646 challengeobj.results[currentid] = [currentname, CHALLENGE_FAILED, score, percentright, 0]
1648 challengeobj.Set(results = challengeobj.results)
1650 g.db.Update(challengeobj)
1652 challengeresult.Set(lessonids = challengeobj.lessonids)
1654 challengeresult.Set(
1655 problemresults = problemresults,
1656 endtime = currenttime,
1657 timeused = (currenttime - challengeresult.starttime).total_seconds(),
1659 maximumscore = maximumscore,
1660 percentright = percentright,
1664 g.db.Update(challengeresult)
1666 if challengeobj
and problemresults:
1667 for r
in problemresults:
1668 if isinstance(r, dict)
and 'id' in r:
1671 'WHERE classid = ? AND userid = ? AND problemid = ?',
1673 challengeobj.classid,
1682 entry = learn_studylist()
1684 classid = challengeobj.classid,
1685 userid = g.session.userid,
1691 if r[
'status'] ==
'right':
1692 scorediff = r[
'timeremaining']
1697 if not entry.results:
1700 entry.results[int(currenttime.timestamp())] = scorediff
1703 score = entry.score + scorediff,
1704 results = entry.results,
1712 challengeobj.Analyze(g)
1714 thisclass = g.db.Load(learn_schoolclass, challengeobj.classid)
1715 thisclass.UpdateGrades(g, g.session.userid)
1717 elif command ==
'analyze':
1718 challengeid =
StrV(d,
'challengeid')
1719 classid =
StrV(d,
'classid')
1721 if not challengeid
or not classid:
1722 raise Exception(g.T.missingparams)
1724 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
1725 raise Exception(g.T.cantchange)
1727 challenge = g.db.Load(learn_challenge,
FromShortCode(challengeid))
1729 if challenge.flags & CHALLENGE_NEEDS_ANALYSIS:
1730 challenge.Analyze(g)
1733 results[
'data'] = challenge.ToJSON()
1734 elif command ==
'getanswers':
1735 challengeid =
StrV(d,
'challengeid')
1736 start =
IntV(d,
'start', 0, 99999999, 0)
1737 limit =
IntV(d,
'limit', 10, 1000, 100)
1740 raise Exception(g.T.missingparams)
1744 challengeobj = g.db.Load(learn_challenge, challengeid,
'rid, classid, results')
1746 if not g.db.HasLink(learn_schoolclass, challengeobj.classid, learn_teacher, g.session.userid):
1747 raise Exception(g.T.cantview)
1752 learn_challengeresult,
1753 'WHERE challengeid = ?',
1755 fields =
'rid, userid, problemresults',
1756 orderby =
'endtime',
1759 answer = r.problemresults
1760 points = challengeobj.results[studentcode][4]
if studentcode
in challengeobj.results
else 0
1762 answers[studentcode] = {
1766 if isinstance(answer, list)
and len(answer) == 5:
1767 answers[studentcode][
'answer'] = {
1768 'idcode': studentcode,
1769 'content': answer[0],
1776 answers[studentcode][
'answer'] = {
1777 'idcode': studentcode,
1786 learn_challengeresult,
1787 'WHERE challengeid = ?',
1789 fields =
'rid, problemresults',
1793 if r.problemresults
and 'answer' in r.problemresults:
1794 answers[
ToShortCode(r.userid)][
'answer'] = r.problemresults[
'answer']
1796 results[
'data'] = answers
1797 elif command ==
'delete':
1798 idcodes =
ArrayV(d,
'ids')
1799 classid =
StrV(d,
'classid')
1801 if not g.db.HasLink(learn_schoolclass,
FromShortCode(classid), learn_teacher, g.session.userid):
1802 raise Exception(g.T.cantdelete)
1806 'WHERE classid = ? AND rid IN (' +
', '.join([
'?' for x
in idcodes]) +
')',
1812 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
1814 except Exception
as e:
1815 traceback.print_exc()
1816 results[
'error'] = str(e)
1823 """Lets the user view study list entries, results, and delete unneeded
1826 Note that in most cases, problems are automatically added to the study
1827 list when a teacher updates a
class, so deleting a problem that
is
1828 in the study list
for that
class will only take effect until the next
1829 time the teacher changes the
class.
1835 d = json.loads(g.request.get_data())
1837 command =
StrV(d,
'command')
1839 if command ==
'search':
1840 studentid =
StrV(d,
'studentid')
1841 classid =
StrV(d,
'classid')
1842 start =
IntV(d,
'start', 0, 99999999, 0)
1843 limit =
IntV(d,
'limit', 10, 1000, 100)
1846 raise Exception(g.T.missingparams)
1853 if studentid
and studentid != g.session.userid
and not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
1854 raise Exception(g.T.cantview)
1856 conds = [
'classid = ?',
"userid = ?"]
1860 params.append(studentid)
1864 conds =
"WHERE " + (
" AND ").join(conds)
1866 entries = g.db.Find(
1870 start = start
if studentid
else 0,
1871 limit = limit
if studentid
else 1,
1872 orderby =
'score, problemid',
1873 fields =
'rid, problemid, score, results'
1878 entry = learn_studylist()
1890 if 'problemids' in entry.results:
1898 for x
in entry.results[
'problemids']
1901 results[
'cardids'] = entry.results[
'problemids']
1906 results[
'count'] = len(entries)
1908 entryobjs = [x.ToJSON(
'problemid, score, results')
for x
in entries]
1915 problems = GetAllProblems(g, [], CARD_LINK_PROBLEM, [x[
'problemid']
for x
in entryobjs])
1921 if problemid == e[
'problemid']:
1926 entryobjs = list(filter(
lambda x:
'problem' in x, entryobjs))
1928 results[
'data'] = entryobjs
if studentid
else entryobjs[start:start + limit]
1931 results[
'count'] = len(entryobjs)
1933 elif command ==
'save':
1934 classid =
StrV(d,
'classid')
1935 studentid =
StrV(d,
'studentid')
1936 cardids =
ArrayV(d,
'cardids')
1937 problemids =
ArrayV(d,
'problemids')
1940 raise Exception(g.T.missingparams)
1947 isteacher = g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid)
1949 if (studentid
and studentid != g.session.userid)
or not isteacher:
1950 raise Exception(g.T.cantchange)
1953 cardids = list(set(cardids))
1955 for r
in GetAllProblems(g, [
FromShortCode(x)
for x
in cardids], CARD_LINK_PROBLEM):
1956 problemids.append(r[
'idcode'])
1959 cardids = list(set(problemids))
1962 existingproblems = [
1966 'WHERE classid = ? AND userid = ?',
1971 fields =
'problemid'
1975 newproblems = filter(
lambda x: x
not in existingproblems, problemids)
1977 for s
in newproblems:
1978 newentry = learn_studylist()
1989 'WHERE classid = ? AND userid = 0',
1994 entry = learn_studylist()
2003 if 'problemids' in entry.results:
2004 problemids.extend(entry.results[
'problemids'])
2006 problemids = list(set(problemids))
2012 'problemids': problemids,
2018 cls = g.db.Load(learn_schoolclass, classid,
'rid')
2020 for r
in g.db.Linked(cls, learn_student, fields =
'userid'):
2021 existingproblems = [
2025 'WHERE classid = ? AND userid = ?',
2030 fields =
'problemid'
2034 newproblems = filter(
lambda x: x
not in existingproblems, problemids)
2036 for s
in newproblems:
2037 newentry = learn_studylist()
2047 elif command ==
'delete':
2048 idcodes =
ArrayV(d,
'ids')
2049 classid =
StrV(d,
'classid')
2050 studentid =
StrV(d,
'studentid')
2057 isteacher = g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid)
2060 (studentid
and studentid != g.session.userid
and not isteacher)
2061 or (
not studentid
and not isteacher)
2063 raise Exception(g.T.cantdelete)
2069 'WHERE classid = ? AND userid = ? AND problemid IN (' +
', '.join([
'?' for x
in idcodes]) +
')',
2070 [classid] + [studentid] + [
FromShortCode(x)
for x
in idcodes],
2077 'WHERE classid = ? AND userid = 0',
2081 if entry
and 'problemids' in entry[0].results:
2085 entry.results[
'problemids'].remove(r)
2087 entry.Set(results = entry.results)
2093 'WHERE classid = ? AND problemid IN (' +
', '.join([
'?' for x
in idcodes]) +
')',
2100 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
2102 except Exception
as e:
2103 traceback.print_exc()
2104 results[
'error'] = str(e)
2110 """Lets new students find a school that is right for them and
2111 handles general school management. You'll need to be a school
2112 administrator or school system administrator
in order to create
or
2113 change school information.
2118 d = json.loads(g.request.get_data())
2120 command =
StrV(d,
'command')
2122 if command ==
'search':
2123 keyword =
StrV(d,
'keyword')
2124 displayname =
StrV(d,
'displayname', keyword)
2125 description =
StrV(d,
'description', keyword)
2126 address =
StrV(d,
'address', keyword)
2127 phone =
StrV(d,
'phone', keyword)
2128 email =
StrV(d,
'email', keyword)
2129 contactinfo =
StrV(d,
'contactinfo', keyword)
2130 start =
IntV(d,
'start', 0, 99999999, 0)
2131 limit =
IntV(d,
'limit', 10, 1000, 20)
2137 conds.append(
"description LIKE ?")
2138 params.append(
EscapeSQL(
'%' + description +
'%'))
2141 conds.append(
"address LIKE ?")
2142 params.append(
EscapeSQL(
'%' + address +
'%'))
2145 conds.append(
"phone LIKE ?")
2146 params.append(
EscapeSQL(
'%' + phone +
'%'))
2149 conds.append(
"email LIKE ?")
2150 params.append(
EscapeSQL(
'%' + email +
'%'))
2153 conds.append(
"contactinfo LIKE ?")
2154 params.append(
EscapeSQL(
'%' + contactinfo +
'%'))
2157 conds =
"WHERE " + (
" AND ").join(conds)
2169 orderby =
'displayname'
2173 ls.append(c.ToJSON())
2175 results[
'data'] = ls
2176 results[
'count'] = len(ls)
2177 elif command ==
'load':
2178 schoolid =
StrV(d,
'schoolid')
2181 raise Exception(T.missingparams)
2185 if not g.db.HasLink(school,
FromShortCode(schoolid), learn_schooladministrator, g.session.userid):
2186 raise Exception(T.cantview)
2188 results[
'data'] = o.ToJSON()
2189 elif command ==
'getteachers':
2190 schoolid =
StrV(d,
'schoolid')
2192 raise Exception(T.missingparams)
2196 if not g.db.HasLink(school,
FromShortCode(schoolid), learn_schooladministrator, g.session.userid):
2197 raise Exception(T.cantview)
2199 keyword =
StrV(d,
'keyword')
2200 displayname =
StrV(d,
'displayname', keyword)
2201 profile =
StrV(d,
'profile', keyword)
2202 phonenumber =
StrV(d,
'phonenumber', keyword)
2203 email =
StrV(d,
'email', keyword)
2204 start =
IntV(d,
'start', 0, 99999999, 0)
2205 limit =
IntV(d,
'limit', 10, 1000, 100)
2211 conds.append(
"displayname LIKE ?")
2212 params.append(
EscapeSQL(
'%' + displayname +
'%'))
2215 conds.append(
"profile LIKE ?")
2216 params.append(
EscapeSQL(
'%' + profile +
'%'))
2219 conds.append(
"phonenumber LIKE ?")
2220 params.append(
EscapeSQL(
'%' + phonenumber +
'%'))
2223 conds.append(
"email LIKE ?")
2224 params.append(
EscapeSQL(
'%' + email +
'%'))
2226 conds.append(
"schoolid = ?")
2230 conds =
"WHERE " + (
" AND ").join(conds)
2236 for r
in g.db.Linked(school,
2238 fields =
'userid, displayname, phonenumber',
2244 o = r.ToJSON(
'displayname, phonenumber')
2248 results[
'data'] = ls
2249 elif command ==
'save':
2252 if not V(
'newinfo',
'rid')
and not g.db.HasGroup(
'school.system.administrators'):
2253 raise Exception(g.T.cantcreate)
2255 g.db.RequireGroup(
'school.administrators', g.T.cantchange)
2258 s.Set(**(d[
'data']))
2262 a = g.db.Load(learn_schooladministrator, g.session.userid)
2263 except Exception
as e:
2264 a = learn_administrator()
2265 a.Set(userid = g.session.userid)
2270 if V(
'newinfo',
'rid'):
2271 for c
in g.db.Linked(s, learn_schoolclass):
2272 c.Set(displayname = s.displayname)
2276 results[
'data'] = s.rid
2277 elif command ==
'delete':
2278 idcodes =
ArrayV(d,
'id')
2281 raise Exception(g.T.missingparams)
2283 g.db.RequireGroup(
'school.system.administrators', g.T.cantdelete)
2285 for idcode
in idcodes:
2288 for a
in g.db.Linked(s, learn_schoolclass):
2291 for a
in g.db.Linked(s, learn_schooladministrator):
2294 for a
in g.db.Linked(s, learn_schoolsystemadministrator):
2297 for a
in g.db.Linked(s, learn_teacher):
2300 for a
in g.db.Linked(s, learn_student):
2306 elif command ==
'getwaitingstudents':
2307 g.db.RequireGroup(
'school.administrators', g.T.cantview)
2309 schoolid =
StrV(d,
'schoolid')
2312 raise Exception(g.T.missingparams)
2316 waitingstudents = []
2318 for r
in g.db.Linked(s, learn_student, STUDENT_APPLIED, fields =
'userid, displayname, phonenumber'):
2321 waitingstudents.append(o)
2323 results[
'data'] = waitingstudents
2324 results[
'count'] = len(waitingstudents)
2325 elif command ==
'approvestudent':
2326 g.db.RequireGroup(
'school.administrators', g.T.cantview)
2328 schoolid =
StrV(d,
'schoolid')
2329 studentid =
StrV(d,
'studentid')
2330 accept =
IntV(d,
'accept')
2332 if not schoolid
or not studentid:
2333 raise Exception(g.T.missingparams)
2336 student = g.db.Load(learn_student,
FromShortCode(studentid))
2338 g.db.Unlink(school, student, STUDENT_APPLIED)
2341 g.db.Link(school, student)
2344 elif command ==
'getwaitingteachers':
2345 g.db.RequireGroup(
'school.administrators', g.T.cantview)
2347 schoolid =
StrV(d,
'schoolid')
2350 raise Exception(g.T.missingparams)
2354 waitingstudents = []
2356 for r
in g.db.Linked(s, learn_teacher, TEACHER_APPLIED, fields =
'userid, displayname, phonenumber'):
2359 waitingstudents.append(o)
2361 results[
'data'] = waitingstudents
2362 results[
'count'] = len(waitingstudents)
2363 elif command ==
'approveteacher':
2364 g.db.RequireGroup(
'school.administrators', g.T.cantview)
2366 schoolid =
StrV(d,
'schoolid')
2367 teacherid =
StrV(d,
'teacherid')
2368 accept =
IntV(d,
'accept')
2370 if not schoolid
or not teacherid:
2371 raise Exception(g.T.missingparams)
2374 teacher = g.db.Load(learn_teacher,
FromShortCode(teacherid))
2376 g.db.Unlink(school, teacherid, TEACHER_APPLIED)
2379 g.db.Link(school, teacherid)
2382 elif command ==
'getlinked':
2383 admin = g.db.Load(learn_schooladministrator, g.session.userid)
2386 x.ToJSON(
'rid, icon, displayname, description, phone')
2387 for x
in g.db.Linked(
2390 fields =
'rid, icon, displayname, description, phone'
2393 results[
'count'] = len(results[
'data'])
2394 elif command ==
'addusers':
2395 admin = g.db.Load(learn_schooladministrator, g.session.userid)
2397 schoolid =
StrV(d,
'schoolid')
2398 studentids =
ArrayV(d,
'studentids')
2399 teacherids =
ArrayV(d,
'teacherids')
2400 administratorids =
ArrayV(d,
'administratorids')
2402 if not schoolid
or (
not studentids
and not teacherids
and not administratorids):
2403 raise Exception(g.T.missingparams)
2405 school = g.db.Load(learn_school,
FromShortCode(schoolid), fields =
'rid')
2407 administratorgroup = g.db.Find(system_group,
'WHERE groupname = ?',
'school.administrators')[0]
2410 studentgroup = g.db.Find(system_group,
'WHERE groupname = ?',
'students')[0]
2412 newusers = g.db.Find(
2414 'WHERE rid IN (' +
', '.join([
'?' for x
in studentids]) +
')',
2418 fields =
'rid, displayname'
2422 g.db.Link(r, studentgroup)
2424 existingstudents = list(
2427 'WHERE userid IN (' +
', '.join([
'?' for x
in newusers]) +
')',
2429 x.rid
for x
in newusers
2435 newuserids = set([x.rid
for x
in newusers])
2436 existingstudentids = set([x.rid
for x
in existingstudents])
2438 studentstocreate = list(newuserids - existingstudentids)
2440 for r
in studentstocreate:
2443 newuser = learn_student()
2446 displayname = s.displayname,
2450 existingstudents.append(newuser)
2453 for r
in existingstudents:
2454 g.db.Link(r, school)
2457 teachergroup = g.db.Find(system_group,
'WHERE groupname = ?',
'teachers')[0]
2459 newusers = g.db.Find(
2461 'WHERE rid IN (' +
', '.join([
'?' for x
in teacherids]) +
')',
2465 fields =
'rid, displayname'
2469 g.db.Link(r, teachergroup)
2471 existingteachers = list(
2474 'WHERE userid IN (' +
', '.join([
'?' for x
in newusers]) +
')',
2476 x.rid
for x
in newusers
2482 newuserids = set([x.rid
for x
in newusers])
2483 existingteacherids = set([x.rid
for x
in existingteachers])
2485 teacherstocreate = list(newuserids - existingteacherids)
2487 for r
in teacherstocreate:
2490 newuser = learn_teacher()
2493 displayname = s.displayname,
2497 existingteachers.append(newuser)
2500 for r
in existingteachers:
2501 g.db.Link(r, school)
2503 if administratorids:
2504 administratorgroup = g.db.Find(system_group,
'WHERE groupname = ?',
'school.administrators')[0]
2506 newusers = g.db.Find(
2508 'WHERE rid IN (' +
', '.join([
'?' for x
in administratorids]) +
')',
2512 fields =
'rid, displayname'
2516 g.db.Link(r, administratorgroup)
2518 existingadministrators = list(
2520 learn_schooladministrator,
2521 'WHERE userid IN (' +
', '.join([
'?' for x
in newusers]) +
')',
2523 x.rid
for x
in newusers
2529 newuserids = set([x.rid
for x
in newusers])
2530 existingadministratorids = set([x.rid
for x
in existingadministrators])
2532 administratorstocreate = list(newuserids - existingadministratorids)
2534 for r
in administratorstocreate:
2537 newuser = learn_schooladministrator()
2540 displayname = s.displayname,
2544 existingadministrators.append(newuser)
2547 for r
in existingadministrators:
2548 g.db.Link(r, school)
2552 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
2553 except Exception
as e:
2554 traceback.print_exc()
2555 results[
'error'] = str(e)
2561 """Handles the management of classes for a school. You'll need to be
2562 an administrator for that school
in order to make any changes to the
2563 class information. If you
're a teacher, you can change autohomework
2564 and other
class settings here.
2569 d = json.loads(g.request.get_data())
2571 command =
StrV(d,
'command')
2573 if command ==
'search':
2574 keyword =
StrV(d,
'keyword')
2575 coursenum =
StrV(d,
'coursenum', keyword)
2576 displayname =
StrV(d,
'displayname', keyword)
2577 description =
StrV(d,
'description', keyword)
2578 schedule =
StrV(d,
'schedule', keyword)
2579 teachers =
StrV(d,
'teachers', keyword)
2580 schoolid =
StrV(d,
'schoolid')
2581 start =
IntV(d,
'start', 0, 99999999, 0)
2582 limit =
IntV(d,
'limit', 10, 1000, 100)
2588 conds.append(
"coursenum LIKE ?")
2589 params.append(
EscapeSQL(
'%' + coursenum +
'%'))
2592 conds.append(
"displayname LIKE ?")
2593 params.append(
EscapeSQL(
'%' + displayname +
'%'))
2596 conds.append(
"description LIKE ?")
2597 params.append(
EscapeSQL(
'%' + description +
'%'))
2600 conds.append(
"description LIKE ?")
2601 params.append(
EscapeSQL(
'%' + description +
'%'))
2604 conds.append(
"teachers LIKE ?")
2605 params.append(
EscapeSQL(
'%' + teachers +
'%'))
2608 conds.append(
"schoolid = ?")
2612 conds =
"WHERE " + (
" AND ").join(conds)
2624 orderby =
'coursenum, schedule, teachers'
2628 ls.append(c.ToJSON())
2630 results[
'data'] = ls
2631 results[
'count'] = len(ls)
2632 elif command ==
'load':
2633 classid =
StrV(d,
'classid')
2636 results[
'data'] = r.ToJSON()
2637 elif command ==
'get':
2638 g.db.RequireGroup([
'teachers',
'students'], g.T.cantview)
2641 if g.db.HasGroup(
'teachers'):
2642 u = g.db.Load(learn_teacher, g.session.userid)
2644 u = g.db.Load(learn_student, g.session.userid)
2648 for r
in g.db.Linked(u, learn_schoolclass):
2649 ls.append(r.ToJSON())
2651 results[
'data'] = ls
2652 except Exception
as e:
2653 results[
'data'] = []
2654 elif command ==
'save':
2656 schoolid =
StrV(d,
'schoolid')
2658 if not schoolid
or not newinfo:
2659 raise Exception(T.missingparams)
2661 if (
not V(
'newinfo',
'rid')
2662 and not g.db.HasLink(learn_school,
FromShortCode(schoolid), learn_schooladministrator, g.session.userid)
2664 raise Exception(g.T.cantcreate)
2666 s = g.db.Load(learn_school,
FromShortCode(schoolid), fields =
'rid, displayname')
2668 c = learn_schoolclass()
2672 displayname = s.displayname,
2680 results[
'data'] = s.rid
2681 elif command ==
'delete':
2682 idcodes =
ArrayV(d,
'ids')
2685 raise Exception(g.T.missingparams)
2687 schooladministrator = g.db.Load(learn_schooladministrator, g.session.userid)
2689 administeredschools = [x.rid
for x
in g.db.Linked(schooladministrator, learn_school, fields =
'rid')]
2691 for idcode
in idcodes:
2694 if s.schoolid
not in administeredschools:
2697 for a
in g.db.Linked(s, learn_teacher):
2700 for a
in g.db.Linked(s, learn_student):
2703 for a
in g.db.Linked(s, learn_school):
2709 elif command ==
'setteachers':
2710 schoolid =
StrV(d,
'schoolid')
2711 classid =
StrV(d,
'classid')
2712 teachers =
ArrayV(d,
'teachers')
2714 if not classid
or not teachers
or not schoolid:
2715 raise Exception(g.T.missingparams)
2717 if not g.session.userid:
2718 raise Exception(g.T.needlogin)
2720 if not g.db.HasLink(learn_school,
FromShortCode(schoolid), learn_schooladministrator, g.session.userid):
2721 raise Exception(g.T.cantview)
2731 'userid, displayname',
2735 for r
in teacherobjs:
2736 for k, v
in r.displayname.items():
2737 if k
in displaynames:
2738 displaynames[k] = displaynames[k] +
', ' + v
2742 c.Set(teachers = displaynames)
2744 g.db.LinkArray(c, teacherobjs)
2747 elif command ==
'setcurriculum':
2748 schoolid =
StrV(d,
'schoolid')
2749 classid =
StrV(d,
'classid')
2750 cardids =
ArrayV(d,
'cardids')
2752 if not classid
or not cardids:
2753 raise Exception(g.T.missingparams)
2756 raise Exception(g.T.missingparams)
2758 if not g.session.userid:
2759 raise Exception(g.T.needlogin)
2761 if not g.db.HasLink(learn_school,
FromShortCode(schoolid), learn_teacher, g.session.userid):
2762 raise Exception(g.T.cantview)
2766 students = g.db.Linked(c, learn_student, fields =
'userid')
2767 teachers = g.db.Linked(c, learn_teacher, fields =
'userid')
2769 previouscards = g.db.Linked(c, learn_card, fields =
'rid')
2770 previouscardids = [x.rid
for x
in previouscards]
2772 for p
in previouscards:
2774 for student
in students:
2775 g.db.Unlink(p, student)
2777 for teacher
in teachers:
2778 g.db.Unlink(p, teacher)
2786 cardobjs.append(card)
2788 if card.rid
not in previouscardids:
2789 for student
in students:
2790 g.db.Link(card, student)
2792 for teacher
in teachers:
2793 g.db.Link(card, teacher)
2795 g.db.LinkArray(c, cardobjs)
2798 updatecardobjs = list(students) + list(teachers)
2801 for r
in updatecardobjs:
2802 UpdateAllowedCards(g, r)
2805 elif command ==
'setstudents':
2806 schoolid =
StrV(d,
'schoolid')
2807 classid =
StrV(d,
'classid')
2808 students =
ArrayV(d,
'students')
2810 if not classid
or not students:
2811 raise Exception(g.T.missingparams)
2814 raise Exception(g.T.missingparams)
2816 if not g.session.userid:
2817 raise Exception(g.T.needlogin)
2819 if not g.db.HasLink(learn_school,
FromShortCode(schoolid), learn_schooladministrator, g.session.userid):
2820 raise Exception(g.T.cantview)
2832 g.db.LinkArray(c, studentobjs)
2834 c.Set(numstudents = len(studentobjs))
2838 courses = g.db.Linked(c, learn_card,
'rid')
2840 for r
in studentobjs:
2844 for r
in studentobjs:
2845 UpdateAllowedCards(g, r)
2848 elif command ==
'setautohomework':
2849 classid =
StrV(d,
'classid')
2850 challengetypes =
ArrayV(d,
'challengetypes')
2851 lessonids =
ArrayV(d,
'lessonids')
2852 problemids =
ArrayV(d,
'problemids')
2853 minscore =
IntV(d,
'minscore')
2854 minpercentage =
IntV(d,
'minpercentage')
2855 numpoints =
IntV(d,
'numpoints')
2856 problemselect =
StrV(d,
'problemselect')
2857 numtries =
IntV(d,
'numtries')
2858 numproblems =
IntV(d,
'numproblems')
2859 timelimit =
IntV(d,
'timelimit')
2860 didnttrypenalty =
IntV(d,
'didnttrypenalty')
2861 usehomeworkmanager =
IntV(d,
'usehomeworkmanager')
2862 usestudylist =
IntV(d,
'usestudylist')
2863 timezone =
IntV(d,
'timezone')
2866 raise Exception(g.T.missingparams)
2870 if not g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid):
2871 raise Exception(g.T.cantchange)
2873 c = g.db.Load(learn_schoolclass, classid,
'rid, autohomework, flags')
2875 if usehomeworkmanager:
2876 c.flags = c.flags | CLASS_AUTO_HOMEWORK
2878 c.flags = c.flags & (~CLASS_AUTO_HOMEWORK)
2881 c.flags = c.flags | CLASS_REVIEW_STUDYLIST
2883 c.flags = c.flags & (~CLASS_REVIEW_STUDYLIST)
2888 for k, v
in GetCardTitles(g, [
FromShortCode(x)
for x
in lessonids]).items():
2897 problems = GetAllProblems(g, [], CARD_LINK_PROBLEM, [
FromShortCode(x)
for x
in problemids])
2900 resettime = timezone,
2903 'challengetypes': challengetypes,
2904 'lessonids': lessonids,
2905 'problemids': problemids,
2907 'problems': problems,
2908 'minscore': minscore,
2909 'minpercentage': minpercentage,
2910 'numpoints': numpoints,
2911 'problemselect': problemselect,
2912 'numtries': numtries,
2913 'numproblems': numproblems,
2914 'timelimit': timelimit,
2915 'didnttrypenalty': didnttrypenalty,
2923 results[
'data'] = c.ToJSON(
'flags, resettime, autohomework')
2925 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
2926 except Exception
as e:
2927 traceback.print_exc()
2928 results[
'error'] = str(e)
2934 """Handles student management. Only a school administrator can use
2940 d = json.loads(g.request.get_data())
2942 command =
StrV(d,
'command')
2944 schoolid =
StrV(d,
'schoolid')
2947 raise Exception(g.T.missingparams)
2949 if not g.session.userid:
2950 raise Exception(g.T.needlogin)
2952 if not g.db.HasLink(learn_school,
FromShortCode(schoolid), learn_schooladministrator, g.session.userid):
2953 raise Exception(g.T.cantview)
2955 if command ==
'search':
2956 displayname =
StrV(d,
'displayname')
2957 phonenumber =
StrV(d,
'phonenumber')
2958 classid =
StrV(d,
'classid')
2959 start =
IntV(d,
'start', 0, 99999999, 0)
2960 limit =
IntV(d,
'limit', 10, 1000, 100)
2966 conds.append(
"displayname LIKE ?")
2967 params.append(
EscapeSQL(
'%' + displayname +
'%'))
2970 conds.append(
"phonenumber LIKE ?")
2971 params.append(
EscapeSQL(
'%' + phonenumber +
'%'))
2974 conds =
"WHERE " + (
" AND ").join(conds)
2978 school = g.db.Load(learn_school,
FromShortCode(schoolid), fields =
'rid')
2989 for r
in g.db.Linked(c, learn_student, fields =
'userid, displayname, phonenumber, grade, coins, items'):
2990 o = r.ToJSON(
'displayname, phonenumber, grade, coins, items')
2992 chosenobjs.append(o)
2993 chosenids.append(r.userid)
2995 students = g.db.Linked(
3001 'userid, displayname, phonenumber, grade, coins, items',
3008 if r.userid
in chosenids:
3011 o = r.ToJSON(
'displayname, phonenumber, grade, coins, items')
3013 o[
'idcode'] = o[
'headshot']
3014 studentobjs.append(o)
3016 results[
'data'] = studentobjs
3017 results[
'count'] = len(students)
3018 results[
'chosen'] = chosenobjs
3019 elif command ==
'expel':
3020 idcode =
StrV(d,
'id')
3023 raise Exception(g.T.missingparams)
3025 school = g.db.Load(learn_school,
FromShortCode(schoolid), fields =
'rid')
3026 student = g.db.Load(learn_student,
FromShortCode(idcode), fields =
'userid')
3028 g.db.Unlink(school, student)
3031 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
3032 except Exception
as e:
3033 traceback.print_exc()
3034 results[
'error'] = str(e)
3040 """Handles teacher management. Only a school administrator can use
3046 d = json.loads(g.request.get_data())
3048 command =
StrV(d,
'command')
3050 schoolid =
StrV(d,
'schoolid')
3053 raise Exception(g.T.missingparams)
3055 if not g.session.userid:
3056 raise Exception(g.T.needlogin)
3058 if not g.db.HasLink(learn_school,
FromShortCode(schoolid), learn_schooladministrator, g.session.userid):
3059 raise Exception(g.T.cantview)
3061 if command ==
'search':
3062 keyword =
StrV(d,
'keyword')
3063 displayname =
StrV(d,
'displayname', keyword)
3064 phonenumber =
StrV(d,
'phonenumber', keyword)
3065 email =
StrV(d,
'email', keyword)
3066 classid =
StrV(d,
'classid')
3067 start =
IntV(d,
'start', 0, 99999999, 0)
3068 limit =
IntV(d,
'limit', 10, 1000, 100)
3074 conds.append(
"displayname LIKE ?")
3075 params.append(
EscapeSQL(
'%' + displayname +
'%'))
3078 conds.append(
"phonenumber LIKE ?")
3079 params.append(
EscapeSQL(
'%' + phonenumber +
'%'))
3082 conds.append(
"phonenumber LIKE ?")
3083 params.append(
EscapeSQL(
'%' + phonenumber +
'%'))
3086 conds =
"WHERE " + (
" AND ").join(conds)
3090 school = g.db.Load(learn_school,
FromShortCode(schoolid), fields =
'rid')
3101 for r
in g.db.Linked(c, learn_teacher, fields =
'userid, displayname, phonenumber, email'):
3102 o = r.ToJSON(
'displayname, phonenumber, email')
3104 chosenobjs.append(o)
3105 chosenids.append(r.userid)
3107 teachers = g.db.Linked(
3113 'userid, displayname, phonenumber',
3120 if r.userid
in chosenids:
3123 o = r.ToJSON(
'displayname, phonenumber')
3125 o[
'idcode'] = o[
'headshot']
3126 teacherobjs.append(o)
3128 results[
'data'] = teacherobjs
3129 results[
'count'] = len(teachers)
3130 results[
'chosen'] = chosenobjs
3131 elif command ==
'expel':
3132 idcode =
StrV(d,
'id')
3135 raise Exception(g.T.missingparams)
3137 school = g.db.Load(learn_school,
FromShortCode(schoolid), fields =
'rid')
3138 teacher = g.db.Load(learn_teacher,
FromShortCode(idcode), fields =
'userid')
3140 g.db.Unlink(school, teacher)
3143 results[
'error'] = f
'{g.T.cantunderstand} "{command}"'
3144 except Exception
as e:
3145 traceback.print_exc()
3146 results[
'error'] = str(e)
3152 """A simple text to speech API for students and teachers to play
3153 around with. You
'll need to install the festival speech synthesis
3154 program on your server to use this.
3159 if (
'students' not in g.session[
'groups']
3160 and 'teachers' not in g.session[
'groups']
3162 raise Exception(g.T.cantview)
3166 filename = re.sub(
r'\W',
'_', text)
3168 cachedir =
'/srv/flasksite/libs/texttomp3'
3170 textfile = cachedir +
'/' + filename +
'.txt'
3171 mp3file = cachedir +
'/' + filename +
'.mp3'
3173 if os.path.exists(mp3file):
3174 return send_file(mp3file)
3176 os.makedirs(cachedir, exist_ok =
True)
3178 with open(textfile,
'w')
as f:
3179 f.write(g.urlargs[0])
3181 p1 = subprocess.Popen([
'text2wave', textfile], stdout = subprocess.PIPE)
3183 output = subprocess.check_output([
'lame',
'-', mp3file], stdin = p1.stdout)
3187 return send_file(mp3file)
3191 """Speech recognition API using the Kaldi project. Of course, it would
3192 be nice if we could simply use Google
's speech recognition, but since
3193 Google servers are blocked in China, we have to use our own solution.
3195 Note that speech recognition
is a very expensive
and memory intensive
3196 operation, so we use the DLock API to run only a single instance at
3197 a time. If you have a faster server, feel free to change the
3198 numprocesses variable to the number of processor cores that you have.
3203 if (
'students' not in g.session[
'groups']
3204 and 'teachers' not in g.session[
'groups']
3206 raise Exception(g.T.cantview)
3208 data = g.request.get_data()
3210 contentlength = g.request.headers.get(
'content-length')
3213 contentlength = int(contentlength)
3215 raise Exception(g.T.nodatauploaded)
3217 if contentlength > (1024 * 64):
3218 raise Exception(g.T.toobig)
3220 filename = uuid.uuid4().hex +
'.ogg'
3222 cachedir =
'/srv/flasksite/static/learn/opus'
3224 os.makedirs(cachedir, exist_ok =
True)
3226 uploadedfile = cachedir +
'/' + filename
3229 currenttime = time.time()
3231 for r
in os.listdir(cachedir):
3232 fullpath = cachedir +
'/' + r
3234 if os.stat(fullpath).st_mtime + 3600 < currenttime:
3237 with open(uploadedfile,
'wb')
as f:
3238 f.write(g.request.get_data())
3240 results[
'uploadedfile'] =
'/learn/opus/' + filename
3246 lock = DLock(
'opustotext' + str(random.randint(1, numprocesses)))
3248 acquired = lock.LoopWhileLocked_ThenLocking()
3251 output = subprocess.check_output(
3253 '/srv/flasksite/libs/vosk/recognizespeech.py',
3260 results[
'error'] = g.T.nothingfound
3262 results[
'output'] = output
3264 results[
'error'] = g.T.serverbusy
3265 except Exception
as e:
3266 results[
'error'] = f
"Error processing request: {e}"
3272 """The helper function for /learn/courses.html.
3274 This loads the main card, child cards, problems, and challenges
3275 for the JavaScript renderer.
3287 classid = g.urlargs.pop(0)
3290 raise Exception(g.T.missingparams)
3292 stackleft = len(g.urlargs)
3307 allowedcards = g.db.Load(learn_teacher, g.session.userid, fields =
'allowedcards').allowedcards
3309 allowedcards.extend(g.db.Load(learn_student, g.session.userid, fields =
'allowedcards').allowedcards)
3310 except Exception
as e:
3311 allowedcards = g.db.Load(learn_student, g.session.userid, fields =
'allowedcards').allowedcards
3315 for arg
in g.urlargs:
3316 if arg
not in allowedcards:
3317 raise Exception(g.T.cantview)
3322 c = g.db.Load(learn_card,
FromShortCode(arg),
'rid, title, description, content, html, image, sound, video, flags')
3324 breadcrumbs.append(arg)
3326 siblingcards = childcards
3328 childcards = g.db.Linked(c, learn_card, CARD_CHILD, fields =
'rid, title, description, image')
3332 childcardids = [
ToShortCode(x.rid)
for x
in childcards]
3334 card = c.ToJSON(
'title, description, content, html, image, sound, video, flags')
3338 cardstack.append(card)
3342 for x
in childcards:
3343 childcardobj = x.ToJSON(
'title, description, image')
3347 childcardobjs.append(childcardobj)
3349 childstacks.append(childcardobjs)
3351 if not stackleft
and c.flags & CARD_LESSON:
3352 problems = GetAllProblems(g, [c.rid], CARD_LINK_PROBLEM)
3357 G.classid = '{classid}';
3358 G.cardstack = {json.dumps(cardstack)};
3359 G.childstacks = {json.dumps(childstacks)};
3360 G.problems = {json.dumps(problems)};
3361 G.caneditcards = {1
if caneditcards
else 0};
3365 <div
class=
"ButtonBar CourseNavBar"></div>
3366 <div
class=
"MainCard"></div>
3367 <div
class=
"ButtonBar MainCardBar"></div>
3368 <div
class=
"ChildCards FlexGrid16"></div>
3369 <div
class=
"ButtonBar ChildCardBar"></div>
3370 <div
class=
"ProblemToggles"></div>
3371 <div
class=
"ProblemCards FlexGrid16"></div>
3372 <div
class=
"ButtonBar ProblemBar"></div>
3373 <div
class=
"ChallengeCards FlexGrid16"></div>
3374 <div
class=
"SiblingNav Pad50"></div>
3377 return '\n'.join(ls)
3378 except Exception
as e:
3379 traceback.print_exc()
3380 result =
'<div class="Error">Error processing: ' + str(e) +
'</div>'
3386 """The helper function for /learn/home.html.
3388 This loads the classes and challenges
for the JavaScript renderer
3389 in addition to generating autohomework challenges
and doing analyses
3390 on challenges which have ended.
3403 currenttime = time.time()
3405 if 'school.administrators' in g.session[
'groups']:
3408 if 'teachers' in g.session[
'groups']:
3410 teacher = g.db.Load(learn_teacher, g.session.userid, fields =
'userid, displayname')
3412 teacherjson = teacher.ToJSON()
3413 teacherjson[
'idcode'] =
ToShortCode(teacher.userid)
3415 for r
in g.db.Linked(teacher, learn_schoolclass):
3416 r.GenerateAutoHomework(g)
3421 x.ToJSON(
'rid, title')
3422 for x
in g.db.Linked(r, learn_card, fields =
'rid, title')
3427 for x
in g.db.Linked(r, learn_student, fields =
'userid, displayname')
3430 teacherclasses.append(o)
3431 except Exception
as e:
3432 traceback.print_exc()
3435 student = g.db.Load(
3438 'userid, displayname, coins, items'
3441 studentjson = student.ToJSON()
3442 studentjson[
'idcode'] =
ToShortCode(student.userid)
3444 for r
in g.db.Linked(student, learn_schoolclass):
3445 r.GenerateAutoHomework(g)
3450 x.ToJSON(
'rid, title')
3451 for x
in g.db.Linked(r, learn_card, fields =
'rid, title')
3456 for x
in g.db.Linked(r, learn_student, fields =
'userid, displayname')
3459 o[
'challenges'] = GetCurrentChallenges(g, r.rid, CHALLENGE_HOMEWORK)
3461 r.UpdateChallenges(g)
3463 studentclasses.append(o)
3468 G.isadministrator = {isadministrator};
3469 G.teacher = {json.dumps(teacherjson)};
3470 G.teacherclasses = {json.dumps(teacherclasses)};
3471 G.student = {json.dumps(studentjson)};
3472 G.studentclasses = {json.dumps(studentclasses)};
3476 <div class=
"PageLoadContent"></div>
3479# =====================================================================
3480def challengesfunc(g):
3481 """The helper function for /learn/challenges.html.
3483 This loads the challenge information for the JavaScript renderer.
3485 args = g.request.args
3487 classid = args.get('classid',
'')
3488 challenge = args.get(
'challenge',
'')
3489 challengeid = args.get(
'challengeid',
'')
3490 lessonids = args.get(
'lessonids',
'')
3491 problemids = args.get(
'problemids',
'')
3494 lessonids = lessonids.split(
',')
3499 problemids = problemids.split(
',')
3505 if not classid
or (
not challenge
and not challengeid)
or (
not challengeid
and not lessonids
and not problemids):
3506 raise Exception(g.T.missingparams)
3510 challengeid =
FromShortCode(challengeid)
if challengeid
else 0
3512 isteacher = g.db.HasLink(learn_schoolclass, classid, learn_teacher, g.session.userid)
3515 if challenge
and not isteacher:
3516 raise Exception(g.T.cantview)
3519 if not isteacher
and not g.db.HasLink(learn_schoolclass, classid, learn_student, g.session.userid):
3520 raise Exception(g.T.cantview)
3524 challengeresult = learn_challengeresult()
3528 remainingattempts = 0
3531 challengeobj = g.db.Load(
3536 currenttime = time.time()
3538 if not isteacher
and currenttime < challengeobj.starttime.timestamp():
3539 diff = challengeobj.starttime - datetime.datetime.fromtimestamp(currenttime)
3541 return f
'''<h1>{g.T.challengenotstarted}</h1>
3543<p class=
"Error Pad50">
3544 {g.T.pleasecomebackat} {diff.days * 24 + int((diff.seconds % 86400) / 3600)}:{int((diff.seconds % 3600) / 60):02}:{int(diff.seconds % 60):02}
3547<div
class=
"ButtonBar">
3554 if not isteacher
and currenttime > challengeobj.endtime.timestamp():
3555 return f
'''<h1>{g.T.challengealreadyover}</h1>
3557<p class=
"Error Pad50">
3558 {g.T.betterlucknexttime}
3561<div
class=
"ButtonBar">
3568 challenge = challengeobj.challenge.split(' ')
3569 challenge =
'.'.join(challenge).lower()
3571 remainingattempts = challengeobj.GetNumRemainingAttempts(g)
3573 if remainingattempts == 0
and not isteacher:
3574 return f
'''<h1>{g.T.nomoretriesavailable}</h1>
3576<p class=
"Error Pad50">
3577 {g.T.nomoretriesavailableexplanation}
3580<div
class=
"ButtonBar">
3586 previousanswer = ['',
'',
'',
'', []]
3588 if (challengeobj.challenge ==
'Write Answer'
3589 and challengeobj.previousresults
3591 challengeresult = challengeobj.previousresults[0]
3592 previousanswer = challengeresult.problemresults
3594 if challengeobj.flags & CHALLENGE_USE_STUDYLIST:
3595 studylist = learn_studylist()
3598 userid = g.session.userid,
3600 problems.extend(studylist.GetProblems(g, challengeobj.numproblems))
3610 elif lessonids
or problemids:
3620 if challengeid
and problems
and challengeobj.numproblems:
3621 problems = random.sample(problems, challengeobj.numproblems)
3623 challengeresult.Set(
3624 userid = g.session.userid,
3625 starttime = datetime.datetime.now(),
3629 challenge = challenge,
3630 challengeid = challengeid,
3631 lessonids =
','.join(sorted(lessonids)),
3632 problemids =
','.join(sorted(problemids)),
3633 flags = CHALLENGERESULT_UNFINISHED,
3634 problemresults = [],
3640 g.db.Save(challengeresult)
3646 challengejson = challengeobj.ToJSON(
'rid, title, instructions, image, sound, video, files, numtries, minscore, minpercentage, timelimit, flags')
3647 challengejson[
'question'] =
BestTranslation(challengeobj.question, g.session.lang)
3649 if challengeobj.challenge ==
'Write Answer':
3650 challengejson[
'title'] = {
'en': g.T.questionanswers}
3655G.classid = '{classid}';
3656G.challenge =
'{challenge}';
3657G.challengeid =
'{challengeid}';
3658G.challengeresultid =
'{ToShortCode(challengeresult.rid)}';
3660G.remainingattempts = {remainingattempts};
3661G.challengeobj = {json.dumps(challengejson)};
3662G.lessonids = {json.dumps(lessonids)};
3663G.problemids = {json.dumps(problemids)};
3665G.problems = {json.dumps(problems)};
3666G.previousanswer = {json.dumps(previousanswer)};
3671# =====================================================================
3673 """The standard Pafera app cleanup handler.
3675 Since Pafera is focused on speed
and fault tolerance rather than
3676 pure correctness, we don
't use foreign keys and check constraints
3677 in the database layer. This function checks
for broken links
and
3678 rows
and deletes them
from the database.
3680 g.db.RequireGroup('admins', g.T.cantchange)
3684 ls.append(
'Cleaning up cards')
3686 for r
in g.db.Find(learn_card, fields =
'rid, title, content'):
3687 s = g.db.Execute(
'''
3692 OR explanationid = ?
3717 ls.append(f
'''\tFound orphaned card {r.rid}\t{r.title}\t{r.content}''')
3719 ls.append(
'Cleaning up problems')
3721 for r
in g.db.Find(learn_problem, fields =
'rid, problemid, answerid'):
3722 s = g.db.Execute(
'''
3734 ls.append(f
'''\tFound broken problem {r.rid}\t{r.problemid}\t{r.answerid}''')
3736 ls.append(
'Cleaning up study list')
3740 for r
in g.db.Execute(
'''
3741 SELECT COUNT(*) as count, classid, userid, problemid
3742 FROM learn_studylist
3743 GROUP BY classid, userid, problemid
3747 ls.append(f'''{r[0]}\t{r[1]}\t{r[2]}\tr{r[3]}''')
3749 numtodelete = r[0] - 1
3753 for s
in g.db.Execute(
'''
3755 FROM learn_studylist
3767 if numtodelete != numdeleted
and (s[1] ==
'{}' or currentnum == r[0]):
3768 ls.append(f
'''\tDeleting rid {s[0]}''')
3769 idstodelete.append(s[0])
3775 print(f
'Cleaned up {len(idstodelete)} duplicate entries')
3778 DELETE FROM learn_studylist
3779 WHERE rid IN ({', '.join([
'?' for x
in idstodelete])})
3788 return '\n'.join(ls)
3793 'problemapi': problemapi,
3794 'answersapi': answersapi,
3795 'schoolapi': schoolapi,
3796 'classapi': classapi,
3797 'studentapi': studentapi,
3798 'teacherapi': teacherapi,
3799 'challengeapi': challengeapi,
3800 'studylistapi': studylistapi,
3801 'texttomp3': texttomp3,
3802 'opustotext': opustotext,
3803 'coursesfunc': coursesfunc,
3804 'homefunc': homefunc,
3805 'challengesfunc': challengesfunc,
def cardapi(g)
Handles card management including setting child cards and problems.
def teacherapi(g)
Handles teacher management.
def homefunc(g)
The helper function for /learn/home.html.
def opustotext(g)
Speech recognition API using the Kaldi project.
def answersapi(g)
Used to view and submit answers for a class, which can be viewed in the teacher's classroom web inter...
def classapi(g)
Handles the management of classes for a school.
def coursesfunc(g)
The helper function for /learn/courses.html.
def schoolapi(g)
Lets new students find a school that is right for them and handles general school management.
def studentapi(g)
Handles student management.
def problemapi(g)
Manages problems, which are essentially a collection of cards.
def studylistapi(g)
Lets the user view study list entries, results, and delete unneeded problems.
def challengeapi(g)
Used to create and save challenge results.
def texttomp3(g)
A simple text to speech API for students and teachers to play around with.
def StrV(d, k, default='')
Utility function to get a string from a dict or object given its name.
def DictV(d, k, default={})
Utility function to get a dict from a dict or object given its name.
def ArrayV(d, k, default=[])
Utility function to get an array from a dict or object given its name.
def IntV(d, k, min=None, max=None, default=0)
Utility function to get an int from a dict or object given its name.
def V(d, k, default=None)
Utility function to get a value from a dict or object given its name.
def EscapeSQL(s)
Escapes special characters to be used in a SQL query string.
def FromShortCode(code, chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_')
Turns a six character alphanumeric code into a 32-bit value.
def ToShortCode(val, chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_')
Turns a 32-bit value into a six character alphanumeric code.
def BestTranslation(translations, languages, defaultlang='en')
Returns the best translation found in a dict of different translations.
def CodeDir(code)
Separates a filename into three character segments with the directory separator '/' in between.
def LoadTranslation(g, app, translation, languages)
Load the translation into the global language dict in g.T.