internal ExpResponse AwardAchievement(int mobileID, int achievementID) { ExpResponse r = new ExpResponse(); string cmdText = @"Merge AwardedAchievements as t using(select @mobileID as MobileID, @achievementID as AchievementID) as s on t.MobileID = s.MobileID and t.AchievementID = s.AchievementID when not matched then insert(MobileID, AchievementID) values (@mobileID, @achievementID);"; using (SqlCommand cmd = new SqlCommand(cmdText, con)) { cmd.Parameters.AddWithValue("@mobileID", mobileID); cmd.Parameters.AddWithValue("@achievementID", achievementID); try { r.result = cmd.ExecuteNonQuery(); return r; } catch (Exception e) { r.setErMsgStk(true, "Exception in AwardAchievement: " + e.Message, e.StackTrace); return r; } } }
/// <summary> /// Creates the sql for an achievement. The sql is stored as a list of sql operation to perform. /// </summary> /// <param name="a">The achievement.</param> /// <param name="DJID">The DJ's ID.</param> /// <param name="sql">Out string that represents the sum of all sql statement.</param> /// <param name="sqlCommands">List of sql commands that must be run to determine who qualifies for the achievement.</param> /// <returns></returns> public static ExpResponse CreateAchievementSQL(Achievement a, int DJID, out string sql, out List<SqlCommand> sqlCommands) { List<SqlCommand> commands = new List<SqlCommand>(); ExpResponse r = new ExpResponse(); SqlCommand cmd; sql = string.Empty; sqlCommands = new List<SqlCommand>(); foreach (AchievementSelect achieveSel in a.selectList) { r = CreateStatementGeneric(achieveSel, DJID, out cmd); if (r.error) return r; sqlCommands.Add(cmd); sql += CreateDebugSQLText(cmd) + "\n"; } return r; }
/// <summary> /// Change a user's password. /// </summary> /// <param name="username">The username.</param> /// <param name="oldPassword">The old password.</param> /// <param name="newPassword">The new password.</param> /// <param name="role">The role: DJ or Mobile</param> /// <returns>The outcome of the operation.</returns> public Response ChangePassword(int ID, string role, string newPassword) { ExpResponse r = new ExpResponse(); if (!role.Equals("DJ") && !role.Equals("Mobile")) { r.setErMsgStk(true, "Bad Role Given", Environment.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); } using (DatabaseConnectivity db = new DatabaseConnectivity()) { // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); // Get the salt from the database and salt/hash the password. string salt = Common.CreateSalt(16); if (role == "DJ") r = db.DJSetSalt(ID, salt); else r = db.MobileSetSalt(ID, salt); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_CRED_WRONG, Common.LogFile.Web); string saltHashPassword = Common.CreatePasswordHash(newPassword, salt); if (role == "DJ") r = db.DJSetPassword(ID, saltHashPassword); else r = db.MobileSetPassword(ID, saltHashPassword); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); return r; } }
/// <summary> /// Change a user's email. /// </summary> /// <param name="username">The username.</param> /// <param name="password">The password.</param> /// <param name="newEmail">The new email address.</param> /// <param name="role">The role, DJ or mobile.</param> /// <returns>The outcome of the operation.</returns> public Response ChangeEmail(int ID, string role, string newEmail) { ExpResponse r = new ExpResponse(); if (!role.Equals("DJ") && !role.Equals("Mobile")) { r.setErMsgStk(true, "Bad Role Given", Environment.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); } // Validate the email address. try { var address = new System.Net.Mail.MailAddress(newEmail); } catch { r.setErMsg(true, Messages.ERR_BAD_EMAIL); return r; } using (DatabaseConnectivity db = new DatabaseConnectivity()) { // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); if (role == "DJ") r = db.DJSetEmail(ID, newEmail); else r = db.MobileSetEmail(ID, newEmail); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); return r; } }
/// <summary> /// Create a playlist. Returns the ID of the playlist in message. /// </summary> /// <param name="name">Playlist Name</param> /// <param name="venueID">VenueID the playlist is associated with.</param> /// <param name="userKey">client mobile key.</param> /// <returns>The outcome of the opearation.</returns> public Response MobileCreatePlaylist(string name, int venueID, long userKey) { ExpResponse r = new ExpResponse(); if (name.Length < 1 || name.Length > 20) { r.setErMsg(true, Messages.ERR_PLYLST_NAME_LONG); return r; } int mobileID = -1; int venueStatus; using (DatabaseConnectivity db = new DatabaseConnectivity()) { // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); // Convert the userKey to MobileID r = MobileKeyToID(userKey, out mobileID, db); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); // Make sure the client isn't already logged out. bool validStatus; r = MobileCheckStatus(mobileID, "!0", db, out validStatus); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); if (!validStatus) { r.setErMsg(true, Messages.ERR_STATUS_IS_NOT_IN); return r; } // Make sure the venueID exists. r = db.DJGetStatus(venueID); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); if (!int.TryParse(r.message.Trim(), out venueStatus)) { r.setErMsg(true, Messages.ERR_BAD_SERVER_INPUT); return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); } r = db.MobileCreatePlaylist(name, venueID, mobileID, DateTime.Now); if(r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); return r; } }
/// <summary> /// Suggests songs based on collaborative filtering of the user's song history with other /// singers' song histories. Songs objects returned are pre-populated except for path on disk. /// </summary> /// <param name="userSongsAndCount">The user's songs and how often sung.</param> /// <param name="mobileID">The mobileID of the user.</param> /// <param name="venueID">The venueID</param> /// <param name="count">How many songs to suggest.</param> /// <param name="suggestCollab">Out parameter of suggested songs.</param> /// <param name="db">Database connectivity.</param> /// <returns>The outcome of the operation.</returns> private ExpResponse SuggestCollabFilter(List<KeyValuePair<string[], int>> userSongsAndCount, int mobileID, int venueID, int count, out List<Song> suggestCollab, DatabaseConnectivity db) { // Assign potential songs a value. If a user has 3 songs in common with me, every song suggestable should get 3 pts. // If 3 users have songs in common with me, and they each have a song in commong with themselves, that song gets 3 pts since it's from 3 users. // Later on rank by points. ExpResponse r = new ExpResponse(); suggestCollab = new List<Song>(); // Go through user history, get all the users who sang songs that I sang. // List of userIds, and how many songs we have in common. List<KeyValuePair<int, int>> userCountInCommon; r = GetUsersSongsInCommon(userSongsAndCount, mobileID, db, out userCountInCommon); if (r.error) return r; //string mes = "Users in common\r\n"; //foreach (KeyValuePair<int, int> k in userCountInCommon) // mes += k.Key + " " + k.Value + "\r\n"; //Common.LogSimpleError(Common.LogFile.Debug, mes); // Stores each user, how similar to them we are, what songs they have sung, and how often they have sung those songs. List<UserAndSongs> userAndSongs = new List<UserAndSongs>(); // Get songs that the in common users have sung at this venue. foreach (KeyValuePair<int, int> other in userCountInCommon) { List<KeyValuePair<string[], int>> OSAC; r = db.MobileGetOtherDistictSongHistory(other.Key, 0, 2*count, out OSAC); if (r.error) return r; // Remove any songs from other user's history that we have already sang. foreach (KeyValuePair<string[], int> excludeSong in userSongsAndCount) { for (int i = 0; i < OSAC.Count; i++) { if (OSAC[i].Key[0].Equals(excludeSong.Key[0]) && OSAC[i].Key[1].Equals(excludeSong.Key[1])) { OSAC.RemoveAt(i); break; } } } if (OSAC.Count > 0) userAndSongs.Add(new UserAndSongs(other.Key, other.Value, OSAC)); //mes = "User songs for " + other.Key + "\r\n"; //foreach (KeyValuePair<string[], int> k in OSAC) // mes += k.Key[0] + " " + k.Key[1] + " " + k.Value + "\r\n"; //Common.LogSimpleError(Common.LogFile.Debug, mes); } if (Settings.SNG_SGGST_MTHD.Equals("round_robin", StringComparison.OrdinalIgnoreCase)) { r = PermuteSuggetsionsRoundRobin(userAndSongs, venueID, count, ref suggestCollab, db); } else if (Settings.SNG_SGGST_MTHD.Equals("top", StringComparison.OrdinalIgnoreCase)) { r = PermuteSuggestionsTop(userAndSongs, venueID, count, ref suggestCollab, db); } else if (Settings.SNG_SGGST_MTHD.Equals("random_weighted", StringComparison.OrdinalIgnoreCase)) { r = PermuteSuggestionsRandom(userAndSongs, venueID, count, ref suggestCollab, db); } else { Common.LogSimpleError(Common.LogFile.Mobile, "Unknown method requested for song song suggestion recombination", "Using the deafult of random_weighted"); r = PermuteSuggestionsRandom(userAndSongs, venueID, count, ref suggestCollab, db); } if(r.error) return r; return r; }
internal ExpResponse DJRemoveUsersFromVenue(int DJID) { ExpResponse r = new ExpResponse(); SqlCommand cmd = new SqlCommand("update MobileUsers set Venue = @null where Venue = @DJID;", con); cmd.Parameters.AddWithValue("@null", DBNull.Value); cmd.Parameters.AddWithValue("@DJID", DJID); try { r.result = cmd.ExecuteNonQuery(); return r; } catch (SqlException e) { r.setErMsgStk(true, "Exception in DJRemoveUsersFromVenue ID: " + e.Number + " " + e.Message, e.StackTrace); return r; } catch (Exception e) { r.setErMsgStk(true, "Exception in DJRemoveUsersFromVenue: " + e.Message, e.StackTrace); return r; } }
internal ExpResponse DJUnbanUser(int DJID, int mobileID) { ExpResponse r = new ExpResponse(); SqlCommand cmd = new SqlCommand("delete from DJBannedUsers where DJID = @DJID and MobileID = @mobileID;", con); cmd.Parameters.AddWithValue("@DJID", DJID); cmd.Parameters.AddWithValue("@mobileID", mobileID); try { r.result = cmd.ExecuteNonQuery(); return r; } catch (Exception e) { r.setErMsgStk(true, "Exception in DJUnbanUser: " + e.Message, e.StackTrace); return r; } }
/// <summary> /// Validates a password reset key to be valid. If it is valid, ID is set to the ID it represents, /// if it is not valid, ID is set to -1. /// </summary> /// <param name="key">The key.</param> /// <param name="isDJ">Whether this is for DJs or Mobile.</param> /// <param name="ID">The ID for the key.</param> /// <returns>The outcome of the operation.</returns> public Response ValidatePasswordResetKey(string key, bool isDJ, out int ID) { ExpResponse r = new ExpResponse(); ID = -1; using (DatabaseConnectivity db = new DatabaseConnectivity()) { // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); if(isDJ) { r = db.DJGetPasswordResetID(key, out ID); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); } else { r = db.MobileGetPasswordResetID(key, out ID); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); } return r; } }
/// <summary> /// Starts the password reset process for users who forgot their passwords. /// </summary> /// <param name="email">The email address of the user.</param> /// <param name="key">Out parameter for the unique key this user will temporarily be associated with.</param> /// <param name="role">The role: DJ or Mobile</param> /// <returns>The outcome of the operation.</returns> public Response StartPasswordReset(string email, string username, bool isDJ, string websiteAddress) { ExpResponse r = new ExpResponse(); using (DatabaseConnectivity db = new DatabaseConnectivity()) { // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); int ID; if (isDJ) r = db.DJValidateUsernameEmail(username, email, out ID); else r = db.MobileValidateUsernameEmail(username, email, out ID); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); if(ID == -1) { r.setErMsg(true, Messages.MSG_EMAIL_NOT_FOUND); return r; } string random = Common.CreateSalt(32); Regex rgx = new Regex("[^a-zA-Z0-9 -]"); random = rgx.Replace(random, "x"); int uniqueIsNegOne = 0; while (uniqueIsNegOne != -1) { if (isDJ) r = db.DJGetPasswordResetID(random, out uniqueIsNegOne); else r = db.MobileGetPasswordResetID(random, out uniqueIsNegOne); if(r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); random = Common.CreateSalt(32); random = rgx.Replace(random, "x"); } if (isDJ) r = db.DJSetPasswordReset(ID, random); else r = db.MobileSetPasswordReset(ID, random); if(r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); try { string resetURL = websiteAddress + "?DJ=" + isDJ.ToString() + "&key=" + random; MailMessage mail = GeneratePasswordResetEmail(email, resetURL); SmtpClient mailServer = new SmtpClient("smtp.live.com"); mailServer.Port = 25; mailServer.UseDefaultCredentials = false; mailServer.Credentials = new System.Net.NetworkCredential(Settings.EMAIL_ADR, Settings.EMAIL_PSWD); mailServer.EnableSsl = true; mailServer.Send(mail); return r; } catch (Exception e) { r.setErMsgStk(true, e.Message, e.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_EMAIL_SERVER, Common.LogFile.Web); } } }
/// <summary> /// Try to remove a users song requests from the venue. If they dont' have any, nothing happens. /// </summary> /// <param name="mobileID">The user's ID.</param> /// <param name="venueID">The venue ID</param> /// <param name="db">Database connectivity.</param> /// <returns>The outcome of the operation.</returns> private ExpResponse MobileTryRemoveAllSongRequests(int mobileID, DatabaseConnectivity db) { ExpResponse r = new ExpResponse(); int venueID; // Get the venueID r = MobileGetVenue(mobileID, db); if (r.error) return r; venueID = r.result; if (venueID == -1) return r; bool validStatus; // Make sure the venue is accepting songs. r = VenueCheckStatus(venueID, "2", db, out validStatus); if (r.error) return r; if (!validStatus) return r; // Get the current song Requests r = db.GetSongRequests(venueID); if (r.error) return r; string requests = r.message; string newRequests = string.Empty; if (requests.Trim().Length == 0) return r; // Since there is a list of requests, call to parse the raw string data into an list of queuesingers. List<queueSinger> queue; r = Common.DBToMinimalList(requests, out queue); if (r.error) return r; // Search to see if the user is already in this list of singers. for (int i = 0; i < queue.Count; i++) { // If the user is found. if (queue[i].user.userID == mobileID) { queue.RemoveAt(i); Common.MinimalListToDB(queue, out newRequests); r = db.SetSongRequests(venueID, newRequests); if (r.error) return r; Common.PushMessageToUsersOfDJ(venueID, "queue", db); return r; } } return r; }
/// <summary> /// Returns a list of keyvaluepairs where the key is the userID of a user who has a song in common with us, and the value is the number of songs they have in common. /// </summary> /// <param name="userHistory"></param> /// <param name="mobileID"></param> /// <param name="db"></param> /// <param name="songsInCommonList"></param> /// <returns></returns> private ExpResponse GetUsersSongsInCommon(List<KeyValuePair<string[], int>> songsAndCount, int mobileID, DatabaseConnectivity db, out List<KeyValuePair<int, int>> songsInCommonList) { songsInCommonList = new List<KeyValuePair<int, int>>(); ExpResponse r = new ExpResponse(); // For each user, see how many songs we have in common. IDictionary<int, int> songsInCommonDict = new Dictionary<int, int>(); // UserID, count // Loop through all of my songs. foreach (KeyValuePair<string[],int> sAc in songsAndCount) { SangSong ss; // Get everyone that shares this song. r = db.MobileGetOthersWhoSangSong(mobileID, sAc.Key[0], sAc.Key[1], 10, out ss); if (r.error) return r; foreach (KeyValuePair<int, int> users in ss.userIDsAndCount) { if (songsInCommonDict.ContainsKey(users.Key)) songsInCommonDict[users.Key] += Math.Min(users.Value, sAc.Value); else songsInCommonDict.Add(users.Key, Math.Min(users.Value, sAc.Value)); } } // List of users and the number of songs we have in common, sort them in order of people we have most in common with. songsInCommonList = songsInCommonDict.ToList(); songsInCommonList.Sort((one, two) => { return one.Value.CompareTo(two.Value); }); return r; }
/// <summary> /// Convert a mobile ID to a key. /// </summary> /// <param name="MobileID">The mobileID.</param> /// <param name="MobileKey">Out parameter of the mobile key.</param> /// <returns>The outcome of the operation.</returns> private ExpResponse MobileGenerateKey(int MobileID, out long MobileKey, DatabaseConnectivity db) { MobileKey = -1; ExpResponse r = new ExpResponse(); System.Security.Cryptography.SHA1 sha = new System.Security.Cryptography.SHA1CryptoServiceProvider(); Random rand = new Random(); byte[] randomBytes = new byte[64]; byte[] result; long tempKey; for(;;) { rand.NextBytes(randomBytes); result = sha.ComputeHash(randomBytes); tempKey = BitConverter.ToInt64(result, 0); r = db.MobileGetIDFromKey(tempKey); if(r.error) return r; if(r.message.Trim().Length != 0) continue; r = db.MobileSetKey(MobileID, tempKey); if(r.error) return r; MobileKey = tempKey; return r; } }
/// <summary> /// Convert the database representation of a queue to the object representation. Fill all fields except for path on disk. /// </summary> /// <param name="raw">The database representation.</param> /// <param name="queue">The out parameter to store the queue in.</param> /// <param name="DJID">The ID of the venue.</param> /// <param name="mobileID">The ID of the client.</param> /// <param name="db">The databse conenctivity to use.</param> /// <returns>The outcome of the operation.</returns> private ExpResponse DBToNearlyFullList(string raw, out List<queueSinger> queue, int DJID, int mobileID, DatabaseConnectivity db) { queue = new List<queueSinger>(); ExpResponse r = new ExpResponse(); int count = 0; string[] clientRequests = raw.Split('`'); for (int i = 0; i < clientRequests.Length; i++) { string[] parts = clientRequests[i].Split('~'); if (parts.Length == 0) { r.setErMsgStk(true, "Error in DBToNearlyFullList", Environment.StackTrace); return r; } queueSinger qs = new queueSinger(); qs.songs = new List<Song>(); User u = new User(); u.userID = int.Parse(parts[0]); if (u.userID < 0) r = db.DJGetTempUserName(u.userID, DJID); else r = db.MobileIDtoUsername(u.userID); if (r.error) return r; if (r.message.Trim().Length == 0) { r.setErMsgStk(true, "DB Username lookup exception in DJGetQueue!", Environment.StackTrace); return r; } u.userName = r.message.Trim(); qs.user = u; for (int j = 1; j < parts.Length; j++) { Song song; r = Common.GetSongInformation(int.Parse(parts[j]), DJID, mobileID, out song, db); if (r.error) return r; qs.songs.Add(song); } queue.Add(qs); count++; } return r; }
/// <summary> /// Rate a song. /// </summary> /// <param name="songID">The songID.</param> /// <param name="rating">The rating -1 to 5.</param> /// <param name="venueID">The venueID of the song.</param> /// <param name="userKey">client mobile key.</param> /// <returns>The outcome of the opearation.</returns> public Response MobileRateSong(int songID, int rating, int venueID, long userKey) { int mobileID = -1; int venueStatus; int songExists; using (DatabaseConnectivity db = new DatabaseConnectivity()) { ExpResponse r = new ExpResponse(); if (rating < -1 || rating > 5) { r.setErMsg(true, "Rating must be between -1 and 5 (inclusive)."); return r; } // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); // Convert the userKey to MobileID r = MobileKeyToID(userKey, out mobileID, db); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); // Make sure the client isn't already logged out. bool validStatus; r = MobileCheckStatus(mobileID, "!0", db, out validStatus); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); if (!validStatus) { r.setErMsg(true, Messages.ERR_STATUS_IS_NOT_IN); return r; } // Make sure the venueID exists. r = db.DJGetStatus(venueID); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); if (!int.TryParse(r.message.Trim(), out venueStatus)) { r.setErMsgStk(true, "MobileGetPlayLists venueID parse fail (Bad venueID given?)", Environment.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); } // Check to see if song exists. r = db.SongExists(venueID, songID); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); if (!int.TryParse(r.message.Trim(), out songExists)) { r.setErMsgStk(true, "Could not find song", Environment.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); } // Set the song rating. r = db.MobileSetSongRating(mobileID, songID, rating); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Mobile); return r; } }
/// <summary> /// Validate a DJ's username and email are consistant and exist. If they exist, the DJID is set and > 0, /// if they do not exist the DJID is set to -1. /// </summary> /// <param name="username">DJ's Username</param> /// <param name="email">DJ's email</param> /// <param name="DJID">Out DJID</param> /// <returns>The outcome of the operation</returns> internal ExpResponse DJValidateUsernameEmail(string username, string email, out int DJID) { ExpResponse r = new ExpResponse(); SqlCommand cmd = new SqlCommand("select ID from DJUsers where Email = @email and Username = @username ;", con); cmd.Parameters.AddWithValue("@email", email); cmd.Parameters.AddWithValue("@username", username); try { using (SqlDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) { DJID = int.Parse(reader[0].ToString()); return r; } DJID = -1; return r; } } catch (Exception e) { DJID = -1; r.setErMsgStk(true, "Exception in DJValidateUsernameEmail:" + e.Message, e.StackTrace); return r; } }
/// <summary> /// Suggest songs for the user to sing. Based off of the song history of the current user. /// Some song suggestions given based on collaborative filtering with other user's song histories. /// Some suggestions based on other songs by artists sang. Returns most popular songs if prior /// information not available. /// </summary> /// <param name="venueID">The venue to suggest songs at.</param> /// <param name="userKey">The user's key.</param> /// <param name="start">Ignored.</param> /// <param name="count">The number of song suggestions.</param> /// <returns>The outcome of the operation</returns> public List<Song> MobileGetSongSuggestions(int venueID, long userKey, int start, int count) { start = 0; int count1 = count - count / 4; try { int mobileID = -1; using (DatabaseConnectivity db = new DatabaseConnectivity()) { #region SongSuggestionBoilerPlate List<Song> finalSuggestions = new List<Song>(); // Try to establish a database connection ExpResponse r = db.OpenConnection(); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); // Convert the userKey to MobileID r = MobileKeyToID(userKey, out mobileID, db); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); // Make sure the venueID exists. r = db.DJGetStatus(venueID); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); int venueStatus; if (!int.TryParse(r.message.Trim(), out venueStatus)) { r.setErMsgStk(true, "MobileGetPlayLists venueID parse fail (Bad venueID given?)", Environment.StackTrace); return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); } #endregion // The user's distinct song history. List<KeyValuePair<string[], int>> songsAndCount; // Song Title/Artist and how many times it shows up. r = db.MobileGetDistictSongHistory(mobileID, 0, 10, out songsAndCount); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); // Get song suggestions based on what other people sang. List<Song> suggestCollab; r = SuggestCollabFilter(songsAndCount, mobileID, venueID, count1, out suggestCollab, db); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); // Add these songs to fill up to half of the final suggestions. for (int i = 0; i < suggestCollab.Count; i++) { Song s = suggestCollab[i]; r = Common.LoadSongRating(ref s, mobileID, db); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); finalSuggestions.Add(s); } if (finalSuggestions.Count < count) { // Get song suggestions based off of artist List<Song> suggestByArtist; // Suggest songs not sung by the user, but which have an artist the user has sung. r = SuggestSongsNotSungByMostSungArtists(count - finalSuggestions.Count, mobileID, venueID, out suggestByArtist, db); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); // Add the artist suggestions to fill out the suggetsions. foreach (Song s in suggestByArtist) { Song song; r = Common.GetSongInformation(s.ID, venueID, mobileID, out song, db, false); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); finalSuggestions.Add(song); } } if (finalSuggestions.Count < count) { // If we are lacking songs still, get from popular songs. List<Song> popSongs; List<int> popCounts; r = db.GetMostPopularSongs(venueID, 0, count - finalSuggestions.Count, out popSongs, out popCounts); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); foreach (Song s in popSongs) { Song song; r = Common.GetSongInformation(s.ID, venueID, mobileID, out song, db, false); if (r.error) return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); finalSuggestions.Add(song); } } return finalSuggestions; } } catch (Exception e) { ExpResponse r = new ExpResponse(true, "Exception in Suggest Song:", e.StackTrace); return Common.LogErrorRetGen<List<Song>>(r, null, Common.LogFile.Mobile); } }
/// <summary> /// A way to decide which of the possible song suggestions to use. This method choses songs to suggest based on a weigted random function. /// Users who share the most songs are given a higher chance to lead to song suggestions. Songs that are repeated are given a higher /// chance to be in the final list. /// </summary> /// <param name="userAndSongs">The list of possible suggestions.</param> /// <param name="venueID">The venueID to suggest for.</param> /// <param name="count">The number of suggestions to return.</param> /// <param name="suggestCollab">ref variable to store the suggestions in.</param> /// <param name="db">Database connectivity.</param> /// <returns>The outcome of the operation.</returns> private ExpResponse PermuteSuggestionsRandom(List<UserAndSongs> userAndSongs, int venueID, int count, ref List<Song> suggestCollab, DatabaseConnectivity db) { ExpResponse r = new ExpResponse(); Random rand = new Random(DateTime.Now.Millisecond); while (userAndSongs.Count > 0) { int userIndex, songIndex; r = selectRandWeightedUser(rand, userAndSongs, out userIndex); if (r.error) return r; r = selectRandomSong(rand, userAndSongs[userIndex].songs, out songIndex); if (r.error) return r; Common.LogSimpleError(Common.LogFile.Debug, "UserIndex: " + userIndex, "SongIndex: " + songIndex); string title = userAndSongs[userIndex].songs[songIndex].Key[0]; string artist = userAndSongs[userIndex].songs[songIndex].Key[1]; userAndSongs[userIndex].songs.RemoveAt(songIndex); if (userAndSongs[userIndex].songs.Count == 0) userAndSongs.RemoveAt(userIndex); Song song; r = db.MobileGetSongFromTitleArtist(title, artist, venueID, out song); if (r.error) return r; if (song != null) suggestCollab.Add(song); if (suggestCollab.Count == count) return r; } return r; }
/// <summary> /// "Weblogin" to the system. Returns the user's ID upon success. /// </summary> /// <param name="username">The username</param> /// <param name="password">The password</param> /// <param name="role">The role, DJ or Mobile</param> /// <param name="ID">Our parameter of the user ID.</param> /// <returns>The outcome of the operation.</returns> public Response Login(string username, string password, string role, out int ID) { ID = 0; ExpResponse r = new ExpResponse(); if (!role.Equals("DJ") && !role.Equals("Mobile")) { r.setErMsgStk(true, "Bad Role Given", Environment.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); } using (DatabaseConnectivity db = new DatabaseConnectivity()) { // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); // Get the salt from the database and salt/hash the password. string salt; if (role == "DJ") r = db.DJGetSalt(username, out salt); else r = db.MobileGetSalt(username, out salt); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_CRED_WRONG, Common.LogFile.Web); string saltHashPassword = Common.CreatePasswordHash(password, salt); // Check validity of username/password. if (role == "DJ") r = db.DJValidateUsernamePassword(username, saltHashPassword); else r = db.MobileValidateUsernamePassword(username, saltHashPassword); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); // If the username/password couldn't be found, inform user. if (r.message.Trim() == string.Empty) { r.setErMsg(true, Messages.ERR_CRED_WRONG); return r; } // Get the ID if (!int.TryParse(r.message.Trim(), out ID)) { r.setErMsgStk(true, "Exception in ChangeEmail: Unable to parse ID from DB!", Environment.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); } return r; } }
/// <summary> /// A way to decide which of the possible song suggestions to use. This method simply takes the suggestions from the first user, then the suggestions from the second, /// and so forth until it gets enough suggestions. /// </summary> /// <param name="userAndSongs">The list of possible suggestions.</param> /// <param name="venueID">The venueID to suggest for.</param> /// <param name="count">The number of suggestions to return.</param> /// <param name="suggestCollab">ref variable to store the suggestions in.</param> /// <param name="db">Database connectivity.</param> /// <returns>The outcome of the operation.</returns> private ExpResponse PermuteSuggestionsTop(List<UserAndSongs> userAndSongs, int venueID, int count, ref List<Song> suggestCollab, DatabaseConnectivity db) { ExpResponse r = new ExpResponse(); foreach (UserAndSongs uas in userAndSongs) { foreach (KeyValuePair<string[], int> s in uas.songs) { Song song; r = db.MobileGetSongFromTitleArtist(s.Key[0], s.Key[1], venueID, out song); if (r.error) return r; if (song != null) suggestCollab.Add(song); if (suggestCollab.Count >= count) return r; } } return r; }
/// <summary> /// Sends the username associated with the email address listed to the email address. /// </summary> /// <param name="email">The email address of the user.</param> /// <param name="role">The role: DJ or Mobile</param> /// <returns>The outcome of the operation.</returns> public Response SendEmailWithUsername(string email) { ExpResponse r = new ExpResponse(); using (DatabaseConnectivity db = new DatabaseConnectivity()) { // Try to establish a database connection r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); List<string> DJUsernames; List<string> mobileUsernames; r = db.DJGetUsernamesByEmail(email, out DJUsernames); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); r = db.MobileGetUsernamesByEmail(email, out mobileUsernames); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); if (DJUsernames.Count == 0 && mobileUsernames.Count == 0) { r.setErMsg(true, Messages.MSG_EMAIL_NOT_FOUND); return r; } List<string> usernames = new List<string>(); List<string> roles = new List<string>(); foreach (string djUsername in DJUsernames) { usernames.Add(djUsername); roles.Add("DJ"); } foreach (string mobileUsername in mobileUsernames) { usernames.Add(mobileUsername); roles.Add("Singer"); } try { MailMessage mail = GenerateUsernameEmail(email, usernames, roles); SmtpClient mailServer = new SmtpClient("smtp.live.com"); mailServer.Port = 25; mailServer.UseDefaultCredentials = false; mailServer.Credentials = new System.Net.NetworkCredential(Settings.EMAIL_ADR, Settings.EMAIL_PSWD); mailServer.EnableSsl = true; mailServer.Send(mail); return r; } catch (Exception e) { r.setErMsgStk(true, e.Message, e.StackTrace); return Common.LogErrorRetNewMsg(r, Messages.ERR_EMAIL_SERVER, Common.LogFile.Web); } } }
/// <summary> /// Method to handle creating the sql for an achievement statement. Calls other methods depending on the /// specific achievement select keyword. Returns an erorr if the achievement was invalid, or cannot be parsed. /// </summary> /// <param name="a">The achievement statement.</param> /// <param name="DJID">The DJ's unique ID.</param> /// <param name="cmd">Out sql command to evaluate the statement.</param> /// <returns>The outcome of the operation.</returns> private static ExpResponse CreateStatementGeneric(AchievementSelect a, int DJID, out SqlCommand cmd) { switch (a.selectKeyword) { case SelectKeyword.Max: case SelectKeyword.Min: return CreateStatementMinMax(a, DJID, out cmd); case SelectKeyword.Newest: case SelectKeyword.Oldest: return CreateStatementOldestNewest(a, DJID, out cmd); case SelectKeyword.CountGTE: case SelectKeyword.CountLTE: return CreateStatementCount(a, DJID, out cmd); default: ExpResponse r = new ExpResponse(); r.setErMsgStk(true, "Bad select keyword CreateStatementGeneric", Environment.StackTrace); cmd = new SqlCommand(); return r; } }
/// <summary> /// Retrieves the DJ or MobileID associated with a password reset key and then removes it from the DB. /// ID will be set to -1 if it was invalid. /// </summary> /// <param name="key">The key to remove.</param> /// <param name="isDJ">Whether this is for DJs or mobile users.</param> /// <param name="ID">The userID associated with this user.</param> /// <returns>The outcome of the operation.</returns> public Response UsePasswordResetKey(string key, bool isDJ, out int ID) { Response r1 = ValidatePasswordResetKey(key, isDJ, out ID); if (r1.error) return r1; ExpResponse r = new ExpResponse(); if (ID != -1) { using (DatabaseConnectivity db = new DatabaseConnectivity()) { r = db.OpenConnection(); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); if (isDJ) r = db.DJClearPasswordResetID(ID, key); else r = db.MobileClearPasswordResetID(ID, key); if (r.error) return Common.LogErrorRetNewMsg(r, Messages.ERR_SERVER, Common.LogFile.Web); } } return r; }
/// <summary> /// Sets up the DB to start the password reset process. The value is tored in the DB along with the ID. /// </summary> /// <param name="DJID">The DJ's ID</param> /// <param name="value">The unique key that will represent this password reset.</param> /// <returns>The outcome of the operation</returns> internal ExpResponse DJSetPasswordReset(int DJID, string value) { ExpResponse r = new ExpResponse(); SqlCommand cmd = new SqlCommand("delete from DJPasswordResets where ID = @ID;", con); cmd.Parameters.AddWithValue("@ID", DJID); cmd.ExecuteNonQuery(); SqlCommand cmd2 = new SqlCommand("insert into DJPasswordResets(ID, Value) values (@ID, @value);", con); cmd2.Parameters.AddWithValue("@ID", DJID); cmd2.Parameters.AddWithValue("@value", value); cmd2.ExecuteNonQuery(); try { cmd.ExecuteNonQuery(); cmd2.ExecuteNonQuery(); return r; } catch (Exception e) { r.setErMsgStk(true, "Exception in DJSetPasswordReset:" + e.Message, e.StackTrace); return r; } }
/// <summary> /// A way to decide which of the possible song suggestions to use. This method gets top suggestion form a user and moves onto the next user until it comes back, /// then it takes the second suggestion, moves on, and so forth. /// </summary> /// <param name="userAndSongs">The list of possible suggestions.</param> /// <param name="venueID">The venueID to suggest for.</param> /// <param name="count">The number of suggestions to return.</param> /// <param name="suggestCollab">ref variable to store the suggestions in.</param> /// <param name="db">Database connectivity.</param> /// <returns>The outcome of the operation.</returns> private ExpResponse PermuteSuggetsionsRoundRobin(List<UserAndSongs> userAndSongs, int venueID, int count, ref List<Song> suggestCollab, DatabaseConnectivity db) { ExpResponse r = new ExpResponse(); int up = 0; while (userAndSongs.Count > 0 && suggestCollab.Count < count) { // If we are past the last user, go back to the first user. if (up == userAndSongs.Count) up = 0; // If this user no longer has songs, remove them, and move onto the next user. if (userAndSongs[up].songs.Count == 0) { userAndSongs.RemoveAt(up); continue; } // Get the song from the DB. Song song; r = db.MobileGetSongFromTitleArtist(userAndSongs[up].songs[0].Key[0], userAndSongs[up].songs[0].Key[1], venueID, out song); if (r.error) return r; // Remove this song from the list of songs belonging to the user. userAndSongs[up].songs.RemoveAt(0); // If the song was valid for this venue, add it to the list of songs. if (song != null) suggestCollab.Add(song); // If we have enough songs, return. if (suggestCollab.Count >= count) return r; // Incremement to the next user. up++; } return r; }
/// <summary> /// Update a DJ's salt. /// </summary> /// <param name="DJID">The DJ's unique ID.</param> /// <param name="salt">The new salt.</param> /// <returns>The outcome of the operation.</returns> internal ExpResponse DJSetSalt(int DJID, string salt) { ExpResponse r = new ExpResponse(); SqlCommand cmd = new SqlCommand("Update DJUsers set Salt = @salt where ID = @ID;", con); cmd.Parameters.AddWithValue("@salt", salt); cmd.Parameters.AddWithValue("@ID", DJID); try { r.result = cmd.ExecuteNonQuery(); return r; } catch (Exception e) { r.setErMsgStk(true, "Exception in DJSetSalt:" + e.Message, e.StackTrace); return r; } }
/// <summary> /// Creates the sql for an achievement statement that involves selecting on oldest or newest. Returns an error of the achievement /// statement cannot be parsed. /// </summary> /// <param name="a">The achievement statement.</param> /// <param name="DJID">The DJ's unique ID.</param> /// <param name="cmd">Out sql command to evaluate the statement.</param> /// <returns>The outcome of the operation.</returns> private static ExpResponse CreateStatementOldestNewest(AchievementSelect a, int DJID, out SqlCommand cmd) { ExpResponse r = new ExpResponse(); cmd = new SqlCommand(); int offset; if (!int.TryParse(a.selectValue, out offset)) { r.setErMsgStk(true, "Could not parse offset", Environment.StackTrace); return r; } offset--; if (offset < 0) offset = 0; cmd.CommandText = "select MobileID from MobileSongHistory inner join DJSongs on MobileSongHistory.SongID = DJSongs.SongID "; cmd.CommandText+= "where DJSongs." + ClauseKeywordToString(a) + " like @clauseKeyword "; cmd.Parameters.AddWithValue("@clauseKeyword", a.clauseValue); cmd.CommandText += "and VenueID = @DJID and DateSung >= @minDate and DateSung <= @maxDate "; cmd.Parameters.AddWithValue("@DJID", DJID); cmd.Parameters.AddWithValue("@minDate", a.startDate); cmd.Parameters.AddWithValue("@maxDate", a.endDate); cmd.CommandText += "order by DateSung " + SelectKeywordToString(a) + " "; cmd.CommandText += "offset @offset rows fetch next @count rows only;"; cmd.Parameters.AddWithValue("@offset", offset); cmd.Parameters.AddWithValue("@count", 1); return r; }
/// <summary> /// Select a random song from a list of weigted songs. /// </summary> /// <param name="rand">Randon number generator to use.</param> /// <param name="all">The weighted list of songs.</param> /// <returns>The index of the random song in the collection.</returns> private ExpResponse selectRandomSong(Random rand, List<KeyValuePair<string[], int>> all, out int index) { ExpResponse r = new ExpResponse(); index = 0; try { int totalSongScore = 0; foreach (KeyValuePair<string[], int> s in all) totalSongScore += s.Value; int sum = 0; // Exception on below line, totalSongScore must be zero for hugo account on rick. int rn = rand.Next(1, totalSongScore + 1); foreach (KeyValuePair<string[], int> s in all) { sum += s.Value; if (rn <= sum) return r; index++; } r.setErMsgStk(true, "Had to select first song", Environment.StackTrace); return r; } catch (Exception e) { r.setErMsgStk(true, e.Message, e.StackTrace); return r; } }
/// <summary> /// Creates the sql for an achievement statement that involves selecting on a count. Returns an error of the achievement /// statement cannot be parsed. /// </summary> /// <param name="a">The achievement statement.</param> /// <param name="DJID">The DJ's unique ID.</param> /// <param name="cmd">Out sql command to evaluate the statement.</param> /// <returns>The outcome of the operation.</returns> private static ExpResponse CreateStatementCount(AchievementSelect a, int DJID, out SqlCommand cmd) { ExpResponse r = new ExpResponse(); cmd = new SqlCommand(); int value; if (!int.TryParse(a.selectValue, out value)) { r.setErMsgStk(true, "Could not select value", Environment.StackTrace); return r; } if (value < 0) { r.setErMsgStk(true, "Select value was less than 0, abort", Environment.StackTrace); return r; } // In this case, statement must be all users that don't have a count > 0. if (value == 0 && a.selectKeyword == SelectKeyword.CountLTE) { cmd.CommandText = "select MobileID from MobileSongHistory where MobileID not in "; cmd.CommandText+= "( "; cmd.CommandText += "select MobileID from MobileSongHistory inner join DJSongs on MobileSongHistory.SongID = DJSongs.SongID "; cmd.CommandText+= "where DJSongs." + ClauseKeywordToString(a) + " like @clauseKeyword and VenueID = @DJID and DateSung >= @minDate and DateSung <= @maxDate "; cmd.CommandText+= "group by MobileID having count(mobileID) > 0"; cmd.CommandText+= ") "; cmd.CommandText+= "and VenueID = @DJID and DateSung >= @minDate and DateSung <= @maxDate "; cmd.CommandText+= "group by MobileID;"; cmd.Parameters.AddWithValue("@clauseKeyword", a.clauseValue); cmd.Parameters.AddWithValue("@DJID", DJID); cmd.Parameters.AddWithValue("@minDate", a.startDate); cmd.Parameters.AddWithValue("@maxDate", a.endDate); } // In this case, select all users who have sang a song 0 or more times, simply returns all users. else if (value == 0 && a.selectKeyword == SelectKeyword.CountGTE) { cmd.CommandText = "select MobileID form MobileSongHistory where VenueID = @DJID group by MobileID"; cmd.Parameters.AddWithValue("@DJID", DJID); } // Not a special case, just regular stuff. else { cmd.CommandText = "select MobileID from MobileSongHistory inner join DJSongs on MobileSongHistory.SongID = DJSongs.SongID "; cmd.CommandText += "where DJSongs." + ClauseKeywordToString(a) + " like @clauseKeyword "; cmd.Parameters.AddWithValue("@clauseKeyword", a.clauseValue); cmd.CommandText += "and VenueID = @DJID and DateSung >= @minDate and DateSung <= @maxDate "; cmd.Parameters.AddWithValue("@DJID", DJID); cmd.Parameters.AddWithValue("@minDate", a.startDate); cmd.Parameters.AddWithValue("@maxDate", a.endDate); cmd.CommandText += "group by MobileID having count(mobileID) " + SelectKeywordToString(a) + " @value;"; cmd.Parameters.AddWithValue("@value", a.selectValue); } return r; }
/// <summary> /// Returns a random user from a weigted collection of users. /// </summary> /// <param name="rand">The random number generator to use.</param> /// <param name="all">The weighted collection of users.</param> /// <returns>The index of the random user in the collection.</returns> private ExpResponse selectRandWeightedUser(Random rand, List<UserAndSongs> all, out int index) { ExpResponse r = new ExpResponse(); index = 0; try { int totalUserScore = 0; foreach (UserAndSongs uas in all) totalUserScore += uas.commonScore; //string mes = "User count: " + all.Count + "\r\n"; //mes += "Total score: " + totalUserScore + "\r\n"; int sum = 0; int rn = rand.Next(1, totalUserScore + 1); // mes += "Random generated to be: " + rn + "\r\n"; foreach (UserAndSongs uas in all) { sum += uas.commonScore; //mes += "Iteration " + index + " sum:" + sum + "\r\n"; if (rn <= sum) { //mes += "RETURNING with a final rn: " + rn + "sum: " + sum + "\r\n"; //Common.LogSimpleError(Common.LogFile.Debug, mes); return r; } index++; } r.setErMsgStk(true, "Had to select first user", Environment.StackTrace); return r; } catch (Exception e) { r.setErMsgStk(true, e.Message, e.StackTrace); return r; } }