Commit 4325184c authored by Alan Marchiori's avatar Alan Marchiori
Browse files

added db commands

parent 0994c05d
......@@ -6,4 +6,6 @@ from .grade import grade
from .report import report
from .menu import menu
from .email import email
__all__ = ['userconfig', 'init', 'check', 'test', 'grade', 'report' ,'menu', 'email']
from .db import db
__all__ = ['userconfig', 'init', 'check', 'test', 'grade',
'report' ,'menu', 'email', 'db']
......@@ -15,7 +15,6 @@ import shutil
import difflib
from commands.checker import Checker
from utils.history import History
def dbg(x):
debug(__name__ + x)
......@@ -124,49 +123,49 @@ def check(lab, part, **kwargs):
# return show_report(rubric, labpath)
# return
with History(location=labpath, save=False) as hist:
presults = []
for partstr, info in rubric['parts'].items():
if isinstance(part, int) and part != info['index']:
continue
# if 'grade' in hist and partstr in hist['grade']:
# show_report(hist, partstr, info)
# continue
if 'check' in info:
# if 'check' in hist and partstr in hist['check']:
# # print("partstr: {}".format(partstr))
# # print("pr: {}".format(presults))
# # print("hi: {}".format(hist['check'][partstr]['result']))
# presults += [hist['check'][partstr]['result']]
# else:
if True:
c = Checker(checkinfo=info,
labroot=labpath,
remotepath=rubric['path'])
newline()
xlen = 47 # length of everything without name
echo("Part {:3}: {}".format(
info['index'],
info['name'][:max(10,width-xlen)].ljust(width-xlen)
))
echo(info['prompt'])
if c.do_check():
presults += [True]
if 'on_pass' in info:
success(info['on_pass'])
else:
success('This part passed!')
# with History(location=labpath, save=False) as hist:
presults = []
for partstr, info in rubric['parts'].items():
if isinstance(part, int) and part != info['index']:
continue
# if 'grade' in hist and partstr in hist['grade']:
# show_report(hist, partstr, info)
# continue
if 'check' in info:
# if 'check' in hist and partstr in hist['check']:
# # print("partstr: {}".format(partstr))
# # print("pr: {}".format(presults))
# # print("hi: {}".format(hist['check'][partstr]['result']))
# presults += [hist['check'][partstr]['result']]
# else:
if True:
c = Checker(checkinfo=info,
labroot=labpath,
remotepath=rubric['path'])
newline()
xlen = 47 # length of everything without name
echo("Part {:3}: {}".format(
info['index'],
info['name'][:max(10,width-xlen)].ljust(width-xlen)
))
echo(info['prompt'])
if c.do_check():
presults += [True]
if 'on_pass' in info:
success(info['on_pass'])
else:
presults += [False]
if 'on_error' in info:
error(info['on_error'])
else:
error('This part failed!')
# stop checking on first part failure
break
success('This part passed!')
else:
presults += [False]
if 'on_error' in info:
error(info['on_error'])
else:
error('This part failed!')
# stop checking on first part failure
break
# show total grade at the end, if it's graded.
# if 'grade' in hist and 'TOTAL' in hist['grade']:
......
import click
import config
from config.echo import *
from pprint import pprint
from pymongo import MongoClient
import pymongo
from utils.db import make_connect_str, encrypt_pass, make_password
import utils.db
@click.group()
def db():
"Debug interface for db"
pass
@db.command()
def show():
"shows the db config data"
with config.UserConfig() as uc:
db = MongoClient(make_connect_str()).get_default_database()
users = db.command({'usersInfo':1})
pprint(users)
@db.command()
@click.argument('username')
@click.option('--role', type=str, default='readWrite',
help='The DB role to assign to the user (read, readWrite, dbAdmin, etc)')
def add(username, role):
"set USERNAME to gradedb"
with config.UserConfig() as uc:
db = MongoClient(make_connect_str()).get_default_database()
# users = db.command({'usersInfo':1})
# pprint(users)
pwd = make_password()
epass = encrypt_pass(pwd)
try:
res = db.command('createUser', username, pwd=pwd, roles=[role])
except pymongo.errors.DuplicateKeyError as x:
error('failed --> ')
pprint(x)
return
echo('success --> ')
pprint(res)
echo('password: ', epass.decode())
@db.command()
@click.argument('username')
def rm(username):
"remove USERNAME from gradedb"
with config.UserConfig() as uc:
db = MongoClient(make_connect_str()).get_default_database()
res = db.command('dropUser', username)
echo('Result')
pprint(res)
......@@ -161,8 +161,8 @@ def grade(lab, part, clone, dograde, regrade, user, skip, push, **kwargs):
if isinstance(part, int) and part != info['index']:
continue
if 'check' in info:
if not regrade and 'check' in hist and partstr in hist['check']:
if not dograde and 'check' in info:
if (not regrade) and 'check' in hist and partstr in hist['check']:
echo("Skip part {:3}, already checked (set regrade to re-run).".format(info['index']))
echo("\tpass? {}".format(
hist['check'][partstr]['result']
......@@ -206,9 +206,9 @@ def grade(lab, part, clone, dograde, regrade, user, skip, push, **kwargs):
'at': utils.timestamp().isoformat()}
if dograde and 'grade' in info:
if not regrade and 'grade' in hist and partstr in hist['grade'] and 'grade' in hist['grade'][partstr] and 'comment' in hist['grade'][partstr]:
if (not regrade) and 'grade' in hist and partstr in hist['grade'] and 'grade' in hist['grade'][partstr] and 'comment' in hist['grade'][partstr]:
echo("Skip part {:3}, already graded (set regrade to re-run).".format(info['index']))
echo("Skip part {}[{:3}], already graded (set regrade to re-run).".format(partstr, info['index']))
echo("Grade {} of {}: {}".format(
hist['grade'][partstr]['grade'],
hist['grade'][partstr]['total'],
......@@ -257,7 +257,7 @@ def grade(lab, part, clone, dograde, regrade, user, skip, push, **kwargs):
echo(info['prompt'])
try:
grade = prompt('Grade', default = grade)
grade = prompt(f'Grade (out of {tot_pts})', default = grade)
if not confirm('Use comment "{}"?'.format(grade_msg),
default=True):
......@@ -282,7 +282,9 @@ def grade(lab, part, clone, dograde, regrade, user, skip, push, **kwargs):
hist['grade']['TOTAL'] = {
'grade': sum(
[hist['grade'][pt]['grade'] for pt in rubric['parts'].keys() if pt in hist['grade']]),
'total': sum([hist['grade'][pt]['total'] for pt in rubric['parts'].keys() if pt in hist['grade']]),
#'total': sum([hist['grade'][pt]['total'] for pt in rubric['parts'].keys() if pt in hist['grade']]),
# make total from rubric points not the graded items only (hist)
'total': sum([x['points'] for x in rubric['parts'].values()]),
'who': who,
'at': utils.timestamp().isoformat()
}
......
......@@ -52,6 +52,7 @@ import commands
import threading
import time
from pprint import pprint
import traceback
def get_statusbar_right_text(buf):
# returns a function to return the passed-in buffer pos
return lambda: "{}:{} ".format(
......@@ -78,17 +79,26 @@ def do_cmd(ctx, cmdstr, projects, proj_list, lab_list, part_list):
t = getattr(commands, cmdstr)
def it():
ctx.invoke(t,
lab=lab_list.current_value,
user=proj_list.current_value,
part=part_list.current_value,
**xargs)
input('Press enter to return to menu.')
global invalidate_projects
invalidate_projects = True
get_app().invalidate()
udbg.send("cmd complete invalidate ui")
try:
ctx.invoke(t,
lab=lab_list.current_value,
user=proj_list.current_value,
part=part_list.current_value,
**xargs)
input('Press enter to return to menu.')
global invalidate_projects
invalidate_projects = True
get_app().invalidate()
udbg.send("cmd complete invalidate ui")
except click.exceptions.Abort:
info("ctrl-c -- aborted command.")
input('Press enter to return to menu.')
except Exception as x:
error("Exception while running command.")
error(x)
print("-"*60)
traceback.print_exc()
input('Press enter to return to menu.')
# invalidate grade list, so when we redraw it gets new grades if they changed.
#global invalidate_projects
#invalidate_projects = True
......
......@@ -26,6 +26,9 @@ def make_feedback_msg(rubric, parts):
width=80
msg = []
for part in rubric['parts'].keys():
# ungraded items are not in the parts, skip them
if not part in parts:
continue
full_partstr= "({}) {}: {}".format(
rubric['parts'][part]['index'],
part, rubric['parts'][part]['prompt'])
......@@ -99,7 +102,7 @@ def ta_report(coursename, coursepath, labname, user, clone):
print("{} ({}): {}".format(student,p['owner']['name'], labname))
print(h['grade']['feedback'])
##
print("="*width)
## print CSV style summary report
report_header = ["{}, {}".format(
"student".ljust(8),
......@@ -134,7 +137,8 @@ def ta_report(coursename, coursepath, labname, user, clone):
h['grade']['TOTAL'] = {
'grade': sum(
[h['grade'][pt]['grade'] for pt in rub['parts'].keys() if pt in h['grade']]),
'total': sum([h['grade'][pt]['total'] for pt in rub['parts'].keys() if pt in h['grade']]),
#'total': sum([h['grade'][pt]['total'] for pt in rub['parts'].keys() if pt in h['grade']]),
'total': sum([x['points'] for x in rub['parts'].values()]),
'who': h['grade']['TOTAL']['who'],
'at': utils.timestamp().isoformat()
}
......@@ -152,19 +156,20 @@ def ta_report(coursename, coursepath, labname, user, clone):
print(", ".join(report_parts))
report_parts = ["STATS".ljust(8)]
for rub in rubrics:
labname = rub['name']
if len(c_tot[labname]) > 0:
report_parts += [
"{:5.1f}, {:5.1f}".format(
statistics.mean(c_tot[labname]),
statistics.stdev(c_tot[labname])),
]
else:
report_parts += [' ', ' ']
print(", ".join(report_parts))
if len(c_tot) > 1:
# show stats if there is more than one sample
report_parts = ["STATS".ljust(8)]
for rub in rubrics:
labname = rub['name']
if len(c_tot[labname]) > 0:
report_parts += [
"{:5.1f}, {:5.1f}".format(
statistics.mean(c_tot[labname]),
statistics.stdev(c_tot[labname])),
]
else:
report_parts += [' ', ' ']
print(", ".join(report_parts))
def student_report(coursename, coursepath, labname, clone):
if labname == None:
......@@ -203,7 +208,8 @@ def student_report(coursename, coursepath, labname, clone):
h['grade']['TOTAL'] = {
'grade': sum(
[h['grade'][pt]['grade'] for pt in rub['parts'].keys() if pt in h['grade']]),
'total': sum([h['grade'][pt]['total'] for pt in rub['parts'].keys() if pt in h['grade']]),
#'total': sum([h['grade'][pt]['total'] for pt in rub['parts'].keys() if pt in h['grade']]),
'total': sum([x['points'] for x in rub['parts'].values()]),
'who': h['grade']['TOTAL']['who'],
'at': utils.timestamp().isoformat()
}
......
......@@ -45,3 +45,11 @@ gitlab_api = "api/v4/"
# mongodb collection name if using mongodb backend
labtool_mongodb_collection = "labtool"
labtool_mongodb_format = "mongodb://{}:{}@eg-mongodb.bucknell.edu/csgrades"
# crypto info
crypto_salt = b"\xfc\xe6\xeeu\xa05=\xfe\xca\xec'\x80\x07\xcd,\x90"
crypto_pkey = b"It's really ten years into the future."
# mongodb pass
#mongodb_pass =
import config
import os
from Crypto.Cipher import AES
from hashlib import scrypt
import binascii
import random
import string
import math
def __extract_pass(secret):
# derive key
key = scrypt(config.crypto_pkey, salt=config.crypto_salt,
dklen=32, n=1024, r=8, p=1)
cipher = AES.new(key, AES.MODE_ECB)
return cipher.decrypt(binascii.a2b_base64(secret)).decode().lstrip()
def make_password(n=16):
"returns a random password"
password_characters = string.ascii_letters + string.digits + string.punctuation
return "".join([random.choice(password_characters) for i in range(n)])
def encrypt_pass(pwd):
# derive key
key = scrypt(config.crypto_pkey, salt=config.crypto_salt,
dklen=32, n=1024, r=8, p=1)
cipher = AES.new(key, AES.MODE_ECB)
blks = math.ceil(len(pwd)/16)
epwd = cipher.encrypt(pwd.rjust(16*blks))
return binascii.b2a_base64(epwd)
def make_connect_str():
"centralized logic for creating the mongodb connection string"
with config.UserConfig() as uc:
if 'gradedb' in uc.cfg:
# highest priority is gradedb defined in userconfig
return uc.cfg['gradedb']
if 'gradedb_user' in uc.cfg:
who = uc.cfg['gradedb_user']
else:
who = os.environ['USER']
if 'gradedb_pass' in uc.cfg:
return config.labtool_mongodb_format.format(
who,
__extract_pass(uc.cfg['gradedb_pass'])
)
else:
return config.labtool_mongodb_format.format(
who,
"none"
)
......@@ -4,6 +4,7 @@ import platform
import json
import sys
import config
from utils.db import make_connect_str
from config.echo import debug, error, echo, warn
from pymongo import MongoClient
class HistoryConfigurationException(Exception):
......@@ -65,10 +66,6 @@ class History(dict):
self.gradedb = None
if self.gradebackend == History.backend_mongodb:
# connect
if self.gradedb == None:
raise HistoryConfigurationException("You must first configure the 'gradedb' value in your userconfig.")
debug("using mongodb for history")
# hack to show not using file backend.
self.filename = None
elif self.gradebackend == History.backend_json:
......@@ -83,7 +80,7 @@ class History(dict):
if self.gradebackend == History.backend_mongodb:
if History.dbclient == None:
# keep a global client to reuse
History.dbclient = MongoClient(self.gradedb)
History.dbclient = MongoClient(make_connect_str())
self.mc = History.dbclient.get_default_database()[config.labtool_mongodb_collection]
d = self.mc.find_one({'path': self.dbpath})
if d and 'lock' in d:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment