/// <summary> /// Evaluates quiz and survey; stores results; returns ID so client can navigate to results page. /// </summary> /// <param name="quiz">The user's quiz choices, JSON serialized in quiz.js.</param> /// <param name="survey">The user's survey input (whatever was provided), JSON serialized in quiz.js.</param> /// <returns></returns> public IActionResult EvalQuiz([FromForm] string quiz, [FromForm] string survey, [FromForm] string quizCount, [FromForm] string surveyCount) { // Parse data from query var oQuiz = JsonConvert.DeserializeObject <IList <string[]> >(quiz); var oSurvey = JsonConvert.DeserializeObject <SurveyData>(survey); // Have sampler evaluate result int score; char[] resCoded; sampler.Eval(oQuiz, out score, out resCoded); // Get country from remote IP. Trickier b/c of NGINX reverse proxy. string country; string xfwd = HttpContext.Request.Headers["X-Real-IP"]; if (xfwd != null) { country = countryResolver.GetContryCode(IPAddress.Parse(xfwd)); } else { country = countryResolver.GetContryCode(HttpContext.Connection.RemoteIpAddress); } // Store result int nQuizCount = int.Parse(quizCount); int nSurveyCount = int.Parse(surveyCount); StoredResult sr = new StoredResult(country, DateTime.Now, nQuizCount, nSurveyCount, score, new string(resCoded), oSurvey); string uid = resultRepo.StoreResult(sr); // Response is result code, pure and simple. return(new ObjectResult(uid)); }
/// <summary> /// Diagnostic function: stores same item N times in a single transaction. Used to grow DB in stress test. /// </summary> /// <returns>UID of last stored item.</returns> public string StoreBatch(StoredResult sr, int count) { long uid = 0; lock (conn) { SqliteTransaction trans = null; try { trans = conn.BeginTransaction(); for (int i = 0; i != count; ++i) { uid = getNextUid(sr.Date, trans); storeResult(uid, trans, sr); } trans.Commit(); trans.Dispose(); trans = null; } catch (Exception ex) { logger.LogError(new EventId(), ex, "Failed to store quiz results."); if (trans != null) { trans.Rollback(); } throw; } finally { if (trans != null) { trans.Dispose(); } } } return(uidToString(uid)); }
/// <summary> /// Performs actual dump. /// </summary> private void doDumpToFile(string tsvFileName) { SqliteConnection dumpConn = null; SqliteCommand cmdSelAll = null; try { // For the dump, we use a separate connection // This allows main connection to be safely accessed from other threads while dump is in progress bool dbExists; getOpenConn(out dumpConn, out dbExists); using (SqliteCommand cmdPragma = dumpConn.CreateCommand()) { // This pragma is a MUST to avoid locking the DB for the duration of the dump // Without it, concurrent store requests will time out. cmdPragma.CommandText = "PRAGMA read_uncommitted = 1;"; cmdPragma.ExecuteNonQuery(); } // Dumping is rare. No need to prepare and reuse this command. cmdSelAll = dumpConn.CreateCommand(); cmdSelAll.CommandText = sqlSelAll; cmdSelAll.Prepare(); // Write from reader, straight to output file. using (FileStream fs = new FileStream(tsvFileName, FileMode.Create, FileAccess.ReadWrite)) using (StreamWriter sw = new StreamWriter(fs)) using (var rdr = cmdSelAll.ExecuteReader()) { sw.WriteLine(StoredResult.GetTSVHeader()); while (rdr.Read()) { // uid, country, quiz_ix, survey_ix, score, res_enc, survey_enc int dateNum = (int)(rdr.GetInt64(0) / 1000000); int xDay = dateNum % 100; dateNum /= 100; int xMonth = dateNum % 100; dateNum /= 100; int xYear = dateNum; DateTime date = new DateTime(xYear, xMonth, xDay); StoredResult sr = new StoredResult(rdr.GetString(1), date, rdr.GetInt32(2), rdr.GetInt32(3), rdr.GetInt32(4), rdr.GetString(5), rdr.GetString(6)); sw.WriteLine(sr.GetTSV()); } } dumpConn.Close(); } finally { if (cmdSelAll != null) { cmdSelAll.Dispose(); } if (dumpConn != null) { dumpConn.Dispose(); } } }
/// <summary> /// Executes "store" SQL command within transaction. /// </summary> private void storeResult(long uid, SqliteTransaction trans, StoredResult sr) { cmdInsertResult.Transaction = trans; cmdInsertResult.Parameters["@uid"].Value = uid; cmdInsertResult.Parameters["@country"].Value = sr.CountryCode; cmdInsertResult.Parameters["@quiz_ix"].Value = sr.PrevQuizCount; cmdInsertResult.Parameters["@survey_ix"].Value = sr.PrevSurveyCount; cmdInsertResult.Parameters["@score"].Value = sr.Score; cmdInsertResult.Parameters["@res_enc"].Value = sr.EncodedResult; cmdInsertResult.Parameters["@survey_enc"].Value = sr.GetEncodedSurvey(); cmdInsertResult.ExecuteNonQuery(); }
/// <summary> /// Stores a new quiz+survey result in the DB. /// </summary> /// <param name="sr">The data to store.</param> /// <returns>The stored item's ID, encoded as a 10-character alphanumeric string.</returns> public string StoreResult(StoredResult sr) { return(StoreBatch(sr, 1)); }