submitWithConfiguration.m 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. function submitWithConfiguration(conf)
  2. addpath('./lib/jsonlab');
  3. parts = parts(conf);
  4. fprintf('== Submitting solutions | %s...\n', conf.itemName);
  5. tokenFile = 'token.mat';
  6. if exist(tokenFile, 'file')
  7. load(tokenFile);
  8. [email token] = promptToken(email, token, tokenFile);
  9. else
  10. [email token] = promptToken('', '', tokenFile);
  11. end
  12. if isempty(token)
  13. fprintf('!! Submission Cancelled\n');
  14. return
  15. end
  16. try
  17. response = submitParts(conf, email, token, parts);
  18. catch
  19. e = lasterror();
  20. fprintf('\n!! Submission failed: %s\n', e.message);
  21. fprintf('\n\nFunction: %s\nFileName: %s\nLineNumber: %d\n', ...
  22. e.stack(1,1).name, e.stack(1,1).file, e.stack(1,1).line);
  23. fprintf('\nPlease correct your code and resubmit.\n');
  24. return
  25. end
  26. if isfield(response, 'errorMessage')
  27. fprintf('!! Submission failed: %s\n', response.errorMessage);
  28. elseif isfield(response, 'errorCode')
  29. fprintf('!! Submission failed: %s\n', response.message);
  30. else
  31. showFeedback(parts, response);
  32. save(tokenFile, 'email', 'token');
  33. end
  34. end
  35. function [email token] = promptToken(email, existingToken, tokenFile)
  36. if (~isempty(email) && ~isempty(existingToken))
  37. prompt = sprintf( ...
  38. 'Use token from last successful submission (%s)? (Y/n): ', ...
  39. email);
  40. reenter = input(prompt, 's');
  41. if (isempty(reenter) || reenter(1) == 'Y' || reenter(1) == 'y')
  42. token = existingToken;
  43. return;
  44. else
  45. delete(tokenFile);
  46. end
  47. end
  48. email = input('Login (email address): ', 's');
  49. token = input('Token: ', 's');
  50. end
  51. function isValid = isValidPartOptionIndex(partOptions, i)
  52. isValid = (~isempty(i)) && (1 <= i) && (i <= numel(partOptions));
  53. end
  54. function response = submitParts(conf, email, token, parts)
  55. body = makePostBody(conf, email, token, parts);
  56. submissionUrl = submissionUrl();
  57. responseBody = getResponse(submissionUrl, body);
  58. jsonResponse = validateResponse(responseBody);
  59. response = loadjson(jsonResponse);
  60. end
  61. function body = makePostBody(conf, email, token, parts)
  62. bodyStruct.assignmentSlug = conf.assignmentSlug;
  63. bodyStruct.submitterEmail = email;
  64. bodyStruct.secret = token;
  65. bodyStruct.parts = makePartsStruct(conf, parts);
  66. opt.Compact = 1;
  67. body = savejson('', bodyStruct, opt);
  68. end
  69. function partsStruct = makePartsStruct(conf, parts)
  70. for part = parts
  71. partId = part{:}.id;
  72. fieldName = makeValidFieldName(partId);
  73. outputStruct.output = conf.output(partId);
  74. partsStruct.(fieldName) = outputStruct;
  75. end
  76. end
  77. function [parts] = parts(conf)
  78. parts = {};
  79. for partArray = conf.partArrays
  80. part.id = partArray{:}{1};
  81. part.sourceFiles = partArray{:}{2};
  82. part.name = partArray{:}{3};
  83. parts{end + 1} = part;
  84. end
  85. end
  86. function showFeedback(parts, response)
  87. fprintf('== \n');
  88. fprintf('== %43s | %9s | %-s\n', 'Part Name', 'Score', 'Feedback');
  89. fprintf('== %43s | %9s | %-s\n', '---------', '-----', '--------');
  90. for part = parts
  91. score = '';
  92. partFeedback = '';
  93. partFeedback = response.partFeedbacks.(makeValidFieldName(part{:}.id));
  94. partEvaluation = response.partEvaluations.(makeValidFieldName(part{:}.id));
  95. score = sprintf('%d / %3d', partEvaluation.score, partEvaluation.maxScore);
  96. fprintf('== %43s | %9s | %-s\n', part{:}.name, score, partFeedback);
  97. end
  98. evaluation = response.evaluation;
  99. totalScore = sprintf('%d / %d', evaluation.score, evaluation.maxScore);
  100. fprintf('== --------------------------------\n');
  101. fprintf('== %43s | %9s | %-s\n', '', totalScore, '');
  102. fprintf('== \n');
  103. end
  104. % use urlread or curl to send submit results to the grader and get a response
  105. function response = getResponse(url, body)
  106. % try using urlread() and a secure connection
  107. params = {'jsonBody', body};
  108. [response, success] = urlread(url, 'post', params);
  109. if (success == 0)
  110. % urlread didn't work, try curl & the peer certificate patch
  111. if ispc
  112. % testing note: use 'jsonBody =' for a test case
  113. json_command = sprintf('echo jsonBody=%s | curl -k -X POST -d @- %s', body, url);
  114. else
  115. % it's linux/OS X, so use the other form
  116. json_command = sprintf('echo ''jsonBody=%s'' | curl -k -X POST -d @- %s', body, url);
  117. end
  118. % get the response body for the peer certificate patch method
  119. [code, response] = system(json_command);
  120. % test the success code
  121. if (code ~= 0)
  122. fprintf('[error] submission with curl() was not successful\n');
  123. end
  124. end
  125. end
  126. % validate the grader's response
  127. function response = validateResponse(resp)
  128. % test if the response is json or an HTML page
  129. isJson = length(resp) > 0 && resp(1) == '{';
  130. isHtml = findstr(lower(resp), '<html');
  131. if (isJson)
  132. response = resp;
  133. elseif (isHtml)
  134. % the response is html, so it's probably an error message
  135. printHTMLContents(resp);
  136. error('Grader response is an HTML message');
  137. else
  138. error('Grader sent no response');
  139. end
  140. end
  141. % parse a HTML response and print it's contents
  142. function printHTMLContents(response)
  143. strippedResponse = regexprep(response, '<[^>]+>', ' ');
  144. strippedResponse = regexprep(strippedResponse, '[\t ]+', ' ');
  145. fprintf(strippedResponse);
  146. end
  147. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  148. %
  149. % Service configuration
  150. %
  151. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  152. function submissionUrl = submissionUrl()
  153. submissionUrl = 'https://www-origin.coursera.org/api/onDemandProgrammingImmediateFormSubmissions.v1';
  154. end