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);
        }
Esempio n. 2
0
        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();
                }
            }
        }
Esempio n. 4
0
        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();
                    }
                }
            }
        }