package fic import ( "errors" "fmt" "time" ) // MCQ represents a flag's challenge, in the form of checkbox. type MCQ struct { Id int `json:"id"` // IdExercice is the identifier of the underlying challenge IdExercice int64 `json:"idExercice"` // Order is used to sort the flag between them Order int8 `json:"order"` // Title is the label of the question Title string `json:"title"` // Entries stores the set of proposed answers Entries []*MCQ_entry `json:"entries"` } // MCQ_entry represents a proposed response for a given MCQ. type MCQ_entry struct { Id int `json:"id"` // Label is the text displayed to players as proposed answer Label string `json:"label"` // Response stores if expected checked state. Response bool `json:"response"` } // GetMCQ returns a list of flags comming with the challenge. func GetMCQ(id int) (m *MCQ, err error) { m = &MCQ{} err = DBQueryRow("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_mcq = ?", id).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title) m.fillEntries() return } func (m *MCQ) fillEntries() ([]*MCQ_entry, error) { if entries_rows, err := DBQuery("SELECT id_mcq_entry, label, response FROM mcq_entries WHERE id_mcq = ?", m.Id); err != nil { return nil, err } else { defer entries_rows.Close() for entries_rows.Next() { e := &MCQ_entry{} if err := entries_rows.Scan(&e.Id, &e.Label, &e.Response); err != nil { return nil, err } m.Entries = append(m.Entries, e) } } return m.Entries, nil } // GetMCQ returns the MCQs coming with the challenge. func (e *Exercice) GetMCQ() ([]*MCQ, error) { if rows, err := DBQuery("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_exercice = ?", e.Id); err != nil { return nil, err } else { defer rows.Close() var mcqs = []*MCQ{} for rows.Next() { m := &MCQ{} m.IdExercice = e.Id if err := rows.Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title); err != nil { return nil, err } m.fillEntries() mcqs = append(mcqs, m) } if err := rows.Err(); err != nil { return nil, err } return mcqs, nil } } // GetMCQById returns a MCQs. func (e *Exercice) GetMCQById(id int) (m *MCQ, err error) { m = &MCQ{} err = DBQueryRow("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_mcq = ? AND id_exercice = ?", id, e.Id).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title) m.fillEntries() return } // GetMCQbyChoice returns the MCQ corresponding to a choice ID. func GetMCQbyChoice(cid int) (m *MCQ, c *MCQ_entry, err error) { m = &MCQ{} if errr := DBQueryRow("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_mcq = (SELECT id_mcq FROM mcq_entries WHERE id_mcq_entry = ?)", cid).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title); errr != nil { return nil, nil, errr } if entries_rows, errr := DBQuery("SELECT id_mcq_entry, label, response FROM mcq_entries WHERE id_mcq = ?", m.Id); errr != nil { return nil, nil, errr } else { defer entries_rows.Close() for entries_rows.Next() { e := &MCQ_entry{} if err = entries_rows.Scan(&e.Id, &e.Label, &e.Response); err != nil { return } if e.Id == cid { c = e } m.Entries = append(m.Entries, e) } } return } // GetId returns the MCQ identifier. func (m *MCQ) GetId() int { return m.Id } // RecoverId returns the MCQ identifier as register in DB. func (m *MCQ) RecoverId() (Flag, error) { if err := DBQueryRow("SELECT id_mcq FROM exercice_mcq WHERE title LIKE ? AND id_exercice = ?", m.Title, m.IdExercice).Scan(&m.Id); err != nil { return nil, err } else { return m, err } } // Create registers a MCQ into the database and recursively add its entries. func (m *MCQ) Create(e *Exercice) (Flag, error) { if res, err := DBExec("INSERT INTO exercice_mcq (id_exercice, ordre, title) VALUES (?, ?, ?)", e.Id, m.Order, m.Title); err != nil { return m, err } else if qid, err := res.LastInsertId(); err != nil { return m, err } else { m.Id = int(qid) m.IdExercice = e.Id // Add entries for k, entry := range m.Entries { if entry, err = m.AddEntry(entry); err != nil { return m, err } else { m.Entries[k] = entry } } return m, nil } } // Update applies modifications back to the database. func (m *MCQ) Update() (int64, error) { if res, err := DBExec("UPDATE exercice_mcq SET id_exercice = ?, ordre = ?, title = ? WHERE id_mcq = ?", m.IdExercice, m.Order, m.Title, m.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // Delete the MCQ from the database. func (m *MCQ) Delete() (int64, error) { if _, err := DBExec("DELETE FROM exercice_files_omcq_deps WHERE id_mcq = ?", m.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_mcq_okey_deps WHERE id_mcq = ?", m.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_mcq_omcq_deps WHERE id_mcq = ?", m.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_mcq_omcq_deps WHERE id_mcq_dep = ?", m.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_flags_omcq_deps WHERE id_mcq_dep = ?", m.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_hints_omcq_deps WHERE id_mcq_dep = ?", m.Id); err != nil { return 0, err } else if res, err := DBExec("DELETE FROM exercice_mcq WHERE id_mcq = ?", m.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // AddEntry creates and fills a new struct MCQ_entry and registers it into the database. func (m *MCQ) AddEntry(e *MCQ_entry) (*MCQ_entry, error) { if res, err := DBExec("INSERT INTO mcq_entries (id_mcq, label, response) VALUES (?, ?, ?)", m.Id, e.Label, e.Response); err != nil { return e, err } else if nid, err := res.LastInsertId(); err != nil { return e, err } else { e.Id = int(nid) return e, nil } } // Update applies modifications back to the database. func (n *MCQ_entry) Update() (int64, error) { if res, err := DBExec("UPDATE mcq_entries SET label = ?, response = ? WHERE id_mcq = ?", n.Label, n.Response, n.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // Delete the MCQ entry from the database. func (n *MCQ_entry) Delete() (int64, error) { if res, err := DBExec("DELETE FROM mcq_entries WHERE id_mcq_entry = ?", n.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // WipeMCQs deletes MCQs coming with the challenge. func (e *Exercice) WipeMCQs() (int64, error) { if _, err := DBExec("DELETE FROM exercice_files_omcq_deps WHERE id_mcq IN (SELECT id_mcq FROM exercice_mcq WHERE id_exercice = ?)", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM mcq_entries WHERE id_mcq IN (SELECT id_mcq FROM exercice_mcq WHERE id_exercice = ?);", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_flags_omcq_deps WHERE id_mcq_dep IN (SELECT id_mcq FROM exercice_mcq WHERE id_exercice = ?);", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_mcq_okey_deps WHERE id_mcq IN (SELECT id_mcq FROM exercice_mcq WHERE id_exercice = ?);", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_mcq_omcq_deps WHERE id_mcq IN (SELECT id_mcq FROM exercice_mcq WHERE id_exercice = ?);", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_mcq_omcq_deps WHERE id_mcq_dep IN (SELECT id_mcq FROM exercice_mcq WHERE id_exercice = ?);", e.Id); err != nil { return 0, err } else if res, err := DBExec("DELETE FROM exercice_mcq WHERE id_exercice = ?;", e.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } func (m *MCQ) GetOrder() int8 { return m.Order } // AddDepend insert a new dependency to a given flag. func (m *MCQ) AddDepend(j Flag) (err error) { if d, ok := j.(*FlagKey); ok { _, err = DBExec("INSERT INTO exercice_mcq_okey_deps (id_mcq, id_flag_dep) VALUES (?, ?)", m.Id, d.Id) } else if d, ok := j.(*MCQ); ok { _, err = DBExec("INSERT INTO exercice_mcq_omcq_deps (id_mcq, id_mcq_dep) VALUES (?, ?)", m.Id, d.Id) } else { err = errors.New("dependancy type not implemented for this flag") } return } // GetDepends retrieve the flag's dependency list. func (m *MCQ) GetDepends() ([]Flag, error) { deps := []Flag{} if rows, err := DBQuery("SELECT id_flag_dep FROM exercice_mcq_okey_deps WHERE id_mcq = ?", m.Id); err != nil { return nil, err } else { defer rows.Close() for rows.Next() { var d int if err := rows.Scan(&d); err != nil { return nil, err } deps = append(deps, &FlagKey{Id: d, IdExercice: m.IdExercice}) } if err := rows.Err(); err != nil { return nil, err } } if rows, err := DBQuery("SELECT id_mcq_dep FROM exercice_mcq_omcq_deps WHERE id_mcq = ?", m.Id); err != nil { return nil, err } else { defer rows.Close() for rows.Next() { var d int if err := rows.Scan(&d); err != nil { return nil, err } deps = append(deps, &MCQ{Id: d, IdExercice: m.IdExercice}) } if err := rows.Err(); err != nil { return nil, err } } return deps, nil } // GetJustifiedFlag searchs for a flag in the scope of the given exercice. func (c *MCQ_entry) GetJustifiedFlag(e *Exercice) (*FlagKey, error) { return e.GetFlagKeyByLabel(fmt.Sprintf("\\%%%d\\%%%%", c.Id)) } // IsOptionnal to know if the flag can be omitted when validating the step. func (m *MCQ) IsOptionnal() bool { return false } // Check if the given vals are the expected ones to validate this flag. func (m *MCQ) Check(v interface{}) int { var vals map[int]bool if va, ok := v.(map[int]bool); !ok { return -1 } else { vals = va } diff := 0 for _, n := range m.Entries { if v, ok := vals[n.Id]; (ok || !n.Response) && v == n.Response { continue } diff += 1 } return diff } // FoundBy registers in the database that the given Team solved the MCQ. func (m *MCQ) FoundBy(t *Team) (err error) { _, err = DBExec("INSERT INTO mcq_found (id_mcq, id_team, time) VALUES (?, ?, ?)", m.Id, t.Id, time.Now()) return }