# Type: Output # Category: Task # Description: Helps to fill working hours # Fields: last, time and tasks # Author: Andriy Lesyuk use Orangutan::User; new Orangutan::Context( request => sub { my ($context, $user) = @_; my $hours = $user->GetWorkTimeHours; my $yesno = $user->GetResponse('YesNo'); if ($yesno && $hours) { my $time = new Orangutan::Date; my $date = $user->PreviousWorkDay($time); if (!$user->IsDayOff($date)) { my $seconds = $user->GetWorkHours($date->WorkDayStart, $date->WorkDayEnd) || 0; if ($seconds <= (0.9 * ($hours * 3600))) { my $prev = $date->Previous; my $diff = ($hours * 3600) - $seconds; if ($prev->MonthStart->Date == $date->MonthStart->Date) { my $days = scalar $user->GetWorkDays($prev->MonthStart, $prev); my $total = $user->GetWorkHours($prev->MonthStart->WorkDayStart, $prev->WorkDayEnd) || 0; my $totaldiff = $total - $days * ($hours * 3600); if ($totaldiff > 0) { if ($totaldiff < $diff) { $diff -= $totaldiff; } else { $user->SetField('lastworkhours', $date); $user->RemoveResponse($context); return undef; } } } if ($diff < 900) { $diff = 900; } my $message; my ($hour, $min) = (int($diff / 3600), int(($diff % 3600) / 60)); my ($day, $month, $year, $wday) = (localtime($date->Date))[3..6]; my $prevdate = $Orangutan::Date::weekdays[$wday]; if (($time->Date - $date->Date) > 518400) { $prevdate .= sprintf(', %s %d', $Orangutan::Date::months[$month], $day); } if ($min == 0) { my $format = Orangutan::Context::Random( "Not enough work hours for $prevdate. You need %d more hour%s.", "There should be added %d more hour%s to $prevdate.", "You should add %d hour%s to $prevdate." ); $message = sprintf($format, $hour, ($hour > 1) ? 's' : ''); } else { my $format = Orangutan::Context::Random( "Not enough work hours for $prevdate. You need additional %d:%02d.", "Please add a task (or more) lasting %d:%02d to $prevdate.", "You should add %d:%02d of working time to $prevdate." ); $message = sprintf($format, $hour, $min); } $message .= ' '.Orangutan::Context::Random( 'I can give you some suggestions. Should I do this?', 'I can suggest what to add... Should I?', 'May I suggest you some tasks to add?..' ); $context->UnsetField('tasks'); $context->SetField('time', $diff); $context->SetField('prevdate', $date); $context->SetField('workhours', $hours); $user->SetField('lastworkhours', $date); $yesno->InsertForeignHandler($context, 1800); return $message; } } } $user->RemoveResponse($context); return undef; }, response => [ '^What(?: do) you (?:suggest|recommend)(?: now)?\?*!*\.*$', '^What *(?:\'s|is|are) your (?:suggestion|recommendation)s?(?: now)?\?*!*\.*$', '^Tell(?: me)? (?:(?:your|the) )?(?:suggestion|recommendation)s?!*\.*$' ], handler => sub { my ($context, $user, $item, $answer) = @_; # Get user's work time hours my $hours = $user->GetWorkTimeHours; if ($hours == 0) { $user->SendMessage([ 'You do not need to add anything if you work part time...', 'Forget it!.. You do not need it...' ]); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); $context->SetTimeout; return 1; } elsif ($hours != $context->GetField('workhours')) { $user->SendMessage([ 'I need to recalculate everything but I do not know how... Sorry.', 'Well... I cannot do this because you changed your work hours.', "Hm... Can't do this... Sorry. You changed your work hours." ]); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); $context->SetTimeout; return 1; } if (($item > 0) || ($answer eq 'Y')) { my $tasks = $context->GetField('tasks'); if (!defined($tasks) || ($item > 0)) { my $lunch = 1; my $required = $context->GetField('time'); my ($starttime, $endtime) = $main::query->GetAvgWorkTime($user); if (!defined($starttime)) { $starttime = sprintf('%02d:00:00', $user->GetWorkTimeStart); } if (!defined($endtime)) { $endtime = sprintf('%02d:00:00', $user->GetWorkTimeEnd); } if ($hours > 4) { $hours++; } else { $lunch = 0; } my $prevdate = $context->GetField('prevdate'); my $start = new Orangutan::Date($starttime, $prevdate->DayStart->Date); my $end = new Orangutan::Date($endtime, $start->Date); if (($end->Date - $start->Date) < ($hours * 3600)) { my $hour = ($end->GetTime)[0]; if ($hour > $user->GetWorkTimeEnd) { $start->Set($end->Date - ($hours * 3600)); } else { my $diff = ($hours * 3600) - ($end->Date - $start->Date); $start->Set($start->Date - int($diff / 2)); $end->Set($end->Date + int($diff / 2)); } } if ($end->DayStart->Date > $start->DayStart->Date) { $end = new Orangutan::Date($endtime, $prevdate->DayStart->Date); $start->Set($end->Date - ($hours * 3600)); my $hour = ($start->GetTime)[0]; if ($hour < 8) { $starttime = sprintf('%02d:00:00', $user->GetWorkTimeStart); $start = new Orangutan::Date($starttime, $prevdate->DayStart->Date); $end->Set($start->Date + ($hours * 3600)); } } # Get free time periods my @periods = $user->GetFreePeriods($start, $end); # Calculate available seconds my $available = 0; foreach my $period (@periods) { $available += $period->[1] - $period->[0]; } if (($available - ($lunch * 3600)) >= (0.9 * $required)) { my @tasks = ( ); while (($#periods >= 0) && ($required > 0)) { my $tasktime = shift(@periods); my ($shour, $smin) = new Orangutan::Date($tasktime->[0])->GetTime; my ($ehour, $emin) = new Orangutan::Date($tasktime->[1])->GetTime; if ($lunch && ($shour <= 13) && ($ehour >= 13)) { if (($tasktime->[1] - $tasktime->[0]) <= 3600) { next; } elsif (($shour < 13 ) && ((($ehour * 60 + $emin) - (13 * 60)) < 30)) { $tasktime->[1] -= 3600; } elsif (($shour == 13 ) && ((($ehour * 60 + $emin) - ($shour * 60 + $smin)) < 30)) { $tasktime->[0] += 3600; } else { my $newend = $tasktime->[1] - (($ehour * 3600 + $emin * 60) - (13 * 3600)); unshift(@periods, [ $newend + 3600, $tasktime->[1] ]); $tasktime->[1] = $newend; } $lunch = 0; } my $diff = $tasktime->[1] - $tasktime->[0]; if ($diff >= 900) { my %taskargs = ( ); $taskargs{'start'} = new Orangutan::Date($tasktime->[0]); if ($diff > $required) { $taskargs{'end'} = new Orangutan::Date($tasktime->[0] + $required); } else { $taskargs{'end'} = new Orangutan::Date($tasktime->[1]); } push(@tasks, \%taskargs); $required -= $taskargs{'end'}->Date - $taskargs{'start'}->Date; } } # Get near tasks my @neartasks = ( ); for (my $task = $main::query->First; defined($task); $task = $main::query->Next) { push(@neartasks, $task); } # Prepend a previous task my $last = $main::query->GetLastEntry($user, $start); if (defined($last->{'end_time'}) && ($last->{'end_time'} > $user->PreviousWorkDay($start)->DayStart->Date)) { unshift(@neartasks, $last); } # Add description, project etc foreach my $taskargs (@tasks) { my $sample = undef; foreach my $task (@neartasks) { if ($task->{'start_time'} > $taskargs->{'end'}->Date) { if (!defined($sample) || (($task->{'end_time'} - $task->{'start_time'}) > ($sample->{'end_time'} - $sample->{'start_time'}))) { $sample = $task; } last; } else { $sample = $task; } } if (defined($sample)) { $taskargs->{'name'} = $sample->{'comments'}; if ($sample->{'issue_id'}) { $taskargs->{'issue'} = $sample->{'issue_id'}; } $taskargs->{'project'} = { id => $sample->{'project_id'}, name => $sample->{'project'} }; $taskargs->{'activity'} = { id => $sample->{'activity_id'}, name => $sample->{'activity'} }; } elsif (!defined($user->GetProject)) { $user->SendMessage([ "Wow! I do not know what is your project... So I can't suggest anything...", 'I need either default project or default issue to suggest anything...', 'To suggest anything I need to know the project name... Sorry!' ]); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); $context->SetTimeout; return 1; } else { $taskargs->{'name'} = 'Working'; if ($user->GetIssue) { $taskargs->{'issue'} = $user->GetIssue; } $taskargs->{'project'} = { id => $user->GetProjectID, name => $user->GetProject }; $taskargs->{'activity'} = { id => $user->GetActivityID, name => $user->GetActivity }; } } $context->SetField('tasks', \@tasks); my $message = Orangutan::Context::Random( 'I would add the following tasks', 'Here is what I can add', 'My suggestion is' ); $message .= ':'; $user->SendMessage($message); my $date; my $time = new Orangutan::Date; my $days = int(($time->DayStart->Date - $prevdate->DayStart->Date) / 86400); if ($days == 1) { $date = 'Yesterday'; } elsif ($days <= 7) { my $wday = (localtime($prevdate->Date))[6]; $date = $Orangutan::Date::weekdays[$wday]; } else { my ($day, $month) = (localtime($prevdate->Date))[3..4]; $date = sprintf('%s %d', $Orangutan::Date::months[$month], $day); } my $number = 1; foreach my $taskargs (@tasks) { my $message = sprintf("%d) ", $number++); my $duration = $taskargs->{'end'}->Date - $taskargs->{'start'}->Date; $message .= $date.' '; $message .= sprintf("%02d:%02d - %02d:%02d (%d:%02d): ", $taskargs->{'start'}->GetTime, $taskargs->{'end'}->GetTime, int($duration / 3600), int(($duration % 3600) / 60)); $message .= $taskargs->{'name'}.' '; if ($taskargs->{'issue'}) { $message .= 'on #'.$taskargs->{'issue'}.' '; } $message .= 'for '.$taskargs->{'project'}->{'name'}; if ($taskargs->{'activity'}->{'name'}) { $message .= ' ('.$taskargs->{'activity'}->{'name'}.')'; } $user->SendMessage($message); } $user->SendMessage([ 'Should I add this?', 'Should I apply?', 'Do you approve?' ]); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->InsertForeignHandler($context, 1800); } } else { $user->SendMessage([ "Hm... Some shit happened... You don't have enough free hours for previous work day... :S", "Can't understand what's wrong... I cannot put tasks for previous work day... :(", 'Hm... Probably you are having too many small tasks for previous work day... :S', 'Geeze... My creator made a mistake somewhere...' ]); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); $context->SetTimeout; } } else { my @periods = $user->GetFreePeriods($tasks->[0]->{'start'}, $tasks->[$#tasks]->{'end'}); foreach my $taskargs (@{$tasks}) { my $valid = 0; foreach my $period (@periods) { if (($taskargs->{'start'}->Date >= $period->[0]) && ($taskargs->{'end'}->Date <= $period->[1])) { $valid = 1; last; } } if ($valid) { my $task = new Orangutan::Task(%{$taskargs}); $user->SubmitPreviousTask($task); $user->FireEvent('task', 'add', $task); } else { $user->SendMessage([ "You have added other tasks manually? Do not answer!.. There are other tasks there. ". "So I can't add my suggestions! Sorry...", 'Oh, no! There are conflicts with other tasks! Not sure how this could happen...', "Damn it! My task conflicts with another task... Can't add it!" ]); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $context->UnsetField('tasks'); $context->SetTimeout; return 1; } } $user->SendMessage([ 'Did it.', 'Added.', 'Done.' ]); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); $context->SetTimeout; } } else { $main::config->Context('Ok')->Tell($user); my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); $context->SetTimeout; } return 1; }, onstatus => sub { my ($context, $user, $type, $status) = @_; if (!$user->IsSet(USER_NOASKTASK) && $user->GetWorkTimeHours && !defined($user->GetTask)) { my $previous = $user->GetStatus; if (!defined($previous) || (defined($status) && ($previous eq 'away') && ($status eq 'online'))) { my $time = new Orangutan::Date; my $date = $user->PreviousWorkDay($time); my $lastcheck = $user->GetField('lastworkhours'); if (!defined($lastcheck) || ($lastcheck->WorkDayStart->Date < $date->WorkDayStart->Date)) { my $workhours = $user->GetResponse($context->GetID); if (!defined($workhours)) { $workhours = $user->AddRequestResponse($context); $workhours->UnsetField('time'); } } } } }, ontask => sub { my ($context, $user, $type, $status, $task) = @_; if ($status eq 'start') { $context = $user->GetResponse($context->GetID); if (defined($context)) { my $date = $context->GetField('prevdate'); if (!defined($date) || ($task->GetStart->WorkDayStart->Date == $date->WorkDayStart->Date)) { if (defined($date)) { $user->SendMessage([ 'Ok... Never mind! I did not know you are still working on the previous workday task.', 'Oh... You are still working on a task of that workday.' ]); } my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); } } } elsif ($status eq 'add') { $context = $user->GetResponse($context->GetID); if (defined($context)) { my $time = $context->GetField('time'); my $date = $context->GetField('prevdate'); if (defined($time) && ($task->GetDate->Date == $date->DayStart->Date)) { $time -= $task->GetDuration; if (!$context->GetField('tasks')) { if ($time >= 900) { if (!$user->GetRequest($context->GetID)) { my ($hour, $min) = (int($time / 3600), int(($time % 3600) / 60)); my $message = Orangutan::Context::Random('Ok...', 'Nice...', 'Great...').' '; if ($min == 0) { my $format = Orangutan::Context::Random( 'You need to have %d more hour%s.', '%d hour%s left...' ); $message .= sprintf($format, $hour, ($hour > 1) ? 's' : ''); } else { my $format = Orangutan::Context::Random( 'Put some more tasks lasting %d:%02d...', '%d:%02d still needs to be added...', '%d:%02d left...' ); $message .= sprintf($format, $hour, $min); } $user->SendMessage($message); } $context->SetField('time', $time); } else { if (!$user->GetRequest($context->GetID)) { $user->SendMessage([ 'Cool. Now working hours are enough.', 'Great! Thanks for fixing it...' ]); } my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); } } else { my @stack = $user->GetStack; if ((scalar @stack == 0) || ($stack[0] ne 'WorkHours')) { $user->SendMessage([ "I guess now my suggestions won't help you...", 'Forget about my suggestions!' ]); } my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } if ($time < 900) { $user->RemoveRequestResponse($context); } else { $context->SetField('time', $time); $context->UnsetField('tasks'); } } } } } elsif (($status ne 'change') && ($status ne 'remove')) { if (!$user->IsSet(USER_NOASKTASK) && $user->GetWorkTimeHours) { my $time = new Orangutan::Date; if ($status eq 'break') { my $task = $user->GetTask; $time = $task->GetStart; } my $date = $user->PreviousWorkDay($time); my $lastcheck = $user->GetField('lastworkhours'); if (!defined($lastcheck) || ($lastcheck->WorkDayStart->Date < $date->WorkDayStart->Date)) { my $workhours = $user->GetResponse($context->GetID); if (!defined($workhours)) { $workhours = $user->AddRequestResponse($context); $workhours->UnsetField('time'); } } } } }, onconfig => sub { my ($context, $user, $type, $config, $value) = @_; if ($config eq 'flag') { if ($value == USER_NOASKTASK) { if ($user->IsSet(USER_NOASKTASK)) { $context = $user->GetResponse($context->GetID); if (defined($context)) { my $yesno = $user->GetResponse('YesNo'); if ($yesno) { $yesno->RemoveForeignHandler($context); } $user->RemoveRequestResponse($context); $context->SetTimeout; } } } } }, ontimeout => sub { my ($context, $user) = @_; $context->SetTimeout; return 1; }, schedule => sub { my ($context, $scheduler, $arg) = @_; if ($arg eq 'clean') { foreach my $username ($main::users->GetUsers) { my $user = $main::users->GetUser($username); my $ucontext = $user->GetResponse($context->GetID); if (defined($ucontext)) { $user->RemoveRequestResponse($ucontext); } } } my ($hour, $day, $month, $year) = (localtime(time))[2..5]; if ($hour >= 6) { ($day, $month, $year) = (localtime(time + 86400))[3..5]; } my $time = POSIX::mktime(0, 0, 6, $day, $month, $year); $scheduler->Schedule($time, $context, 'clean'); }, timeout => 1800, weight => undef ); # kate: syntax perl