Created
September 8, 2020 05:30
-
-
Save fcayci/d5dd85fb9366b5e4f760551ea7e9b880 to your computer and use it in GitHub Desktop.
autograde midterm 2020
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# grade midterm 2020 | |
# requirements: | |
# python3 | |
# pip3 install boolean.py (boolean evaluation) | |
# matplotlib for plotting | |
# numpy - a couple small parts | |
import logging | |
# where do you wanna log? go to DEBUG / INFO / WARNING | |
#logging.basicConfig(filename='grading.log', format='%(levelname)s:%(message)s', level=logging.DEBUG) | |
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) | |
# fixed values for problems | |
tsetup = 40 | |
thold = 75 | |
tffpq = 50 | |
tffcq = 25 | |
# question numbers and their relative points | |
quests = { | |
'q4' : 1, 'q5' : 1, 'q6' : 1, 'q7' : 1, 'q8' : 1, 'q9' : 2, | |
'q10': 1, 'q11': 2, 'q12': 1, 'q13': 2, 'q14': 1, 'q15': 2, | |
'q16': 1, 'q17': 3, 'q18': 10, 'q19': 10, 'q20': 3, 'q21': 1, | |
'q22': 3, 'q23': 1, 'q24': 5, 'q25': 4, 'q26': 4, 'q27': 5, | |
'q28': 2, 'q29': 2, 'q30': 5, 'q31': 3, 'q32': 2, 'q33': 9, | |
'q34': 2, 'q35': 3, 'q36': 4, 'q37': 2 | |
} | |
# total number of questions | |
NUM_OF_QUESTIONS = len(quests) | |
# boolean dict for evaluation | |
bools = { | |
'0' : '(~a & ~b & ~c & ~d)', | |
'1' : '(~a & ~b & ~c & d)', | |
'2' : '(~a & ~b & c & ~d)', | |
'3' : '(~a & ~b & c & d)', | |
'4' : '(~a & b & ~c & ~d)', | |
'5' : '(~a & b & ~c & d)', | |
'6' : '(~a & b & c & ~d)', | |
'7' : '(~a & b & c & d)', | |
'8' : '( a & ~b & ~c & ~d)', | |
'9' : '( a & ~b & ~c & d)', | |
'a' : '( a & ~b & c & ~d)', | |
'b' : '( a & ~b & c & d)', | |
'c' : '( a & b & ~c & ~d)', | |
'd' : '( a & b & ~c & d)', | |
'e' : '( a & b & c & ~d)', | |
'f' : '( a & b & c & d)' | |
} | |
# sekil1 | |
# shows the paths between ports | |
# will be used to calculate the paths | |
s1paths = { | |
'ax' : 'tand + txor', | |
'az' : 'tand + tnot', | |
'xx' : 'tnot + txor', | |
'xy' : 'tnot + tor', | |
'xz' : 'tnot + tand + tnot', | |
'yy' : 'tor + tnot + tor', | |
'zx' : 'tand + txor', | |
'zy' : 'tor + tnot + tor' | |
} | |
def boolean_compare(r, e): | |
'''compare two boolean strings. First check if they are the same boolean expressions | |
if not, boolean module does not provide an equality check. so just generate 16 different | |
true false inputs, and feed into both equations to see if they produce the same results''' | |
import boolean | |
algebra = boolean.BooleanAlgebra() | |
try: | |
r = algebra.parse(r) | |
except: | |
# some other character is in use | |
# auto fail | |
return 0, 17 | |
e = algebra.parse(e) | |
# lets try our luck | |
if r == e: | |
return 1, 0 | |
# damn | |
a, b, c, d = algebra.symbols('a','b','c','d') | |
t = algebra.TRUE | |
f = algebra.FALSE | |
hacks = { | |
'0' : [f, f, f, f], | |
'1' : [f, f, f, t], | |
'2' : [f, f, t, f], | |
'3' : [f, f, t, t], | |
'4' : [f, t, f, f], | |
'5' : [f, t, f, t], | |
'6' : [f, t, t, f], | |
'7' : [f, t, t, t], | |
'8' : [t, f, f, f], | |
'9' : [t, f, f, t], | |
'10' : [t, f, t, f], | |
'11' : [t, f, t, t], | |
'12' : [t, t, f, f], | |
'13' : [t, t, f, t], | |
'14' : [t, t, t, f], | |
'15' : [t, t, t, t] | |
} | |
# replace all the terms with true or false, then simplify them | |
# simplification should return 0 or 1 | |
# do that for all input combinations, and count the number of mismatches | |
err = 0 | |
for i in range(16): | |
rres = r.subs({a:hacks[str(i)][0], | |
b:hacks[str(i)][1], | |
c:hacks[str(i)][2], | |
d:hacks[str(i)][3]}).simplify() | |
eres = e.subs({a:hacks[str(i)][0], | |
b:hacks[str(i)][1], | |
c:hacks[str(i)][2], | |
d:hacks[str(i)][3]}).simplify() | |
if eres != rres: | |
err += 1 | |
if err == 0: | |
return 1, err | |
else: | |
return 0, err | |
# all the question results return string | |
def q4(r): | |
'''Rank inizi ikilik sistemde 16-bit gösteriniz. (0b kullanmayınız. Örn. 1100000000010010)''' | |
return format(r, '016b') | |
def q5(r): | |
'''Rank inizi onluk sistemde gösteriniz. (Örn. 49170)''' | |
return str(r) | |
def q6(r): | |
'''16-bit rank << 5, hex olarak yazınız. (0x kullanmayınız. Örn. C012)''' | |
# make sure only 4 hex digits return | |
return format(r << 5, '04x')[-4:] | |
def q7(r): | |
'''16-bit rank >> 3, hex olarak yazınız. (0x kullanmayınız. Örn. C012)''' | |
return format(r >> 3, '04x') | |
def q8(r): | |
'''16-bit rank >>> 11, hex olarak yazınız. (0x kullanmayın. Örn. C012)''' | |
# arithmetic shift is hacky | |
# create a list of digits, then check the first item in the list | |
a = list(str(format(r >> 11, '016b'))) | |
if (r & 0x8000): | |
for i in range(11): | |
a[i] = '1' | |
# convert the list back to string, and format it back to hex | |
return format(int(''.join(a), 2), '04x') | |
def q9(r): | |
'''16-bit rank + 0x7F1A işleminin sonucunu hesaplayınız. (0x kullanmayınız. Örn. C012)''' | |
# last four hex digits | |
return format(r + 0x7F1A, '04x')[-4:] | |
def q10(r): | |
'''16-bit rank + 0x7F1A işlemi sonucu NVC flaglarını hesaplayınız. (NVC sırasıyla giriniz. Örn. 010)''' | |
# 17. bit will hold the carry | |
x = format(r + 0x7F1A, '017b') | |
r = format(r, '016b') | |
c = x[0] | |
# overflow check with 16.bits | |
if r[0] == '0' and x[1] == '1': | |
v = '1' | |
else: | |
v = '0' | |
# negative check with 16.bit | |
n = x[1] | |
return n + v + c | |
def q11(r): | |
'''16-bit rank + 0xC2F5 işleminin 16-bit sonucunu hesaplayınız. (0x kullanmayınız. Örn. C012)''' | |
# last four hex digits | |
return format(r + 0xC2F5, '04x')[-4:] | |
def q12(r): | |
'''16-bit rank + 0xC2F5 işlemi sonucu NVC flaglarını hesaplayınız. (NVC sırasıyla giriniz. Örn. 010)''' | |
# 17. bit will hold the carry | |
x = format(r + 0xC2F5, '017b') | |
r = format(r, '016b') | |
c = x[0] | |
# overflow check with 16.bits | |
if r[0] == '1' and x[1] == '0': | |
v = '1' | |
else: | |
v = '0' | |
# negative check with 16.bit | |
n = x[1] | |
return n + v + c | |
def q13(r): | |
'''16-bit rank - 0x4E3B işleminin 16-bit sonucunu hesaplayınız. (0x kullanmayınız. Örn. C012)''' | |
x = r - 0x4E3B | |
# negative is tricky. it will go -, so take mod 2**16 | |
if (x < 0): | |
x = int(format(x % (1 << 16), '016b'), base=2) | |
return format(x, '04x') | |
def q14(r): | |
'''16-bit rank - 0x4E3B işlemi sonucu NVC flaglarını hesaplayınız. (NVC sırasıyla giriniz. Örn. 010)''' | |
x = r - 0x4E3B | |
# no carry if it is less than 0, (!borrow) | |
if (x < 0): | |
x = int(format(x % (1 << 16), '016b'), base=2) | |
c = '0' | |
else: | |
c = '1' | |
# reformat it to 16 bits | |
x = format(x, '016b') | |
r = format(r, '016b') | |
# overflow check with 16.bits | |
if r[0] == '1' and x[0] == '0': | |
v = '1' | |
else: | |
v = '0' | |
# negative check with 16.bits | |
n = x[0] | |
return n + v + c | |
def q15(r): | |
'''16-bit rank - 0x9BD2 işleminin 16-bit sonucunu hesaplayınız. (0x kullanmayınız. Örn. C012)''' | |
x = r - 0x9BD2 | |
# negative is tricky. it will go -, so take mod 2**16 | |
if (x < 0): | |
x = int(format(x % (1 << 16), '016b'), base=2) | |
return format(x, '04x') | |
def q16(r): | |
'''16-bit rank - 0x9BD2 işlemi sonucu NVC flaglarını hesaplayınız. (NVC sırasıyla giriniz. Örn. 010)''' | |
x = r - 0x9BD2 | |
# no carry if it is less than 0, (!borrow) | |
if (x < 0): | |
x = int(format(x % (1 << 16), '016b'), base=2) | |
c = '0' | |
else: | |
c = '1' | |
# reformat it to 16 bits | |
x = format(x, '016b') | |
r = format(r, '016b') | |
# overflow check with 16.bits | |
if r[0] == '0' and x[0] == '1': | |
v = '1' | |
else: | |
v = '0' | |
# negative check with 16.bits | |
n = x[0] | |
return n + v + c | |
def q17(r): | |
'''Rank inizin odd parity sini hesaplayınız. (Örn. 1)''' | |
x = format(r, '016b') | |
p = 0 | |
# xor all digits and for odd parity invert the result (last xor) | |
for i in x: | |
p = p ^ int(i) | |
return str(p ^ 1) | |
def q18(r): | |
'''Rank inizin her hex digitini minterm olarak alıp, 4-giriş | |
(ABCD), 1-çıkış (X) doğruluk tablosu çıkarınız. | |
K-map ile en sade şeklini gerçekleştiriniz. | |
SOP formunu SystemVerilog syntax ına uygun olarak giriniz.''' | |
import boolean | |
algebra = boolean.BooleanAlgebra() | |
# minterm | |
# read the rank as a hex digit list | |
# bring the corresponding boolean expressions from the bools table | |
# then create a SOP representation with | stiching | |
r = list(format(r, '04x').lower()) | |
exp = ' | '.join(map(lambda x: bools[str(x)], r)) | |
# simplify and return the simplified expression | |
e = algebra.parse(exp, simplify=True) | |
return str(e) | |
def q19(r): | |
'''Rank inizin her hex digitini maxterm olarak alıp, 4-giriş | |
(ABCD), 1-çıkış (X) doğruluk tablosu çıkarınız. | |
K-map ile en sade şeklini gerçekleştiriniz. | |
SOP formunu SystemVerilog syntax ına uygun olarak giriniz.''' | |
import boolean | |
algebra = boolean.BooleanAlgebra() | |
# maxterm | |
# read the rank as a hex digit list | |
# since this is max term, exclude the ones in the rank | |
# bring the rest of the boolean expressions from the bools table | |
# then create a SOP representation with | stiching | |
r = list(format(r, '04x').lower()) | |
rb = [format(i, '1x').lower() for i in range(16) if format(i, '1x').lower() not in r] | |
exp = ' | '.join(map(lambda x: bools[str(x)], rb)) | |
# simplify and return the simplified expression | |
e = algebra.parse(exp, simplify=True) | |
return str(e) | |
def q20(r): | |
'''Şekil 1 de verilen devrenin propagation gecikmesini hesaplayınız.''' | |
r = format(r, '04x') | |
# 6x + 10 is the formula. waste to calculate for each problem, | |
# but the problems are self contained this way | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
tnot = 6*int(r[3], 16) + 10 | |
# propagation delay | |
# evaluate teh s1paths dict values with the given tand tor txor and tnot valus | |
# return the max number | |
x = {} | |
for path, v in s1paths.items(): | |
x[path] = eval(v) | |
return str(max(x.values())) | |
def q21(r): | |
'''Şekil 1 de verilen devrenin propagation gecikmesinin hangi portlar arasında olduğunu giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
tnot = 6*int(r[3], 16) + 10 | |
# propagation delay paths | |
# evaluate teh s1paths dict values with the given tand tor txor and tnot valus | |
# return the paths as a list that give max numbers | |
x = {} | |
for path, v in s1paths.items(): | |
x[path] = eval(v) | |
m = max(x.values()) | |
res = [] | |
for i, v in x.items(): | |
if v == m: | |
res.append(i) | |
return res | |
def q22(r): | |
'''Şekil 1 de verilen devrenin contamination gecikmesini hesaplayınız.''' | |
r = format(r, '04x') | |
tand = (6*int(r[0], 16) + 10)//2 | |
tor = (6*int(r[1], 16) + 10)//2 | |
txor = (6*int(r[2], 16) + 10)//2 | |
tnot = (6*int(r[3], 16) + 10)//2 | |
# contamination delay | |
# evaluate teh s1paths dict values with the given tand tor txor and tnot valus | |
# return the min number | |
x = {} | |
for path, v in s1paths.items(): | |
x[path] = eval(v) | |
return str(min(x.values())) | |
def q23(r): | |
'''Şekil 1 de verilen devrenin contamination gecikmesinin hangi portlar arasında olduğunu giriniz.''' | |
r = format(r, '04x') | |
tand = (6*int(r[0], 16) + 10)//2 | |
tor = (6*int(r[1], 16) + 10)//2 | |
txor = (6*int(r[2], 16) + 10)//2 | |
tnot = (6*int(r[3], 16) + 10)//2 | |
# contamination delay paths | |
# evaluate teh s1paths dict values with the given tand tor txor and tnot valus | |
# return the paths as a list that give min numbers | |
x = {} | |
for path, v in s1paths.items(): | |
x[path] = eval(v) | |
m = min(x.values()) | |
res = [] | |
for i, v in x.items(): | |
if v == m: | |
res.append(i) | |
return res | |
def q24(r): | |
'''Şekil 1 de verilen devrenin çalıştırılabilir maximum frekansını hesaplayıp | |
MegaHertz cinsinden tam sayı olarak giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
tnot = 6*int(r[3], 16) + 10 | |
# max freq depend on the propagation delay (max) | |
x = {} | |
for path, v in s1paths.items(): | |
x[path] = eval(v) | |
# append tffpq and tsetup, (no jitter) | |
# convert tc to mhz (int) and return as string | |
m = max(x.values()) | |
tc = m + tffpq + tsetup | |
freq = int(1/tc * 10**6) # megaherz | |
return str(freq) | |
def q25(r): | |
'''Şekil 2 de verilen Full Adder devresini kullanarak Şekil 3 de | |
verildiği gibi 3-bit toplayıcı / çıkarıcı devresi tasarladılar. | |
Şekil 3a'da bu devrenin kendisi, 3b'de aynı devrenin giriş ve | |
çıkışlarına register eklenmiş hali gösterilmektedir. Tablo 1 deki | |
değerlere göre devrenin propagation gecikmesini hesaplayınız.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
#tnot = 6*int(r[3], 16) + 10 | |
# first FA | |
# max will be y0 -> cout1 signal since it encapsulates | |
# all others. Will be the same for all people | |
m1 = txor + txor + tand + tor | |
# second FA | |
# max will be cin -> cout signal, since it will be longer than | |
# y1 -> cout2 which is xor + xor + and + or | |
m2 = tand + tor | |
# third FA | |
# max will be cin -> s signal, (we dont care about the cout) | |
m3 = txor | |
# total delay | |
m = m1 + m2 + m3 | |
return str(m) | |
def q26(r): | |
'''Şekil 3 de verilen devrenin contamination gecikmesini hesaplayınız.''' | |
r = format(r, '04x') | |
#tand = (6*int(r[0], 16) + 10)//2 | |
#tor = 6*int(r[1], 16) + 10)//2 | |
txor = (6*int(r[2], 16) + 10)//2 | |
#tnot = (6*int(r[3], 16) + 10)//2 | |
# contamination delay is shortest path, | |
# which is min(c0 -> s0, x0 -> s0) since first is one xor, | |
# that is faster (c0 -> s0) | |
m = txor | |
return str(m) | |
def q27(r): | |
'''Şekil 3 de verilen devrenin çalıştırılabilir maximum frekansını | |
hesaplayıp MegaHertz cinsinden tam sayı olarak giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
#tnot = 6*int(r[3], 16) + 10 | |
m1 = txor + txor + tand + tor | |
m2 = tand + tor | |
m3 = txor | |
m = m1 + m2 + m3 | |
# return the max freq | |
tc = m + tffpq + tsetup | |
freq = int(1/tc * 10**6) # megaherz | |
return str(freq) | |
def q28(r): | |
'''Şekil 3 de verilen devrenin throughput unu hesaplayıp | |
Megabits per second cinsinden tam sayı olarak giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
#tnot = 6*int(r[3], 16) + 10 | |
m1 = txor + txor + tand + tor | |
m2 = tand + tor | |
m3 = txor | |
m = m1 + m2 + m3 | |
tc = m + tffpq + tsetup | |
freq = int(1/tc * 10**6) # megaherz | |
throuput = 3 * freq # 3 bits come out at this freq | |
return str(throuput) | |
def q29(r): | |
'''Şekil 3 de verilen devrenin latency sini hesaplayıp | |
picosecond cinsinden tam sayı olarak giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
#tnot = 6*int(r[3], 16) + 10 | |
m1 = txor + txor + tand + tor | |
m2 = tand + tor | |
m3 = txor | |
m = m1 + m2 + m3 | |
tc = m + tffpq + tsetup | |
# the incoming bits will wait 1 clock cycle to go out | |
return str(tc) | |
def q30(r): | |
'''Firmadaki bir mühendis, sistemi geliştirdiğini ileri | |
sürerek Şekil 4 de gösterildiği gibi bir devre tasarımı yaptı. | |
Yeni devrenin çalıştırılabilir maximum frekansını hesaplayıp | |
MegaHertz cinsinden tam sayı olarak giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
#tnot = 6*int(r[3], 16) + 10 | |
# max will be either from Y -> S which is 3 x xors | |
# or from Y -> cout which is 2 x xors + and + or | |
m1 = txor + txor + txor | |
m2 = txor + txor + tand + tor | |
m = max(m1, m2) | |
tc = m + tffpq + tsetup | |
freq = int(1/tc * 10**6) # megaherz | |
return str(freq) | |
def q31(r): | |
'''Şekil 4 de verilen devrenin throughput unu hesaplayıp | |
Megabits per second cinsinden tam sayı olarak giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
#tnot = 6*int(r[3], 16) + 10 | |
m1 = txor + txor + txor | |
m2 = txor + txor + tand + tor | |
m = max(m1, m2) | |
tc = m + tffpq + tsetup | |
freq = int(1/tc * 10**6) # megaherz | |
throuput = 3 * freq # 3 bits come out at this freq | |
return str(throuput) | |
def q32(r): | |
'''Şekil 4 de verilen devrenin latency sini hesaplayıp | |
picosecond cinsinden tam sayı olarak giriniz.''' | |
r = format(r, '04x') | |
tand = 6*int(r[0], 16) + 10 | |
tor = 6*int(r[1], 16) + 10 | |
txor = 6*int(r[2], 16) + 10 | |
#tnot = 6*int(r[3], 16) + 10 | |
m1 = txor + txor + txor | |
m2 = txor + txor + tand + tor | |
m = max(m1, m2) | |
tc = m + tffpq + tsetup | |
# the incoming bits will wait 3 clock cycles to go out | |
return str(3 * tc) | |
def q33(r): | |
'''Şekil 5 te verilen SystemVerilog kod bloğunun ilk clk rising edge | |
inde (t0) devreye rankinizi verdiğinizi varsayarsak, 25 clock rising | |
edge sonrasında (t25) q çıkışını hesaplayınız.''' | |
r = format(r, '016b') | |
f = lambda r: str(int(r[0]) ^ int(r[3]) ^ int(r[7]) ^ int(r[10])) | |
# that is lfsr. a random number generator | |
# it will shift out the msb and shift in a new lsb in each clock cycle | |
# the calculation depends on the formula in f function | |
# do that for 25 cycles | |
for i in range(25): | |
n = f(r) | |
r = r[1:] + n | |
r = format(int(r, 2), '04x') | |
return r | |
def q34(r): | |
'''Şekil 6 da verilen SystemVerilog kod bloğunun a hesabi''' | |
r = format(r, '016b') | |
# simple concat | |
return r[2:] + r[:2] | |
def q35(r): | |
'''Şekil 6 da verilen SystemVerilog kod bloğunun b hesabi''' | |
r = format(r, '016b') | |
x = str(int(r[3]) & int(r[4]) & int(r[5]) & int(r[6])) | |
return 4*x | |
def q36(r): | |
'''Şekil 6 da verilen SystemVerilog kod bloğunun c hesabi''' | |
r = format(r, '016b') | |
# two conditional statements | |
if r[2] == '1': | |
x = r[0] + r[14:] + str(int(r[10]) & int(r[11])) | |
elif r[12] == '1': | |
x = str(int(r[7]) ^ int(r[8]) ^ int(r[9])) + r[14:] + r[0] | |
else: | |
x = r[7] + str(int(r[13]) | int(r[14]) | int(r[15])) + r[:2] | |
return x | |
def q37(r): | |
'''Şekil 6 da verilen SystemVerilog kod bloğunun d hesabi''' | |
r = format(r, '016b') | |
# if any of the xors produce a 1 or will produce a 1 | |
for i in range(8): | |
if int(r[15-i]) ^ int(r[i]): | |
return '1' | |
return '0' | |
def read_csv(fname): | |
'''generic csv read function. omit the first row | |
return the rest as a list of rows''' | |
import csv | |
with open(fname, 'r') as f: | |
n = [] | |
s = csv.reader(f, delimiter=',') | |
for i, row in enumerate(s): | |
# dont read the first row | |
if i == 0: | |
continue | |
n.append(row) | |
return n | |
def clean_exam(r): | |
'''get rid of the points and feedback sections from the rows. | |
this is specific to ms forms result format''' | |
start = 7 | |
f = [] | |
for row in r: | |
n = [] | |
for i, ir in enumerate(row[start:]): | |
if i % 3 == 0: | |
n.append(ir.strip()) # strip whitespace from the cells if there are any | |
f.append(n) | |
return f | |
def generate_answers(rname, fname, gennum=False): | |
'''generate rank - answers sheet, optionally add student id. | |
standalone function''' | |
import csv | |
ranks = read_csv(rname) | |
ranks = {l[0] : l[1] for l in ranks} # convert ranks to dict | |
with open(fname, 'w') as f: | |
s = csv.writer(f, delimiter=',') | |
if gennum: | |
x = ['Ogrenci Numarasi', 'Rank'] | |
else: | |
x = ['Rank'] | |
x.extend(list(quests.keys())) | |
s.writerow(x) | |
# iterate through ranks | |
for num, r in ranks.items(): | |
rd = int(r, 16) | |
if gennum: | |
ans = [num, r] | |
else: | |
ans = [r] | |
# evaluate the answers for a given rank | |
for i, (q, p) in enumerate(quests.items()): | |
ans.append(eval(q)(rd)) | |
s.writerow(ans) | |
class Student(): | |
def __init__(self, name, number, rank): | |
'''one class to rule them all''' | |
self.name = name.lower() | |
self.number = number | |
self.rank = int(rank, 16) | |
self.exp = NUM_OF_QUESTIONS * [None] | |
self.calculate_expected() | |
self.ans = NUM_OF_QUESTIONS * [None] | |
self.points = NUM_OF_QUESTIONS * [0] | |
def calculate_expected(self): | |
'''calculate the expected answers''' | |
r = self.rank | |
for i, (q, p) in enumerate(quests.items()): | |
self.exp[i] = eval(q)(r) | |
def evaluate_questions(self): | |
r = self.rank | |
logging.debug('Evaluating : %s with rank %s', self.name, format(self.rank, '04x')) | |
for i, (q, p) in enumerate(quests.items()): | |
# evaluate all questions, tho with different criteria | |
if len(self.ans[i]): | |
# exact matches with hex | |
if i in [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 29, 30, 31, 32, 33]: | |
# if exact match great! | |
if self.ans[i] == self.exp[i]: | |
self.points[i] = p | |
else: | |
try: | |
# this will also match 0 with 0000 but oh well. let the poor souls be happy | |
if int(self.ans[i], 16) == int(self.exp[i], 16): | |
self.points[i] = p | |
logging.debug('hex match: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
else: | |
logging.debug('hex NO match: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
except: | |
logging.debug('hex ERR: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
# exact matches with int | |
elif i in [1, 16, 18, 21, 22]: | |
# if exact match great! | |
if self.ans[i] == self.exp[i]: | |
self.points[i] = p | |
else: | |
logging.debug('str NO match: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
# %.05 tolerance on int conversions | |
elif i in [20, 23, 24, 25, 26, 27, 28]: | |
# if exact match great! | |
if self.ans[i] == self.exp[i]: | |
self.points[i] = p | |
else: | |
e = int(self.exp[i]) | |
try: | |
a = int(self.ans[i]) | |
if a < e+.05*e and a > e-.05*e: | |
self.points[i] = p | |
logging.debug('tilda match: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
else: | |
logging.debug('tilda NO match: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
except: | |
logging.debug('tilda ERR: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
# evaluate boolean | |
elif i in [14, 15]: | |
logging.debug('boolean comparison: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
# correct? and number of fails | |
crct, err = boolean_compare(self.ans[i], self.exp[i]) | |
# 17 is special case where there is an alian character in the answer | |
if err == 17: | |
logging.debug('boolean ERR: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
logging.debug(' ? %d, err: %d', crct, err) | |
if crct: | |
self.points[i] = p | |
# any in the array is acceptable | |
elif i in [17, 19]: | |
if self.ans[i] in self.exp[i]: | |
self.points[i] = p | |
logging.debug('array match: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
else: | |
logging.debug('array NO match: rank %s on quest: %s. cevap: %s, expected: %s', format(self.rank, '04x'), q, self.ans[i], self.exp[i]) | |
else: | |
# if any questions are left come here | |
# should not come here | |
print('WHERIS ', i) | |
else: | |
self.points[i] = 0 | |
logging.info('%s got: %d points', self.name, sum(self.points)) | |
logging.info('|--> point distribution: %s', self.points) | |
def create_answer_sheet(rankname): | |
'''generates answers based on the ranks''' | |
answers = 'elm234_arasinav2020_cevap_anahtari.csv' | |
generate_answers(rankname, answers) | |
def create_score_sheet(studs, sname, detailed=False): | |
'''create the grading sheet to hold the scores and answers and points''' | |
import csv | |
with open(sname, 'w', encoding='utf-8-sig',) as f: | |
s = csv.writer(f, delimiter=',') | |
# this is the order | |
row = ['Ogrenci Adi', 'Ogrenci Numarasi', 'Rank'] | |
if detailed: | |
for k in quests.keys(): | |
row.append(k + ' beklenen') | |
row.append(k + ' cevap') | |
row.append(k + ' puan') | |
row.append('Toplam puan') | |
s.writerow(row) | |
# '\t' is just a hakc to excel to open normally | |
# other programs might behave differently | |
for i, st in enumerate(studs): | |
row = [st.name, str(st.number) + '\t', format(st.rank, '04x') + '\t'] | |
if detailed: | |
for e, a, p in zip(st.exp, st.ans, st.points): | |
row.append(str(e) + '\t') | |
row.append(str(a) + '\t') | |
row.append(p) | |
row.append(sum(st.points)) | |
s.writerow(row) | |
def create_grade_figure(studs): | |
'''plot grade histogram''' | |
import matplotlib.pyplot as plt | |
# points array | |
pts = [] | |
for i, s in enumerate(studs): | |
pts.append(sum(s.points)) | |
mnn = np.mean(pts) | |
mdn = np.median(pts) | |
fig = plt.figure() | |
plt.hist(pts, bins=10, range=(0, 100)) | |
smn = 'mean: {:.1f}\nmedian: {:.1f}'.format(mnn, mdn) | |
plt.text(80, 13, smn) | |
plt.ylabel('kişi sayısı') | |
plt.xlabel('puan aralıkları') | |
plt.title('ELM234 Ara sınav puan dağılımları') | |
plt.show() | |
#plt.savefig('elm234-arasinav-puan-dagilimi.png') | |
def create_questions_figure(studs): | |
'''plot question correct answer freq''' | |
import matplotlib.pyplot as plt | |
import numpy as np | |
qs = NUM_OF_QUESTIONS * [0] | |
for s in studs: | |
for i, p in enumerate(s.points): | |
if p != 0: | |
qs[i] += 1 | |
fig = plt.figure() | |
plt.bar(quests.keys(), qs) | |
plt.xticks(np.arange(NUM_OF_QUESTIONS), quests.keys(), rotation=60) | |
plt.xlabel('soru numarası') | |
plt.ylabel('çözüm sayısı') | |
plt.title('ELM234 Ara sınav soru çözüm dağılımları') | |
plt.show() | |
#plt.savefig('elm234-arasinav-soru-cozum-dagilimi.png') | |
if __name__ == "__main__": | |
import numpy as np | |
# test and make sure points add up to 100 | |
if sum(quests.values()) != 100: | |
raise ValueError('question points does not sum to 100') | |
examname = 'elm234_sinav.csv' | |
rankname = 'elm234_ranks.csv' | |
# generate answers | |
create_answer_sheet(rankname) | |
# rest is for evaluation | |
ranks = read_csv(rankname) | |
exam2 = read_csv(examname) | |
exam = clean_exam(exam2) | |
ranks = {l[0] : l[1] for l in ranks} # convert ranks to dict | |
studs = [] # master student array | |
# test and make sure ranks match | |
for i, s in enumerate(exam): | |
if (ranks[s[1]][2:] != s[2].lower()): | |
serr = s[0], s[1], s[2], ranks[s[1]], ranks[s[1]][2:], s[2].lower() | |
raise ValueError('Ranks dont match: %s', serr) | |
# add all the answers of the students | |
for i, s in enumerate(exam): | |
studs.append(Student(s[0], s[1], s[2])) | |
for j, ans in enumerate(s[3:]): | |
studs[i].ans[j] = ans.lower() | |
# evaluate the questions for the student | |
studs[i].evaluate_questions() | |
create_grade_figure(studs) | |
create_questions_figure(studs) | |
#gradesname = 'elm234_sonuclar.csv' | |
#create_score_sheet(studs, gradesname, True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment