Coverage for model/copycat.py: 100%

116 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-11-05 04:22 +0000

1from typing import Dict 

2from flask import Blueprint, request, current_app 

3from mongo import * 

4from mongo.utils import * 

5from .utils import * 

6from .auth import * 

7 

8import mosspy 

9import threading 

10import logging 

11import requests 

12import re 

13 

14__all__ = ['copycat_api'] 

15 

16copycat_api = Blueprint('copycat_api', __name__) 

17 

18 

19def is_valid_url(url): 

20 import re 

21 regex = re.compile( 

22 r'^https?://' # http:// or https:// 

23 r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' # domain... 

24 r'localhost|' # localhost... 

25 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip 

26 r'(?::\d+)?' # optional port 

27 r'(?:/?|[/?]\S+)$', 

28 re.IGNORECASE) 

29 return url is not None and regex.search(url) 

30 

31 

32def get_report_task(user, problem_id, student_dict: Dict): 

33 # select all ac code 

34 submissions = Submission.filter( 

35 user=user, 

36 offset=0, 

37 count=-1, 

38 status=0, 

39 problem=problem_id, 

40 ) 

41 

42 last_cc_submission = {} 

43 last_python_submission = {} 

44 for submission in submissions: 

45 s = Submission(submission.id) 

46 if s.user.username in student_dict: 

47 if s.language in [0, 1] \ 

48 and s.user.username not in last_cc_submission: 

49 last_cc_submission[submission.user.username] = s.main_code_path 

50 elif s.language in [2] \ 

51 and s.user.username not in last_python_submission: 

52 last_python_submission[ 

53 submission.user.username] = s.main_code_path 

54 

55 moss_userid = 97089070 

56 

57 # get logger 

58 logger = logging.getLogger('guincorn.error') 

59 

60 # Get problem object 

61 problem = Problem(problem_id) 

62 

63 cpp_report_url = '' 

64 python_report_url = '' 

65 # check for c or cpp code 

66 if problem.allowed_language != 4: 

67 m1 = mosspy.Moss(moss_userid, "cc") 

68 

69 for user, code_path in last_cc_submission.items(): 

70 logger.info(f'send {user} {code_path}') 

71 m1.addFile(code_path) 

72 

73 response = m1.send() 

74 if is_valid_url(response): 

75 cpp_report_url = response 

76 else: 

77 logger.info(f"[copycat] {response}") 

78 cpp_report_url = '' 

79 

80 # check for python code 

81 if problem.allowed_language >= 4: 

82 m2 = mosspy.Moss(moss_userid, "python") 

83 

84 for user, code_path in last_python_submission.items(): 

85 logger.info(f'send {user} {code_path}') 

86 m2.addFile(code_path) 

87 

88 response = m2.send() 

89 if is_valid_url(response): 

90 python_report_url = response 

91 else: 

92 logger.info(f"[copycat] {response}") 

93 python_report_url = '' 

94 

95 # download report from moss 

96 if cpp_report_url != '': 

97 mosspy.download_report( 

98 cpp_report_url, 

99 f"submissions_report/{problem_id}/cpp_report/", 

100 connections=8, 

101 log_level=10, 

102 ) 

103 if python_report_url != '': 

104 mosspy.download_report( 

105 python_report_url, 

106 f"submissions_report/{problem_id}/python_report/", 

107 connections=8, 

108 log_level=10, 

109 ) 

110 

111 # insert report url into DB & update status 

112 problem.obj.update( 

113 cpp_report_url=cpp_report_url, 

114 python_report_url=python_report_url, 

115 moss_status=2, 

116 ) 

117 

118 

119def get_report_by_url(url: str): 

120 try: 

121 response = requests.get(url) 

122 return response.text 

123 except (requests.exceptions.MissingSchema, 

124 requests.exceptions.InvalidSchema): 

125 return 'No report.' 

126 

127 

128@copycat_api.route('/', methods=['GET']) 

129@login_required 

130@Request.args('course', 'problem_id') 

131def get_report(user, course, problem_id): 

132 if not (problem_id and course): 

133 return HTTPError( 

134 'missing arguments! (In HTTP GET argument format)', 

135 400, 

136 data={ 

137 'need': ['course', 'problemId'], 

138 }, 

139 ) 

140 # some privilege or exist check 

141 try: 

142 problem = Problem(int(problem_id)) 

143 except ValueError: 

144 return HTTPError('problemId must be integer', 400) 

145 

146 course = Course(course) 

147 if not course.permission(user, Course.Permission.GRADE): 

148 return HTTPError('Forbidden.', 403) 

149 if not problem: 

150 return HTTPError('Problem not exist.', 404) 

151 if not course: 

152 return HTTPError('Course not found.', 404) 

153 

154 cpp_report_url = problem.cpp_report_url 

155 python_report_url = problem.python_report_url 

156 

157 if problem.moss_status == 0: 

158 return HTTPError( 

159 "No report found. Please make a post request to copycat api to generate a report", 

160 404, 

161 data={}, 

162 ) 

163 elif problem.moss_status == 1: 

164 return HTTPResponse("Report generating...", data={}) 

165 else: 

166 cpp_report = get_report_by_url(cpp_report_url) 

167 python_report = get_report_by_url(python_report_url) 

168 return HTTPResponse( 

169 "Success.", 

170 data={ 

171 "cpp_report": cpp_report, 

172 "python_report": python_report 

173 }, 

174 ) 

175 

176 

177@copycat_api.route('/', methods=['POST']) 

178@login_required 

179@Request.json('course', 'problem_id', 'student_nicknames') 

180def detect(user, course, problem_id, student_nicknames): 

181 if not (problem_id and course and type(student_nicknames) is dict): 

182 return HTTPError( 

183 'missing arguments! (In Json format)', 

184 400, 

185 data={ 

186 'need': ['course', 'problemId', 'studentNicknames'], 

187 }, 

188 ) 

189 

190 course = Course(course) 

191 problem = Problem(problem_id) 

192 

193 # Check if student is in course 

194 student_dict = {} 

195 for student, nickname in student_nicknames.items(): 

196 if not User(student): 

197 return HTTPResponse(f'User: {student} not found.', 404) 

198 student_dict[student] = nickname 

199 # Check student_dict 

200 if not student_dict: 

201 return HTTPResponse('Empty student list.', 404) 

202 # some privilege or exist check 

203 if not course.permission(user, Course.Permission.GRADE): 

204 return HTTPError('Forbidden.', 403) 

205 if not problem: 

206 return HTTPError('Problem not exist.', 404) 

207 if not course: 

208 return HTTPError('Course not found.', 404) 

209 

210 problem = Problem(problem_id) 

211 problem.update( 

212 cpp_report_url="", 

213 python_report_url="", 

214 moss_status=1, 

215 ) 

216 if not current_app.config['TESTING']: 

217 threading.Thread( 

218 target=get_report_task, 

219 args=( 

220 user, 

221 problem_id, 

222 student_dict, 

223 ), 

224 ).start() 

225 

226 # return Success 

227 return HTTPResponse('Success.')