# Type: Input # Category: Task # Description: Shows tasks of user for some period, project etc # Author: Andriy Lesyuk sub ShowTasks_ParseDay($$) { my ($message, $result) = @_; my $date; my $now = time; if ((localtime($now))[2] < 6) { $now -= 21600; } # Weekday my $wday = Orangutan::Date::ParseWeekDay($message); if (defined($wday)) { my $cwday = (localtime($now))[6]; my ($day, $mon, $year); if ($wday >= $cwday) { ($day, $mon, $year) = (localtime($now - (7 - ($wday - $cwday)) * 86400))[3..5]; } else { ($day, $mon, $year) = (localtime($now - ($cwday - $wday) * 86400))[3..5]; } $date = POSIX::mktime(0, 0, 0, $day, $mon, $year); } # Yesterday if (!defined($date) && (lc($message) eq 'yesterday')) { my ($day, $mon, $year) = (localtime($now - 86400))[3..5]; $date = POSIX::mktime(0, 0, 0, $day, $mon, $year); } # Today if (!defined($date) && (lc($message) eq 'today')) { my ($day, $mon, $year) = (localtime($now))[3..5]; $date = POSIX::mktime(0, 0, 0, $day, $mon, $year); } if (defined($date)) { push(@{$result}, new Orangutan::Date($date + 21600)); push(@{$result}, new Orangutan::Date($date + 86399 + 21600)); } } sub ShowTasks_ParsePeriod($$) { my ($message, $result) = @_; my $regexp; my ($start, $end); # Base on work day my $now = time; if ((localtime($now))[2] < 6) { $now -= 21600; } # From Nth Month, YYYY to Nth Month, YYYY $regexp = ' *(On|at|for|during|from|since) '. '(?:[A-z]{3,9}, )?([0-9]{1,2})(?:(?:st|nd|rd|th) ([A-z]{3,9})(?:, ([0-9]{4}))?)? *'. '(?:(?:-|to|till) *'. '(?:[A-z]{3,9}, )?([0-9]{1,2})(?:(?:st|nd|rd|th) ([A-z]{3,9})(?:, ([0-9]{4}))?)?)?'; if (${$message} =~ /\b$regexp$/i) { my ($case, $sday, $smon, $syear, $eday, $emon, $eyear) = ($1, $2, $3, $4, $5, $6, $7); if (!defined($eday) && defined($smon)) { if ($case =~ /^(?:from|since)$/i) { ($eday, $emon, $eyear) = (localtime($now))[3..5]; $emon = $Orangutan::Date::months[$emon]; $eyear += 1900; } else { $eday = $sday; } } if (!defined($smon)) { $smon = $emon; } elsif (!defined($emon)) { $emon = $smon; } if (!defined($syear)) { $syear = $eyear; } elsif (!defined($eyear)) { $eyear = $syear; } if (defined($smon)) { $smon = Orangutan::Date::ParseMonth($smon); $emon = Orangutan::Date::ParseMonth($emon); } if (defined($smon) && defined($emon)) { if (!defined($syear)) { $syear = $eyear = (localtime($now))[5]; my $etime = POSIX::mktime(59, 59, 23, $eday, $emon, $eyear); if (defined($etime) && ($etime > $now)) { $eyear--; $syear = $eyear; } my $stime = POSIX::mktime(0, 0, 0, $sday, $smon, $syear); if (defined($stime) && ($stime > $etime)) { $syear--; } $syear += 1900; $eyear += 1900; } $start = POSIX::mktime(0, 0, 0, $sday, $smon, $syear - 1900); $end = POSIX::mktime(59, 59, 23, $eday, $emon, $eyear - 1900); if (defined($start) && defined($end)) { ${$message} =~ s/\b$regexp$//i; } } } # From Month N, YYYY to Month N, YYYY $regexp = ' *(On|at|for|during|from|since) '. '(?:[A-z]{3,9}, )?(?:([A-z]{3,9})[ -])?([0-9]{1,2})(?:, ([0-9]{4}))? *'. '(?:(?:-|to|till) *'. '(?:[A-z]{3,9}, )?(?:([A-z]{3,9})[ -])?([0-9]{1,2})(?:, ([0-9]{4}))?)?'; if (!defined($start) && (${$message} =~ /\b$regexp$/i)) { my ($case, $smon, $sday, $syear, $emon, $eday, $eyear) = ($1, $2, $3, $4, $5, $6, $7); if (!defined($eday) && defined($smon)) { if ($case =~ /^(?:from|since)$/i) { ($eday, $emon, $eyear) = (localtime($now))[3..5]; $emon = $Orangutan::Date::months[$emon]; $eyear += 1900; } else { $eday = $sday; } } if (!defined($smon)) { $smon = $emon; } elsif (!defined($emon)) { $emon = $smon; } if (!defined($syear)) { $syear = $eyear; } elsif (!defined($eyear)) { $eyear = $syear; } if (defined($smon)) { $smon = Orangutan::Date::ParseMonth($smon); $emon = Orangutan::Date::ParseMonth($emon); } if (defined($smon) && defined($emon)) { if (!defined($syear)) { $syear = $eyear = (localtime($now))[5]; my $etime = POSIX::mktime(59, 59, 23, $eday, $emon, $eyear); if (defined($etime) && ($etime > $now)) { $eyear--; $syear = $eyear; } my $stime = POSIX::mktime(0, 0, 0, $sday, $smon, $syear); if (defined($stime) && ($stime > $etime)) { $syear--; } $syear += 1900; $eyear += 1900; } $start = POSIX::mktime(0, 0, 0, $sday, $smon, $syear - 1900); $end = POSIX::mktime(59, 59, 23, $eday, $emon, $eyear - 1900); if (defined($start) && defined($end)) { ${$message} =~ s/\b$regexp$//i; } } } # From Weekday to Weekday $regexp = ' *(On|at|for|during|from|since) ([A-z]{3,9}) *(?:(?:-|to|till) *([A-z]{3,9}))?'; if (!defined($start) && (${$message} =~ /\b$regexp$/i)) { my ($case, $wday1, $wday2) = ($1, $2, $3); my ($swday, $ewday); $swday = Orangutan::Date::ParseWeekDay($wday1); if (defined($wday2)) { $ewday = Orangutan::Date::ParseWeekDay($wday2); if (!defined($ewday)) { $swday = undef; } } elsif ($case !~ /^(?:from|since)$/i) { $ewday = $swday; } if (defined($swday)) { my ($smon, $sday, $syear, $emon, $eday, $eyear); my $cwday = (localtime($now))[6]; if (!defined($ewday)) { ($eday, $emon, $eyear) = (localtime($now))[3..5]; } elsif ($ewday >= $cwday) { ($eday, $emon, $eyear) = (localtime($now - (7 - ($ewday - $cwday)) * 86400))[3..5]; } else { ($eday, $emon, $eyear) = (localtime($now - ($cwday - $ewday) * 86400))[3..5]; } $end = POSIX::mktime(59, 59, 23, $eday, $emon, $eyear); if (defined($wday2)) { if ($swday >= $ewday) { ($sday, $smon, $syear) = (localtime($end - (7 - ($swday - $ewday)) * 86400))[3..5]; } else { ($sday, $smon, $syear) = (localtime($end - ($ewday - $swday) * 86400))[3..5]; } } elsif (defined($ewday)) { ($sday, $smon, $syear) = ($eday, $emon, $eyear); } elsif ($swday >= $cwday) { ($sday, $smon, $syear) = (localtime($now - (7 - ($swday - $cwday)) * 86400))[3..5]; } else { ($sday, $smon, $syear) = (localtime($now - ($cwday - $swday) * 86400))[3..5]; } $start = POSIX::mktime(0, 0, 0, $sday, $smon, $syear); ${$message} =~ s/\b$regexp$//i; } } # From Month YYYY to Month YYYY $regexp = ' *(In|on|at|for|during|from|since) '. '([A-z]{3,9})(?:,? ([0-9]{4}))? *'. '(?:(?:-|to|till) *'. '([A-z]{3,9})(?:,? ([0-9]{4}))?)?'; if (!defined($start) && (${$message} =~ /\b$regexp$/i)) { my ($case, $smon, $syear, $emon, $eyear) = ($1, $2, $3, $4, $5); $smon = Orangutan::Date::ParseMonth($smon); if (defined($emon)) { $emon = Orangutan::Date::ParseMonth($emon); } elsif ($case =~ /^(?:from|since)$/i) { ($emon, $eyear) = (localtime($now))[4..5]; $eyear += 1900; } else { ($emon, $eyear) = ($smon, $syear); } if (defined($smon) && defined($emon)) { my $time = new Orangutan::Date($now); my ($cmon, $cyear) = (localtime($now))[4..5]; $cyear += 1900; if (!defined($eyear)) { $eyear = $cyear; if ($emon > $cmon) { $eyear--; } } $end = POSIX::mktime(0, 0, 0, 1, $emon, $eyear - 1900); if (defined($end)) { $time->Set($end); $end = $time->MonthEnd->Date; } if (!defined($syear)) { $syear = $eyear; if ($smon > $emon) { $syear--; } } $start = POSIX::mktime(0, 0, 0, 1, $smon, $syear - 1900); if (defined($start) && defined($end)) { ${$message} =~ s/\b$regexp$//i; } } } # From DD/MM/YYYY to DD/MM/YYYY $regexp = ' *(On|at|for|during|from|since) '. '(?:[A-z]{3,9}, )?(?:([0-9]{4})[./-])?([0-9]{1,2})[./-]([0-9]{1,2})(?:[./-]([0-9]{4}))? *'. '(?:(?:-|to|till) *'. '(?:[A-z]{3,9}, )?(?:([0-9]{4})[./-])?([0-9]{1,2})[./-]([0-9]{1,2})(?:[./-]([0-9]{4}))?)?'; if (!defined($start) && (${$message} =~ /\b$regexp$/i)) { my ($case, $smon, $sday, $syear, $emon, $eday, $eyear); $case = $1; if (($4 > 12) || defined($2)) { ($sday, $smon) = ($4, $3); } else { ($sday, $smon) = ($3, $4); } if ((defined($8) && ($8 > 12)) || defined($6)) { ($eday, $emon) = ($8, $7); } else { ($eday, $emon) = ($7, $8); } if (defined($2)) { $syear = $2; } elsif (defined($5)) { $syear = $5; } if (defined($6)) { $eyear = $6; } elsif (defined($9)) { $eyear = $9; } if (!defined($eday)) { if ($case =~ /^(?:from|since)$/i) { ($eday, $emon, $eyear) = (localtime($now))[3..5]; $emon++; $eyear += 1900; } else { $eday = $sday; $emon = $smon; } } if (!defined($syear)) { $syear = $eyear; } elsif (!defined($eyear)) { $eyear = $syear; } if (defined($smon) && defined($emon)) { if (!defined($syear)) { $syear = $eyear = (localtime($now))[5]; my $etime = POSIX::mktime(59, 59, 23, $eday, $emon - 1, $eyear); if (defined($etime) && ($etime > $now)) { $eyear--; $syear = $eyear; } my $stime = POSIX::mktime(0, 0, 0, $sday, $smon - 1, $syear); if (defined($stime) && ($stime > $etime)) { $syear--; } $syear += 1900; $eyear += 1900; } $start = POSIX::mktime(0, 0, 0, $sday, $smon - 1, $syear - 1900); $end = POSIX::mktime(59, 59, 23, $eday, $emon - 1, $eyear - 1900); if (defined($start) && defined($end)) { ${$message} =~ s/\b$regexp$//i; } } } # On this week $regexp = ' *(?:(?:On|in|at|for|during) )?(?:the )?(?:(this|current|last|previous) )?(day|week|month)'; if (!defined($start) && (${$message} =~ /\b$regexp$/i)) { my ($which, $what) = ($1, $2); my $time = new Orangutan::Date($now); if (lc($what) eq 'day') { if (defined($which) && ((lc($which) eq 'last') || (lc($which) eq 'previous'))) { $time->Set($time->Date - 86400); } $start = $time->DayStart->Date; $end = $time->DayEnd->Date; } elsif (lc($what) eq 'week') { if (defined($which) && ((lc($which) eq 'last') || (lc($which) eq 'previous'))) { $time->Set($time->Date - 604800); } $start = $time->WeekStart->Date; $end = $time->WeekEnd->Date; } else { if (defined($which) && ((lc($which) eq 'last') || (lc($which) eq 'previous'))) { $time->Set($time->MonthStart->Date - 86400); } $start = $time->MonthStart->Date; $end = $time->MonthEnd->Date; } ${$message} =~ s/\b$regexp$//i; } # Yesterday $regexp = ' *(?:for )?yesterday'; if (!defined($start) && (${$message} =~ /\b$regexp$/i)) { my ($day, $month, $year) = (localtime($now - 86400))[3..5]; $start = POSIX::mktime(0, 0, 0, $day, $month, $year); $end = POSIX::mktime(59, 59, 23, $day, $month, $year); ${$message} =~ s/\b$regexp$//i; } # Today $regexp = ' *(?:for )?today'; if (!defined($start) && (${$message} =~ /\b$regexp$/i)) { my ($day, $month, $year) = (localtime($now))[3..5]; $start = POSIX::mktime(0, 0, 0, $day, $month, $year); $end = POSIX::mktime(59, 59, 23, $day, $month, $year); ${$message} =~ s/\b$regexp$//i; } if (defined($start) && defined($end)) { push(@{$result}, new Orangutan::Date($start + 21600)); push(@{$result}, new Orangutan::Date($end + 21600)); } } sub ShowTasks_DefaultPeriod($$) { my ($message, $result) = @_; # Base on work day my $now = time; if ((localtime($now))[2] < 6) { $now -= 21600; } # Today my ($day, $month, $year) = (localtime($now))[3..5]; my $start = POSIX::mktime(0, 0, 0, $day, $month, $year); my $end = POSIX::mktime(59, 59, 23, $day, $month, $year); push(@{$result}, new Orangutan::Date($start + 21600)); push(@{$result}, new Orangutan::Date($end + 21600)); } sub ShowTasks_ParseFilter($$) { my ($message, $result) = @_; my $regexp; # Extract/remove activity my $activity; $regexp = ' *\(([^)]+)\) *'; if (${$message} =~ /$regexp$/i) { my $id = $main::query->ActivityExists($1); if (defined($id) && ($id ne '')) { $activity = $1; ${$message} =~ s/$regexp$//i; } } # Extract/remove project $regexp = ' *(?:On|for|at) ["\']?(.+?)["\']? *'; if (${$message} =~ /\b$regexp$/i) { my $project = $main::query->ProjectExists($1); if (defined($project) && (ref $project eq 'HASH')) { push(@{$result}, $project); ${$message} =~ s/\b$regexp$//i; } else { push(@{$result}, undef); } } else { push(@{$result}, undef); } # Extract/remove issue my $trackers = $main::config->Get('redmine', '_trackers_regexp'); if ($trackers) { $regexp = ' *(?:On|for|at) (?:the )?(?:(?:issue|'.$trackers.') )?(?:#|No\.? ?)?([0-9]+) *'; } else { $regexp = ' *(?:On|for|at) (?:the )?(?:issue )?(?:#|No\.? ?)?([0-9]+) *'; } if (${$message} =~ /\b$regexp$/i) { push(@{$result}, $1); ${$message} =~ s/\b$regexp$//i; } else { push(@{$result}, undef); } if (defined($activity)) { my $id = $main::query->ActivityExists($activity, $result->[$#{$result}-1]->{'project_id'}); if ($id) { $activity = { id => $id, name => $activity }; } } push(@{$result}, $activity); } new Orangutan::Context( response => sub { my ($context, $user, $message) = @_; my @result = ( ); # Remove ending $message =~ s/\?*!*\.*$//; my $dayre = 'Sun(?:day)?|Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|yesterday|today'; my @regexps = ( 'What (?:(?:am|was) )?I(?:\'m| (?:am|was))? (?:working on|doing)', 'What (?:was )?([^ ]+)(?: was)? (?:working on|doing)', 'What (?:did )?(?:I|([^ ]+)) (?:do|did)', 'What (?:have )?I(?:\'ve| have)? done', 'What (?:has )?([^ ]+)(?: has)? done', 'What (?:are|were) (?:('.$dayre.') )?tasks of ([^ ]+)', '(?:(?:Show|list) )?(?:('.$dayre.') )?tasks of ([^ ]+)', 'What (?:are|were) (?:('.$dayre.') )?tasks', '(?:(?:Show|list) )?(?:('.$dayre.') )?tasks', 'What (?:are|were) (?:(?:my|([^ ]+?)(?:\'s)?) )?(?:('.$dayre.') )?tasks', '(?:(?:Show|list) )?(?:(?:my|([^ ]+?)(?:\'s)?) )?(?:('.$dayre.') )?tasks' ); # Check base query for (my $i = 0; $i < scalar @regexps; $i++) { if ($message =~ /^$regexps[$i] */i) { my ($day, $username) = (undef, undef); if ($i > 8) { ($day, $username) = ($2, $1); } elsif ($i > 6) { $day = $1; } elsif ($i > 4) { ($day, $username) = ($1, $2); } else { $username = $1; } if (defined($username)) { my $id = $main::query->UserExists($username); if ($id) { push(@result, 1); push(@result, $id); if (defined($day)) { ShowTasks_ParseDay($day, \@result); } $message =~ s/^$regexps[$i] *//i; last; } } else { push(@result, 1); push(@result, undef); if (defined($day)) { ShowTasks_ParseDay($day, \@result); } $message =~ s/^$regexps[$i] *//i; last; } } } if (scalar @result > 0) { if (scalar @result == 2) { ShowTasks_ParsePeriod(\$message, \@result); } if (scalar @result == 2) { ShowTasks_DefaultPeriod(\$message, \@result); } ShowTasks_ParseFilter(\$message, \@result); if ($message !~ /^ *$/) { @result = ( ); } } return @result; }, handler => sub { my ($context, $manager, $item, $userid, $start, $end, $project, $issue, $activity) = @_; my $user; if (!defined($userid) || ($userid == $manager->GetUserID)) { $userid = $manager->GetUserID; $user = $manager; } elsif (!$manager->IsCreator && !$manager->IsManager) { $manager->SendMessage([ "Who told you that I can do this?! I can't!.. I do not know how...", 'Why do you care?.. Do not answer... I will not tell you this...', 'I was not permitted to give such information to you. Sorry...', 'Perhaps I could do this if I was a human...' ]); return; } else { $user = $main::users->GetUserID($userid); } if (!defined($start) || !defined($end)) { $manager->SendMessage([ "Can't handle the request - it looks like you made some mistake...", "Something's wrong with the request. Can't understand it!", "Can't understand what do you want me to do..." ]); return; } if (defined($activity) && (ref $activity ne 'HASH')) { my $format; if (defined($project)) { $format = Orangutan::Context::Random( 'There is no activity with name %s in project %s...', 'Activity %s does not exist in project %s...' ); } else { $format = Orangutan::Context::Random( 'There is no activity with name %s...', 'Activity %s does not exist...' ); } $manager->SendMessage(sprintf($format, $activity, $project->{'name'})); return; } my $projectid = defined($project) ? $project->{'id'} : undef; my $activityid = defined($activity) ? $activity->{'id'} : undef; my @events = Orangutan::User::GetEntries(defined($user) ? $user : $userid, $start, $end, $projectid, $issue, $activityid); if (scalar @events > 0) { my $message = Orangutan::Context::Random( 'This is what matches your request', 'This is what I found', 'Here they go' ).":\n"; my $total = 0; my $number = 1; foreach my $task (@events) { utf8::decode($task->{'comments'}); my ($day, $mon, $year, $wday) = (localtime($task->{'order_time'}))[3..6]; $message .= sprintf("%d) %s, %s %02d ", $number, $Orangutan::Date::weekdays_short[$wday], $Orangutan::Date::months_short[$mon], $day); if (defined($task->{'start_time'})) { my ($smin, $shour) = (localtime($task->{'start_time'}))[1..2]; if (defined($task->{'end_time'})) { my ($emin, $ehour) = (localtime($task->{'end_time'}))[1..2]; $message .= sprintf("%02d:%02d - %02d:%02d ", $shour, $smin, $ehour, $emin); } else { my $format = Orangutan::Context::Random( '%02d:%02d till now', 'since %02d:%02d', '%02d:%02d' ); $message .= sprintf($format, $shour, $smin).' '; } } my $duration = $task->{'duration'}; if (!defined($duration)) { $duration = int(time() / 60) * 60 - $task->{'start_time'}; } $message .= sprintf("(%d:%02d): ", int($duration / 3600), int(($duration % 3600) / 60)); if (defined($task->{'comments'})) { $message .= $task->{'comments'}; } if (defined($task->{'issue_id'})) { $message .= ' on #'.$task->{'issue_id'}; } if (defined($task->{'project'})) { $message .= ' for '.$task->{'project'}; if (defined($task->{'activity'})) { $message .= ' ('.$task->{'activity'}.")"; } } $message .= "\n"; $total += $duration; $number++; } if (scalar @events > 1) { my $format = Orangutan::Context::Random( 'Total time is %d:%02d.', 'Total time: %d:%02d.' ); $message .= sprintf($format, int($total / 3600), int(($total % 3600) / 60)); } $manager->SendMessage($message); } else { $manager->SendMessage([ 'Cannot find any task...', 'No tasks at that time.', 'No tasks!' ]); } }, help => { title => 'How can I list tasks?', question => '^(?:How )?(?:(?:can|do) I |to )list (?:my )?tasks(?: (?:during|for|at) some period)?\?*!*\.*$', answer => "Orangutan can list tasks for today, yesterday, current week, last month or any other period.\n". "Sample queries:\n". " o What was I working on last week?\n". " o What are my tasks for Monday?\n". " o What did I do in January?\n". " o What am I doing today?\n". " o Show tasks\n". "You can optionally specify issue, project or activity:\n". " o What I did for Orangutan?\n". " o What was I doing on #98?\n". "Tasks can be shown only for days (you can't specify hours)...", weight => 810 }, weight => 91 ); # kate: syntax perl