예제 #1
0
        private User ScrapeUserData(HtmlNode node, User user)
        {
            node.NullCheck();
            user = user ?? new User();

            /// name
            HtmlNode nameNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageName);
            if (nameNode != null && nameNode.NextSibling != null)
            {
                user.Name = nameNode.NextSibling.InnerText.SafeTrim();
            }

            /// email
            HtmlNode emailNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageEmail);
            if (emailNode != null && emailNode.NextSibling != null && emailNode.NextSibling.NextSibling != null)
            {
                user.Email = emailNode.NextSibling.NextSibling.InnerText.SafeTrim();
            }

            /// gender
            HtmlNode genderNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageGender);
            if (genderNode != null && genderNode.NextSibling != null)
            {
                string genderString = genderNode.NextSibling.InnerText.SafeTrim();
                if (genderString == ParsingHelper.GenderMale)
                {
                    user.Gender = UserGenderEnum.Male;
                }
                else if (genderString == ParsingHelper.GenderFemale)
                {
                    user.Gender = UserGenderEnum.Female;
                }
                else
                {
                    user.Gender = UserGenderEnum.NotSet;
                    this._logger.DebugFormat("ScrapeUserData, Gender not set - STRING: {0}", genderString);
                }
            }

            /// birthdate
            HtmlNode birthdateNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageBirthDate);
            if (birthdateNode != null && birthdateNode.NextSibling != null)
            {
                DateTime birthdate;
                if (birthdateNode.NextSibling.InnerText.TryParseLogging(out birthdate))
                {
                    user.Birthdate = birthdate;
                }
            }

            Regex digitsRegex = new Regex(@"(?<digits>\d+)");

            /// forum posts
            HtmlNode forumPostsNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageForumPosts);
            if (forumPostsNode != null && forumPostsNode.NextSibling != null)
            {
                Match match = digitsRegex.Match(forumPostsNode.NextSibling.InnerText);
                if (match.Success)
                {
                    user.ForumPosts = int.Parse(match.Groups["digits"].Value);
                }
            }

            /// blog posts
            HtmlNode blogPostsNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageBlogPosts);
            if (blogPostsNode != null && blogPostsNode.NextSibling != null)
            {
                Match match = digitsRegex.Match(blogPostsNode.NextSibling.InnerText);
                if (match.Success)
                {
                    user.BlogPosts = int.Parse(match.Groups["digits"].Value);
                }
            }

            /// picture posts
            HtmlNode picturePostsNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPagePicturePosts);
            if (picturePostsNode != null && picturePostsNode.NextSibling != null)
            {
                Match match = digitsRegex.Match(picturePostsNode.NextSibling.InnerText);
                if (match.Success)
                {
                    user.PublishedPictures = int.Parse(match.Groups["digits"].Value);
                }
            }

            /// comment posts
            HtmlNode commentPostsNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageCommentPosts);
            if (commentPostsNode != null && commentPostsNode.NextSibling != null)
            {
                Match match = digitsRegex.Match(commentPostsNode.NextSibling.InnerText);
                if (match.Success)
                {
                    user.PublishedComments = int.Parse(match.Groups["digits"].Value);
                }
            }

            /// video posts
            HtmlNode videoPostsNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageVideoPosts);
            if (videoPostsNode != null && videoPostsNode.NextSibling != null)
            {
                Match match = digitsRegex.Match(videoPostsNode.NextSibling.InnerText);
                if (match.Success)
                {
                    user.PublishedVideos = int.Parse(match.Groups["digits"].Value);
                }
            }

            /// registered
            HtmlNode registeredNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageRegistered);
            if (registeredNode != null && registeredNode.NextSibling != null)
            {
                DateTime registered;
                if (registeredNode.NextSibling.InnerText.TryParseLogging(out registered))
                {
                    user.DateCreated = registered;
                }
            }

            /// description
            HtmlNode descriptionNode = node.ChildNodes.FirstOrDefault(x => x.InnerText == ParsingHelper.UserPageDescription);
            if (descriptionNode != null && descriptionNode.NextSibling != null)
            {
                HtmlNode sibling = descriptionNode.NextSibling;
                StringBuilder description = new StringBuilder();

                do
                {
                    string text = sibling.InnerText.SafeTrim();
                    if (text.Length > 0)
                    {
                        description.AppendFormat("{0} ", text);
                    }

                    sibling = sibling.NextSibling;

                } while (sibling.NextSibling != null);

                user.Description = description.ToString().SafeTrim();
            }

            return user;
        }
예제 #2
0
        private Post ScrapePost(HtmlNode contentNode, Post post)
        {
            post = post ?? new Post();

            contentNode.NullCheck();

            /// title
            HtmlNode titleNode = contentNode.SelectSingleNode(PostPageXPath.Title);
            if (titleNode != null)
            {
                post.Title = titleNode.InnerText.SafeTrimAndEscapeHtml();
            }
            else
            {
                this._logger.ErrorFormat("ScrapingService, ScrapePost, Title node is null - URL: {0}, NODE: {1}", post.Url, contentNode.SerializeHtmlNode());
            }

            /// subtitle
            HtmlNode subtitleNode = contentNode.SelectSingleNode(PostPageXPath.Subtitle);
            if (subtitleNode != null)
            {
                post.Subtitle = subtitleNode.InnerText.SafeTrimAndEscapeHtml();
            }
            else
            {
                this._logger.ErrorFormat("ScrapingService, ScrapePost, Subtitle node is null - URL: {0}, NODE: {1}", post.Url, contentNode.SerializeHtmlNode());
            }

            /// author
            HtmlNode authorNode = contentNode.SelectSingleNode(PostPageXPath.Auhtor);
            if (authorNode != null)
            {
                IList<HtmlNode> authorNameNodes = authorNode.ChildNodes.Where(x => x.Name == "b" && x.ChildNodes.Where(t => t.Name == "a").Count() == 0).ToList();
                if (!authorNameNodes.IsEmpty())
                {
                    foreach (HtmlNode author in authorNameNodes)
                    {
                        //TODO http://www.rtvslo.si/mmc-priporoca/dame-niso-sposobne-zmagati-na-dirki-formule-ena/306771
                        User authorUser = new User()
                        {
                            Name = author.InnerText.SafeTrim().Replace(",", string.Empty).Replace("foto:", string.Empty).SafeTrimAndEscapeHtml(),
                            Function = UserFunctionEnum.Journalist
                        };

                        post.Authors.Add(authorUser);
                    }
                }

                //HtmlNode authorName = authorNode.ChildNodes.FindFirst("b");
                //if (authorName != null)
                //{
                //    post.Authors = authorName.InnerText.SafeTrimAndEscapeHtml();
                //}
            }

            if (post.Authors.IsEmpty())
            {
                //this._logger.WarnFormat("ScrapingService, ScrapePost, Author is empty - URL: {0}, NODE: {1}", post.Url, contentNode.SerializeHtmlNode());
                this._logger.WarnFormat("ScrapingService, ScrapePost, Author is empty - URL: {0}", post.Url);
            }

            /// info
            HtmlNode infoNode = contentNode.SelectSingleNode(PostPageXPath.InfoContent);
            if (infoNode != null)
            {
                // <div class="info">16. februar 2013 ob 07:22,<br>zadnji poseg: 16. februar 2013 ob 15:16<br>Schladming - MMC RTV SLO</div>

                IList<HtmlNode> textNodes = infoNode.ChildNodes.Where(x => x.Name == "#text").ToList();
                if (textNodes != null && textNodes.Count > 1)
                {
                    /// Created datetime
                    string createdDateTimeString = textNodes.First().InnerText.SafeTrim();

                    DateTime createdDate;
                    if (createdDateTimeString.TryParseExactLogging(ParsingHelper.LongDateTimeParseExactPattern, this.cultureInfo, DateTimeStyles.None, out createdDate))
                    {
                        post.DateCreated = createdDate.ToUniversalTime();
                        post.LastUpdated = createdDate.ToUniversalTime();
                    }

                    /// Location
                    string locationString = textNodes.Last().InnerText;
                    IList<string> locationList = locationString.Split(new string[]{"-"}, StringSplitOptions.RemoveEmptyEntries).ToList();
                    if (locationList != null && locationList.Count > 1)
                    {
                        post.Location = locationList.First().SafeTrim();

                        if (locationList.Last().SafeTrim() != "MMC RTV SLO")
                        {
                            this._logger.DebugFormat("ScrapingService, ScrapePost, InfoNode, Location - URL: {0}, LIST: {1}", post.Url, locationList.SerializeObject());
                        }
                    }
                    else
                    {
                        this._logger.WarnFormat("ScrapingService, ScrapePost, InfoNode, Location - URL: {0}, NODE: {1}", post.Url, infoNode.SerializeHtmlNode());
                    }

                    if (textNodes.Count == 3)
                    {
                        /// Updated datetime
                        string updatedDateTimeString = textNodes[1].InnerText.SafeTrim();

                        Regex dateTimeRegex = new Regex(@"(?<date>[0-9\.]+[\w+\s+]+[0-9\:]+)", RegexOptions.IgnoreCase);

                        //TODO fix regex
                        Match dateTimeMatch = dateTimeRegex.Match(updatedDateTimeString);

                        if (dateTimeMatch.Success)
                        {
                            updatedDateTimeString = dateTimeMatch.Groups["date"].Value;

                            DateTime updatedDate;
                            if (updatedDateTimeString.TryParseExactLogging(ParsingHelper.LongDateTimeParseExactPattern, this.cultureInfo, DateTimeStyles.None, out updatedDate))
                            {
                                post.DateCreated = updatedDate.ToUniversalTime();
                            }
                        }
                    }
                }
                else
                {
                    this._logger.ErrorFormat("ScrapingService, ScrapePost, InfoNode - URL: {0}, NODE: {1}", post.Url, infoNode.SerializeHtmlNode());
                }
            }

            /// Main content
            IList<HtmlNode> contentNodes = new List<HtmlNode>();
            foreach (HtmlNode node in contentNode.ChildNodes)
            {
                /// ends with author
                if (node.Name == "div" && node.Attributes.FirstOrDefault(x => x.Value == "author") != null)
                {
                    break;
                }

                if ((node.Name == "p" || node.Name == "div") && node.FirstChild != null && node.FirstChild.Name != "div" && contentNodes.Count > 0)
                {
                    contentNodes.Add(node);
                }

                /// starts with p tag
                if (node.Name == "p" && node.FirstChild.Name != "div" && contentNodes.Count == 0)
                {
                    contentNodes.Add(node);
                }
            }

            //TODO remove
            string sasas = post.Url;

            if (!contentNodes.IsEmpty())
            {
                /// Abstract - text inside strong tag in first node
                HtmlNode abstractNode = contentNodes.First();
                HtmlNode strongAbstractNode = abstractNode.ChildNodes.First(x => x.Name == "strong");
                post.Abstract = strongAbstractNode.InnerText.SafeTrimAndEscapeHtml();

                /// remove abstract from main content
                abstractNode.ChildNodes.Remove(strongAbstractNode);

                /// Content
                StringBuilder content = new StringBuilder();

                foreach (HtmlNode node in contentNodes)
                {
                    // to get white space after paragraph title
                    foreach (HtmlNode childNode in node.ChildNodes)
                    {
                        string text = childNode.InnerText.SafeTrimAndEscapeHtml();
                        if (text.Length > 0)
                        {
                            content.AppendFormat("{0} ", text);
                        }
                    }
                }

                post.Content = content.ToString().SafeTrim();
            }
            else
            {
                this._logger.ErrorFormat("ScrapingService, ScrapePost - Post content is null - URL: {0}, NODE: {1}", post.Url, contentNode.SerializeHtmlNode());
            }

            return post;
        }
예제 #3
0
        /// <summary>
        /// Scrape user page
        /// </summary>
        /// <param name="userUrl"></param>
        /// <returns></returns>
        public User ScrapeUserPage(Uri userUrl)
        {
            User user = new User()
            {
                Url = userUrl.AbsoluteUri,
                AccessedDate = DateTime.UtcNow.ToUniversalTime(),
                Id = userUrl.AbsoluteUri.Substring(userUrl.AbsoluteUri.LastIndexOf("/") + 1)
            };

            string html = this.CreateWebRequest(userUrl);
            if (!string.IsNullOrEmpty(html))
            {
                HtmlNode rootNode = html.CreateRootNode();

                /// user rating
                HtmlNode ratingContent = rootNode.SelectSingleNode(UserPageXPath.RatingContent);
                user = this.ScrapeUserRating(ratingContent, user);

                /// user data
                HtmlNode dataContent = rootNode.SelectSingleNode(UserPageXPath.UserContentMedium);
                if (dataContent == null)
                {
                    dataContent = rootNode.SelectSingleNode(UserPageXPath.UserContentBig);
                }

                if (dataContent == null)
                {
                    dataContent = rootNode.SelectSingleNode(UserPageXPath.UserContentSmall);
                }

                //TODO check null
                if (dataContent != null)
                {
                    user = this.ScrapeUserData(dataContent, user);
                }
                else
                {
                    this._logger.FatalFormat("ScrapingService, ScrapeUserPage, dataContent == NULL - REQUEST: {0}", userUrl);
                }

                return user;
            }
            return null;
        }
예제 #4
0
        private User ScrapeUserRating(HtmlNode node, User user)
        {
            /// <div id="rate_text" style="font-weight:normal;color:#000000;font:9px Arial;">Ocena <strong>4.5</strong> od <strong>642</strong> glasov</div>

            node.NullCheck();
            user = user ?? new User();

            IList<HtmlNode> nodes = node.ChildNodes.Where(x => x.Name == "strong").ToList();

            if (nodes.Count != 2)
            {
                throw new IndexOutOfRangeException();
            }

            decimal rating = -1;
            int ratings = -1;
            if (!string.IsNullOrEmpty(nodes[0].InnerHtml) && decimal.TryParse(nodes[0].InnerHtml, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out rating))
            {
                user.Rating = rating;
            }

            if (!string.IsNullOrEmpty(nodes[1].InnerHtml) && int.TryParse(nodes[1].InnerHtml, out ratings))
            {
                user.NumOfRatings = ratings;
            }

            return user;
        }
        /// <summary>
        /// Save post author to repository
        /// </summary>
        /// <param name="user"></param>
        /// <param name="update"></param>
        /// <returns></returns>
        public string SaveAuthor(User user, bool update = false)
        {
            using (SesameHttpProtocolConnector connector = new SesameHttpProtocolConnector(RtvSloConfig.RepositoryUrl, RtvSloConfig.RepositoryName))
            {
                if (connector.IsReady)
                {
                    /// SELECT ?guidUrl
                    /// WHERE {
                    ///     ?guidUrl rdf:type sioc:UserAccount
                    ///     ; news:nickname "name"
                    /// }
                    string query = string.Format("SELECT ?guidUrl WHERE {{ ?guidUrl {0} {1} ; {2} \"{3}\" }}",
                                                                    Predicate.RdfType, Predicate.SiocUserAccount, Predicate.NewsNickname, user.Name);

                    if (update)
                    {
                        /// SELECT ?guidUrl ?predicate ?object
                        /// WHERE {
                        ///     ?guidUrl rdf:type sioc:UserAccount
                        ///     ; news:nickname "name"
                        ///     ; ?predicate ?object
                        /// }
                        query = string.Format("SELECT ?guidUrl ?predicate ?object WHERE {{ ?guidUrl {0} {1} ; {2} \"{3}\" ; ?predicate ?object }}",
                                                                    Predicate.RdfType, Predicate.SiocUserAccount, Predicate.NewsNickname, user.Name);
                    }

                    SparqlResultSet queryResult = connector.QueryFormat(query);

                    string guidUrl = null;
                    INode guidUrlNode = null;
                    if (queryResult != null && !queryResult.Results.IsEmpty())
                    {
                        /// user already exist
                        guidUrlNode = queryResult.Results.First().Value("guidUrl");
                        guidUrl = ((UriNode)guidUrlNode).Uri.AbsoluteUri;
                        if (!update)
                        {
                            return guidUrl;
                        }
                    }

                    /// save new or update
                    using (IGraph g = new Graph())
                    {
                        g.BaseUri = RepositoryHelper.BaseUrl.ToUri();
                        IList<Triple> newTriples = new List<Triple>();
                        IList<Triple> removeTriples = new List<Triple>();

                        if (string.IsNullOrEmpty(guidUrl))
                        {
                            update = false;
                            guidUrl = string.Format(RepositoryHelper.UserUrlPattern, Guid.NewGuid().ToString());
                        }

                        INode subject = guidUrlNode != null ? guidUrlNode.CopyNode(g) : guidUrl.ToUriNode(g);
                        user.RepositoryGuidUrl = guidUrl;

                        if (!update)
                        {
                            /// initialize
                            newTriples.Add(new Triple(subject, Predicate.RdfType.ToUriNode(g), Predicate.SiocUserAccount.ToUriNode(g)));

                            /// user function
                            if (user.Function == UserFunctionEnum.Reader)
                            {
                                newTriples.Add(new Triple(subject, Predicate.SiocHasFunction.ToUriNode(g), RepositoryHelper.ReaderRoleUrl.ToUriNode(g)));
                            }
                            else if (user.Function == UserFunctionEnum.Journalist)
                            {
                                newTriples.Add(new Triple(subject, Predicate.SiocHasFunction.ToUriNode(g), RepositoryHelper.JournalistRoleUrl.ToUriNode(g)));
                            }
                        }

                        /// accessed date
                        newTriples.Add(new Triple(subject, Predicate.NewsAccessed.ToUriNode(g),
                            user.AccessedDate.ToString(RepositoryHelper.DateTimeFormat).ToLiteralNode(g, dataType: RepositoryHelper.DateTimeDataType)));

                        /// nickname
                        newTriples.Add(new Triple(subject, Predicate.NewsNickname.ToUriNode(g), user.Name.ToLiteralNode(g)));

                        /// remove old triples
                        if (update)
                        {
                            this.RemoveTriples(removeTriples, queryResult, g, subject,
                                new string[] { Predicate.NewsAccessed, Predicate.NewsNickname });
                        }

                        connector.UpdateGraph(g.BaseUri, newTriples, removeTriples);
                        return guidUrl;
                    }
                }
                else
                {
                    this._logger.FatalFormat("RepositoryService, SaveAuthor, SesameHttpProtocolConnector is not ready");
                }
            }

            return null;
        }
        /// <summary>
        /// Save new user
        /// </summary>
        /// <param name="user"></param>
        /// <param name="update"></param>
        /// <returns>User guid url</returns>
        public string SaveUser(User user, bool update = false)
        {
            using (SesameHttpProtocolConnector connector = new SesameHttpProtocolConnector(RtvSloConfig.RepositoryUrl, RtvSloConfig.RepositoryName))
            {
                if (connector.IsReady)
                {
                    /// SELECT ?guidUrl
                    /// WHERE {
                    ///     ?guidUrl rdf:type sioc:UserAccount
                    ///     ; news:ID "id"
                    /// }
                    string query = string.Format("SELECT ?guidUrl WHERE {{ ?guidUrl {0} {1} ; {2} \"{3}\" }}",
                                                Predicate.RdfType, Predicate.SiocUserAccount, Predicate.NewsId, user.Id);

                    if (update)
                    {
                        /// SELECT ?guidUrl ?predicate ?object
                        /// WHERE {
                        ///     ?guidUrl rdf:type sioc:UserAccount
                        ///     ; news:ID "id"
                        ///     ; ?predicate ?object
                        /// }
                        query = string.Format("SELECT ?guidUrl ?predicate ?object WHERE {{ ?guidUrl {0} {1} ; {2} \"{3}\" ; ?predicate ?object }}",
                                                Predicate.RdfType, Predicate.SiocUserAccount, Predicate.NewsId, user.Id);
                    }

                    SparqlResultSet queryResult = connector.QueryFormat(query);

                    string guidUrl = null;
                    UriNode guidUrlNode = null;
                    if (queryResult != null && !queryResult.Results.IsEmpty())
                    {
                        /// user already exist
                        guidUrlNode = queryResult.Results.First().Value("guidUrl") as UriNode;
                        guidUrl = guidUrlNode.Uri.AbsoluteUri;
                        if (!update)
                        {
                            return guidUrl;
                        }
                    }

                    /// save new or update
                    using (IGraph g = new Graph())
                    {
                        g.BaseUri = RepositoryHelper.BaseUrl.ToUri();
                        IList<Triple> newTriples = new List<Triple>();
                        IList<Triple> removeTriples = new List<Triple>();

                        if (string.IsNullOrEmpty(guidUrl))
                        {
                            update = false;
                            guidUrl = string.Format(RepositoryHelper.UserUrlPattern, Guid.NewGuid().ToString());
                        }
                        INode subject = guidUrlNode != null ? guidUrlNode.CopyNode(g) : guidUrl.ToUriNode(g);
                        user.RepositoryGuidUrl = guidUrl;

                        #region User

                        if (!update)
                        {
                            /// initialize
                            newTriples.Add(new Triple(subject, Predicate.RdfType.ToUriNode(g), Predicate.SiocUserAccount.ToUriNode(g)));

                            /// user function
                            if (user.Function == UserFunctionEnum.Reader)
                            {
                                newTriples.Add(new Triple(subject, Predicate.SiocHasFunction.ToUriNode(g), RepositoryHelper.ReaderRoleUrl.ToUriNode(g)));
                            }
                            else if (user.Function == UserFunctionEnum.Journalist)
                            {
                                newTriples.Add(new Triple(subject, Predicate.SiocHasFunction.ToUriNode(g), RepositoryHelper.JournalistRoleUrl.ToUriNode(g)));
                            }
                        }

                        /// accessed date
                        newTriples.Add(new Triple(subject, Predicate.NewsAccessed.ToUriNode(g),
                            user.AccessedDate.ToString(RepositoryHelper.DateTimeFormat).ToLiteralNode(g, dataType: RepositoryHelper.DateTimeDataType)));

                        /// ID
                        newTriples.Add(new Triple(subject, Predicate.NewsId.ToUriNode(g), user.Id.ToLiteralNode(g)));

                        /// Url
                        newTriples.Add(new Triple(subject, Predicate.RdfsSeeAlso.ToUriNode(g), user.Url.ToUriNode(g)));

                        /// gender
                        if (user.Gender == UserGenderEnum.Male)
                        {
                            newTriples.Add(new Triple(subject, Predicate.MmcHasGender.ToUriNode(g), RepositoryHelper.GenderMaleUrl.ToUriNode(g)));
                        }
                        else if (user.Gender == UserGenderEnum.Female)
                        {
                            newTriples.Add(new Triple(subject, Predicate.MmcHasGender.ToUriNode(g), RepositoryHelper.GenderFemaleUrl.ToUriNode(g)));
                        }

                        /// registered date
                        if (user.DateCreated.HasValue)
                        {
                            newTriples.Add(new Triple(subject, Predicate.DctCreated.ToUriNode(g),
                                user.DateCreated.Value.ToString(RepositoryHelper.DateFormat).ToLiteralNode(g)));
                        }

                        /// nickname
                        if (!string.IsNullOrEmpty(user.Name))
                        {
                            newTriples.Add(new Triple(subject, Predicate.NewsNickname.ToUriNode(g), user.Name.ToLiteralNode(g)));
                        }

                        /// email
                        if (!string.IsNullOrEmpty(user.Email))
                        {
                            newTriples.Add(new Triple(subject, Predicate.MmcEmail.ToUriNode(g), user.Email.ToLiteralNode(g)));
                        }

                        /// birthdate
                        if (user.Birthdate.HasValue)
                        {
                            newTriples.Add(new Triple(subject, Predicate.MmcBirthDate.ToUriNode(g),
                                user.Birthdate.Value.ToString(RepositoryHelper.DateFormat).ToLiteralNode(g, dataType: RepositoryHelper.DateDataType)));
                        }

                        /// about
                        if (!string.IsNullOrEmpty(user.Description))
                        {
                            newTriples.Add(new Triple(subject, Predicate.MmcAbout.ToUriNode(g), user.Description.ToLiteralNode(g)));
                        }

                        /// remove old triples
                        if (update)
                        {
                            this.RemoveTriples(removeTriples, queryResult, g, subject,
                                new string[] { Predicate.NewsAccessed, Predicate.NewsId, Predicate.RdfsSeeAlso, Predicate.MmcHasGender, Predicate.DctCreated,
                                                Predicate.NewsNickname, Predicate.MmcEmail, Predicate.MmcBirthDate, Predicate.MmcAbout });
                        }

                        #endregion User

                        #region UserStatistics

                        string statsGuid = Guid.NewGuid().ToString();
                        string statsGuidUrl = string.Format(RepositoryHelper.UserStatisticsUrlPattern, statsGuid);
                        UriNode statsGuidNode = null;

                        /// read existing statsGuidUrl
                        if (update)
                        {
                            statsGuidNode = queryResult.Results
                                .First(x => x.Value("predicate").ToSafeString() == Predicate.MmcUserStatistics.ToFullNamespaceUrl())
                                .Value("object") as UriNode;

                            statsGuidUrl = statsGuidNode.Uri.AbsoluteUri;

                            /// SELECT ?predicate ?object
                            /// WHERE {
                            ///     <guid> rdf:type mmc:Statistics
                            ///     ; ?predicate ?object
                            /// }

                            query = string.Format("SELECT ?predicate ?object WHERE {{ <{0}> {1} {2} ; ?predicate ?object }}",
                                        statsGuidUrl, Predicate.RdfType, Predicate.MmcStatistics);

                            queryResult = connector.QueryFormat(query);

                            if (queryResult == null || queryResult.Results.IsEmpty())
                            {
                                this._logger.FatalFormat("RepositoryService, SaveUser, Update statistics ERROR - QUERY: {0}", query);
                            }
                        }

                        INode statsSubject = statsGuidNode != null ? statsGuidNode.CopyNode(g) : statsGuidUrl.ToUriNode(g);

                        if(!update)
                        {
                            /// initialize
                            newTriples.Add(new Triple(statsSubject, Predicate.RdfType.ToUriNode(g), Predicate.MmcStatistics.ToUriNode(g)));
                            newTriples.Add(new Triple(subject, Predicate.MmcUserStatistics.ToUriNode(g), statsSubject));
                        }

                        /// rating
                        if (user.Rating > -1)
                        {
                            newTriples.Add(new Triple(statsSubject, Predicate.NewsAvgRating.ToUriNode(g),
                                user.Rating.ToString().ToLiteralNode(g, dataType: RepositoryHelper.DecimalDataType)));
                        }

                        /// num of ratings
                        if (user.NumOfRatings > -1)
                        {
                            newTriples.Add(new Triple(statsSubject, Predicate.NewsNRatings.ToUriNode(g),
                                user.NumOfRatings.ToString().ToLiteralNode(g, dataType: RepositoryHelper.IntegerDataType)));
                        }

                        /// num of forum posts
                        if (user.ForumPosts > -1)
                        {
                            newTriples.Add(new Triple(statsSubject, Predicate.MmcNumOfForumPosts.ToUriNode(g),
                                user.ForumPosts.ToString().ToLiteralNode(g, dataType: RepositoryHelper.IntegerDataType)));
                        }

                        /// num of blog posts
                        if (user.BlogPosts > -1)
                        {
                            newTriples.Add(new Triple(statsSubject, Predicate.MmcNumOfBlogPosts.ToUriNode(g),
                                user.BlogPosts.ToString().ToLiteralNode(g, dataType: RepositoryHelper.IntegerDataType)));
                        }

                        /// num of published pictures
                        if (user.ForumPosts > -1)
                        {
                            newTriples.Add(new Triple(statsSubject, Predicate.MmcNumOfPublishedPictures.ToUriNode(g),
                                user.PublishedPictures.ToString().ToLiteralNode(g, dataType: RepositoryHelper.IntegerDataType)));
                        }

                        /// num of published comments
                        if (user.PublishedComments > -1)
                        {
                            newTriples.Add(new Triple(statsSubject, Predicate.MmcNumOfPublishedComments.ToUriNode(g),
                                user.PublishedComments.ToString().ToLiteralNode(g, dataType: RepositoryHelper.IntegerDataType)));
                        }

                        /// num of published videos
                        if (user.PublishedVideos > -1)
                        {
                            newTriples.Add(new Triple(statsSubject, Predicate.MmcNumOfPublishedVideos.ToUriNode(g),
                                user.PublishedVideos.ToString().ToLiteralNode(g, dataType: RepositoryHelper.IntegerDataType)));
                        }

                        /// remove old triples
                        if (update)
                        {
                            this.RemoveTriples(removeTriples, queryResult, g, statsSubject,
                                new string[] { Predicate.NewsAvgRating, Predicate.NewsNRatings, Predicate.MmcNumOfForumPosts, Predicate.MmcNumOfBlogPosts,
                                        Predicate.MmcNumOfPublishedPictures, Predicate.MmcNumOfPublishedComments, Predicate.MmcNumOfPublishedVideos });
                        }

                        #endregion UserStatistics

                        connector.UpdateGraph(g.BaseUri, newTriples, removeTriples);
                        return guidUrl;
                    }
                }
                else
                {
                    this._logger.FatalFormat("RepositoryService, SaveUser, SesameHttpProtocolConnector is not ready");
                }
            }

            return null;
        }