PaferaPy Async 0.1
ASGI framework focused on simplicity and efficiency
Loading...
Searching...
No Matches
schoolclass.py
Go to the documentation of this file.
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4import random
5import time
6import datetime
7
8from statistics import mean
9
10import pafera.db
12
13from pafera.utils import *
14from pafera.validators import *
15
18import apps.learn.card
19
20# Flag constants
21CLASS_AUTO_APPROVE = 0x01
22CLASS_AUTO_HOMEWORK = 0x02
23CLASS_REVIEW_STUDYLIST = 0x04
24
25# *********************************************************************
27 """Container class for students, courses, and teachers. This
28 permits easy searching for students to apply and teachers to manage
29 courses and students.
30 """
31
32 _dbfields = {
33 'rid': ('INTEGER PRIMARY KEY', 'NOT NULL',),
34 'schoolid': ('INT', 'NOT NULL',),
35 'schoolname': ('TRANSLATION', 'NOT NULL', BlankValidator()),
36 'coursenum': ('TEXT', 'NOT NULL', BlankValidator()),
37 'displayname': ('TRANSLATION', 'NOT NULL', BlankValidator()),
38 'description': ('TRANSLATION', 'NOT NULL', BlankValidator()),
39 'schedule': ('TRANSLATION', 'NOT NULL', BlankValidator()),
40 'teachers': ('TRANSLATION', "NOT NULL DEFAULT ''",),
41 'rankings': ('NEWLINELIST', "NOT NULL DEFAULT ''",),
42 'numstudents': ('INT', 'NOT NULL DEFAULT 0',),
43 'capacity': ('INT', 'NOT NULL',),
44 'resettime': ('INT', 'NOT NULL',),
45 'autohomework': ('DICT', "NOT NULL DEFAULT ''",),
46 'flags': ('INT', 'NOT NULL DEFAULT 0',),
47 }
48 _dbindexes = ()
49 _dblinks = [
50 'learn_school',
51 'learn_teacher',
52 'learn_student'
53 ]
54 _dbdisplay = ['coursenum', 'displayname', 'description']
55 _dbflags = 0
56
57 # -------------------------------------------------------------------
58 def __init__(self):
59 super().__init__()
60
61 # -------------------------------------------------------------------
62 def UpdateChallenges(self, g):
63 """Look for any challenges that have passed endtime and calculate
64 statistics for them.
65 """
66
67 needcommit = 0
68
69 for c in g.db.Find(apps.learn.challenge.learn_challenge,
70 'WHERE classid = ? AND starttime < ? AND flags & ?',
71 [
72 self.rid,
73 time.time(),
74 apps.learn.challenge.CHALLENGE_NEEDS_ANALYSIS
75 ]
76 ):
77 c.Analyze(g)
78 needcommit = 1
79
80 if needcommit:
81 g.db.Commit()
82
83 # -------------------------------------------------------------------
84 def UpdateGrades(self, g, studentid):
85 """Recalculate grades for studentid and update class rankings.
86 """
87
88 grade = g.db.Find(
89 apps.learn.classgrade.learn_classgrade,
90 'WHERE classid = ? AND userid = ?',
91 [
92 self.rid,
93 studentid,
94 ]
95 )
96
97 if not grade:
98 grade = apps.learn.classgrade.learn_classgrade()
99
100 grade.Set(
101 classid = self.rid,
102 userid = studentid,
103 )
104 else:
105 grade = grade[0]
106
107 classparticipations = []
108 homeworks = []
109
110 studentcode = ToShortCode(studentid)
111 persistencebonus = 0
112 persistencestreak = 0
113 numhomework = 0
114 homeworkcompleted = 0
115
116 for r in g.db.Find(
117 apps.learn.challenge.learn_challenge,
118 'WHERE classid = ? AND endtime < ?',
119 [
120 self.rid,
121 time.time(),
122 ],
123 fields = 'challengetype, results',
124 ):
125
126 if studentcode not in r.results:
127 persistencestreak = 0
128 continue
129
130 result = r.results[studentcode]
131
132 if r.challengetype == apps.learn.challenge.CHALLENGE_CLASSPARTICIPATION:
133 classparticipations.append((result['score'], result['percentage']))
134 elif r.challengetype == apps.learn.challenge.CHALLENGE_HOMEWORK:
135 numhomework += 1
136
137 homeworks.append((result[2], result[3]))
138
139 completed = result[1] == apps.learn.challenge.CHALLENGE_COMPLETED
140
141 if completed:
142 homeworkcompleted += 1
143 persistencestreak += 1
144 persistencebonus += persistencestreak
145
146 grade.Set(
147 numhomework = numhomework,
148 homeworkcompleted = homeworkcompleted,
149 classscore = sum([x[0] for x in classparticipations]) if classparticipations else 0,
150 classpercent = mean([x[1] for x in classparticipations]) if classparticipations else 0,
151 reviewscore = sum([x[0] for x in homeworks]) if homeworks else 0,
152 reviewpercent = mean([x[1] for x in homeworks]) if homeworks else 0,
153 persistencescore = persistencebonus,
154 persistencepercent = (homeworkcompleted / numhomework * 100) if numhomework else 0,
155 )
156
157 grade.Set(
158 finalscore = (
159 (grade.classscore + grade.reviewscore + grade.persistencescore)
160 * (grade.classpercent / 100)
161 * (grade.reviewpercent / 100)
162 * (grade.persistencepercent / 100)
163 ),
164 )
165
166 g.db.Save(grade)
167
168 if not self.rankings:
169 self.rankings = []
170
171 newrankings = [
172 ToShortCode(x.userid)
173 for x in g.db.Find(
174 apps.learn.classgrade.learn_classgrade,
175 'WHERE classid = ?',
176 self.rid,
177 fields = 'userid',
178 orderby = 'finalscore, classpercent, reviewpercent, persistencepercent',
179 )
180 ]
181
182 if self.rankings != newrankings:
183 self.Set(rankings = newrankings)
184
185 g.db.Update(self)
186
187 g.db.Commit()
188
189 # -------------------------------------------------------------------
191 """If autohomework is enabled, this function will generate a random
192 challenge every day for your students to complete.
193
194 If review studylist is enabled, this will generate a challenge
195 every day using each student's studylist.
196 """
197
198 if self.flags & CLASS_AUTO_HOMEWORK or self.flags & CLASS_REVIEW_STUDYLIST:
199 currenttime = time.time()
200
201 currenthomework = g.db.Find(
202 apps.learn.challenge.learn_challenge,
203 'WHERE classid = ? AND starttime < ? AND endtime > ? AND challengetype = ?',
204 [
205 self.rid,
206 currenttime,
207 currenttime,
208 apps.learn.challenge.CHALLENGE_HOMEWORK,
209 ]
210 )
211
212 needhomework = 1 if self.flags & CLASS_AUTO_HOMEWORK else 0
213 needstudylist = 1 if self.flags & CLASS_REVIEW_STUDYLIST else 0
214
215 for s in currenthomework:
216 if self.flags & CLASS_REVIEW_STUDYLIST and s.flags & apps.learn.challenge.CHALLENGE_USE_STUDYLIST:
217 needstudylist = 0
218 elif self.flags & CLASS_AUTO_HOMEWORK:
219 needhomework = 0
220
221 if needhomework or needstudylist:
222 today = datetime.datetime.now(
223 datetime.timezone(
224 datetime.timedelta(hours = self.resettime)
225 )
226 )
227
228 today = today.replace(hour = 0, minute = 0, second = 0)
229
230 try:
231 tomorrow = today.replace(day = today.day + 1)
232 except Exception as e:
233 tomorrow = today.replace(month = today.month + 1, day = 1)
234
235 lessonidspool = self.autohomework['lessonids']
236 problemidspool = self.autohomework['problemids']
237 challengetypes = self.autohomework['challengetypes']
238
239 if needhomework and (lessonidspool or problemidspool) and challengetypes:
240 newhomework = apps.learn.challenge.learn_challenge()
241 newhomework.Set(
242 classid = self.rid,
243 challengetype = apps.learn.challenge.CHALLENGE_HOMEWORK,
244 starttime = today,
245 endtime = tomorrow,
246 challenge = random.choice(challengetypes),
247 flags = apps.learn.challenge.CHALLENGE_NEEDS_ANALYSIS,
248 )
249
250 newhomework.Set(**(self.autohomework))
251
252 if self.autohomework['problemselect'] == 'randomly':
253 newhomework.Set(
254 lessonids = [random.choice(lessonidspool)],
255 )
256 elif self.autohomework['problemselect'] == 'sequentially':
257 pasthomework = g.db.Find(
258 apps.learn.challenge.learn_challenge,
259 'WHERE classid = ? AND endtime < ? AND challengetype = ?',
260 [
261 self.rid,
262 currenttime,
263 apps.learn.challenge.CHALLENGE_HOMEWORK,
264 ],
265 orderby = 'endtime DESC',
266 )
267
268 for p in pasthomework:
269 if p.lessonids:
270 lastlessonid = p.lessonids[0]
271
272 try:
273 lastlessonpos = lessonidspool.index(lastlessonid)
274
275 if lastlessonpos == len(lessonidspool) - 1:
276 newhomework.Set(lessonids = [lessonidspool[0]])
277 else:
278 newhomework.Set(lessonids = [lessonidspool[lastlessonpos + 1]])
279 except Exception as e:
280 pass
281
282 if newhomework.lessonids:
283 cardtitles = apps.learn.card.GetCardTitles(g, [FromShortCode(x) for x in newhomework.lessonids])
284
285 newtitle = {}
286 newtitle[g.session.lang] = ', '.join(cardtitles.values())
287
288 newhomework.Set(title = newtitle)
289 else:
290 newtitle = {}
291 newtitle[g.session.lang] = g.T.dailychallenge
292
293 newhomework.Set(title = newtitle)
294
295 g.db.Save(newhomework)
296
297 if needstudylist and challengetypes:
298 newhomework = apps.learn.challenge.learn_challenge()
299
300 newtitle = {}
301 newtitle[g.session.lang] = g.T.studylist
302
303 newhomework.Set(**(self.autohomework))
304
305 newhomework.Set(
306 title = newtitle,
307 classid = self.rid,
308 lessonids = [],
309 problemids = [],
310 challengetype = apps.learn.challenge.CHALLENGE_HOMEWORK,
311 starttime = today,
312 endtime = tomorrow,
313 challenge = random.choice(challengetypes),
314 flags = apps.learn.challenge.CHALLENGE_NEEDS_ANALYSIS | apps.learn.challenge.CHALLENGE_USE_STUDYLIST,
315 )
316
317 g.db.Save(newhomework)
318
319 g.db.Commit()
320
Container class for students, courses, and teachers.
Definition: schoolclass.py:26
def __init__(self)
Initialize all fields at creation like a good programmer should.
Definition: schoolclass.py:58
def GenerateAutoHomework(self, g)
If autohomework is enabled, this function will generate a random challenge every day for your student...
Definition: schoolclass.py:190
def UpdateChallenges(self, g)
Look for any challenges that have passed endtime and calculate statistics for them.
Definition: schoolclass.py:62
def UpdateGrades(self, g, studentid)
Recalculate grades for studentid and update class rankings.
Definition: schoolclass.py:84
Base class for all database models.
Definition: modelbase.py:20
def Set(self, **kwargs)
We use this method instead of direct attribute access in order to keep track of what values have been...
Definition: modelbase.py:115
Throws an exception on blank values.
Definition: validators.py:43
Definition: db.py:1
def FromShortCode(code, chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_')
Turns a six character alphanumeric code into a 32-bit value.
Definition: utils.py:64
def ToShortCode(val, chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_')
Turns a 32-bit value into a six character alphanumeric code.
Definition: utils.py:36