diff --git a/app/Console/Commands/GenerateNuke.php b/app/Console/Commands/GenerateNuke.php new file mode 100644 index 0000000..25ecec2 --- /dev/null +++ b/app/Console/Commands/GenerateNuke.php @@ -0,0 +1,44 @@ +call(function() { \App\MatsuriHime\Election::createSnapshot(); })->everyMinute(); - } /** diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 27d70cb..71d91a3 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -16,23 +16,57 @@ class HomeController extends Controller public function getIdolDB() { return json_decode(Storage::get('idols.json')); } + public function getIdolColor($idol) { + $idolColors = $this->getIdolColors(); + return $idolColors[explode("\n",$idol)[0]]; + } public function getIdolColors(){ + if(!Cache::get('idol_colors')) { $colors = []; $idolDB = collect($this->getIdolDB()->idols); foreach($idolDB as $idol) { $colors[$idol->name->kanji->value] = $idol->color; } -// dd($colors); - - return $colors; + Cache::forever('idol_colors',$colors); + } +// return $colors; + return Cache::get('idol_colors'); } public function statistics(Request $request) { $dataPoints = Rank::count(); return view('statistics',['data_points' => $dataPoints]); - + } + public function nukes(Request $request) { + $nukes = Cache::get('nukes'); + return view('nukes',["nukes" => $nukes]); + } + public function newChart($timelabels,$roleName,$request,$amount,$dramaNames,$colors,$enlarge = false) { + $chart = new RankChart; + $height = 400; + if($enlarge) $height = 1000; + $options = [ + 'axisOptions' => [ + 'xIsSeries' => true, + 'xAxisMode' => 'tick' + ], + 'lineOptions' => [ + 'regionFill' => true, + ], + 'title' => $roleName.' ('.($roleName == 'global' ? 'Global' : $dramaNames[$roleName]).')', + 'colors' => $colors, + 'height' => $height, + ]; + $chart->options($options); + $chart->isNavigable(true); + $chart->labels($timelabels->toArray()); + $chart->hideDots(true); + $api = url('/api/chart/'.$roleName."?date_since=".$request->date_since.'&since='.$request->since.'&take='.$request->take); + $chart->load($api); + return $chart; } public function home(Request $request) { - $minutes = $request->since ?? 60*24*90; + $idolColors = $this->getIdolColors(); + $minutes = $request->since ?? 60*24; $timepoints = Cache::get('timepoints'); $requested_date = $request->date_since; if($requested_date) { @@ -45,51 +79,100 @@ class HomeController extends Controller return $item->gte(Carbon::now()->addHours(9)->subMinutes($minutes+5)); }); $take = $timepoints->count(); - $idolColors = $this->getIdolColors(); $dramaNames = Election::getDramaNames(); - $ranks = Rank::all()->groupBy('role'); + $ranks = collect($dramaNames)->keys(); $charts = []; - $timelabels = []; - foreach($timepoints as $timepoint) { - $timelabels[] = $timepoint->toDateTimeString(); - } - $timelabels = collect($timelabels); - $timelabels = $timelabels->splice(count($timelabels) - $take); + $timelabels = Cache::get('timelabels'); + $timelabels = $timelabels->take(-$take)->values(); if($request->take) $timelabels = $timelabels->take($request->take); - foreach($ranks as $roleName => $roleRank) { - $chartPoint = Cache::get('data_'.$roleName); -// dd($chartPoint); - if(!isset($chartPoint)) { - \App\MatsuriHime\Election::createSnapshot(); - $chartPoint = Cache::get('data_'.$roleName); - } - $chart = new RankChart; - $chart->options([ - 'axisOptions' => [ - 'xIsSeries' => true, - 'xAxisMode' => 'tick' - ], - 'lineOptions' => [ - 'regionFill' => true - ], - 'title' => $roleName.' ('.$dramaNames[$roleName].')' - ]); - $chart->isNavigable(true); - $chart->labels($timelabels->toArray()); - $chart->hideDots(true); - foreach($chartPoint as $idol => $dataPoints) { - $values = collect($dataPoints); - $values = $values->splice(count($values) - $take); - if($request->take) $values = $values->take($request->take); - $idol_color = $idolColors[$idol]; - $chart->dataSet($idol,'line',$values->toArray())->color($idol_color); + foreach($ranks as $roleName) { + $chartPoint = null; + $chartPoint = collect(Cache::get('data_'.$roleName))->keys(); + $colors = []; + foreach($chartPoint as $point) { + $colors[] = $this->getIdolColor($point); } + $chart =$this->newChart($timelabels,$roleName,$request,$request->take,$dramaNames,$colors,$request->enlarge); $chartData["drama"] = $dramaNames[$roleName]; $chartData["name"] = $roleName; $chartData["chart"] = $chart; $charts[] = $chartData; } - return view('home', [ 'chart_groups' => collect($charts)->groupBy("drama"), 'since' => $request->since ]); + return view('home', [ 'chart_groups' => collect($charts)->groupBy("drama"), 'since' => $request->since, 'enlarge' => $request->enlarge , 'date_since' => $request->date_since, 'take' => $request->take]); + } + public function homeNew(Request $request) { + $idolColors = $this->getIdolColors(); + $minutes = $request->since ?? 60*24; + $snap_time = Cache::get('snap_time'); + $end_date = $snap_time->toDateTimeString(); + $start_date = ( $request->date_since != "" ? $request->date_since : $snap_time->subMinutes($request->since ?? 24*60)->toDateTimeString()); + $timerange = \App\MatsuriHime\Election::generateChartPointsDate($start_date,$end_date); + $timepoints = Cache::get('timepoints_'.$timerange); + $take = $timepoints->count(); + $dramaNames = Election::getDramaNames(); + $ranks = collect($dramaNames)->keys(); + $charts = []; + $timelabels = Cache::get('timelabels_'.$timerange); + $timelabels = $timelabels->take(-$take)->values(); + if($request->take) $timelabels = $timelabels->take($request->take); + foreach($ranks as $roleName) { + $chartPoint = null; + $chartPoint = collect(Cache::get('data_'.$roleName.'_'.$timerange))->keys(); + $colors = []; + foreach($chartPoint as $point) { + $colors[] = $this->getIdolColor($point); + } + $chart =$this->newChart($timelabels,$roleName,$request,$request->take,$dramaNames,$colors,$request->enlarge); + $chartData["drama"] = $dramaNames[$roleName]; + $chartData["name"] = $roleName; + $chartData["chart"] = $chart; + $charts[] = $chartData; + } + return view('home', [ 'chart_groups' => collect($charts)->groupBy("drama"), 'since' => $request->since, 'enlarge' => $request->enlarge , 'date_since' => $request->date_since, 'take' => $request->take]); + } + public function viewChart($chart_name, Request $request) { + $minutes = $request->since ?? 60*24; + $minutes = $request->since ?? 60*24; + $snap_time = Cache::get('snap_time'); + $end_date = $snap_time->toDateTimeString(); $start_date = ( $request->date_since != "" ? $request->date_since : $snap_time->subMinutes($request->since ?? 24*60)->toDateTimeString()); + $timerange = \App\MatsuriHime\Election::generateChartPointsDate($start_date,$end_date); + $timepoints = Cache::get('timepoints_'.$timerange); + $take = $timepoints->count(); + $idolColors = $this->getIdolColors(); + $dramaNames = Election::getDramaNames(); + $charts = []; + $timelabels = Cache::get('timelabels_'.$timerange); + $timelabels = $timelabels->take(-$take)->values(); + if($request->take) $timelabels = $timelabels->take($request->take); + $roleName = $chart_name; + $roleRank = "Global"; + $chartPoint = collect(Cache::get('data_'.$roleName.'_'.$timerange))->keys(); + $colors = []; + foreach($chartPoint as $point) { + $colors[] = $this->getIdolColor($point); + } + $chart =$this->newChart($timelabels,$roleName,$request,$request->take,$dramaNames,$colors,$request->enlarge); + $chartData["drama"] = ($roleName == 'global' ? 'Global' : $dramaNames[$roleName]); + $chartData["name"] = $roleName; + $chartData["chart"] = $chart; + $charts[] = $chartData; + return view('home', [ 'chart_groups' => collect($charts)->groupBy("drama"), 'since' => $request->since, 'enlarge' => $request->enlarge , 'date_since' => $request->date_since, 'take' => $request->take, 'chart_name' => $chart_name]); + } + public function viewChartApi($chart_name, Request $request) { + $snap_time = Cache::get('snap_time'); + $end_date = $snap_time->toDateTimeString(); + $start_date = ( $request->date_since != "" ? $request->date_since : $snap_time->subMinutes($request->since ?? 24*60)->toDateTimeString()); + $timerange = \App\MatsuriHime\Election::generateChartPointsDate($start_date,$end_date); + $take = $request->since; + $roleName = $chart_name; + $chartPoint = Cache::get('data_'.$roleName.'_'.$timerange); + $chart = new RankChart; + foreach($chartPoint as $idol => $dataPoints) { + $values = $dataPoints; + if($request->take) $values = $dataPoints->take($request->take)->values(); + $chart->dataSet($idol,'line',$values->toArray()); + } + return $chart->api(); } // } diff --git a/app/MatsuriHime/Election.php b/app/MatsuriHime/Election.php index 8921ab3..2315df6 100644 --- a/app/MatsuriHime/Election.php +++ b/app/MatsuriHime/Election.php @@ -10,6 +10,7 @@ use App\Rank; class Election { + public const nuke_threshold = 2500; public static function getDramaNames() { if(!Cache::has('drama_names')) { $response = Guzzle::get("https://api.matsurihi.me/mltd/v1/election")->getBody()->getContents(); @@ -35,8 +36,9 @@ class Election $hasSnapshot = true; DB::beginTransaction(); foreach($rankings as $ladder) { - $existingSnapshot = Rank::where('role',$ladder->name)->where('summary_time',Carbon::parse($ladder->summaryTime))->count(); - if($existingSnapshot == 0) { + $existingSnapshot = Cache::get('snap_time_'.$ladder->name); + if(!isset($existingSnapshot) || $existingSnapshot->lt(Carbon::parse($ladder->summaryTime)) ) { + $hasSnapshot = false; $idols = []; foreach($ladder->data[0] as $data) { $rank = new Rank(); @@ -55,49 +57,169 @@ class Election $rank->score = $remnant->first()->score; $rank->summary_time = Carbon::parse($ladder->summaryTime); $rank->save(); - \Log::error($remnant_char.' '.$remnant->first()->score); } + Cache::forever('snap_time_'.$ladder->name,Carbon::parse($ladder->summaryTime)); + Cache::forever('snap_time',Carbon::parse($ladder->summaryTime)); } else { - \Log::info("snapshot has been taken for " . (string)$ladder->summaryTime); } } DB::commit(); - Election::generateChartPointsNew(); - } - public static function generateChartPointsNew() { - $ranks = Rank::orderBy('summary_time')->get()->groupBy('role'); - $timepoints = Rank::distinct('summary_time')->orderBy('summary_time')->pluck('summary_time'); - Cache::forever('timepoints',$timepoints); - foreach($ranks as $roleName => $roleRank) { - $dataPoints = []; - $ladder = $roleRank->sortBy('summary_time')->groupBy('character')->sortByDesc(function($rank,$key) { - return $rank->last()->score; - }); - foreach($ladder as $character => $rank) { - $standing = $rank; - - $standing = $standing->pad(-$timepoints->count(),(int)0); - $dataPoints[$character] = $standing->pluck('score'); - } - Cache::forever('data_'.$roleName,$dataPoints); + if(!$hasSnapshot) { + $snap_time = Cache::get('snap_time'); + $end_date = $snap_time->toDateTimeString(); + $start_date = $snap_time->subMinutes(24*60)->toDateTimeString(); + Election::generateChartPointsDate($start_date,$end_date); + Election::incrementNukeData(); } } - public static function generateChartPoints() { - $ranks = Rank::all()->groupBy('role'); - foreach($ranks as $roleName => $roleRank) { - $chartPoint = []; - $idols = $roleRank->unique('character')->pluck('character'); - $timepoints = $roleRank->sortBy('summary_time')->unique('summary_time')->pluck('summary_time'); - foreach($idols as $idol) { - foreach($timepoints as $timepoint) { - $chartPoint[$idol][$timepoint->toDateTimeString()] = 0; - } - foreach($roleRank->where('character',$idol) as $point) { - $chartPoint[$idol][$point->summary_time->toDateTimeString()] = $point->score; - } - } - Cache::forever('chart_point_'.$roleName,$chartPoint); + public static function generateChartPointsDate($start_date,$end_date) { + if(Cache::has('timepoints_'.$start_date.'-'.$end_date)) return $start_date.'-'.$end_date; + $ranks = Rank::where('summary_time','>=',$start_date)->where('summary_time','<=',$end_date)->orderBy('summary_time')->get(); + $timepoints = $ranks->pluck('summary_time')->unique(); + $ranks = $ranks->groupBy('role'); + Cache::forever('timepoints_'.$start_date.'-'.$end_date,$timepoints); + $timelabels = []; + foreach($timepoints as $timepoint) { + $timelabels[] = $timepoint->toDateTimeString(); } - + $timelabels = collect($timelabels); + Cache::forever('timelabels_'.$start_date.'-'.$end_date,$timelabels); + $timepoints_count = $timepoints->count(); + $globalDataPoints = []; + foreach($ranks as $roleName => $roleRank) { + $dataPoints = []; + $ladder = $roleRank->groupBy('character')->sortByDesc(function($rank,$key) { + return $rank->last()->score; + }); + foreach($ladder as $character => $rank) { + $standing = $rank->pluck('score')->take($timepoints_count)->values(); + $standing = $standing->pad(-$timepoints_count,(int)0)->values(); + $dataPoints[$character] = $standing; + if(!array_key_exists($character,$globalDataPoints)) $globalDataPoints[$character] = $standing->values(); + else { + $scores = $standing->values(); + \Log::error($globalDataPoints[$character]->count().' vs '.$standing->count().' the jury is '.$timepoints_count); + $globalDataPoints[$character]->transform(function($item,$index) use ($scores) { + return $item+$scores[$index]; + }); + } + } + $dataPoints = collect($dataPoints)->take(10); + Cache::forever('data_'.$roleName.'_'.$start_date.'-'.$end_date,$dataPoints); + } + $globalDataPoints = collect($globalDataPoints); + $globalDataPoints = $globalDataPoints->sortByDesc(function($item, $key) { + return $item->last(); + }); + $globalDataPoints = $globalDataPoints->take(10); + Cache::forever('data_global_'.$start_date.'-'.$end_date,$globalDataPoints); + return $start_date.'-'.$end_date; + } + public static function generateChartPointsNew() { + $ranks = Rank::orderBy('summary_time')->get(); + $timepoints = $ranks->pluck('summary_time')->unique(); + $ranks = $ranks->groupBy('role'); + Cache::forever('timepoints',$timepoints); + $timelabels = []; + foreach($timepoints as $timepoint) { + $timelabels[] = $timepoint->toDateTimeString(); + } + $timelabels = collect($timelabels); + Cache::forever('timelabels',$timelabels); + $timepoints_count = $timepoints->count(); + $globalDataPoints = []; + foreach($ranks as $roleName => $roleRank) { + $dataPoints = []; + $ladder = $roleRank->groupBy('character')->sortByDesc(function($rank,$key) { + return $rank->last()->score; + }); + foreach($ladder as $character => $rank) { + $standing = $rank->pluck('score'); + $standing = $standing->pad(-$timepoints_count,(int)0); + $dataPoints[$character] = $standing; + if(!array_key_exists($character,$globalDataPoints)) $globalDataPoints[$character] = $standing->pluck('score'); + else { + $scores = $standing->pluck('score'); + $globalDataPoints[$character]->transform(function($item,$index) use ($scores) { + return $item+$scores[$index]; + }); + } + } + $dataPoints = collect($dataPoints)->take(10); + Cache::forever('data_'.$roleName,$dataPoints); + } + $globalDataPoints = collect($globalDataPoints); + $globalDataPoints = $globalDataPoints->sortByDesc(function($item, $key) { + return $item->last(); + }); + $globalDataPoints = $globalDataPoints->take(10); + Cache::forever('data_global',$globalDataPoints); + } + public static function incrementNukeData() { + $nukes = Cache::get('nukes'); + $dramaNames = Election::getDramaNames(); + $ranks = collect(Cache::get('drama_names'))->keys(); + $firstPoint = Cache::get('snap_time'); + foreach($ranks as $role) { + $ladder = Rank::where('role',$role)->orderBy('summary_time','desc')->get()->groupBy('character')->take(2); + foreach($ladder as $character => $points) { + $future_point = $points[0]; + $point = $points[1] ?? null; + $future_point_score = $future_point->score; + $point_score = 0; + if($point) { + $point_score = $point->score; + if($future_point_score - $point_score >= Election::nuke_threshold) { + \Log::error($role.$character.$point->summary_time); + $nuke = [ + 'code' => $role.$character.$point->summary_time, + 'drama' => $dramaNames[$role], + 'role' => $role, + 'character' => $character, + 'nuke_time_start' => $point->summary_time, + 'nuke_time_end' => $future_point->summary_time, + 'nuke_pts' => $future_point_score - $point_score, + ]; + $nukes[] = $nuke; + } + } + } + } + $collect = collect($nukes)->sortBy('nuke_time_start')->reverse(); + Cache::forever('nukes',$collect); + } + public static function generateNukeData() { + $dramaNames = Election::getDramaNames(); + $nukes = []; + $ranks = Rank::orderBy('summary_time')->get()->groupBy('role'); + $firstPoint = $ranks->first()->first()->summary_time; + foreach($ranks as $role => $ladder) { + $ladder = $ladder->groupBy('character'); + foreach($ladder as $character => $points) { + $count = $points->count(); + foreach($points as $index => $future_point) { + $point = null; + if(($index-1) >= 0) $point = $points[$index-1]; + if(isset($future_point)) { + $future_point_score = $future_point->score; + $point_score = $point->score ?? 0; + if($future_point_score - $point_score >= Election::nuke_threshold && !$future_point->summary_time->eq($firstPoint)) { + \Log::error($role.$character.($point ? $point->summary_time : $future_point->summary_time->subMinutes(5))); + $nuke = [ + 'code' => $role.$character.$future_point->summary_time, + 'role' => $role, + 'drama' => $dramaNames[$role], + 'character' => $character, + 'nuke_time_start' => ($point ? $point->summary_time : $future_point->summary_time->subMinutes(5)), + 'nuke_time_end' => $future_point->summary_time, + 'nuke_pts' => $future_point_score - $point_score, + ]; + $nukes[] = $nuke; + } + } + } + } + } + Cache::forever('nukes',collect($nukes)->sortBy('nuke_time_start')->reverse()); } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index bac2e9d..86e5308 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -19,14 +19,20 @@
-
+
Settings
-
+ + @if(isset($since) || isset($date_since)) +
+ Reset +
+ @endif + @if(!isset($date_since))
- @@ -35,27 +41,20 @@ - +
-
-
+ @endif + @if(!isset($since))
- -
- -
-
-
-
- + + +
+ @endif + +
+ +
@@ -67,7 +66,7 @@
@foreach($charts as $chart)
-
{{ $chart["name"] }}
+
{!! $chart["chart"]->container() !!} diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index ecd8c20..2a859a7 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -53,6 +53,8 @@
diff --git a/resources/views/layouts/app.blade.php.save b/resources/views/layouts/app.blade.php.save deleted file mode 100644 index d06a22e..0000000 --- a/resources/views/layouts/app.blade.php.save +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - {{ config('app.name', 'Laravel') }} - - - - - - - - - - - - - - - - - -
- - - -
- @yield('content') -
-
- - diff --git a/resources/views/nukes.blade.php b/resources/views/nukes.blade.php new file mode 100644 index 0000000..c0d99a3 --- /dev/null +++ b/resources/views/nukes.blade.php @@ -0,0 +1,21 @@ + @extends('layouts.app') + +@section('content') +
+
+
+
+
+
Nukes
+
+ +
+
+
+
+
+@endsection diff --git a/routes/api.php b/routes/api.php index c641ca5..ccb7cdb 100644 --- a/routes/api.php +++ b/routes/api.php @@ -16,3 +16,6 @@ use Illuminate\Http\Request; Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); + +Route::get('/chart/{chart}', 'HomeController@viewChartApi'); + diff --git a/routes/web.php b/routes/web.php index 69380f4..d34fb6e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -11,5 +11,7 @@ | */ -Route::get('/', 'HomeController@home'); +Route::get('/', 'HomeController@homeNew'); +Route::get('/chart/{chart}', 'HomeController@viewChart'); Route::get('/statistics', 'HomeController@statistics'); +Route::get('/nukes','HomeController@nukes');