-
-
Notifications
You must be signed in to change notification settings - Fork 178
Refactor credit runout calculation and simplify related logic #1351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Changes from all commits
ce8489e
12cba8a
23bfccc
51321cd
66e6d3c
8dcbe50
dd40a2d
e5a971f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,11 +8,13 @@ | |
| use App\Settings\WebsiteSettings; | ||
| use App\Settings\ReferralSettings; | ||
| use Illuminate\Support\Facades\Auth; | ||
| use Illuminate\Support\Facades\Cache; | ||
| use Illuminate\Support\Facades\DB; | ||
| use Illuminate\Support\Facades\Hash; | ||
| use Illuminate\Support\Facades\Http; | ||
| use Illuminate\Support\Facades\Storage; | ||
| use Illuminate\Support\Facades\URL; | ||
| use Carbon\Carbon; | ||
|
|
||
|
|
||
| class HomeController extends Controller | ||
| { | ||
| const TIME_LEFT_BG_SUCCESS = 'bg-success'; | ||
|
|
@@ -25,177 +27,108 @@ public function __construct() | |
| } | ||
|
|
||
| /** | ||
| * Calculate when user will run out of credits. Holy shit what have i done? for just 1 fucking box on the dashboard? | ||
| * @description Get the Background Color for the Days-Left-Box in HomeView | ||
| * | ||
| * @param float $daysLeft | ||
| * @return string | ||
| */ | ||
| protected function calculateCreditRunout($user, $credits) | ||
| public function getTimeLeftBoxBackground(float $daysLeft): string | ||
| { | ||
| $servers = $user->getServersWithProduct(); | ||
| if ($servers->isEmpty()) { | ||
| return [ | ||
| 'run_out_date' => null, | ||
| 'simulation_steps' => [] | ||
| ]; | ||
| if ($daysLeft >= 15) { | ||
| return $this::TIME_LEFT_BG_SUCCESS; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Works, but since these are class constants, |
||
| } | ||
|
|
||
| // Prepare all servers: get next billing date and price (in credits) | ||
| $serverStates = []; | ||
| foreach ($servers as $server) { | ||
| $product = $server->product; | ||
| $period = $product->billing_period; | ||
| $price = $product->price; | ||
| $lastBilled = $server->last_billed ? Carbon::parse($server->last_billed) : now(); | ||
| $nextBilling = $lastBilled->copy(); | ||
| while ($nextBilling->lessThanOrEqualTo(now())) { | ||
| switch ($period) { | ||
| case 'hourly': $nextBilling->addHour(); break; | ||
| case 'daily': $nextBilling->addDay(); break; | ||
| case 'weekly': $nextBilling->addWeek(); break; | ||
| case 'monthly': $nextBilling->addMonth(); break; | ||
| case 'quarterly': $nextBilling->addMonths(3); break; | ||
| case 'half-annually': $nextBilling->addMonths(6); break; | ||
| case 'annually': $nextBilling->addYear(); break; | ||
| } | ||
| } | ||
| $serverStates[] = [ | ||
| 'server' => $server, | ||
| 'product' => $product, | ||
| 'period' => $period, | ||
| 'price' => $price, | ||
| 'nextBilling' => $nextBilling | ||
| ]; | ||
| if ($daysLeft <= 7) { | ||
| return $this::TIME_LEFT_BG_DANGER; | ||
| } | ||
|
|
||
| $simulationSteps = []; | ||
| $currentCredits = $credits; | ||
| $runOutDate = null; | ||
| $maxSteps = 1000; // max steps to generate events. Good accuracy for most cases, prevents infinite loops. | ||
| $step = 0; | ||
|
|
||
| while ($step < $maxSteps) { | ||
| // Find the next billing date among all servers | ||
| $nextDates = array_map(fn($s) => $s['nextBilling'], $serverStates); | ||
| $minDate = collect($nextDates)->min(); | ||
| // Find all servers that bill at this date | ||
| $dueServers = array_filter($serverStates, fn($s) => $s['nextBilling']->equalTo($minDate)); | ||
| $sum = 0; | ||
| $actions = []; | ||
| foreach ($dueServers as $idx => $s) { | ||
| $sum += $s['price']; | ||
| $actions[] = $s['product']->name . ' (' . $s['period'] . ')'; | ||
| } | ||
| if ($currentCredits < $sum) { | ||
| $runOutDate = $minDate; | ||
| break; | ||
| } | ||
| $currentCredits -= $sum; | ||
| $simulationSteps[] = [ | ||
| 'date' => $minDate->format('Y-m-d H:i:s'), | ||
| 'action' => implode(' + ', $actions), | ||
| 'amount' => -$sum, | ||
| 'remaining' => $currentCredits, | ||
| 'details' => '' | ||
| ]; | ||
| // Advance nextBilling for all due servers | ||
| foreach ($serverStates as &$s) { | ||
| if ($s['nextBilling']->equalTo($minDate)) { | ||
| switch ($s['period']) { | ||
| case 'hourly': $s['nextBilling']->addHour(); break; | ||
| case 'daily': $s['nextBilling']->addDay(); break; | ||
| case 'weekly': $s['nextBilling']->addWeek(); break; | ||
| case 'monthly': $s['nextBilling']->addMonth(); break; | ||
| case 'quarterly': $s['nextBilling']->addMonths(3); break; | ||
| case 'half-annually': $s['nextBilling']->addMonths(6); break; | ||
| case 'annually': $s['nextBilling']->addYear(); break; | ||
| } | ||
| } | ||
| } | ||
| unset($s); | ||
| $step++; | ||
| } | ||
| if ($runOutDate === null && count($simulationSteps) > 0) { | ||
| $runOutDate = Carbon::parse($simulationSteps[count($simulationSteps)-1]['date']); | ||
| } | ||
| return [ | ||
| 'run_out_date' => $runOutDate, | ||
| 'simulation_steps' => $simulationSteps | ||
| ]; | ||
| return $this::TIME_LEFT_BG_WARNING; | ||
| } | ||
|
|
||
| /** | ||
| * Format time left for display | ||
| * @description Set unit behind the remaining time (deprecated - units now in value) | ||
| * | ||
| * @param float $daysLeft | ||
| * @param float $hoursLeft | ||
| * @return string|void | ||
| */ | ||
| protected function formatTimeLeft($date) | ||
| public function getTimeLeftBoxUnit(float $daysLeft, float $hoursLeft) | ||
| { | ||
| if (!$date) return null; | ||
|
|
||
| $now = now(); | ||
| $daysLeft = $now->diffInDays($date, false); | ||
| $hoursLeft = $now->diffInHours($date, false); | ||
| $minutesLeft = $now->diffInMinutes($date, false); | ||
|
|
||
| if ($daysLeft > 1) { | ||
| return [ | ||
| 'value' => floor($daysLeft), | ||
| 'unit' => 'days', | ||
| 'bg' => $daysLeft >= 15 ? self::TIME_LEFT_BG_SUCCESS : | ||
| ($daysLeft <= 7 ? self::TIME_LEFT_BG_DANGER : self::TIME_LEFT_BG_WARNING) | ||
| ]; | ||
| return null; | ||
| } | ||
|
Comment on lines
+54
to
+57
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dead function: always returns null (marked deprecated in the docblock), but it's still called in |
||
|
|
||
| /** | ||
| * @description Get the Text for the Days-Left-Box in HomeView | ||
| * | ||
| * @param float $daysLeft | ||
| * @param float $hoursLeft | ||
| * @return string | ||
| */ | ||
| public function getTimeLeftBoxText(float $daysLeft, float $hoursLeft) | ||
| { | ||
| if ($hoursLeft < 1) { | ||
| return __('You ran out of Credits'); | ||
| } | ||
|
|
||
| if ($hoursLeft > 1) { | ||
| return [ | ||
| 'value' => floor($hoursLeft), | ||
| 'unit' => 'hours', | ||
| 'bg' => $hoursLeft <= 24 ? self::TIME_LEFT_BG_DANGER : self::TIME_LEFT_BG_WARNING | ||
| ]; | ||
| $fullDays = (int) floor($daysLeft); | ||
| $remainingHours = (int) ceil($hoursLeft - ($fullDays * 24)); | ||
| if ($fullDays > 0 && $remainingHours > 0) { | ||
| return strval(number_format($fullDays, 0)) . __('d') . ' ' . strval(number_format($remainingHours, 0)) . __('h'); | ||
| } | ||
|
|
||
| if ($minutesLeft > 1) { | ||
| return [ | ||
| 'value' => floor($minutesLeft), | ||
| 'unit' => 'minutes', | ||
| 'bg' => self::TIME_LEFT_BG_DANGER | ||
| ]; | ||
| if ($fullDays > 0) { | ||
| return strval(number_format($fullDays, 0)) . __('d'); | ||
| } | ||
|
|
||
| return [ | ||
| 'value' => 'Less than 1', | ||
| 'unit' => 'minute', | ||
| 'bg' => self::TIME_LEFT_BG_DANGER | ||
| ]; | ||
| return strval(number_format($hoursLeft, 0)) . __('h'); | ||
| } | ||
|
|
||
| /** | ||
| * Show the application dashboard | ||
| */ | ||
| /** Show the application dashboard. */ | ||
| public function index(GeneralSettings $general_settings, WebsiteSettings $website_settings, ReferralSettings $referral_settings) | ||
| { | ||
| $user = Auth::user(); | ||
| $credits = $user->credits; | ||
| $usage = Auth::user()->creditUsage(); | ||
| $credits = Auth::user()->credits; | ||
|
Comment on lines
+88
to
+89
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| $bg = ''; | ||
| $boxText = ''; | ||
| $unit = ''; | ||
| $timeLeft = null; | ||
|
|
||
| if ($credits > 0) { | ||
| $cacheKey = 'user_credits_left:' . $user->id; | ||
| $calculation = Cache::remember($cacheKey, now()->addMinutes(5), function() use ($user, $credits) { | ||
| return $this->calculateCreditRunout($user, $credits); | ||
| }); | ||
| /** Build our Time-Left-Box */ | ||
| if ($credits > 10 && $usage > 0) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why 10 credits and not 0 as it was? |
||
| $daysLeft = $credits / ($usage / 30); | ||
| $hoursLeft = $credits / ($usage / 30 / 24); | ||
|
|
||
| if ($calculation['run_out_date']) { | ||
| $timeLeft = $this->formatTimeLeft($calculation['run_out_date']); | ||
| $timeLeft['message'] = 'Estimated run out: ' . $calculation['run_out_date']->format('d.m.Y H:i'); | ||
| $bg = $this->getTimeLeftBoxBackground($daysLeft); | ||
| $boxText = $this->getTimeLeftBoxText($daysLeft, $hoursLeft); | ||
| $unit = $this->getTimeLeftBoxUnit($daysLeft, $hoursLeft); | ||
|
|
||
| // For debugging | ||
| // $timeLeft['simulation'] = $calculation['simulation_steps']; | ||
| if ($daysLeft > 1) { | ||
| $estimatedDate = Carbon::now()->addDays((int) ceil($daysLeft)); | ||
| } else { | ||
| $estimatedDate = Carbon::now()->addHours((int) ceil($hoursLeft)); | ||
| } | ||
|
|
||
| $timeLeft = [ | ||
| 'bg' => $bg, | ||
| 'message' => __('Estimated run out: :date', ['date' => $estimatedDate->format('d-m-Y H:i')]), | ||
| 'date' => $estimatedDate->toDateString(), | ||
| 'value' => $boxText, | ||
| 'unit' => $unit | ||
| ]; | ||
| } | ||
|
|
||
|
|
||
| // RETURN ALL VALUES | ||
| return view('home')->with([ | ||
| 'usage' => $user->creditUsage(), | ||
| 'usage' => $usage, | ||
| 'credits' => $credits, | ||
| 'useful_links_dashboard' => UsefulLink::where("position","like","%dashboard%")->get()->sortby("id"), | ||
| 'timeLeft' => $timeLeft, | ||
| 'numberOfReferrals' => DB::table('user_referrals')->where('referral_id', '=', $user->id)->count(), | ||
| 'partnerDiscount' => PartnerDiscount::where('user_id', $user->id)->first(), | ||
| 'bg' => $bg, | ||
| 'boxText' => $boxText, | ||
| 'unit' => $unit, | ||
| 'numberOfReferrals' => DB::table('user_referrals')->where('referral_id', '=', Auth::user()->id)->count(), | ||
| 'partnerDiscount' => PartnerDiscount::where('user_id', Auth::user()->id)->first(), | ||
| 'myDiscount' => PartnerDiscount::getDiscount(), | ||
| 'timeLeft' => $timeLeft, | ||
| 'general_settings' => $general_settings, | ||
| 'website_settings' => $website_settings, | ||
| 'referral_settings' => $referral_settings | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused imports