public void Set(DB.Author author, DB.Title title, DB.DailyScore daily) { var s = new StringBuilder(); if (author != null) { s.AppendLine($"{author.Name}[{author.ID}]"); } if (title != null) { s.AppendLine($"{title.Name}"); s.AppendLine($"{title.ID} since {title.FirstUp:yyyy/MM/dd}"); } if (daily != null) { Text = daily.Date.ToString("yyyy/MM/dd"); s.AppendLine($"{daily.PageView} pv."); if (daily.HasScoreInfo) { } if (daily.PartPvChecked) { s.AppendLine("[部位別PV取得済]"); } txtEvent.Text = daily.Event; } lblInfo.Text = s.ToString(); }
/// <summary> /// 部分別PV取得 /// </summary> /// <param name="title"></param> /// <param name="daily"></param> public void AnalyzePartPv(DB.Title title, DB.DailyScore daily) { AnalyzingLockContext(() => { if ((daily == null) || (daily.PageView == 0) || (daily.Series == 1)) { return; //見るまでもないやつ(PV=0または1話しかない) } if (daily.PartPvChecked) { return; //取得済みならもうしない } var waitFor = LastKasasagiDate.AddSeconds(KasasagiIntervalSec); var waitMsec = (waitFor - DateTime.Now).TotalMilliseconds; if (waitMsec > 0) { DebugReport.Log(this, $"kasasagi wait {waitMsec}"); System.Threading.Thread.Sleep((int)waitMsec); } var narou = new NarouAPI(); var useKasasagi = narou.GetPartPvData(title, daily); if (useKasasagi) { LastKasasagiDate = DateTime.Now; } }); }
/// <summary> /// kasasagiのスクレイピング /// </summary> /// <param name="title">作品</param> /// <param name="html">コンテンツ</param> /// <param name="isUnique">ユニークのデータか?</param> private void parsePvHTML(DB.Title title, string html, bool isUnique) { var regTable = new Regex("\\<table class=\"access_per_day\"\\>(.+?)\\</table\\>", RegexOptions.Singleline); var regDate = new Regex("(\\d+)年(\\d+)月(\\d+)日"); var regView = new Regex("\\<td class=\"item\"\\>([\\d,]+)人?\\</td\\>.+?\\<div class=\"(pc|mobile|smp)\"", RegexOptions.Singleline); var matches = regTable.Matches(html); foreach (Match match in matches) { var dayTable = match.Groups[1].Value; var dm = regDate.Match(dayTable); if (dm.Success) { var date = new DateTime(int.Parse(dm.Groups[1].Value), int.Parse(dm.Groups[2].Value), int.Parse(dm.Groups[3].Value)); var tempPv = new DB.DailyScore(); tempPv.Date = date; var vms = regView.Matches(dayTable); foreach (Match vm in vms) { var ns = vm.Groups[1].Value; ns = ns.Replace(",", ""); var pv = int.Parse(ns); var cls = vm.Groups[2].Value; setPv(isUnique, tempPv, pv, cls); } //作品データにマージ title.AddPageView(tempPv, isUnique); } } }
/// <summary> /// スコアデータ登録 /// </summary> /// <param name="dailyValue"></param> /// <remarks> /// この機能は、ロードした作品データにPVデータをマージするための物なので、REST-API分のデータはノータッチ。 /// </remarks> public void AddPageView(DailyScore pv, bool isUnique) { //探す日付 var findDate = pv.Date.Date; //おんなじ日付があるか探す for (var i = 0; i < Score.Count; i++) { if (Score[i].Date.Date == findDate) { //同じ日→マージ if (isUnique) { Score[i].MergeUniquePageView(pv); } else { Score[i].MergePageView(pv); } return; } else if (Score[i].Date.Date > findDate) { //過ぎた→インサート Score.Insert(i, pv); return; } } //最期に足す(普通はUpdate時のScoreデータがあるのでここには来ない) Score.Add(pv); }
/// <summary> /// ユニークPV部分のマージ /// </summary> /// <param name="pv"></param> public void MergeUniquePageView(DailyScore pv) { if (Date.Date != pv.Date.Date) { throw new InvalidOperationException("Date Mismatch"); } PCUnique = pv.PCUnique; MobileUnique = pv.MobileUnique; SmartPhoneUnique = pv.SmartPhoneUnique; }
/// <summary> /// PV部分のマージ /// </summary> /// <param name="pv"></param> public void MergePageView(DailyScore pv) { if (Date.Date != pv.Date.Date) { throw new InvalidOperationException("Date Mismatch"); } PC = pv.PC; Mobile = pv.Mobile; SmartPhone = pv.SmartPhone; //ユニークは別HTMLから取るので別関数です }
/// <summary> /// PVを加算 /// </summary> /// <param name="days">何日分?(負なら全部)</param> /// <returns></returns> /// <remarks>加算結果</remarks> public DailyScore SumUpPv(int days) { var score = new DailyScore(); var last = days < 0 ? new DateTime() : LatestScore.Date.Date.AddDays(-days); foreach (var s in Score) { if (s.Date.Date >= last) { score.PC += s.PC; score.PCUnique += s.PCUnique; score.Mobile += s.Mobile; score.MobileUnique += s.MobileUnique; score.SmartPhone += s.SmartPhone; score.SmartPhoneUnique += s.SmartPhoneUnique; } } return(score); }
/// <summary> /// Yamlの作品情報を作品情報オブジェクトに変換 /// </summary> /// <param name="node"></param> /// <returns>作品情報オブジェクト</returns> private DB.Title YamlMapToTitleObj(YamlMappingNode node) { if (node == null) { return(null); } var t = new DB.Title { ID = GetStringFromYamlMap(node, "ncode"), Name = GetStringFromYamlMap(node, "title"), Author = GetStringFromYamlMap(node, "writer"), FirstUp = GetDateFromYamlMap(node, "general_firstup"), LastUp = GetDateFromYamlMap(node, "general_lastup"), LastCheck = DateTime.Now }; var score = new DB.DailyScore { Series = GetIntFromYamlMap(node, "general_all_no"), Size = GetIntFromYamlMap(node, "length"), ConversationRatio = GetIntFromYamlMap(node, "kaiwaritu"), Impressions = GetIntFromYamlMap(node, "impression_cnt"), Reviews = GetIntFromYamlMap(node, "review_cnt"), Votes = GetIntFromYamlMap(node, "all_hyoka_cnt"), VoteScore = GetIntFromYamlMap(node, "all_point"), Bookmarks = GetIntFromYamlMap(node, "fav_novel_cnt"), DailyPoint = GetIntFromYamlMap(node, "daily_point"), WeeklyPoint = GetIntFromYamlMap(node, "weekly_point"), MonthlyPoint = GetIntFromYamlMap(node, "monthly_point"), QuarterPoint = GetIntFromYamlMap(node, "quarter_point"), YearPoint = GetIntFromYamlMap(node, "yearly_point"), Points = GetIntFromYamlMap(node, "global_point"), Date = t.LastCheck.Date }; DebugReport.Log(this, $"REST get info {t.ID} {t.Name}"); t.Score.Add(score); return(t); }
/// <summary> /// 作品データ表示 /// </summary> /// <param name="title"></param> public void Arrange(DB.Title title) { ArrangedAuthor = null; ArrangedTitle = title; chDate.Text = "Date"; lvDisplay.BeginUpdate(); lvDisplay.Items.Clear(); var mobileColumnWidth = 0; //総計 { var item = new ListViewItem("total"); var pv = new DB.DailyScore { PC = title.Score.Sum(x => x.PC), PCUnique = title.Score.Sum(x => x.PCUnique), Mobile = title.Score.Sum(x => x.Mobile), MobileUnique = title.Score.Sum(x => x.MobileUnique), SmartPhone = title.Score.Sum(x => x.SmartPhone), SmartPhoneUnique = title.Score.Sum(x => x.SmartPhoneUnique) }; if (pv.Mobile != 0) { mobileColumnWidth = DefaultMobileColumnWidth; } var score = title.LatestScore; registorScoreInfo(pv, score, null, item); lvDisplay.Items.Add(item); } //個別(逆順) foreach (var score in (title.Score as IEnumerable <DB.DailyScore>).Reverse()) { var item = new ListViewItem(score.Date.ToString("yyyy/MM/dd")); registorScoreInfo(score, score, null, item); lvDisplay.Items.Add(item); } lvDisplay.Columns[indexOfMobileColumn].Width = mobileColumnWidth; lvDisplay.EndUpdate(); }
/// <summary> /// 部分別PVを解析 /// </summary> /// <param name="title"></param> /// <param name="date"></param> /// <param name="html"></param> private void parsePartPvHTML(DB.DailyScore score, string html) { if (string.IsNullOrEmpty(html)) { return; } score.PartPv = new List <DB.PartPv>(); score.PartPvChecked = true; var reg = new Regex(@">第(\d+)部分:(\d+)人"); MatchCollection ms = reg.Matches(html); foreach (Match m in ms) { var p = int.Parse(m.Groups[1].Value); var pv = int.Parse(m.Groups[2].Value); score.PartPv.Add(new DB.PartPv { Part = p, PageView = pv }); } }
/// <summary> /// スコア部分のマージ /// </summary> /// <param name="score"></param> public void MergeScore(DailyScore score) { if (Date.Date != score.Date.Date) { throw new InvalidOperationException("Date Mismatch"); } Series = score.Series; Size = score.Size; ConversationRatio = score.ConversationRatio; Impressions = score.Impressions; Reviews = score.Reviews; Votes = score.Votes; VoteScore = score.VoteScore; Bookmarks = score.Bookmarks; DailyPoint = score.DailyPoint; WeeklyPoint = score.WeeklyPoint; MonthlyPoint = score.MonthlyPoint; QuarterPoint = score.QuarterPoint; YearPoint = score.YearPoint; Points = score.Points; }
/// <summary> /// 1日データに値を設定 /// </summary> /// <param name="isUnique"></param> /// <param name="tempPv"></param> /// <param name="pv"></param> /// <param name="cls"></param> private void setPv(bool isUnique, DB.DailyScore tempPv, int pv, string cls) { switch (cls) { case "pc": if (isUnique) { tempPv.PCUnique = pv; } else { tempPv.PC = pv; } break; case "mobile": if (isUnique) { tempPv.MobileUnique = pv; } else { tempPv.Mobile = pv; } break; case "smp": if (isUnique) { tempPv.SmartPhoneUnique = pv; } else { tempPv.SmartPhone = pv; } break; } }
/// <summary> /// 部位別PVデータ取得 /// </summary> /// <param name="title">どの作品の</param> /// <param name="daily">いつ?</param> /// <remarks> /// ユニークが出てからしか更新されず、以降データ変動しない(と思われる)ので常にキャッシュが有効。 /// そもそもPVがない日は取れないので、当日のScoreがないときは何もせずに帰る。 /// </remarks> public bool GetPartPvData(DB.Title title, DB.DailyScore daily) { if (title == null) { return(false); } if (daily == null) { return(false); } var date = daily.Date; bool useKasasagi = false; var dt = date.ToString("yyyy-MM-dd"); var url = $"{KasasagiPartPv}{title.ID}/?date={dt}"; var cacheFile = $"KasasagiP{title.ID}{date.Year:0000}{date.Month:00}{date.Day:00}.html"; cacheFile = Path.Combine(CachePath, cacheFile); string html = null; if (File.Exists(cacheFile)) { html = File.ReadAllText(cacheFile); } if (html == null) { useKasasagi = true; html = RequestKasasagi(cacheFile, url, x => x.Contains(PreparingMarker) || x.Contains(PartPvNotPreparedMarker)); } //パースする parsePartPvHTML(daily, html); return(useKasasagi); }
/// <summary> /// 1項目分追加 /// </summary> /// <param name="pv">PVデータを含むやつ</param> /// <param name="score">スコアデータを含むやつ</param> /// <param name="item">追加する対象ンリストビューアイテム</param> private static void registorScoreInfo(DB.DailyScore pv, DB.DailyScore score, DB.DailyScore diffScore, ListViewItem item) { item.Tag = score; item.UseItemStyleForSubItems = false; if (pv != null) { setPvColumnData(item, pv.PC, pv.PCUnique); setPvColumnData(item, pv.Mobile, pv.MobileUnique); setPvColumnData(item, pv.SmartPhone, pv.SmartPhoneUnique); setPvColumnData(item, pv.PageView, pv.UniquePageView); } else { item.SubItems.Add("no data"); item.SubItems.Add("no data"); item.SubItems.Add("no data"); item.SubItems.Add("no data"); } if (score.HasScoreInfo) { if ((diffScore == null) || (!diffScore.HasScoreInfo)) { //通常表示モード if (score.Votes == 0) { item.SubItems.Add("none"); } else { var voteScore = score.VoteAverage; item.SubItems.Add($"{voteScore:0.0}({score.Votes}人)"); } item.SubItems.Add($"{score.Bookmarks}"); item.SubItems.Add($"{score.Impressions}"); item.SubItems.Add($"{score.Reviews}"); item.SubItems.Add($"{score.DailyPoint}"); item.SubItems.Add($"{score.WeeklyPoint}"); item.SubItems.Add($"{score.MonthlyPoint}"); item.SubItems.Add($"{score.QuarterPoint}"); item.SubItems.Add($"{score.YearPoint}"); item.SubItems.Add($"{score.Points}"); item.SubItems.Add($"{score.Series}"); item.SubItems.Add($"{score.Size:#,0}"); } else { //比較表示モード var signed = "+#,0;-#,0; "; item.SubItems.Add($"{(score.VoteAverage - diffScore.VoteAverage).ToString("+#,0.0;-#,0.0")}({(score.Votes - diffScore.Votes).ToString("+#,0;-#,0;±0")}人)"); item.SubItems.Add($"{(score.Bookmarks - diffScore.Bookmarks).ToString(signed)}"); item.SubItems.Add($"{(score.Impressions - diffScore.Impressions).ToString(signed)}"); item.SubItems.Add($"{(score.Reviews - diffScore.Reviews).ToString(signed)}"); item.SubItems.Add($"{(score.DailyPoint - diffScore.DailyPoint).ToString(signed)}"); item.SubItems.Add($"{(score.WeeklyPoint - diffScore.WeeklyPoint).ToString(signed)}"); item.SubItems.Add($"{(score.MonthlyPoint - diffScore.MonthlyPoint).ToString(signed)}"); item.SubItems.Add($"{(score.QuarterPoint - diffScore.QuarterPoint).ToString(signed)}"); item.SubItems.Add($"{(score.YearPoint - diffScore.YearPoint).ToString(signed)}"); item.SubItems.Add($"{(score.Points - diffScore.Points).ToString(signed)}"); item.SubItems.Add($"{(score.Series - diffScore.Series).ToString(signed)}"); item.SubItems.Add($"{(score.Size - diffScore.Size).ToString(signed)}"); } } else { for (var i = 0; i < DailyColumnWidth; i++) { item.SubItems.Add(""); } } item.SubItems.Add($"{score.Event}"); }