private static string GetPopularRoutes(Area area, ResultParameters parameters)
        {
            string result = "";

            List <Route> popularRoutes = new List <Route>();

            if (area.PopularRouteIDs.Count == 0) //MountainProject doesn't list any popular routes. Figure out some ourselves
            {
                popularRoutes = area.GetPopularRoutes(3);
            }
            else
            {
                area.PopularRouteIDs.ForEach(id => popularRoutes.Add(MountainProjectDataSearch.GetItemWithMatchingID(id, MountainProjectDataSearch.DestAreas) as Route));
            }

            foreach (Route popularRoute in popularRoutes)
            {
                result += $"\n- {Markdown.Link(popularRoute.Name, popularRoute.URL)} {GetRouteAdditionalInfo(popularRoute, parameters)}";
            }

            if (string.IsNullOrEmpty(result))
            {
                return("");
            }

            return("Popular routes:" + Markdown.NewLine + result + Markdown.NewLine);
        }
        public void TestSearch()
        {
            InitMPData();

            for (int i = 0; i < testCriteria_search.GetLength(0); i++)
            {
                string query       = testCriteria_search[i, 0];
                string expectedUrl = testCriteria_search[i, 1];

                _ = ResultParameters.ParseParameters(ref query); //This is here just to filter out any query items (not to be used)
                SearchParameters searchParameters = SearchParameters.ParseParameters(ref query);

                SearchResult searchResult = MountainProjectDataSearch.Search(query, searchParameters);

                if (string.IsNullOrEmpty(expectedUrl))
                {
                    Assert.IsNull(searchResult.FilteredResult, "Failed for " + testCriteria_search[i, 0]);
                }
                else
                {
                    Assert.AreEqual(Utilities.GetSimpleURL(Utilities.MPBASEURL + expectedUrl),
                                    searchResult.FilteredResult.URL, "Failed for " + testCriteria_search[i, 0]);
                }

                Assert.IsTrue(searchResult.TimeSpanTaken().TotalSeconds < 5, $"{query} took too long ({searchResult.TimeTakenMS} ms)");
            }
        }
        public static string GetLocationString(MPObject child, Area referenceLocation = null)
        {
            MPObject innerParent = MountainProjectDataSearch.GetInnerParent(child);
            MPObject outerParent = MountainProjectDataSearch.GetOuterParent(child);

            if (referenceLocation != null) //Override the "innerParent" in situations where we want the location string to include the "insisted" location
            {
                //Only override if the location is not already present
                if (innerParent?.URL != referenceLocation.URL &&
                    outerParent?.URL != referenceLocation.URL)
                {
                    innerParent = referenceLocation;
                }
            }

            if (innerParent == null)
            {
                return("");
            }

            string locationString = $"Located in {Markdown.Link(innerParent.Name, innerParent.URL)}";

            if (outerParent != null && outerParent.URL != innerParent.URL)
            {
                locationString += $", {Markdown.Link(outerParent.Name, outerParent.URL)}";
            }

            locationString += Markdown.NewLine;

            return(locationString);
        }
Beispiel #4
0
        static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.UTF8;

            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            if (args.FirstOrDefault(p => p.Contains("xmlpath=")) != null)
            {
                xmlPath = args.FirstOrDefault(p => p.Contains("xmlpath=")).Split('=')[1];
            }

            if (args.FirstOrDefault(p => p.Contains("credentials=")) != null)
            {
                credentialsPath = args.FirstOrDefault(p => p.Contains("credentials=")).Split('=')[1];
            }

            if (args.FirstOrDefault(p => p.Contains("repliedto=")) != null)
            {
                repliedToPath = args.FirstOrDefault(p => p.Contains("repliedto=")).Split('=')[1];
            }

            if (args.FirstOrDefault(p => p.Contains("blacklisted=")) != null)
            {
                blacklistedPath = args.FirstOrDefault(p => p.Contains("blacklisted=")).Split('=')[1];
            }

            CheckRequiredFiles();
            MountainProjectDataSearch.InitMountainProjectData(xmlPath);
            redditHelper.Auth(credentialsPath).Wait();
            DoBotLoop().Wait();
        }
Beispiel #5
0
        public static void InitStreams()
        {
            while (true)
            {
                try
                {
                    MountainProjectDataSearch.InitMountainProjectData(xmlPath);
                    break;
                }
                catch
                {
                    ConsoleHelper.Write("MountainProjectAreas.xml is in use. Waiting 5s before trying again...");
                    Thread.Sleep(TimeSpan.FromSeconds(5));
                }
            }

            BotFunctions.RedditHelper = new RedditHelper();
            BotFunctions.RedditHelper.Auth(credentialsPath).Wait();
            requestForApprovalURL = GetCredentialValue(credentialsPath, "requestForApprovalURL");
            WebServerURL          = GetCredentialValue(credentialsPath, "webServerURL");
            spreadsheetHistoryURL = GetCredentialValue(credentialsPath, "spreadsheetURL");

            //Start approval server
            ApprovalServer = new Server(9999)
            {
                HandleRequest = ApprovalServerRequestHandler.HandleRequest
            };
            ApprovalServer.Start();
        }
        public static string GetReplyForMPLinks(Comment comment)
        {
            List <MPObject> foundMPObjects = new List <MPObject>();

            foreach (string url in ExtractMPLinks(WebUtility.HtmlDecode(comment.Body)))
            {
                MPObject mpObjectWithID = MountainProjectDataSearch.GetItemWithMatchingID(Utilities.GetID(url));
                if (mpObjectWithID != null)
                {
                    foundMPObjects.Add(mpObjectWithID);
                }
            }

            string response = "";

            if (foundMPObjects.Count == 0)
            {
                return(null);
            }

            foundMPObjects.ForEach(p => response += GetFormattedString(p, includeUrl: false) + Markdown.HRule);
            response += GetBotLinks(comment);

            return(response);
        }
Beispiel #7
0
        private static string GetPopularRoutes(Area area, ResultParameters parameters)
        {
            string result = "Popular routes:\n";

            List <Route> popularRoutes = new List <Route>();

            if (area.PopularRouteUrls.Count == 0) //MountainProject doesn't list any popular routes. Figure out some ourselves
            {
                popularRoutes = area.GetPopularRoutes(3);
            }
            else
            {
                List <MPObject> itemsToSearch = new List <MPObject>();
                itemsToSearch.AddRange(area.SubAreas);
                itemsToSearch.AddRange(area.Routes);

                area.PopularRouteUrls.ForEach(p => popularRoutes.Add(MountainProjectDataSearch.GetItemWithMatchingUrl(p, itemsToSearch) as Route));
            }

            foreach (Route popularRoute in popularRoutes)
            {
                result += $"\n- {Markdown.Link(popularRoute.Name, popularRoute.URL)} {GetRouteAdditionalInfo(popularRoute, parameters)}";
            }

            if (string.IsNullOrEmpty(result))
            {
                return("");
            }

            result += Markdown.NewLine;

            return(result);
        }
 private void InitMPData()
 {
     //The MountainProject data will actually persist between unit tests if multiple unit tests are run at once. Doing it this way
     //ensures that the delay time to init the MountainProject data is only part of the first test
     if (MountainProjectDataSearch.DestAreas.Count == 0)
     {
         MountainProjectDataSearch.InitMountainProjectData(@"..\..\MountainProjectDBBuilder\bin\MountainProjectAreas.xml");
     }
 }
Beispiel #9
0
        public void TestCommentBodyParse()
        {
            MountainProjectDataSearch.InitMountainProjectData(@"..\..\MountainProjectDBBuilder\bin\MountainProjectAreas.xml");

            for (int i = 0; i < testCriteria_keyword.GetLength(0); i++)
            {
                string commentBody = testCriteria_keyword[i, 0];
                string expectedUrl = testCriteria_keyword[i, 1];
                string resultReply = BotReply.GetReplyForCommentBody(commentBody);

                Assert.IsTrue(resultReply.Contains(expectedUrl));
            }
        }
Beispiel #10
0
        public void TestSearch()
        {
            MountainProjectDataSearch.InitMountainProjectData(@"..\..\MountainProjectDBBuilder\bin\MountainProjectAreas.xml");

            for (int i = 0; i < testCriteria_search.GetLength(0); i++)
            {
                string   query       = testCriteria_search[i, 0];
                string   expectedUrl = testCriteria_search[i, 1];
                MPObject result      = MountainProjectDataSearch.SearchMountainProject(query);

                Assert.AreEqual(Utilities.MPBASEURL + expectedUrl, result.URL);
            }
        }
Beispiel #11
0
        private static void ParseInputString()
        {
            MountainProjectDataSearch.InitMountainProjectData(serializationPath);
            if (MountainProjectDataSearch.DestAreas.Count() == 0)
            {
                Console.WriteLine("The xml either doesn't exist or is empty");
                Environment.Exit(0);
            }

            Console.WriteLine("File read.");

            bool keepSearching = true;

            while (keepSearching)
            {
                Console.WriteLine("\n\nPlease input the string you would like to parse: ");
                string input = Console.ReadLine();

                Stopwatch stopwatch = Stopwatch.StartNew();
                MPObject  result    = MountainProjectDataSearch.SearchMountainProject(input);
                stopwatch.Stop();

                if (result == null)
                {
                    Console.WriteLine("Nothing found matching \"" + input + "\"");
                }
                else
                {
                    string resultStr = "";
                    if (result is Area)
                    {
                        resultStr = (result as Area).ToString();
                    }
                    else if (result is Route)
                    {
                        resultStr = (result as Route).ToString();
                    }

                    Console.WriteLine("The following was found: " + resultStr + " (Found in " + stopwatch.ElapsedMilliseconds + " ms)");
                    Console.WriteLine($"Parent: {MountainProjectDataSearch.GetParent(result, -1).Name}");
                    Console.WriteLine("\nOpen result? (y/n) ");
                    if (Console.ReadKey().Key == ConsoleKey.Y)
                    {
                        Process.Start(result.URL);
                    }
                }

                Console.WriteLine("\nSearch something else? (y/n) ");
                keepSearching = Console.ReadKey().Key == ConsoleKey.Y;
            }
        }
Beispiel #12
0
        public static string GetLocationString(MPObject child)
        {
            MPObject innerParent, outerParent;

            innerParent = null;
            if (child is Route)
            {
                innerParent = MountainProjectDataSearch.GetParent(child, -2); //Get the "second to last" parent https://github.com/derekantrican/MountainProject/issues/12
            }
            else if (child is Area)
            {
                innerParent = MountainProjectDataSearch.GetParent(child, -1); //Get immediate parent
            }
            if (innerParent == null ||                                        //If "child" is a dest area, the parent will be "All Locations" which won't be in our directory
                innerParent.URL == Utilities.INTERNATIONALURL)                //If "child" is an area like "Europe"
            {
                return("");
            }

            outerParent = MountainProjectDataSearch.GetParent(child, 1); //Get state that route/area is in
            if (outerParent.URL == Utilities.INTERNATIONALURL)           //If this is international, get the country instead of the state (eg "China")
            {
                if (child.ParentUrls.Count > 3)
                {
                    if (child.ParentUrls.Contains(Utilities.AUSTRALIAURL)) //Australia is both a continent and a country so it is an exception
                    {
                        outerParent = MountainProjectDataSearch.GetParent(child, 2);
                    }
                    else
                    {
                        outerParent = MountainProjectDataSearch.GetParent(child, 3);
                    }
                }
                else
                {
                    return(""); //Return a blank string if we are in an area like "China" (so we don't return a string like "China is located in Asia")
                }
            }

            string locationString = $"Located in {innerParent.Name}";

            if (outerParent != null && outerParent.URL != innerParent.URL)
            {
                locationString += $", {outerParent.Name}";
            }

            locationString += "\n\n";

            return(locationString);
        }
Beispiel #13
0
        public static string GetReplyForCommentBody(string commentBody)
        {
            string queryText = commentBody.Split(new string[] { BOTKEYWORD }, StringSplitOptions.None)[1].Trim();

            MPObject searchResult = MountainProjectDataSearch.SearchMountainProject(queryText);
            string   replyText    = GetFormattedString(searchResult);

            if (string.IsNullOrEmpty(replyText))
            {
                replyText = $"I could not find anything for \"{queryText}\". Please use the Feedback button below if you think this is a bug";
            }

            return(replyText);
        }
Beispiel #14
0
        public static string HandleRequest(ServerRequest request)
        {
            if (request.RequestMethod == HttpMethod.Get && !request.IsFaviconRequest && !request.IsDefaultPageRequest)
            {
                Dictionary <string, string> parameters = request.GetParameters();
                if (parameters.ContainsKey("status")) //UpTimeRobot will ping this
                {
                    return("UP");
                }
                else if (parameters.ContainsKey("posthistory"))
                {
                    string query = parameters.ContainsKey("query") ? parameters["query"] : "";
                    int    page  = parameters.ContainsKey("page") ? Convert.ToInt32(parameters["page"]) : 1;
                    return(ShowPostHistory(query, page));
                }
                else if (parameters.ContainsKey("postid") && (parameters.ContainsKey("approve") || parameters.ContainsKey("approveall") || parameters.ContainsKey("approveother")))
                {
                    if (BotFunctions.PostsPendingApproval.ContainsKey(parameters["postid"]))
                    {
                        ApprovalRequest approvalRequest = BotFunctions.PostsPendingApproval[parameters["postid"]];

                        return(GetApproval(parameters, approvalRequest));
                    }
                    else if (parameters.ContainsKey("force"))
                    {
                        //Because the ApprovalRequest & SearchResult have been disposed by now, we need to recreate them. Maybe we can do this in a better way in the future
                        Post         post         = BotFunctions.RedditHelper.GetPost(parameters["postid"]).Result;
                        SearchResult searchResult = MountainProjectDataSearch.ParseRouteFromString(post.Title);
                        return(GetApproval(parameters, new ApprovalRequest {
                            Force = true, RedditPost = post, SearchResult = searchResult
                        }));
                    }
                    else
                    {
                        return(WrapHtml($"<h1>Post '{parameters["postid"]}' expired</h1>" +
                                        $"<br>" +
                                        $"<br>" +
                                        $"<input type=\"button\" onclick=\"force()\" value=\"Force\">" +
                                        $"<script>" +
                                        $"  function force(){{" +
                                        $"    window.location.replace(\"{BotUtilities.ApprovalServerUrl}?{string.Join("&", parameters.Select(p => $"{p.Key}={p.Value}").Concat(new[] { "force" }))}\");" +
                                        $"}}" +
                                        $"</script>"));
                    }
                }
            }

            return(WrapHtml($"<h1>Path '{request.Path}' not understood</h1>"));
        }
Beispiel #15
0
        public void TestLocationString()
        {
            MountainProjectDataSearch.InitMountainProjectData(@"..\..\MountainProjectDBBuilder\bin\MountainProjectAreas.xml");

            for (int i = 0; i < testCriteria_location.GetLength(0); i++)
            {
                string query            = testCriteria_location[i, 0];
                string expectedLocation = testCriteria_location[i, 1];
                string resultLocation   = BotReply.GetLocationString(MountainProjectDataSearch.SearchMountainProject(query));
                resultLocation = resultLocation.Replace("\n\n", "");        //Remove markdown newline
                resultLocation = resultLocation.Replace("Located in ", ""); //Simplify results for unit test

                Assert.AreEqual(expectedLocation, resultLocation);
            }
        }
        public void TestGradeEquality()
        {
            InitMPData();

            for (int i = 0; i < testCriteria_gradeEquality.GetLength(0); i++)
            {
                string      inputUrl      = testCriteria_gradeEquality[i, 0].ToString();
                GradeSystem gradeSystem   = (GradeSystem)testCriteria_gradeEquality[i, 1];
                string      inputGrade    = testCriteria_gradeEquality[i, 2].ToString();
                Grade       expectedGrade = Grade.ParseString(inputGrade)[0];

                Route route = MountainProjectDataSearch.GetItemWithMatchingID(Utilities.GetID(Utilities.MPBASEURL + inputUrl)) as Route;

                Assert.IsTrue(route.Grades.Any(g => expectedGrade.Equals(g, true, true)));
            }
        }
        public static string GetReplyForRequest(string commentBody)
        {
            //Get everything AFTER the keyword, but on the same line
            string queryText = Regex.Match(commentBody, BOTKEYWORDREGEX).Groups[1].Value.Trim();

            if (string.IsNullOrWhiteSpace(queryText))
            {
                return("I didn't understand what you were looking for. Please use the Feedback button below if you think this is a bug");
            }

            ResultParameters resultParameters = ResultParameters.ParseParameters(ref queryText);
            SearchParameters searchParameters = SearchParameters.ParseParameters(ref queryText);

            SearchResult searchResult = MountainProjectDataSearch.Search(queryText, searchParameters);

            return(GetResponse(queryText, searchParameters?.SpecificLocation, searchResult, resultParameters));
        }
        public void TestLocationString()
        {
            InitMPData();

            for (int i = 0; i < testCriteria_location.GetLength(0); i++)
            {
                string       query            = testCriteria_location[i, 0];
                string       expectedLocation = testCriteria_location[i, 1];
                SearchResult searchResult     = MountainProjectDataSearch.Search(query);
                string       resultLocation   = BotReply.GetLocationString(searchResult.FilteredResult);
                resultLocation = resultLocation.Replace(Markdown.NewLine, "");       //Remove markdown newline
                resultLocation = resultLocation.Replace("Located in ", "");          //Simplify results for unit test
                resultLocation = Regex.Replace(resultLocation, @"\[|\]\(.*?\)", ""); //Remove markdown link formatting

                Assert.AreEqual(expectedLocation, resultLocation, "Failed for " + testCriteria_location[i, 0]);
                Assert.IsTrue(searchResult.TimeSpanTaken().TotalSeconds < 5, $"{query} took too long ({searchResult.TimeTakenMS} ms)");
            }
        }
Beispiel #19
0
        static void Main(string[] args)
        {
            if (args.FirstOrDefault(p => p.Contains("xmlpath=")) != null)
            {
                xmlPath = args.FirstOrDefault(p => p.Contains("xmlpath=")).Split('=')[1];
            }

            if (args.FirstOrDefault(p => p.Contains("credentials=")) != null)
            {
                credentialsPath = args.FirstOrDefault(p => p.Contains("credentials=")).Split('=')[1];
            }

            if (args.FirstOrDefault(p => p.Contains("repliedto=")) != null)
            {
                repliedToPath = args.FirstOrDefault(p => p.Contains("repliedto=")).Split('=')[1];
            }

            CheckRequiredFiles();
            MountainProjectDataSearch.InitMountainProjectData(xmlPath);
            AuthReddit().Wait();
            DoBotLoop().Wait();
        }
Beispiel #20
0
        public static string GetPopularRoutes(Area area)
        {
            string result = "Popular routes:\n";

            if (area.PopularRouteUrls.Count == 0)
            {
                return("");
            }

            foreach (string url in area.PopularRouteUrls)
            {
                List <MPObject> itemsToSearch = new List <MPObject>();
                itemsToSearch.AddRange(area.SubAreas);
                itemsToSearch.AddRange(area.Routes);

                Route popularRoute = MountainProjectDataSearch.GetItemWithMatchingUrl(url, itemsToSearch) as Route;
                result += $"\n- {popularRoute.Name}";
            }

            result += "\n\n";

            return(result);
        }
Beispiel #21
0
        public static async Task CheckPostsForAutoReply(List <Subreddit> subreddits)
        {
            List <Post> recentPosts = new List <Post>();

            foreach (Subreddit subreddit in subreddits)
            {
                List <Post> subredditPosts = await RedditHelper.GetPosts(subreddit, 10);

                subredditPosts = BotUtilities.RemoveAlreadySeenPosts(subredditPosts);
                subredditPosts = BotUtilities.RemoveBlacklisted(subredditPosts, new[] { BlacklistLevel.NoPostReplies, BlacklistLevel.OnlyKeywordReplies, BlacklistLevel.Total }); //Remove posts from users who don't want the bot to automatically reply to them

                foreach (Post post in subredditPosts.ToList())
                {
                    if (post.IsSelfPost)
                    {
                        subredditPosts.Remove(post);
                        ConsoleHelper.Write($"\tSkipping {post.Id} (self-post)", ConsoleColor.Red);
                        BotUtilities.LogPostBeenSeen(post, "self-post");
                    }

                    double ageInMin = (DateTime.UtcNow - post.CreatedUTC).TotalMinutes;
                    if (ageInMin > 30)
                    {
                        subredditPosts.Remove(post);
                        ConsoleHelper.Write($"\tSkipping {post.Id} (too old: {Math.Round(ageInMin, 2)} min)", ConsoleColor.Red);
                        BotUtilities.LogPostBeenSeen(post, $"too old ({Math.Round(ageInMin, 2)} min)");
                    }
                }

                recentPosts.AddRange(subredditPosts);
            }

            foreach (Post post in recentPosts)
            {
                try
                {
                    string postTitle = WebUtility.HtmlDecode(post.Title);

                    Console.WriteLine($"\tTrying to get an automatic reply for post (/r/{post.SubredditName}): {postTitle}");

                    SearchResult searchResult = MountainProjectDataSearch.ParseRouteFromString(postTitle);
                    if (!searchResult.IsEmpty())
                    {
                        ApprovalRequest approvalRequest = new ApprovalRequest
                        {
                            RedditPost   = post,
                            SearchResult = searchResult
                        };

                        PostsPendingApproval.TryAdd(post.Id, approvalRequest);

                        BotUtilities.LogPostBeenSeen(post, searchResult.Confidence == 1 ? "auto-replying" : "pending approval");

                        if (!DryRun)
                        {
                            if (searchResult.Confidence == 1)
                            {
                                string reply = BotReply.GetFormattedString(searchResult);
                                reply += Markdown.HRule;
                                reply += BotReply.GetBotLinks(post);

                                Comment botReplyComment = await RedditHelper.CommentOnPost(post, reply);

                                monitoredComments.Add(new CommentMonitor()
                                {
                                    Parent = post, BotResponseComment = botReplyComment
                                });
                                ConsoleHelper.Write($"\n\tAuto-replied to post {post.Id}", ConsoleColor.Green);
                            }
                            else
                            {
                                //Until we are more confident with automatic results, we're going to request for approval for confidence values greater than 1 (less than 100%)
                                ConsoleHelper.Write($"\tRequesting approval for post {post.Id}", ConsoleColor.Yellow);
                                BotUtilities.RequestApproval(approvalRequest);
                            }

                            BotUtilities.LogOrUpdateSpreadsheet(approvalRequest);
                        }
                    }
                    else
                    {
                        Console.WriteLine("\tNothing found");
                        BotUtilities.LogPostBeenSeen(post, "nothing found");
                    }
                }
                catch (RateLimitException)
                {
                    Console.WriteLine("\tRate limit hit. Postponing reply until next iteration");
                }
                catch (Exception e)
                {
                    Console.WriteLine($"\tException occurred with post {RedditHelper.GetFullLink(post.Permalink)}");
                    Console.WriteLine($"\t{e.Message}\n{e.StackTrace}");
                }
            }
        }
Beispiel #22
0
        private static string GetApproval(Dictionary <string, string> parameters, ApprovalRequest approvalRequest)
        {
            string result = "";

            if (parameters.ContainsKey("approveother"))
            {
                if (parameters.ContainsKey("option"))
                {
                    foreach (string approvedId in parameters["option"].Split(','))
                    {
                        MPObject matchingOption = approvalRequest.SearchResult.AllResults.Find(p => p.ID == approvedId) ?? MountainProjectDataSearch.GetItemWithMatchingID(approvedId);
                        if (matchingOption == null)
                        {
                            result += $"Option '{approvedId}' not found<br>";
                        }
                        else
                        {
                            approvalRequest.ApprovedResults.Add(matchingOption);
                        }
                    }
                }
                else
                {
                    return(ShowApproveOtherPicker(parameters, approvalRequest));
                }
            }
            else if (parameters.ContainsKey("approve"))
            {
                approvalRequest.ApprovedResults = new List <MPObject> {
                    approvalRequest.SearchResult.FilteredResult
                };
                approvalRequest.RelatedLocation = approvalRequest.SearchResult.RelatedLocation;
            }
            else if (parameters.ContainsKey("approveall"))
            {
                approvalRequest.ApprovedResults = approvalRequest.SearchResult.AllResults;
                approvalRequest.RelatedLocation = approvalRequest.SearchResult.RelatedLocation;
            }

            if (approvalRequest.IsApproved)
            {
                BotFunctions.PostsPendingApproval[parameters["postid"]] = approvalRequest;
                result = $"Approved:<br>{string.Join("<br>", approvalRequest.ApprovedResults.Select(r => $"&#8226; {r.Name} ({r.ID})"))}"; //Print out approved results as a bulleted list
            }

            return(WrapHtml($"<h1>{result}</h1>"));
        }
        public void TestPostTitleParse(/*bool outputExtraInfo, bool isGoogleSheetsTest*/)
        {
            bool outputExtraInfo    = false;
            bool isGoogleSheetsTest = false;

            Stopwatch totalStopwatch = Stopwatch.StartNew();

            int           totalPasses                 = 0;
            int           totalFailures               = 0;
            int           yessesWithConfidence1       = 0;
            List <string> failingPostsWithConfidence1 = new List <string>();

            StringWriter writer = new StringWriter();

            Console.SetOut(writer);
            MountainProjectDataSearch.OutputExtraInfo = outputExtraInfo;

            InitMPData();
            string[] testCriteria;
            if (isGoogleSheetsTest)
            {
                string requestUrl = BotUtilities.GetCredentialValue(@"..\..\MountainProjectBot\Credentials.txt", "spreadsheetURL") + "PostHistory";
                testCriteria = Utilities.GetHtml(requestUrl).Split('\n');
            }
            else
            {
                testCriteria = File.ReadAllLines(@"..\PostTitleTest.txt");
            }

            for (int i = 0; i < testCriteria.Length; i++)
            {
                Stopwatch routeStopwatch = Stopwatch.StartNew();

                string[] lineParts = testCriteria[i].Split('\t');

                string inputPostTitle, expectedMPLink, comment;
                if (isGoogleSheetsTest)
                {
                    inputPostTitle = lineParts[2];
                    expectedMPLink = lineParts[9].ToUpper() == "YES" ? Utilities.GetSimpleURL(lineParts[7]) : !string.IsNullOrEmpty(lineParts[10]) ? $"{Utilities.MPROUTEURL}/{lineParts[10]}" : null;
                    comment        = lineParts.Length > 11 && !string.IsNullOrEmpty(lineParts[11]) ? $"//{lineParts[11]}" : null;
                }
                else
                {
                    inputPostTitle = lineParts[0];
                    expectedMPLink = lineParts[1] == "null" ? null : Utilities.GetSimpleURL(lineParts[1]);
                    comment        = lineParts.Length > 2 && !string.IsNullOrEmpty(lineParts[2]) ? $"//{lineParts[2]}" : null;
                }

                //Override input title (uncomment only for debugging)
                //inputPostTitle = "Happy Star Wars Day! Had to practice my mental game for this one: \"Jedi Mind Tricks\" (v4). One of the Best.";

                writer.WriteLine($"POST TITLE: {inputPostTitle}");

                SearchResult result = MountainProjectDataSearch.ParseRouteFromString(WebUtility.HtmlDecode(inputPostTitle));
                Route        route  = result.FilteredResult as Route;

                if (expectedMPLink == null)
                {
                    if (route == null)
                    {
                        writer.WriteLine($"PASS (Correctly guessed no match)");
                        totalPasses++;
                    }
                    else
                    {
                        writer.WriteLine($"FAILED (confidence {result.Confidence}). EXPECTED: , ACTUAL: {route?.URL} {comment}");
                        totalFailures++;

                        if (result.Confidence == 1)
                        {
                            failingPostsWithConfidence1.Add($"{inputPostTitle} {comment}");
                        }
                    }
                }
                else
                {
                    if (route == null || route.URL != expectedMPLink)
                    {
                        writer.WriteLine($"FAILED (confidence {result.Confidence}). EXPECTED: {expectedMPLink} , ACTUAL: {route?.URL} {comment}");
                        totalFailures++;

                        if (result.Confidence == 1)
                        {
                            failingPostsWithConfidence1.Add($"{inputPostTitle} {comment}");
                        }
                    }
                    else
                    {
                        writer.WriteLine($"PASS (confidence: {result.Confidence})");
                        totalPasses++;

                        if (isGoogleSheetsTest && result.Confidence == 1)
                        {
                            yessesWithConfidence1++;
                        }
                    }
                }

                if (outputExtraInfo)
                {
                    writer.WriteLine($"Time taken: {routeStopwatch.Elapsed}");
                }
            }

            if (outputExtraInfo)
            {
                Debug.WriteLine($"Test completed in {totalStopwatch.Elapsed}\n");
            }

            if (!isGoogleSheetsTest)
            {
                Debug.WriteLine($"Passes: {totalPasses}, Failures: {totalFailures}, Pass percentage: {Math.Round((double)totalPasses / (totalPasses + totalFailures) * 100, 2)}%\n");
            }
            else
            {
                Debug.WriteLine($"Yesses that now have confidence 1: {yessesWithConfidence1} (out of {testCriteria.Count(p => p.Split('\t')[9].ToUpper() == "YES")} total yesses)\n");
                Debug.WriteLine($"Passes: {totalPasses}, Failures: {totalFailures}\n");
            }

            if (failingPostsWithConfidence1.Any())
            {
                Debug.WriteLine($"Failing posts with confidence 1 ({failingPostsWithConfidence1.Count()}):\n\n{string.Join("\n", failingPostsWithConfidence1)}\n");
            }

            Debug.WriteLine(writer.ToString());
            writer.Dispose();

            Assert.IsTrue(failingPostsWithConfidence1.Count == 0, "Some failed matches have a confidence of 1");

            if (!isGoogleSheetsTest) //Todo: may want to rework how the spreadsheet is setup so that this line is also relevant for GoogleSheetsTest
            {
                Assert.IsTrue((double)totalPasses / (totalPasses + totalFailures) > 0.95);
            }
        }
        private static void AddNewItems()
        {
            Parsers.TotalTimer = totalTimer;
            List <Area> destAreas = Parsers.GetDestAreas();

            DateTime        lastBuild = File.GetLastWriteTime(serializationPath);
            string          rssUrl    = $"https://www.mountainproject.com/rss/new?selectedIds={string.Join(",", destAreas.Select(p => p.ID))}&routes=on&areas=on";
            SyndicationFeed feed      = null;

            using (XmlReader reader = XmlReader.Create(rssUrl))
            {
                feed = SyndicationFeed.Load(reader);
            }

            IEnumerable <string> newlyAddedItemUrls = feed.Items.Where(p => p.PublishDate > lastBuild).OrderBy(p => p.PublishDate).Select(p => p.Links[0].Uri.ToString());

            MountainProjectDataSearch.InitMountainProjectData(serializationPath);

            foreach (string newItemUrl in newlyAddedItemUrls)
            {
                string newId = Utilities.GetID(newItemUrl);

                if (MountainProjectDataSearch.GetItemWithMatchingID(newId) != null) //Item has already been added (probably via a recursive area add)
                {
                    continue;
                }

                MPObject newItem;
                if (newItemUrl.Contains(Utilities.MPAREAURL))
                {
                    newItem = new Area {
                        ID = newId
                    };
                    Parsers.ParseAreaAsync(newItem as Area).Wait();
                }
                else
                {
                    newItem = new Route {
                        ID = newId
                    };
                    Parsers.ParseRouteAsync(newItem as Route).Wait();
                }

                Area currentParent = null;
                bool itemAddedViaRecursiveParse = false;
                foreach (string parentId in newItem.ParentIDs) //Make sure all parents are populated
                {
                    MPObject matchingItem = MountainProjectDataSearch.GetItemWithMatchingID(parentId);
                    if (matchingItem == null)
                    {
                        Area newArea = new Area {
                            ID = parentId
                        };
                        Parsers.ParseAreaAsync(newArea).Wait();
                        currentParent.SubAreas.Add(newArea);
                        itemAddedViaRecursiveParse = true;
                        break;
                    }
                    else
                    {
                        currentParent = matchingItem as Area;
                    }
                }

                if (!itemAddedViaRecursiveParse)
                {
                    if (newItem is Area)
                    {
                        (MountainProjectDataSearch.GetItemWithMatchingID(newItem.ParentIDs.Last()) as Area).SubAreas.Add(newItem as Area);
                    }
                    else
                    {
                        (MountainProjectDataSearch.GetItemWithMatchingID(newItem.ParentIDs.Last()) as Area).Routes.Add(newItem as Route);
                    }
                }
            }

            destAreas = MountainProjectDataSearch.DestAreas;

            totalTimer.Stop();
            Console.WriteLine($"------PROGRAM FINISHED------ ({totalTimer.Elapsed})");
            Console.WriteLine();
            Console.WriteLine($"Total # of areas: {Parsers.TotalAreas}, total # of routes: {Parsers.TotalRoutes}");
            SerializeResults(destAreas);

            SendReport($"MountainProjectDBBuilder database updated SUCCESSFULLY in {totalTimer.Elapsed}", $"{newlyAddedItemUrls.Count()} new items:\n\n{string.Join("\n", newlyAddedItemUrls)}");
        }
        private static void ParseInputString()
        {
            MountainProjectDataSearch.InitMountainProjectData(serializationPath);
            if (MountainProjectDataSearch.DestAreas.Count() == 0)
            {
                Console.WriteLine("The xml either doesn't exist or is empty");
                Environment.Exit(0);
            }

            Console.WriteLine("File read.");

            bool keepSearching = true;

            while (keepSearching)
            {
                Console.WriteLine("\n\nPlease input the string you would like to parse: ");
                string input = Console.ReadLine();

                SearchParameters searchParameters = SearchParameters.ParseParameters(ref input);
                ResultParameters resultParameters = ResultParameters.ParseParameters(ref input);

                bool allResults = input.Contains("-all");
                if (allResults)
                {
                    input = input.Replace("-all", "").Trim();
                }

                Stopwatch    stopwatch    = Stopwatch.StartNew();
                SearchResult searchResult = MountainProjectDataSearch.Search(input, searchParameters);
                stopwatch.Stop();

                if (searchResult.IsEmpty())
                {
                    Console.WriteLine("Nothing found matching \"" + input + "\"");
                }
                else if (allResults)
                {
                    List <MPObject> matchedObjectsByPopularity = searchResult.AllResults.OrderByDescending(p => p.Popularity).ToList();
                    Console.WriteLine($"Found {matchedObjectsByPopularity.Count} items match that search query (found in {stopwatch.ElapsedMilliseconds} ms):");
                    foreach (MPObject result in matchedObjectsByPopularity)
                    {
                        string url = result.URL.Replace(Utilities.MPBASEURL, "");
                        if (result is Route)
                        {
                            Console.WriteLine($"    Route: {result.Name} (Pop: {result.Popularity}) | Location: {GetLocationString(result)} | {url}");
                        }
                        else if (result is Area)
                        {
                            Console.WriteLine($"    Area: {result.Name} (Pop: {result.Popularity}) | Location: {GetLocationString(result)} | {url}");
                        }
                    }
                }
                else
                {
                    string   resultStr = "";
                    MPObject result    = searchResult.FilteredResult;
                    if (result is Area)
                    {
                        resultStr = (result as Area).ToString();
                    }
                    else if (result is Route)
                    {
                        resultStr = (result as Route).ToString(resultParameters);
                    }

                    Console.WriteLine($"The following was found (found in {stopwatch.ElapsedMilliseconds} ms):");
                    Console.WriteLine("    " + resultStr);
                    Console.WriteLine($"    Location: {GetLocationString(result, searchResult.RelatedLocation)}");
                    Console.WriteLine("\nOpen result? (y/n) ");
                    if (Console.ReadLine().ToLower() == "y")
                    {
                        Process.Start(result.URL);
                    }
                }

                Console.WriteLine("\nSearch something else? (y/n) ");
                keepSearching = Console.ReadLine().ToLower() == "y";
            }
        }
Beispiel #26
0
        public static string GetLocationString(MPObject child, Area referenceLocation = null)
        {
            MPObject innerParent, outerParent;

            innerParent = null;
            outerParent = MountainProjectDataSearch.GetParent(child, 1); //Get state that route/area is in
            if (child is Route)
            {
                innerParent = MountainProjectDataSearch.GetParent(child, -2); //Get the "second to last" parent https://github.com/derekantrican/MountainProject/issues/12

                if (innerParent.URL == outerParent.URL)
                {
                    innerParent = MountainProjectDataSearch.GetParent(child, -1);
                }
            }
            else if (child is Area)
            {
                innerParent = MountainProjectDataSearch.GetParent(child, -1); //Get immediate parent
            }
            if (innerParent == null ||                                        //If "child" is a dest area, the parent will be "All Locations" which won't be in our directory
                innerParent.URL == Utilities.INTERNATIONALURL)                //If "child" is an area like "Europe"
            {
                return("");
            }

            if (outerParent.URL == Utilities.INTERNATIONALURL) //If this is international, get the country instead of the state (eg "China")
            {
                if (child.ParentUrls.Count > 3)
                {
                    if (child.ParentUrls.Contains(Utilities.AUSTRALIAURL)) //Australia is both a continent and a country so it is an exception
                    {
                        outerParent = MountainProjectDataSearch.GetParent(child, 2);
                    }
                    else
                    {
                        outerParent = MountainProjectDataSearch.GetParent(child, 3);
                    }
                }
                else
                {
                    return(""); //Return a blank string if we are in an area like "China" (so we don't return a string like "China is located in Asia")
                }
            }

            if (referenceLocation != null) //Override the "innerParent" in situations where we want the location string to include the "insisted" location
            {
                //Only override if the location is not already present
                if (innerParent.URL != referenceLocation.URL &&
                    outerParent.URL != referenceLocation.URL)
                {
                    innerParent = referenceLocation;
                }
            }

            string locationString = $"Located in {Markdown.Link(innerParent.Name, innerParent.URL)}";

            if (outerParent != null && outerParent.URL != innerParent.URL)
            {
                locationString += $", {Markdown.Link(outerParent.Name, outerParent.URL)}";
            }

            locationString += Markdown.NewLine;

            return(locationString);
        }