// PostSubmission creates a new submission. func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) { p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } if r.Method != "POST" { return http.StatusMethodNotAllowed, nil } resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"]) if !util.HasParent(p.UserKey, resultKey) { return http.StatusBadRequest, errors.New("cannot submit answer for other users") } taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"]) // Note: When more task kinds are added, see controllers.CreateFinalResult. switch taskKey.Kind() { case model.CodeTaskKind: return runner.HandleCodeSubmission(ctx, w, r, resultKey, taskKey) // TODO(victorbalan, flowlo): Use correct kind when possible. case "QuestionTask": return http.StatusInternalServerError, errors.New("question submissions are not yet implemented") default: return http.StatusBadRequest, errors.New("Unknown submission kind.") } }
func GetResult(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) { if !util.CheckMethod(r, "GET") { return http.StatusMethodNotAllowed, nil } resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"]) if err != nil { return http.StatusBadRequest, err } var result model.Result if err := datastore.Get(ctx, resultKey, &result); err != nil { return http.StatusInternalServerError, nil } p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } if p.UserKey.Parent() != nil { json.NewEncoder(w).Encode(result.Key(resultKey)) return http.StatusOK, nil } if !util.HasParent(resultKey, p.UserKey) { return http.StatusUnauthorized, nil } return createFinalResult(ctx, w, resultKey, result) }
func GetResult(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) { if r.Method != "GET" { return http.StatusMethodNotAllowed, nil } resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"]) if err != nil { return http.StatusBadRequest, err } var result model.Result if err := datastore.Get(ctx, resultKey, &result); err != nil { return http.StatusInternalServerError, nil } p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } var u model.User if err = datastore.Get(ctx, p.User, &u); err != nil { return http.StatusInternalServerError, nil } if u.Company == nil && !util.HasParent(p.User, resultKey) { return http.StatusUnauthorized, nil } if result.Finished.Equal(time.Time{}) { if util.HasParent(p.User, resultKey) { return createFinalResult(ctx, w, *result.Key(resultKey)) } var challenge model.Challenge if err := datastore.Get(ctx, result.Challenge, &challenge); err != nil { return http.StatusInternalServerError, err } if u.Company != nil && result.Started.Add(challenge.Duration).Before(time.Now()) { return createFinalResult(ctx, w, *result.Key(resultKey)) } } json.NewEncoder(w).Encode(result.Key(resultKey)) return http.StatusOK, nil }
// FinalSubmission makes the last submission final. func FinalSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) { if r.Method != "POST" { return http.StatusMethodNotAllowed, nil } p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } var resultKey *datastore.Key if resultKey, err = datastore.DecodeKey(mux.Vars(r)["resultKey"]); err != nil { return http.StatusInternalServerError, err } if !util.HasParent(p.User, resultKey) { return http.StatusBadRequest, errors.New("cannot submit answer for other users") } var index int if index, err = strconv.Atoi(mux.Vars(r)["index"]); err != nil { return http.StatusInternalServerError, err } if len(r.URL.Query()["submissionKey"]) == 0 { return http.StatusOK, nil } var submissionKey *datastore.Key if submissionKey, err = datastore.DecodeKey(r.URL.Query()["submissionKey"][0]); err != nil { return http.StatusInternalServerError, err } var result model.Result if err = datastore.Get(ctx, resultKey, &result); err != nil { return http.StatusInternalServerError, err } result.FinalSubmissions[index] = submissionKey if _, err = result.Put(ctx, resultKey); err != nil { return http.StatusInternalServerError, err } w.Write([]byte("OK")) return }
// PostSubmission creates a new submission. func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) { if r.Method != "POST" { return http.StatusMethodNotAllowed, nil } p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) if err != nil { return http.StatusBadRequest, err } if !strings.HasPrefix(mediaType, "multipart/") { return http.StatusUnsupportedMediaType, nil } resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"]) if err != nil { return http.StatusNotFound, err } if !util.HasParent(p.User, resultKey) { return http.StatusBadRequest, errors.New("cannot submit answer for other users") } taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"]) if err != nil { return http.StatusNotFound, err } if err := r.ParseMultipartForm(16 << 20); err != nil { return http.StatusBadRequest, err } files, ok := r.MultipartForm.File["files"] if !ok { return http.StatusBadRequest, errors.New("missing files") } var task model.Task if err = datastore.Get(ctx, taskKey, &task); err != nil { return http.StatusNotFound, err } // Furthermore, the name of the GCS object is derived from the of the // encapsulating Submission. To avoid race conditions, allocate an ID. low, _, err := datastore.AllocateIDs(ctx, model.SubmissionKind, resultKey, 1) if err != nil { return http.StatusInternalServerError, err } submissionKey := datastore.NewKey(ctx, model.SubmissionKind, "", low, resultKey) storedCode := model.StoredObject{ Bucket: util.SubmissionBucket(), Name: nameObject(submissionKey) + "/Code/", } submission := model.Submission{ Task: taskKey, Time: time.Now(), Language: detectLanguage(files), Code: storedCode, } if _, err = datastore.Put(ctx, submissionKey, &submission); err != nil { return http.StatusInternalServerError, err } var tests model.Tests testKeys, err := model.NewQueryForTest(). Ancestor(taskKey). GetAll(ctx, &tests) if err != nil { return http.StatusInternalServerError, err } prrs, pwrs := multiPipe(len(tests)) go maketar(pwrs, files) for i, t := range tests { go func(i int, t model.Test) { if err := test.Tester(t.Tester).Call(ctx, *t.Key(testKeys[i]), *submission.Key(submissionKey), prrs[i]); err != nil { log.Warningf(ctx, "%s", err) } }(i, t) } if err := upload(util.CloudContext(ctx), storedCode.Bucket, storedCode.Name, files); err != nil { return http.StatusInternalServerError, err } return http.StatusOK, nil }
// TaskByKey loads a task by key. func TaskByKey(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) { p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } if r.Method != "GET" { return http.StatusMethodNotAllowed, nil } taskKey, err := datastore.DecodeKey(mux.Vars(r)["key"]) if err != nil { return http.StatusInternalServerError, err } var u model.User if err = datastore.Get(ctx, p.User, &u); err != nil { return http.StatusInternalServerError, nil } if len(r.URL.Query()["result"]) > 0 { rk, err := datastore.DecodeKey(r.URL.Query()["result"][0]) if err != nil { return http.StatusInternalServerError, err } if util.HasParent(p.User, rk) { var result model.Result if err = datastore.Get(ctx, rk, &result); err != nil { return http.StatusInternalServerError, err } var challenge model.Challenge if err = datastore.Get(ctx, result.Challenge, &challenge); err != nil { return http.StatusInternalServerError, err } emptyTime := time.Time{} updateResult := false for i, val := range challenge.Tasks { if taskKey.Equal(val) { if result.StartTimes[i].Equal(emptyTime) { result.StartTimes[i] = time.Now() updateResult = true break } } } if updateResult { if _, err = result.Put(ctx, rk); err != nil { return http.StatusInternalServerError, err } } } } var task model.Task if err = datastore.Get(ctx, taskKey, &task); err != nil { return http.StatusInternalServerError, err } json.NewEncoder(w).Encode(task.Key(taskKey)) return http.StatusOK, nil }
// PostSubmission creates a new submission. func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) { if r.Method != "POST" { return http.StatusMethodNotAllowed, nil } var body = struct { Code string Language string }{} if err := json.NewDecoder(r.Body).Decode(&body); err != nil { return http.StatusBadRequest, err } p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"]) if err != nil { return http.StatusNotFound, err } if !util.HasParent(p.User, resultKey) { return http.StatusBadRequest, errors.New("cannot submit answer for other users") } taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"]) if err != nil { return http.StatusNotFound, err } var task model.Task if err = datastore.Get(ctx, taskKey, &task); err != nil { return http.StatusInternalServerError, err } // Furthermore, the name of the GCS object is derived from the of the // encapsulating Submission. To avoid race conditions, allocate an ID. low, _, err := datastore.AllocateIDs(ctx, model.SubmissionKind, resultKey, 1) if err != nil { return http.StatusInternalServerError, err } submissionKey := datastore.NewKey(ctx, model.SubmissionKind, "", low, resultKey) submission := model.Submission{ Task: taskKey, Time: time.Now(), } if body.Code != "" { submission.Code, err = store(ctx, submissionKey, body.Code, body.Language) if err != nil { return http.StatusInternalServerError, err } submission.Language = body.Language } // Set the submission in stone. if _, err = datastore.Put(ctx, submissionKey, &submission); err != nil { return http.StatusInternalServerError, err } var tests model.Tests _, err = model.NewQueryForTest(). Ancestor(taskKey). GetAll(ctx, &tests) if err != nil { return http.StatusInternalServerError, err } for _, t := range tests { if err := test.Tester(t.Tester).Call(ctx, t.Params, *submission.Key(submissionKey)); err != nil { log.Warningf(ctx, "%s", err) continue } } // TODO(flowlo): Return something meaningful. return http.StatusOK, nil }
// TaskByKey loads a task by key. func TaskByKey(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) { p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } if r.Method != "GET" { return http.StatusMethodNotAllowed, nil } taskKey, err := datastore.DecodeKey(mux.Vars(r)["key"]) if err != nil { return http.StatusInternalServerError, err } // User is a coder if p.UserKey.Parent() == nil { rk, err := datastore.DecodeKey(r.URL.Query()["result"][0]) if err != nil { return http.StatusInternalServerError, err } if !util.HasParent(p.UserKey, rk) { return http.StatusUnauthorized, nil } var result model.Result if err = datastore.Get(ctx, rk, &result); err != nil { return http.StatusInternalServerError, err } var challenge model.Challenge if err = datastore.Get(ctx, result.Challenge, &challenge); err != nil { return http.StatusInternalServerError, err } emptyTime := time.Time{} updateResult := false for i, val := range challenge.Tasks { if taskKey.Equal(val) && result.StartTimes[i] == emptyTime { result.StartTimes[i] = time.Now() updateResult = true break } } if updateResult { if _, err = result.Save(ctx, rk); err != nil { return http.StatusInternalServerError, err } } } switch taskKey.Kind() { case model.CodeTaskKind: var task model.CodeTask if err = datastore.Get(ctx, taskKey, &task); err != nil { return http.StatusInternalServerError, err } json.NewEncoder(w).Encode(task.Key(taskKey)) return http.StatusOK, nil default: return http.StatusInternalServerError, errors.New("Not implemented") } }