Skip to content

Commit 9320339

Browse files
wabscaleSejinkonye
andauthored
V2.0.1 fixes (#65)
* fix ontime icon and multiple repo handling * graceful rolling update * INIT design.md (no images yet) * ADD more to design doc (at WIP state) * "made version differences more concise " * fix the annoying auth.nyu.edu sdc issue * add dagling reset and no regrade dangling * typos in reset-danling * FIX fix-dangling submissions * UPDATE stats endpoint, and add stats to cli * CHG improve stats caching Co-authored-by: Sejinkonye <[email protected]>
1 parent 9ca319f commit 9320339

File tree

15 files changed

+525
-102
lines changed

15 files changed

+525
-102
lines changed

api/anubis/config.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import logging
22
import os
3+
import hashlib
34

45

56
class Config:
6-
SECRET_KEY = os.environ.get('SECRET_KEY')
7+
SECRET_KEY = os.environ.get('SECRET_KEY', default=hashlib.sha512(os.urandom(10)).hexdigest())
78

89
# sqlalchemy
910
SQLALCHEMY_DATABASE_URI = None
@@ -31,6 +32,9 @@ def __init__(self):
3132
self.OAUTH_CONSUMER_KEY = os.environ.get('OAUTH_CONSUMER_KEY', default='DEBUG')
3233
self.OAUTH_CONSUMER_SECRET = os.environ.get('OAUTH_CONSUMER_SECRET', default='DEBUG')
3334

35+
# Redis
36+
self.CACHE_REDIS_HOST = os.environ.get('CACHE_REDIS_HOST', default='redis')
37+
3438
logging.info('Starting with DATABASE_URI: {}'.format(
3539
self.SQLALCHEMY_DATABASE_URI))
3640
logging.info('Starting with SECRET_KEY: {}'.format(self.SECRET_KEY))

api/anubis/models/__init__.py

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import base64
22
import logging
33
import os
4+
from copy import deepcopy
45
from datetime import datetime
56

67
from flask_sqlalchemy import SQLAlchemy
@@ -307,6 +308,10 @@ def init_submission_models(self):
307308
sb = SubmissionBuild(submission=self)
308309
db.session.add(sb)
309310

311+
self.processed = False
312+
self.state = 'Reset'
313+
db.session.add(self)
314+
310315
# Commit new models
311316
db.session.commit()
312317

@@ -355,13 +360,19 @@ def data(self):
355360
'state': self.state,
356361
'created': str(self.created),
357362
'last_updated': str(self.last_updated),
358-
359-
# Connected models
360-
'repo': self.repo.repo_url,
361-
'tests': self.tests,
362-
'build': self.build.data if self.build is not None else None,
363363
}
364364

365+
@property
366+
def full_data(self):
367+
data = self.data
368+
369+
# Add connected models
370+
data['repo'] = self.repo.repo_url
371+
data['tests'] = self.tests
372+
data['build'] = self.build.data if self.build is not None else None
373+
374+
return data
375+
365376

366377
class SubmissionTestResult(db.Model):
367378
__tablename__ = 'submission_test_result'
@@ -398,6 +409,12 @@ def data(self):
398409
'last_updated': str(self.last_updated),
399410
}
400411

412+
@property
413+
def stat_data(self):
414+
data = self.data
415+
del data['stdout']
416+
return data
417+
401418
def __str__(self):
402419
return 'testname: {}\nerrors: {}\npassed: {}\n'.format(
403420
self.testname,
@@ -432,3 +449,9 @@ def data(self):
432449
'stdout': self.stdout,
433450
'passed': self.passed,
434451
}
452+
453+
@property
454+
def stat_data(self):
455+
data = self.data
456+
del data['stdout']
457+
return data

api/anubis/routes/pipeline.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def pipeline_report_panic(submission: Submission):
2828
:return:
2929
"""
3030

31-
logger.info('submission panic reported', extra={
31+
logger.error('submission panic reported', extra={
3232
'type': 'panic_report',
3333
'submission_id': submission.id, 'assignment_id': submission.assignment_id,
3434
'owner_id': submission.owner_id, 'data': json.dumps(request.json)})

api/anubis/routes/private.py

+51-70
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,25 @@
1818
from anubis.utils.elastic import log_endpoint
1919
from anubis.utils.redis_queue import enqueue_webhook_rpc
2020
from anubis.utils.logger import logger
21-
from anubis.utils.data import fix_dangling
21+
from anubis.utils.data import fix_dangling, bulk_stats, get_students
2222

2323
private = Blueprint('private', __name__, url_prefix='/private')
2424

2525

26-
@cache.memoize(timeout=30)
27-
def stats_for(student_id, assignment_id):
28-
# TODO rewrite this
29-
raise
30-
31-
32-
@cache.cached(timeout=30)
33-
def get_students():
34-
return [s.data for s in User.query.all()]
35-
36-
3726
@private.route('/')
3827
def private_index():
3928
return 'super duper secret'
4029

4130

42-
if is_debug():
43-
@private.route('/token/<netid>')
44-
def private_token_netid(netid):
45-
user = User.query.filter_by(netid=netid).first()
46-
if user is None:
47-
return error_response('User does not exist')
48-
res = Response(json.dumps(success_response(get_token(user.netid))), headers={'Content-Type': 'application/json'})
49-
res.set_cookie('token', get_token(user.netid), httponly=True)
50-
return res
31+
@private.route('/token/<netid>')
32+
def private_token_netid(netid):
33+
user = User.query.filter_by(netid=netid).first()
34+
if user is None:
35+
return error_response('User does not exist')
36+
token = get_token(user.netid)
37+
res = Response(json.dumps(success_response(token)), headers={'Content-Type': 'application/json'})
38+
res.set_cookie('token', token, httponly=True)
39+
return res
5140

5241

5342
@private.route('/assignment/sync', methods=['POST'])
@@ -102,7 +91,7 @@ def private_assignment_sync(assignment_data: dict, tests: List[str]):
10291
Assignment.id == a.id,
10392
AssignmentTest.name == test_name,
10493
).join(Assignment).first()
105-
94+
10695
if at is None:
10796
at = AssignmentTest(assignment=a, name=test_name)
10897
db.session.add(at)
@@ -124,7 +113,7 @@ def private_dangling():
124113
"""
125114

126115
dangling = Submission.query.filter(
127-
Submission.student_id == None,
116+
Submission.owner_id == None,
128117
).all()
129118
dangling = [a.data for a in dangling]
130119

@@ -134,6 +123,38 @@ def private_dangling():
134123
})
135124

136125

126+
@private.route('/reset-dangling')
127+
@log_endpoint('reset-dangling', lambda: 'reset-dangling')
128+
@json_response
129+
def private_reset_dangling():
130+
resets = []
131+
for s in Submission.query.filter_by(owner_id=None).all():
132+
s.init_submission_models()
133+
resets.append(s.data)
134+
return success_response({'reset': resets})
135+
136+
137+
@private.route('/regrade-submission/<commit>')
138+
@log_endpoint('cli', lambda: 'regrade-commit')
139+
@json_response
140+
def private_regrade_submission(commit):
141+
s = Submission.query.filter(
142+
Submission.commit == commit,
143+
Submission.owner_id != None,
144+
).first()
145+
146+
if s is None:
147+
return error_response('not found')
148+
149+
s.init_submission_models()
150+
enqueue_webhook_rpc(s.id)
151+
152+
return success_response({
153+
'submission': s.data,
154+
'user': s.owner.data
155+
})
156+
157+
137158
@private.route('/regrade/<assignment_name>')
138159
@log_endpoint('cli', lambda: 'regrade')
139160
@json_response
@@ -159,8 +180,9 @@ def private_regrade_assignment(assignment_name):
159180
if assignment is None:
160181
return error_response('cant find assignment')
161182

162-
submission = Submission.query.filter_by(
163-
assignment=assignment
183+
submission = Submission.query.filter(
184+
Submission.assignment_id == assignment.id,
185+
Submission.owner_id != None
164186
).all()
165187

166188
response = []
@@ -184,12 +206,11 @@ def private_fix_dangling():
184206
return fix_dangling()
185207

186208

187-
@private.route('/stats/<assignment_name>')
188-
@private.route('/stats/<assignment_name>/<netid>')
209+
@private.route('/stats/<assignment_id>')
210+
@private.route('/stats/<assignment_id>/<netid>')
189211
@log_endpoint('cli', lambda: 'stats')
190-
@cache.memoize(timeout=60, unless=lambda: request.args.get('netids', None) is not None)
191212
@json_response
192-
def private_stats_assignment(assignment_name, netid=None):
213+
def private_stats_assignment(assignment_id, netid=None):
193214
netids = request.args.get('netids', None)
194215

195216
if netids is not None:
@@ -199,47 +220,7 @@ def private_stats_assignment(assignment_name, netid=None):
199220
else:
200221
netids = list(map(lambda x: x['netid'], get_students()))
201222

202-
students = get_students()
203-
students = filter(
204-
lambda x: x['netid'] in netids,
205-
students
206-
)
207-
208-
bests = {}
209-
210-
assignment = Assignment.query.filter_by(name=assignment_name).first()
211-
if assignment is None:
212-
return error_response('assignment does not exist')
213-
214-
for student in students:
215-
submissionid = stats_for(student['id'], assignment.id)
216-
netid = student['netid']
217-
if submissionid is None:
218-
# no submission
219-
bests[netid] = None
220-
else:
221-
submission = Submission.query.filter_by(
222-
id=submissionid
223-
).first()
224-
build = len(submission.builds) > 0
225-
best_count = sum(map(lambda x: 1 if x.passed else 0, submission.reports))
226-
late = 'past due' if assignment.due_date < submission.timestamp else False
227-
late = 'past grace' if assignment.grace_date < submission.timestamp else late
228-
bests[netid] = {
229-
'submission': submission.data,
230-
'builds': build,
231-
'reports': [rep.data for rep in submission.reports],
232-
'total_tests_passed': best_count,
233-
'repo_url': submission.repo,
234-
'master': 'https://github.com/{}'.format(
235-
submission.repo[submission.repo.index(':') + 1:-len('.git')],
236-
),
237-
'commit_tree': 'https://github.com/{}/tree/{}'.format(
238-
submission.repo[submission.repo.index(':') + 1:-len('.git')],
239-
submission.commit
240-
),
241-
'late': late
242-
}
223+
bests = bulk_stats(assignment_id, netids)
243224
return success_response({'stats': bests})
244225

245226

api/anubis/routes/public.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def public_submission(commit: str):
207207
return error_response('Commit does not exist'), 406
208208

209209
# Hand back submission
210-
return success_response({'submission': s.data})
210+
return success_response({'submission': s.full_data})
211211

212212

213213
def webhook_log_msg():
@@ -308,6 +308,7 @@ def public_webhook():
308308
repo = AssignmentRepo.query.join(Assignment).join(Class_).join(InClass).join(User).filter(
309309
User.github_username == github_username,
310310
Assignment.unique_code == assignment.unique_code,
311+
AssignmentRepo.repo_url == repo_url,
311312
).first()
312313

313314
logger.debug('webhook data', extra={

0 commit comments

Comments
 (0)