예제 #1
0
파일: execution.go 프로젝트: rakeen/cactus
func ApplyExecution(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || (me.Level != data.Judge && me.Level != data.Administrator) {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	exec, err := data.GetExecution(id)
	catch(err)

	if exec.Status != 7 {
		http.Error(w, "", http.StatusBadRequest)
		return
	}

	subm, err := exec.Submission()
	catch(err)

	subm.Apply(exec)
	err = subm.Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "submissions", subm.Id})

	err = data.NewActivity(me, fmt.Sprintf("applied execution %d to submission %d", exec.Id, subm.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #2
0
파일: submission.go 프로젝트: rakeen/cactus
func JudgeSubmission(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || (me.Level != data.Judge && me.Level != data.Administrator) {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	subm, err := data.GetSubmission(id)
	catch(err)

	subm.Reset()
	err = subm.Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "submissions", subm.Id})

	exec := &data.Execution{
		SubmissionId: subm.Id,
		Apply:        true,
	}
	err = exec.Put()
	catch(err)

	belt.Push(exec)

	err = data.NewActivity(me, fmt.Sprintf("judged submission %d", subm.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #3
0
파일: problem.go 프로젝트: rakeen/cactus
func DeleteProblem(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Level != data.Administrator {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	prob, err := data.GetProblem(id)
	catch(err)

	err = prob.Del()
	catch(err)

	err = json.NewEncoder(w).Encode(&struct {
		Id int64 `json:"id"`
	}{
		Id: prob.Id,
	})
	catch(err)
	hub.Send([]interface{}{"SYNC", "problems"})

	err = data.NewActivity(me, fmt.Sprintf("deleted problem %d", prob.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #4
0
파일: submission.go 프로젝트: rakeen/cactus
func UpdateSubmission(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || (me.Level != data.Judge && me.Level != data.Administrator) {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	subm, err := data.GetSubmission(id)
	catch(err)

	body := struct {
		Verdict data.Verdict `json:"verdict"`
	}{}
	err = json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	subm.Tamper(body.Verdict)
	err = subm.Put()
	catch(err)

	err = json.NewEncoder(w).Encode(subm)
	catch(err)
	hub.Send([]interface{}{"SYNC", "submissions", subm.Id})

	err = data.NewActivity(me, fmt.Sprintf("updated submission %d", subm.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #5
0
func DeleteClarification(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || (me.Level != data.Judge && me.Level != data.Administrator) {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	clar, err := data.GetClarification(id)
	catch(err)

	err = clar.Del()
	catch(err)

	json.NewEncoder(w).Encode(&struct {
		Id int64 `json:"id"`
	}{
		Id: clar.Id,
	})
	hub.Send([]interface{}{"SYNC", "clarifications"})

	err = data.NewActivity(me, fmt.Sprintf("deleted clarification %d", clar.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #6
0
func UpdateClarification(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || (me.Level != data.Judge && me.Level != data.Administrator) {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	clar, err := data.GetClarification(id)
	catch(err)

	body := struct {
		ProblemId int64         `json:"problemId"`
		Question  string        `json:"question"`
		Response  data.Response `json:"response"`
		Message   string        `json:"message"`
	}{}
	err = json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	prob, err := data.GetProblem(body.ProblemId)
	catch(err)
	if prob != nil {
		clar.ProblemId = prob.Id
	} else {
		clar.ProblemId = 0
	}

	clar.Question = body.Question
	clar.Response = body.Response
	clar.Message = body.Message

	err = clar.Put()
	catch(err)

	err = json.NewEncoder(w).Encode(clar)
	catch(err)
	hub.Send([]interface{}{"SYNC", "clarifications", clar.Id})

	err = data.NewActivity(me, fmt.Sprintf("updated clarification %d", clar.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})

	if clar.Response != data.Unresponded {
		if clar.Response == data.Broadcasted {
			err = data.NewNotification(0, data.Participant, fmt.Sprintf("Clarification %d updated", clar.Id)).Put()
			catch(err)
		} else {
			err = data.NewNotification(clar.AskerId, 0, fmt.Sprintf("Clarification %d updated", clar.Id)).Put()
			catch(err)
		}
		hub.Send([]interface{}{"SYNC", "notifications"})
	}
}
예제 #7
0
파일: account.go 프로젝트: rakeen/cactus
func CreateAccount(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Level != data.Administrator {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	body := struct {
		Handle   string     `json:"handle"`
		Password string     `json:"password"`
		Level    data.Level `json:"level"`
		Name     string     `json:"name"`
	}{}
	err := json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	switch {
	case len(body.Handle) < 4:
		http.Error(w, "", http.StatusBadRequest)
		return

	case len(body.Password) < 4:
		http.Error(w, "", http.StatusBadRequest)
		return

	case body.Level != data.Participant && body.Level != data.Judge && body.Level != data.Administrator:
		http.Error(w, "", http.StatusBadRequest)
		return

	case body.Name == "":
		body.Name = body.Handle
	}

	acc := &data.Account{}
	acc.Handle = body.Handle
	err = acc.SetPassword(body.Password)
	catch(err)
	acc.Level = body.Level
	acc.Name = body.Name
	err = acc.Put()
	if err, ok := err.(*sqlite3.Error); ok && err.Code() == sqlite3.CONSTRAINT_UNIQUE {
		http.Error(w, "", http.StatusConflict)
		return
	}
	catch(err)

	err = json.NewEncoder(w).Encode(acc)
	catch(err)
	hub.Send([]interface{}{"SYNC", "accounts"})

	err = data.NewActivity(me, fmt.Sprintf("created account %d", acc.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #8
0
파일: account.go 프로젝트: rakeen/cactus
func ImportAccounts(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Level != data.Administrator {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	body := csv.NewReader(r.Body)
	for {
		cols, err := body.Read()
		if err == io.EOF {
			break
		}
		catch(err)

		acc := &data.Account{}
		acc.Handle = cols[0]
		err = acc.SetPassword(cols[1])
		catch(err)
		col2, err := strconv.ParseInt(cols[2], 10, 32)
		catch(err)
		acc.Level = data.Level(col2)
		acc.Name = cols[3]

		switch {
		case len(acc.Handle) < 4:
			continue

		case len(acc.Password) < 4:
			continue

		case acc.Level != data.Participant && acc.Level != data.Judge && acc.Level != data.Administrator:
			continue

		case acc.Name == "":
			acc.Name = acc.Handle
		}

		err = acc.Put()
		if err, ok := err.(*sqlite3.Error); ok && err.Code() == sqlite3.CONSTRAINT_UNIQUE {
			continue
		}
		catch(err)

		err = data.NewActivity(me, fmt.Sprintf("created account %d", acc.Id)).Put()
		catch(err)
		hub.Send([]interface{}{"SYNC", "activities"})
	}
	hub.Send([]interface{}{"SYNC", "accounts"})
}
예제 #9
0
파일: submission.go 프로젝트: rakeen/cactus
func CreateSubmission(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	body := struct {
		ProblemId int64  `json:"problemId"`
		Language  string `json:"language"`
		Source    string `json:"source"`
	}{}
	err := json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	prob, err := data.GetProblem(body.ProblemId)
	catch(err)

	key, err := data.Blobs.Put("", strings.NewReader(body.Source))
	catch(err)

	subm := &data.Submission{
		AuthorId:  me.Id,
		ProblemId: prob.Id,
		Language:  body.Language,
		SourceKey: string(key),
	}
	err = subm.Put()
	catch(err)

	err = json.NewEncoder(w).Encode(subm)
	catch(err)
	hub.Send([]interface{}{"SYNC", "submissions"})

	err = data.NewActivity(me, fmt.Sprintf("created submission %d", subm.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})

	if prob.Judge == "automatic" {
		exec := &data.Execution{
			SubmissionId: subm.Id,
			Apply:        true,
		}
		err = exec.Put()
		catch(err)
		belt.Push(exec)
	}
}
예제 #10
0
파일: account.go 프로젝트: rakeen/cactus
func HandleLogin(w http.ResponseWriter, r *http.Request) {
	acc, err := data.GetAccountByHandle(r.FormValue("handle"))
	catch(err)
	if acc == nil {
		http.Error(w, "", http.StatusUnauthorized)
		return
	}

	ok, err := acc.CmpPassword(r.FormValue("password"))
	catch(err)
	if !ok {
		http.Error(w, "", http.StatusUnauthorized)
		return
	}

	sess, err := Store.Get(r, "s")
	catch(err)

	sess.Values["me.id"] = acc.Id
	err = sess.Save(r, w)
	catch(err)

	host, _, err := net.SplitHostPort(r.RemoteAddr)
	catch(err)
	err = data.NewActivity(acc, fmt.Sprintf("logged in from %s", host)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #11
0
파일: account.go 프로젝트: rakeen/cactus
func UpdateAccountPart(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	acc, err := data.GetAccount(id)
	catch(err)

	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Id != acc.Id {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	body := struct {
		Notified time.Time `json:"notified"`
	}{}
	err = json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	acc.Notified = body.Notified
	err = acc.Put()
	catch(err)

	json.NewEncoder(w).Encode(acc)
	hub.Send([]interface{}{"SYNC", "accounts", acc.Id})
}
예제 #12
0
파일: execution.go 프로젝트: rakeen/cactus
func CreateExecution(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || (me.Level != data.Judge && me.Level != data.Administrator) {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	body := struct {
		SubmissionId int64 `json:"submissionId"`
	}{}
	err := json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	subm, err := data.GetSubmission(body.SubmissionId)
	catch(err)

	exec := &data.Execution{
		SubmissionId: subm.Id,
	}
	err = exec.Put()
	catch(err)

	belt.Push(exec)

	err = json.NewEncoder(w).Encode(exec)
	catch(err)

	err = data.NewActivity(me, fmt.Sprintf("created execution %d for submission %d", exec.Id, subm.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #13
0
파일: problem.go 프로젝트: rakeen/cactus
func CreateProblem(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Level != data.Administrator {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	body := struct {
		Char  string `json:"char"`
		Title string `json:"title"`
	}{}
	err := json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	switch {
	case len(body.Char) != 1 || !strings.Contains("abcdefghijklmnopqrstuvwxyz", body.Char):
		http.Error(w, "", http.StatusBadRequest)
		return

	case body.Title == "":
		http.Error(w, "", http.StatusBadRequest)
		return
	}

	prob := &data.Problem{
		Slug:    strings.Trim(regexp.MustCompile("[^a-z0-9]+").ReplaceAllString(strings.ToLower(body.Char+" "+body.Title), "-"), " -"),
		Char:    body.Char,
		Title:   body.Title,
		Judge:   "automatic",
		Scoring: "strict",
	}
	err = prob.Put()
	catch(err)

	json.NewEncoder(w).Encode(prob)
	hub.Send([]interface{}{"SYNC", "problems"})

	err = data.NewActivity(me, fmt.Sprintf("created problem %d", prob.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #14
0
파일: account.go 프로젝트: rakeen/cactus
func UpdateAccount(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Level != data.Administrator {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	acc, err := data.GetAccount(id)
	catch(err)

	body := struct {
		Handle   string     `json:"handle"`
		Password string     `json:"password"`
		Level    data.Level `json:"level"`
		Name     string     `json:"name"`
	}{}
	err = json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	acc.Handle = body.Handle
	if body.Password != "" {
		err = acc.SetPassword(body.Password)
		catch(err)
	}
	acc.Level = body.Level
	acc.Name = body.Name
	err = acc.Put()
	catch(err)

	json.NewEncoder(w).Encode(acc)
	hub.Send([]interface{}{"SYNC", "accounts", acc.Id})

	err = data.NewActivity(me, fmt.Sprintf("updated account %d", acc.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #15
0
func CreateClarification(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)

	body := struct {
		ProblemId int64  `json:"problemId"`
		Question  string `json:"question"`
	}{}
	err := json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	clar := &data.Clarification{
		AskerId:  me.Id,
		Question: body.Question,
	}

	prob, err := data.GetProblem(body.ProblemId)
	catch(err)
	if prob != nil {
		clar.ProblemId = prob.Id
	}

	err = clar.Put()
	catch(err)

	err = json.NewEncoder(w).Encode(clar)
	catch(err)
	hub.Send([]interface{}{"SYNC", "clarifications"})

	err = data.NewActivity(me, fmt.Sprintf("created clarification %d", clar.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})

	err = data.NewNotification(0, data.Judge, fmt.Sprintf("Clarification %d requested", clar.Id)).Put()
	catch(err)
	err = data.NewNotification(0, data.Administrator, fmt.Sprintf("Clarification %d requested", clar.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "notifications"})
}
예제 #16
0
파일: contest.go 프로젝트: rakeen/cactus
func UpdateContest(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Level != data.Administrator {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	cnt, err := data.GetContest()
	catch(err)

	err = json.NewDecoder(r.Body).Decode(cnt)
	catch(err)

	err = cnt.Put()
	catch(err)

	err = json.NewEncoder(w).Encode(cnt)
	catch(err)
	hub.Send([]interface{}{"SYNC", "contest"})

	err = data.NewActivity(me, fmt.Sprintf("updated contest %d", cnt.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #17
0
파일: problem.go 프로젝트: rakeen/cactus
func UpdateProblem(w http.ResponseWriter, r *http.Request) {
	me, _ := context.Get(r, "me").(*data.Account)
	if me == nil || me.Level != data.Administrator {
		http.Error(w, "", http.StatusForbidden)
		return
	}

	vars := mux.Vars(r)

	id, err := strconv.ParseInt(vars["id"], 10, 64)
	catch(err)
	prob, err := data.GetProblem(id)
	catch(err)

	if prob == nil {
		http.Error(w, "", http.StatusNotFound)
	}

	body := struct {
		Char      string `json:"char"`
		Title     string `json:"title"`
		Statement struct {
			Body   string `json:"body"`
			Input  string `json:"input"`
			Output string `json:"output"`
		} `json:"statement"`
		Samples []struct {
			Input  string `json:"input"`
			Answer string `json:"answer"`
		} `json:"samples"`
		Notes   string `json:"notes"`
		Judge   string `json:"judge"`
		Checker struct {
			Language  string `json:"language"`
			Source    string `json:"source"`
			SourceKey string `json:"sourceKey"`
		} `json:"checker"`
		Limits struct {
			Cpu    float64 `json:"cpu"`
			Memory int     `json:"memory"`
		} `json:"limits"`
		Languages []string `json:"languages"`
		Tests     []struct {
			Input     string `json:"input"`
			InputKey  string `json:"inputKey"`
			Answer    string `json:"answer"`
			AnswerKey string `json:"answerKey"`
			Points    int    `json:"points"`
		} `json:"tests"`
		Scoring string `json:"scoring"`
	}{}
	err = json.NewDecoder(r.Body).Decode(&body)
	catch(err)

	switch {
	case len(body.Char) != 1 || !strings.Contains("abcdefghijklmnopqrstuvwxyz", body.Char):
		http.Error(w, "", http.StatusBadRequest)
		return

	case body.Title == "":
		http.Error(w, "", http.StatusBadRequest)
		return
	}

	prob.Slug = strings.Trim(regexp.MustCompile("[^a-z0-9]+").ReplaceAllString(strings.ToLower(body.Char+" "+body.Title), "-"), " -")
	prob.Char = body.Char
	prob.Title = body.Title
	prob.Statement = body.Statement
	prob.Samples = body.Samples
	prob.Notes = body.Notes
	prob.Judge = body.Judge
	if body.Checker.Language != "" {
		if body.Checker.SourceKey == "" {
			body.Checker.SourceKey = fmt.Sprintf("problems:%d:checker:source", prob.Id)
			_, err := data.Blobs.Put(body.Checker.SourceKey, strings.NewReader(body.Checker.Source))
			catch(err)
		}
		prob.Checker = struct {
			Language  string `json:"language"`
			SourceKey string `json:"sourceKey"`
		}{
			Language:  body.Checker.Language,
			SourceKey: body.Checker.SourceKey,
		}

	} else {
		prob.Checker = struct {
			Language  string `json:"language"`
			SourceKey string `json:"sourceKey"`
		}{
			Language:  "",
			SourceKey: "",
		}
	}
	prob.Limits = body.Limits
	prob.Languages = body.Languages
	prob.Tests = nil
	for i, test := range body.Tests {
		if test.InputKey == "" {
			test.InputKey = fmt.Sprintf("problems:%d:tests:%d:in", prob.Id, i)
			_, err := data.Blobs.Put(test.InputKey, strings.NewReader(test.Input))
			catch(err)
		}

		if test.AnswerKey == "" {
			test.AnswerKey = fmt.Sprintf("problems:%d:tests:%d:ans", prob.Id, i)
			_, err := data.Blobs.Put(test.AnswerKey, strings.NewReader(test.Answer))
			catch(err)
		}

		prob.Tests = append(prob.Tests, struct {
			InputKey  string `json:"inputKey"`
			AnswerKey string `json:"answerKey"`
			Points    int    `json:"points"`
		}{
			InputKey:  test.InputKey,
			AnswerKey: test.AnswerKey,
			Points:    test.Points,
		})
	}
	prob.Scoring = body.Scoring
	err = prob.Put()
	catch(err)

	err = json.NewEncoder(w).Encode(prob)
	catch(err)
	hub.Send([]interface{}{"SYNC", "problems", prob.Id})

	err = data.NewActivity(me, fmt.Sprintf("updated problem %d", prob.Id)).Put()
	catch(err)
	hub.Send([]interface{}{"SYNC", "activities"})
}
예제 #18
0
파일: standing.go 프로젝트: rakeen/cactus
func init() {
	go func() {
		for id := range chZerk {
			cnt, err := GetContest()
			catch(err)

			acc, err := GetAccount(id)
			catch(err)

			s, err := GetStandingByAuthor(acc)
			catch(err)
			if s == nil {
				continue
			}

			if acc.Level != Participant {
				err = s.Del()
				catch(err)
				continue
			}

			s.Author.Handle = acc.Handle
			s.Author.Name = acc.Name

			s.Attempts = map[string]*Attempt{}
			probs := map[int64]*Problem{}
			subms, err := ListSubmissionsByAuthor(acc)
			catch(err)
			for i := len(subms) - 1; i >= 0; i-- {
				subm := subms[i]
				prob, _ := probs[subm.ProblemId]
				if prob == nil {
					prob, err = GetProblem(subm.ProblemId)
					catch(err)
					probs[subm.ProblemId] = prob

					delete(s.Attempts, strconv.FormatInt(prob.Id, 10))
				}

				att := s.Attempts[strconv.FormatInt(prob.Id, 10)]
				if att == nil {
					att = &Attempt{
						ProblemId: prob.Id,
					}
					s.Attempts[strconv.FormatInt(prob.Id, 10)] = att
				}

				if subm.Verdict == Accepted {
					score := 0
					perfect := true
					if subm.Manual {
						for _, test := range prob.Tests {
							score += test.Points
						}
					} else {
						for _, test := range subm.Tests {
							if test.Verdict == Accepted {
								score += test.Points
							} else {
								perfect = false
							}
						}
					}

					if score > att.Score {
						att.Score = score
						att.Tries += att.Extras + 1
						att.Extras = 0
						att.Penalty = (att.Tries-1)*20 + int(subm.Created.Sub(cnt.Starts)/time.Minute)
						att.Perfect = perfect
					}
				} else if subm.Verdict != CompilationError {
					att.Extras += 1
				}
			}
			s.Score = 0
			s.Penalty = 0
			for _, att := range s.Attempts {
				s.Score += att.Score
				s.Penalty += att.Penalty
			}

			err = s.Put()
			catch(err)

			hub.Send([]interface{}{"SYNC", "standings"})
		}
	}()
}