Merge pull request #1348 from duckduckgo/mintsoft/week-update_with_dates

Continuation: Convert Week Goodie to Structured Template, update tests
master
Zaahir Moolla 2015-08-10 09:34:08 -04:00
commit 81b7116ce1
4 changed files with 185 additions and 73 deletions

66
lib/DDG/Goodie/Week.pm Normal file → Executable file
View File

@ -2,6 +2,7 @@ package DDG::Goodie::Week;
# ABSTRACT: Find the current week number or when a week began
use DDG::Goodie;
with 'DDG::GoodieRole::Dates';
# My imports
use strict;
@ -42,44 +43,61 @@ my @months = qw/
December
/;
handle query_raw => sub {
handle query_lc => sub {
return unless /^\s*
what(?:'?s|\sis|\swas)?\s+
(?:the\s+)?
(?:(current|(\d{1,2})(?:nd|th|rd|st)?)\s+)?
(?:(?:what|when)(?:'?s|\sis|\swas)?\s)?
(?:the\s)?
(?:(current|(?<week>\d{1,2})(?:nd|th|rd|st)?)\s)?
week
(
\s+of\s+
(?:(?:the|this)\s+)?
(year|\d{4})
\sof\s
(?:(?:the(?:\scurrent)?|this)\s)?
(?<year>year|\d{4})
|
\s+is\s+this
\sis\sthis
)?\??
\s*$/x;
my $week = $1;
my $year = defined $4 ? ($4 eq 'year' ? 'current' : $4) : 'current';
my $week = $+{week};
my $year = $+{year} || 'current';
$year = 'current' if $year eq 'year';
($week, $year) = qw/current current/ if (not defined $week);
return if $week =~ s/(nd|th|rd|st)$// and $week > 52;
# ensure week number is legitimate
return unless $week eq 'current' or ($week > 0 && $week <=52);
my $dt = DateTime->now(time_zone => $loc->time_zone);
my $dt = DateTime->now(time_zone => $loc->time_zone)
if ($week eq 'current' or $year eq 'current');
my $response;
# Asking about current week of the current year
if ($week eq 'current' and $year eq 'current') {
return "We are in currently in the " . ordinate($dt->week_number) .
' week of ' . $dt->year . '.',
html => 'We are in currently in the ' . $dt->week_number
. '<sup>' . ordsuf($dt->week_number) . '</sup>'
. ' week of ' . $dt->year . '.';
} elsif ($year eq 'current') {
my ($dt_week_num, $dt_year) = (ordinate($dt->week_number), $dt->year);
$response = "We are currently in the $dt_week_num week of $dt_year.";
}
# Asking about nth week of the current year
elsif ($year eq 'current') {
$year = $dt->year();
}
my (undef, $month, $day) = Monday_of_Week($week, $year);
return "The " . ordinate($week) . " week of $year began on " .
"$months[--$month] " . ordinate($day) . '.',
html =>"The $week<sup>" . ordsuf($week) . "</sup> week of $year began on " .
"$months[$month] $day<sup>" . ordsuf($day) . '</sup>.';
return unless $year eq 'current' || is_valid_year($year);
# Asking about nth week of some year
unless ($response){
my (undef, $month, $day) = Monday_of_Week($week, $year);
my ($week_num, $day_num, $out_month, $start_term) = (ordinate($week), ordinate($day), $months[--$month], 'begins');
$start_term = "began" if ($year < $dt->year || $year == $dt->year && ($week< $dt->week && $day < $dt->day));
$response = "The $week_num week of $year $start_term on $out_month $day_num.";
}
return $response,
structured_answer => {
input => [],
operation => "Assuming the week starts on Monday",
result => $response
};
};
1;

View File

@ -41,6 +41,7 @@ my $month_regex = qr#$full_month|$short_month#;
my $time_24h = qr#(?:(?:[0-1][0-9])|(?:2[0-3]))[:]?[0-5][0-9][:]?[0-5][0-9]#i;
my $time_12h = qr#(?:(?:0[1-9])|(?:1[012])):[0-5][0-9]:[0-5][0-9]\s?(?:am|pm)#i;
my $date_number = qr#[0-3]?[0-9]#;
my $full_year = qr#[0-9]{4}#;
my $relative_dates = qr#
now | today | tomorrow | yesterday |
(?:(?:current|previous|next)\sday) |
@ -53,8 +54,8 @@ my $relative_dates = qr#
# DMY: 27/11/2014 with a variety of delimiters
# MDY: 11/27/2014 -- fundamentally non-sensical date format, for americans
my $date_delim = qr#[\.\\/\,_-]#;
my $ambiguous_dates = qr#(?:$date_number)$date_delim(?:$date_number)$date_delim(?:[0-9]{4})#i;
my $ambiguous_dates_matches = qr#^(?<m>$date_number)$date_delim(?<d>$date_number)$date_delim(?<y>[0-9]{4})$#i;
my $ambiguous_dates = qr#(?:$date_number)$date_delim(?:$date_number)$date_delim(?:$full_year)#i;
my $ambiguous_dates_matches = qr#^(?<m>$date_number)$date_delim(?<d>$date_number)$date_delim(?<y>$full_year)$#i;
# like: 1st 2nd 3rd 4-20,24-30th 21st 22nd 23rd 31st
my $number_suffixes = qr#(?:st|nd|rd|th)#i;
@ -243,14 +244,14 @@ my %tz_offsets = (
my $tz_strings = join('|', keys %tz_offsets);
my $tz_suffixes = qr#(?:[+-][0-9]{4})|$tz_strings#i;
my $date_standard = qr#$short_day_of_week $short_month\s{1,2}$date_number $time_24h $tz_suffixes [0-9]{4}#i;
my $date_standard_matches = qr#$short_day_of_week (?<m>$short_month)\s{1,2}(?<d>$date_number) (?<t>$time_24h) (?<tz>$tz_suffixes) (?<y>[0-9]{4})#i;
my $date_standard = qr#$short_day_of_week $short_month\s{1,2}$date_number $time_24h $tz_suffixes $full_year#i;
my $date_standard_matches = qr#$short_day_of_week (?<m>$short_month)\s{1,2}(?<d>$date_number) (?<t>$time_24h) (?<tz>$tz_suffixes) (?<y>$full_year)#i;
# formats parsed by vague datestring, without colouring
# the context of the code using it
my $descriptive_datestring = qr{
(?:(?:next|last)\s(?:$month_regex)) | # next June, last jan
(?:(?:$month_regex)\s(?:[0-9]{4})) | # Jan 2014, August 2000
(?:(?:$month_regex)\s(?:$full_year)) | # Jan 2014, August 2000
(?:(?:$date_number)\s?$number_suffixes?\s(?:$month_regex)) | # 18th Jan, 01 October
(?:(?:$month_regex)\s(?:$date_number)\s?$number_suffixes?) | # Dec 25, July 4th
(?:$month_regex) | # February, Aug
@ -260,7 +261,7 @@ my $descriptive_datestring = qr{
# Used for parse_descriptive_datestring_to_date
my $descriptive_datestring_matches = qr#
(?:(?<q>next|last)\s(?<m>$month_regex)) |
(?:(?<m>$month_regex)\s(?<y>[0-9]{4})) |
(?:(?<m>$month_regex)\s(?<y>$full_year)) |
(?:(?<d>$date_number)\s?$number_suffixes?\s(?<m>$month_regex)) |
(?:(?<m>$month_regex)\s(?<d>$date_number)\s?$number_suffixes?) |
(?<m>$month_regex) |
@ -270,6 +271,9 @@ my $descriptive_datestring_matches = qr#
my $formatted_datestring = build_datestring_regex();
# Accessors for useful regexes
sub full_year_regex {
return $full_year;
}
sub full_month_regex {
return $full_month;
}
@ -304,35 +308,42 @@ sub formatted_datestring_regex {
return $formatted_datestring;
}
sub is_valid_year {
my ($year) = @_;
return ($year =~ /^[0-9]{1,4}$/)
&& (1*$year > 0)
&& (1*$year < 10000);
}
# Called once to build $formatted_datestring
sub build_datestring_regex {
my @regexes = ();
## unambigous and awesome date formats:
# ISO8601: 2014-11-27 (with a concession to single-digit month and day numbers)
push @regexes, qr#[0-9]{4}-?[0-1]?[0-9]-?$date_number(?:[ T]$time_24h)?(?: ?$tz_suffixes)?#i;
push @regexes, qr#$full_year-?[0-1]?[0-9]-?$date_number(?:[ T]$time_24h)?(?: ?$tz_suffixes)?#i;
# HTTP: Sat, 09 Aug 2014 18:20:00
push @regexes, qr#$short_day_of_week, [0-9]{2} $short_month [0-9]{4} $time_24h?#i;
push @regexes, qr#$short_day_of_week, [0-9]{2} $short_month $full_year $time_24h?#i;
# RFC850 08-Feb-94 14:15:29 GMT
push @regexes, qr#[0-9]{2}-$short_month-(?:[0-9]{2}|[0-9]{4}) $time_24h?(?: ?$tz_suffixes)#i;
push @regexes, qr#[0-9]{2}-$short_month-(?:[0-9]{2}|$full_year) $time_24h?(?: ?$tz_suffixes)#i;
# RFC2822 Sat, 13 Mar 2010 11:29:05 -0800
push @regexes, qr#$short_day_of_week, $date_number $short_month [0-9]{4} $time_24h $tz_suffixes#i;
push @regexes, qr#$short_day_of_week, $date_number $short_month $full_year $time_24h $tz_suffixes#i;
# date(1) default format Sun Sep 7 15:57:56 EDT 2014
push @regexes, $date_standard;
# month-first date formats
push @regexes, qr#$date_number$date_delim$short_month$date_delim[0-9]{4}#i;
push @regexes, qr#$date_number$date_delim$full_month$date_delim[0-9]{4}#i;
push @regexes, qr#(?:$short_month|$full_month) $date_number(?: ?$number_suffixes)?[,]? [0-9]{4}#i;
push @regexes, qr#$date_number$date_delim$short_month$date_delim$full_year#i;
push @regexes, qr#$date_number$date_delim$full_month$date_delim$full_year#i;
push @regexes, qr#(?:$short_month|$full_month) $date_number(?: ?$number_suffixes)?[,]? $full_year#i;
# day-first date formats
push @regexes, qr#$short_month$date_delim$date_number$date_delim[0-9]{4}#i;
push @regexes, qr#$full_month$date_delim$date_number$date_delim[0-9]{4}#i;
push @regexes, qr#$date_number[,]?(?: ?$number_suffixes)? (?:$short_month|$full_month)[,]? [0-9]{4}#i;
push @regexes, qr#$short_month$date_delim$date_number$date_delim$full_year#i;
push @regexes, qr#$full_month$date_delim$date_number$date_delim$full_year#i;
push @regexes, qr#$date_number[,]?(?: ?$number_suffixes)? (?:$short_month|$full_month)[,]? $full_year#i;
## Ambiguous, but potentially valid date formats
push @regexes, $ambiguous_dates;

View File

@ -432,7 +432,27 @@ subtest 'Dates' => sub {
restore_time();
};
subtest 'Valid Years' => sub {
#my @valids = ('1', '0001', '9999', 2015, 1997);
my @valids = ('1');
my @invalids = (-1, 0, 10000);
foreach my $case (@valids) {
my $result;
lives_ok {
$result = DatesRoleTester::is_valid_year($case)
};
is($result, "1", "$case is a valid year");
}
foreach my $case (@invalids) {
my $result;
lives_ok {
$result = DatesRoleTester::is_valid_year($case)
};
is($result, '', "$case is an invalid year");
}
}
};
subtest 'ImageLoader' => sub {

129
t/Week.t Normal file → Executable file
View File

@ -4,52 +4,115 @@ use strict;
use warnings;
use Test::More;
use DDG::Test::Goodie;
use Test::MockTime qw( :all );
zci answer_type => "week";
zci is_cached => 1;
# Output verified with UNIX cal program
my @current_week = (
qr/We are currently in the \d{1,2}\w{2} week of \d{4}./,
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => qr/We are currently in the \d{1,2}\w{2} week of \d{4}./,
});
ddg_goodie_test(
[qw(
DDG::Goodie::Week
)],
['DDG::Goodie::Week'],
"what week is this?" => test_zci(
qr/We are in currently in the \d+\w+ week of \d+\./,
html => qr:We are in currently in the \d+<sup>\w+</sup> week of \d+\.:),
# Current Week Queries
'what is the current week of the year?' => test_zci(@current_week),
"what week is this?" => test_zci(@current_week),
"what is the current week" => test_zci(@current_week),
"what's the current week? " => test_zci(@current_week),
"whats the current week of the year" => test_zci(@current_week),
"what is the current week" => test_zci(
qr/We are in currently in the \d+\w+ week of \d+\./,
html => qr:We are in currently in the \d+<sup>\w+</sup> week of \d+\.:),
"what's the current week? " => test_zci(
qr/We are in currently in the \d+\w+ week of \d+\./,
html => qr:We are in currently in the \d+<sup>\w+</sup> week of \d+\.:),
# Nth Week Queries
"what was the 5th week of this year" => test_zci(
qr/The \d{1,2}\w{2} week of \d{4} (begins|began) on January \d{1,2}\w{2}\./,
structured_answer => {
input => [],
operation => "Assuming the week starts on Monday",
result => qr/The \d{1,2}\w{2} week of \d{4} (begins|began) on January \d{1,2}\w{2}\./,
}
),
"whats the current week of the year" => test_zci(
qr/We are in currently in the \d+\w+ week of \d+\./,
html => qr:We are in currently in the \d+<sup>\w+</sup> week of \d+\.:),
"what was the 43rd week of 1984" => test_zci(
"The 43rd week of 1984 began on October 22nd.",
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => "The 43rd week of 1984 began on October 22nd.",
}
),
"what was the 5th week of this year" => test_zci(
qr/The \d+\w+ week of \d+ began on January \d+\w+\./,
html => qr:The \d+<sup>\w+</sup> week of \d+ began on January \d+<sup>\w+</sup>\.:),
"what was the 8th week of 1956" => test_zci(
"The 8th week of 1956 began on February 20th.",
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => "The 8th week of 1956 began on February 20th.",
}
),
"what was the 43rd week of 1984" => test_zci(
"The 43rd week of 1984 began on October 22nd.",
html => "The 43<sup>rd</sup> week of 1984 began on October 22<sup>nd</sup>."),
"what was the 21st week of 1987" => test_zci(
"The 21st week of 1987 began on May 18th.",
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => "The 21st week of 1987 began on May 18th.",
}
),
"what was the 8th week of 1956" => test_zci(
"The 8th week of 1956 began on February 20th.",
html => "The 8<sup>th</sup> week of 1956 began on February 20<sup>th</sup>."),
"what was the 21st week of 1987" => test_zci(
"The 21st week of 1987 began on May 18th.",
html => "The 21<sup>st</sup> week of 1987 began on May 18<sup>th</sup>."),
'what was the 5th week of 1944' => test_zci(
'The 5th week of 1944 began on January 31st.',
html => 'The 5<sup>th</sup> week of 1944 began on January 31<sup>st</sup>.'
),
'what was the 5th week of 1944' => test_zci(
"The 5th week of 1944 began on January 31st.",
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => "The 5th week of 1944 began on January 31st.",
}
),
'8th week of 2015' => test_zci(
"The 8th week of 2015 began on February 16th.",
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => "The 8th week of 2015 began on February 16th."
}
),
'what was the 5th week of 0000' => undef,
"what was the 0 week of 2011" => undef,
"what was the 99th week of 2011" => undef,
);
set_fixed_time('2014-01-01T00:00:00');
ddg_goodie_test(
['DDG::Goodie::Week'],
'when is the 8th week of 2015' => test_zci(
"The 8th week of 2015 begins on February 16th.",
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => "The 8th week of 2015 begins on February 16th.",
}
)
);
restore_time();
set_fixed_time('2015-07-31T00:00:00');
ddg_goodie_test(
['DDG::Goodie::Week'],
'when is the 8th week of 2015' => test_zci(
"The 8th week of 2015 began on February 16th.",
structured_answer => {
input => [],
operation => 'Assuming the week starts on Monday',
result => "The 8th week of 2015 began on February 16th.",
}
)
);
restore_time();
done_testing;