public async Task <bool> LoginAsync(string requestUri, StringContent queryString, HttpClient ancestryClient) { using (var loginResponse = await ancestryClient.PostAsync(requestUri, queryString)) { if (loginResponse.StatusCode == HttpStatusCode.Unauthorized) { return(false); } try { loginResponse.EnsureSuccessStatusCode(); var result = await loginResponse.Content.ReadAsAsync <LoginResult>(); if (result.Status == "invalidCredentials") { return(false); } } catch (Exception e) { FileUtils.LogException(e, false); return(false); } } return(true); }
private async Task <LoginResult> LoginAsync(string requestUri, StringContent queryString, HttpClient ancestryClient) { using (var loginResponse = await ancestryClient.PostAsync(requestUri, queryString)) { if (loginResponse.StatusCode == HttpStatusCode.Unauthorized) { return(LoginResult.Unauthorized); } try { loginResponse.EnsureSuccessStatusCode(); var result = await loginResponse.Content.ReadAsAsync <LoginResultDto>(); if (result.Status.Equals("invalidCredentials", StringComparison.OrdinalIgnoreCase)) { return(LoginResult.InvalidCredentials); } else if (result.Status.Equals("mfa", StringComparison.OrdinalIgnoreCase)) { return(LoginResult.MultifactorAuthentication); } } catch (Exception e) { FileUtils.LogException(e, false); return(LoginResult.Exception); } } return(LoginResult.Success); }
public async Task <Match> GetMatchAsync(string guid, string testGuid, HashSet <int> tagIds, Throttle throttle, ProgressData progressData) { var retryCount = 0; var retryMax = 5; while (true) { await throttle.WaitAsync(); try { using (var testsResponse = await _ancestryLoginHelper.AncestryClient.GetAsync($"discoveryui-matchesservice/api/samples/{guid}/matches/{testGuid}/details")) { if (!testsResponse.IsSuccessStatusCode) { return(null); } return(ConvertMatch(await testsResponse.Content.ReadAsAsync <MatchV2>(), tagIds)); } } catch (Exception ex) { if (++retryCount >= retryMax) { FileUtils.LogException(ex, true); await Task.Delay(ex is UnsupportedMediaTypeException? 30000 : 3000); return(null); } await Task.Delay(ex is UnsupportedMediaTypeException? 30000 : 3000); } finally { progressData.Increment(); throttle.Release(); } } }
public async Task UpdateNotesAsync(string guid, string matchFile, Throttle throttle, ProgressData progressData) { var originalTags = await _matchesRetriever.GetTagsAsync(guid, throttle); var originalTagIds = new HashSet <int>(originalTags.Select(tag => tag.TagId)); var notes = ReadMatchFile(matchFile, originalTags, progressData).ToList(); var originalNumNotes = notes.Count; await MaybeUpdateFilesAsync(notes, originalTags); notes = (await FilterModifiedNodesAsync(guid, notes, originalTagIds, throttle, progressData)).ToList(); if (notes.Count == 0) { MessageBox.Show("No changed notes were found.", "No changed notes", MessageBoxButton.OK, MessageBoxImage.Information); return; } var duplicateTags = originalTags .GroupBy(tag => tag.Label) .Where(g => g.Count() > 1) .SelectMany(g => g).ToList(); if (duplicateTags.Count > 1 && duplicateTags.Select(tag => tag.TagId).Intersect(notes.SelectMany(note => note.NewTags.Concat(note.NewTagsRemoved))).Any()) { MessageBox.Show($"Duplicate group names found: '{duplicateTags.First().Label}'. Cannot update groups when more than one group has a matching name.", "Duplicate groups", MessageBoxButton.OK, MessageBoxImage.Information); return; } var toChangeCount = notes.Count( note => (note.NewNotes != null && note.NewNotes != "") || (note.NewStarred != null && note.NewStarred != note.OldStarred) || note.NewTags.Except(note.OldTags).Any() || note.NewTagsRemoved.Intersect(note.OldTags).Any()); var toRemoveCount = notes.Count(note => note.NewNotes == ""); var message = "Found " + (toChangeCount > 0 && toRemoveCount > 0 ? $"{toChangeCount} matches to change and {toRemoveCount} notes to remove." : toChangeCount > 0 ? $"{toChangeCount} matches to change." : $"{toRemoveCount} notes to remove.") + " Are you sure that you want to make changes to your notes on the Ancestry website?"; if (MessageBox.Show( message, "Notes to change", MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes) { return; } progressData.Reset("Updating notes", notes.Count); var updateTasks = notes.Select(async note => { try { if (note.NewNotes != null) { await _matchesRetriever.UpdateNotesAsync(guid, note.TestId, note.NewNotes, throttle); } if (note.NewStarred != null) { await _matchesRetriever.UpdateStarredAsync(guid, note.TestId, note.NewStarred.Value, throttle); } foreach (var tagId in note.NewTags.Except(note.OldTags)) { await _matchesRetriever.AddTagAsync(guid, note.TestId, tagId, throttle); } foreach (var tagId in note.NewTagsRemoved.Intersect(note.OldTags)) { await _matchesRetriever.DeleteTagAsync(guid, note.TestId, tagId, throttle); } return(note); } catch (Exception ex) { FileUtils.LogException(new Exception($"Failed to update notes for {note.Name} ({note.TestId})", ex), false); return(null); } finally { progressData.Increment(); } }); var updatedNotes = await Task.WhenAll(updateTasks); await SaveUpdatedNotesToFileAsync(updatedNotes.Where(note => note != null).ToList(), originalTags, progressData); if (originalNumNotes > notes.Count * 100 || originalNumNotes > notes.Count + 1000) { MessageBox.Show($"Only {notes.Count} out of {originalNumNotes} were updated. The uploading process would be much quicker if you trim down your file to just the changed matches, before uploading the changes.", "Few notes updated", MessageBoxButton.OK, MessageBoxImage.Information); } }
private async Task <(IEnumerable <Match>, bool)> GetMatchesInCommonPageAsync(string guid, string guidInCommon, int pageNumber, Throttle throttle) { if (guid == guidInCommon) { return(Enumerable.Empty <Match>(), false); } var nameUnavailableCount = 0; var nameUnavailableMax = 60; var retryCount = 0; var retryMax = 5; while (true) { await throttle.WaitAsync(); var throttleReleased = false; try { using (var testsResponse = await _dnaHomeClient.GetAsync($"discoveryui-matchesservice/api/samples/{guid}/matchesv2?page={pageNumber}&relationguid={guidInCommon}&bookmarkdata={{\"moreMatchesAvailable\":true,\"lastMatchesServicePageIdx\":{pageNumber - 1}}}")) { throttle.Release(); throttleReleased = true; if (testsResponse.StatusCode == System.Net.HttpStatusCode.Gone) { return(Enumerable.Empty <Match>(), false); } if (testsResponse.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable) { await Task.Delay(120000); continue; } testsResponse.EnsureSuccessStatusCode(); var matches = await testsResponse.Content.ReadAsAsync <MatchesV2>(); var matchesInCommon = matches.MatchGroups.SelectMany(matchGroup => matchGroup.Matches) .Select(match => new Match { MatchTestAdminDisplayName = match.AdminDisplayName, MatchTestDisplayName = match.DisplayName, TestGuid = match.TestGuid, SharedCentimorgans = match.Relationship?.SharedCentimorgans ?? 0, SharedSegments = match.Relationship?.SharedSegments ?? 0, Starred = match.Starred, Note = match.Note, }) .ToList(); if (matchesInCommon.Any(match => match.Name == "name unavailable") && ++nameUnavailableCount < nameUnavailableMax) { await Task.Delay(3000); continue; } return(matchesInCommon, matches.BookmarkData.MoreMatchesAvailable); } } catch (Exception ex) { if (++retryCount >= retryMax) { FileUtils.LogException(ex, true); await Task.Delay(ex is UnsupportedMediaTypeException? 30000 : 3000); return(Enumerable.Empty <Match>(), false); } await Task.Delay(ex is UnsupportedMediaTypeException? 30000 : 3000); } finally { if (!throttleReleased) { throttle.Release(); } } } }
public async Task <IEnumerable <Match> > GetMatchesPageAsync(string guid, int pageNumber, bool includeAdditionalInfo, Throttle throttle, ProgressData progressData) { var nameUnavailableCount = 0; var nameUnavailableMax = 60; var retryCount = 0; var retryMax = 5; while (true) { await throttle.WaitAsync(); var throttleReleased = false; try { using (var testsResponse = await _dnaHomeClient.GetAsync($"discoveryui-matchesservice/api/samples/{guid}/matchesv2?page={pageNumber}&bookmarkdata={{\"moreMatchesAvailable\":true,\"lastMatchesServicePageIdx\":{pageNumber - 1}}}")) { throttle.Release(); throttleReleased = true; testsResponse.EnsureSuccessStatusCode(); var matches = await testsResponse.Content.ReadAsAsync <MatchesV2>(); var result = matches.MatchGroups.SelectMany(matchGroup => matchGroup.Matches) .Select(match => new Match { MatchTestAdminDisplayName = match.AdminDisplayName, MatchTestDisplayName = match.DisplayName, TestGuid = match.TestGuid, SharedCentimorgans = match.Relationship?.SharedCentimorgans ?? 0, SharedSegments = match.Relationship?.SharedSegments ?? 0, Starred = match.Starred, Note = match.Note, }) .ToList(); // Sometimes Ancestry returns matches with partial data. // If that happens, retry and hope to get full data the next time. if (result.Any(match => match.Name == "name unavailable") && ++nameUnavailableCount < nameUnavailableMax) { await Task.Delay(3000); continue; } progressData.Increment(); if (includeAdditionalInfo) { try { await GetAdditionalInfoAsync(guid, result, throttle); } catch { // non-fatal if unable to download trees } } progressData.Increment(); return(result); } } catch (Exception ex) { if (++retryCount >= retryMax) { FileUtils.LogException(ex, true); await Task.Delay(ex is UnsupportedMediaTypeException? 30000 : 3000); return(Enumerable.Empty <Match>()); } await Task.Delay(ex is UnsupportedMediaTypeException? 30000 : 3000); } finally { if (!throttleReleased) { throttle.Release(); } } } }