Skip to content

Commit b33bc12

Browse files
committed
marks
1 parent ce81717 commit b33bc12

File tree

2 files changed

+281
-0
lines changed

2 files changed

+281
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ Some fairly standalone python scripts
1111

1212
* tinisDummyProgram: a demo program for tinisMultiRun
1313

14+
* marks: db for marking a task - assign marks for bits of the task to candidates, get stats. Designed to be called by other parts of the marking process, but also usable interactively.
15+
1416
* results: code for managing results of long neural network training runs

marks.py

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
import sqlite3
2+
import os, tabulate, numpy
3+
from six import print_
4+
5+
#Jeremy Reizenstein 2016
6+
#This is a simple module for managing the marks to parts of an assignment
7+
#It relies internally on a single sqlite database
8+
9+
#Each candidate can be awarded marks of either numeric or text types.
10+
#A numeric mark type has an OUT_OF value, and optionally a weighting. If a mark type has no weighting, it is taken to be the same as OUT_OF. weightings are used to calculate the total percentage marks.
11+
# The vast majority of public functions are just for reporting. Aside from initial table creation, the db is only ever modified by the last few functions in this file. They can be disabled by calling disableMutation
12+
#mark types can be tagged, this facility isn't really used
13+
#There is no string handling in this file - unicode in comes out as unicode. If you have a non-ascii character in a mark, maybe you need to add something like -fno-color-diagnostics in your call to the compiler
14+
#candidates can either be specified by candidate number or a string consisting of the candidate number followed by "d".
15+
16+
file = "marks.sqlite"
17+
if os.environ.has_key("MARKS_FILE"):
18+
file = os.environ["MARKS_FILE"]
19+
20+
con = sqlite3.connect(file)
21+
if 1:
22+
c = con.cursor()
23+
c.execute("CREATE TABLE IF NOT EXISTS MARKS (CANDIDATE INT, TYPE TEXT, VALUE, OVERRIDDEN)")
24+
c.execute("CREATE TABLE IF NOT EXISTS TYPES (NAME TEXT, DESCRIPTION TEXT, NOTES TEXT, OUT_OF REAL, WEIGHTING REAL, IMPORTANCE INT)")
25+
c.execute("CREATE TABLE IF NOT EXISTS TYPE_TAG (TAG TEXT, MARKTYPE TEXT)")
26+
con.commit()
27+
28+
def candidateToInt(candidate):
29+
if type(candidate)==str and candidate[-1]=="d":
30+
return int(candidate[:-1])
31+
return int(candidate)
32+
33+
def _getMark(markType, candidate):#candidate must be int
34+
i = con.execute("select value from marks where overridden is null and candidate = ? and type = ?",(candidate,markType)).fetchall()
35+
if len(i)==0:
36+
return None
37+
if len(i)>1:
38+
raise RuntimeError("more than one value")
39+
return i[0][0]
40+
41+
def truncate(s, n=10):
42+
if (str==type(s) or unicode == type(s)) and len(s)>n:
43+
return s[:n]
44+
return s
45+
46+
def getMark(markType, candidate):
47+
assertMarkType(markType)
48+
return _getMark(markType, candidateToInt(candidate))
49+
50+
def tab(tag=None):
51+
headers = listMarkTypes() if (tag is None) else listMarksWithTag(tag)
52+
rows = listCandidates()
53+
out = [[r]+[truncate(_getMark(markType, r)) for markType in headers] for r in rows]
54+
headers = [truncate(i,12) for i in headers]
55+
print (tabulate.tabulate(out, headers = ["candidate"] + headers))
56+
57+
def checkAllMarksAssigned():
58+
cs = listCandidates()
59+
for markType in listMarkTypes():
60+
for candidate in cs:
61+
if _getMark(markType,candidate) is None:
62+
print ("no "+markType+" for "+str(candidate))
63+
64+
def tabNumeric(file = False):
65+
# total=True
66+
headers = listNumeric()
67+
rows = listCandidates()
68+
out = [[r]+[truncate(_getMark(markType, r)) for markType in headers] + [candidateTotalPercentage_(r)] for r in rows]
69+
headers.append("TOTAL")
70+
tab = tabulate.tabulate(out, headers = ["candidate"] + headers)
71+
if file:
72+
with open("numericSummary.txt","w") as f:
73+
print_(tab,file=f)
74+
else:
75+
print (tab)
76+
77+
def listMarkTypes():
78+
i = con.execute("select name from types ").fetchall()
79+
return [ii for (ii,) in i]
80+
def listNumeric():
81+
i = con.execute("select name from types where OUT_OF is not null").fetchall()
82+
return [ii for (ii,) in i]
83+
def listNonNumeric():
84+
i = con.execute("select name from types where OUT_OF is null").fetchall()
85+
return [ii for (ii,) in i]
86+
def listCandidates():
87+
i = con.execute("select distinct(candidate) from marks where OVERRIDDEN is null order by candidate").fetchall()
88+
return [ii for (ii,) in i]
89+
def listTags():
90+
i = con.execute("select distinct(tag) from type_tag order by tag").fetchall()
91+
return [ii for (ii,) in i]
92+
def listTagsOfMark(marktype):
93+
i = con.execute("select tag from type_tag where marktype = ? order by tag", (marktype,)).fetchall()
94+
return [ii for (ii,) in i]
95+
def listMarksWithTag(tag):
96+
i = con.execute("select marktype from type_tag where tag = ?", (tag,)).fetchall()
97+
return [ii for (ii,) in i]
98+
99+
def hasMark(candidate, markType):
100+
assertMarkType(markType)
101+
return _getMark(markType, candidateToInt(candidate)) is not None
102+
def hasTag(mark, tag):
103+
return (1,) == con.execute("select count(*) from type_tag where marktype = ? and tag = ?", (mark,tag)).fetchone()
104+
105+
def isMarkType(marktype):
106+
check = con.execute("SELECT COUNT(*) FROM TYPES WHERE NAME = ?", (marktype,)).fetchone()
107+
return check == (1,)
108+
def assertMarkType(marktype):
109+
if not isMarkType(marktype):
110+
raise RuntimeError(str(marktype) + " is not a type of mark I know of")
111+
112+
def isUsedTag(tag):
113+
return (0,) != con.execute("select count(*) from type_tag where tag = ?", (tag,)).fetchone()
114+
115+
def tagTable():
116+
headers = listTags()
117+
rows = listMarkTypes()
118+
out = [ [r]+["+" if hasTag(r,t) else " " for t in headers] for r in rows]
119+
print (tabulate.tabulate(out, headers = [""]+headers))
120+
121+
def m():
122+
return con.execute("select * from types").fetchall()
123+
def n():
124+
return con.execute("select * from marks").fetchall()
125+
def t():
126+
return con.execute("select * from type_tag").fetchall()
127+
128+
#def out_of_total():
129+
# return con.execute("select sum(OUT_OF) from types").fetchone()[0]
130+
def candidateTotalPercentage_(candidate): #candidate an int
131+
total = 0.0
132+
denominator = 0.0
133+
for markType in listNumeric():
134+
mark = _getMark(markType,candidate)
135+
if mark is None:
136+
print ("no "+markType+" for "+str(candidate))
137+
out_of,weighting = con.execute("select OUT_OF, WEIGHTING from types where name = ?",(markType,)).fetchone()
138+
if weighting is None:
139+
weighting = out_of
140+
denominator = denominator + weighting
141+
total = total + float(mark) * float(weighting) / float(out_of)
142+
return 100*total / denominator
143+
144+
def totalWeighting():
145+
denominator = 0.0
146+
for markType in listNumeric():
147+
out_of,weighting = con.execute("select OUT_OF, WEIGHTING from types where name = ?",(markType,)).fetchone()
148+
if weighting is None:
149+
weighting = out_of
150+
denominator = denominator + weighting
151+
return denominator
152+
153+
def tableWeightings():
154+
denominator = 0.0
155+
a=[]
156+
for markType in listNumeric():
157+
out_of,weighting = con.execute("select OUT_OF, WEIGHTING from types where name = ?",(markType,)).fetchone()
158+
if weighting is None:
159+
weighting = out_of
160+
denominator = denominator + weighting
161+
a.append([markType,out_of,weighting])
162+
print ("totalWeighting: "+str(denominator))
163+
for aa in a:
164+
aa.append(100*aa[-1]/denominator)
165+
print(tabulate.tabulate(a,headers=("name","out of","weight","percentage")))
166+
167+
def candidateTotalPercentage(candidate, decimalPlaces = 0):
168+
return round(candidateTotalPercentage_(candidateToInt(candidate)),decimalPlaces)
169+
170+
def distribution():
171+
a=numpy.round(numpy.sort([candidateTotalPercentage_(c) for c in listCandidates()]))
172+
print_ (numpy.mean(a), numpy.std(a))
173+
return a
174+
175+
def printMarks(markType):
176+
for c in listCandidates():
177+
print_ (c, _getMark(markType,c))
178+
179+
def writeSummary():
180+
with open("MarkingSummary.txt","w") as f:
181+
num = listNumeric()
182+
outofs=dict()
183+
def p(x): print_(x, file=f)
184+
p("Marks ")
185+
totalWeight=totalWeighting()
186+
for markType in num:
187+
out_of,weighting, descr = con.execute("select OUT_OF, WEIGHTING, DESCRIPTION from types where name = ?",(markType,)).fetchone()
188+
outofs[markType]=out_of
189+
if descr is None:
190+
descr = ""
191+
elif len(descr)>1:
192+
descr = "("+descr+")"
193+
if weighting is None:
194+
weighting = out_of
195+
percentage = 100.0 * weighting / totalWeight
196+
print_(markType,descr, "out of", out_of, " worth "+str(percentage)+"%", file=f)
197+
for candidate in listCandidates():
198+
print_("\n",candidate, ": ", round(candidateTotalPercentage_(candidate),1), "%", file=f, sep="")
199+
for markType in listMarkTypes():
200+
if markType in num:
201+
print_(markType,": ",_getMark(markType,candidate),"/", outofs[markType],file=f,sep="")
202+
else:
203+
res = _getMark(markType,candidate)
204+
if(len(unicode(res))>1):
205+
206+
p(markType)
207+
p(res.encode("iso-8859-1"))
208+
209+
#Mutation functions are below here
210+
mutationEnabled = True
211+
212+
def disableMutation():
213+
global mutationEnabled
214+
mutationEnabled=False
215+
216+
def startMutation():
217+
if not mutationEnabled:
218+
raise RuntimeError("no mutation allowed")
219+
220+
def newMark(name, description, outOf = None): #supply outOf if the mark is to be a numeric value
221+
startMutation()
222+
if isMarkType(name):
223+
raise RuntimeError(str(name) + " is a type of mark I already know of")
224+
c=con.cursor()
225+
c.execute("INSERT INTO TYPES (NAME, DESCRIPTION, NOTES, OUT_OF, WEIGHTING, IMPORTANCE) VALUES (?,?,'',?,NULL,1)",(name,description,outOf))
226+
con.commit()
227+
228+
def setWeighting(markType, weighting):
229+
startMutation()
230+
if markType not in listNumeric():
231+
raise RuntimeError(str(markType) + " is not a type of numeric mark I already know of")
232+
if type(weighting) not in (type(None), float,int):
233+
raise RuntimeError("not a good weight type")
234+
c.execute("update types set WEIGHTING=? WHERE NAME = ?",(weighting,markType))
235+
con.commit()
236+
237+
def tagMark(marktype, tag, remove = False):
238+
startMutation()
239+
assertMarkType(marktype)
240+
if (not remove) and hasTag(marktype, tag):
241+
raise RuntimeError(str(marktype) + " is already tagged with "+tag)
242+
if remove and not hasTag(marktype, tag):
243+
raise RuntimeError(str(marktype) + " is not already tagged with "+tag)
244+
if remove:
245+
con.execute("delete from type_tag where marktype = ? and tag = ?", (marktype, tag))
246+
else:
247+
con.execute("insert into type_tag (marktype, tag) values (?,?)", (marktype, tag))
248+
con.commit()
249+
250+
def _removeMark(candidate, marktype, c):
251+
c.execute ("UPDATE MARKS SET OVERRIDDEN = DATETIME('NOW') WHERE CANDIDATE = ? AND TYPE = ? and OVERRIDDEN is NULL", (candidate, marktype))
252+
253+
def removeMark(candidate, marktype):
254+
startMutation()
255+
c = con.cursor()
256+
assertMarkType(marktype)
257+
if not hasMark(candidate, markType):
258+
raise RuntimeError(str(marktype) + " has not been given to "+candidate)
259+
_removeMark(candidateToInt(candidate), marktype, c)
260+
con.commit()
261+
262+
def assignMark(candidate, marktype, value, force = False):
263+
startMutation()
264+
candidate = candidateToInt(candidate)
265+
c = con.cursor()
266+
assertMarkType(marktype)
267+
isNumeric = c.execute("SELECT OUT_OF is not NULL from TYPES where name = ?", (marktype,)).fetchone()
268+
isNumeric = isNumeric != (0,)
269+
if isNumeric and type(value) == str:
270+
raise RuntimeError(str(marktype) + " is a numeric mark. It cannot be given this value.")
271+
#perhaps should check numeric marks are in range.
272+
if _getMark(marktype,candidate) is not None:
273+
if force:
274+
_removeMark(candidate, marktype, c)
275+
else:
276+
raise RuntimeError(str(marktype) + " has already been assigned to "+str(candidate))
277+
c.execute("INSERT INTO MARKS (CANDIDATE, TYPE, VALUE, OVERRIDDEN) VALUES (?,?,?,NULL)",(candidate,marktype,value))
278+
con.commit()
279+

0 commit comments

Comments
 (0)