Skip to content
This repository was archived by the owner on Oct 15, 2022. It is now read-only.

Commit 82ec048

Browse files
author
Jag Talon
committed
Merge pull request #480 from wilkox/chinese_zodiac
Add Chinese Zodiac goodie
2 parents b4d1c58 + 769f3ca commit 82ec048

File tree

4 files changed

+212
-0
lines changed

4 files changed

+212
-0
lines changed

dist.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ Locale::Currency::Format = 1.30
6161
Net::IP = 0
6262
Math::BaseConvert = 0
6363
Telephony::CountryDialingCodes = 1.04
64+
DateTime::Calendar::Chinese = 1.00
65+
DateTime::Event::Chinese = 1.00
6466

6567
[Prereqs / TestRequires]
6668
Test::More = 0.98

lib/DDG/Goodie/ChineseZodiac.pm

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package DDG::Goodie::ChineseZodiac;
2+
# ABSTRACT: Return the Chinese zodiac animal for a given year.
3+
4+
use DDG::Goodie;
5+
use DateTime::Calendar::Chinese;
6+
use DateTime::Event::Chinese qw(chinese_new_year_before chinese_new_year_after);
7+
use utf8;
8+
use feature 'state';
9+
10+
triggers any => 'chinese zodiac', 'shēngxiào', 'shengxiao', 'shēng xiào', 'sheng xiao';
11+
zci is_cached => 1;
12+
13+
name 'Chinese Zodiac';
14+
description 'Return the Chinese zodiac animal for a given year';
15+
primary_example_queries 'chinese zodiac for 1969';
16+
secondary_example_queries '2004 chinese zodiac animal', 'what was the chinese zodiac animal in 1992', 'what will the chinese zodiac animal be for 2056', 'last year\'s chinese zodiac';
17+
category 'dates';
18+
topics 'special_interest';
19+
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/ChineseZodiac.pm';
20+
attribution github => ['http://github.com/wilkox', 'wilkox'];
21+
22+
my %animal_to_language = (
23+
'hare' => { en => 'Rabbit', zh => '' },
24+
'dragon' => { en => 'Dragon', zh => '' },
25+
'snake' => { en => 'Snake', zh => '' },
26+
'horse' => { en => 'Horse', zh => '' },
27+
'sheep' => { en => 'Goat', zh => '' },
28+
'monkey' => { en => 'Monkey', zh => '' },
29+
'fowl' => { en => 'Rooster', zh => '' },
30+
'dog' => { en => 'Dog', zh => '' },
31+
'pig' => { en => 'Pig', zh => '' },
32+
'rat' => { en => 'Rat', zh => '' },
33+
'ox' => { en => 'Ox', zh => '' },
34+
'tiger' => { en => 'Tiger', zh => '' }
35+
);
36+
37+
handle remainder => sub {
38+
39+
#Figure out what year the user is interested in
40+
my $year_gregorian;
41+
42+
#Return if more than one number has been included;
43+
# this IA only supports years (for now)
44+
return if /\d+[^\d]+\d+/;
45+
46+
#Parse out a relative year expression if it was supplied
47+
if (/this\syear('s)?/) {
48+
$year_gregorian = DateTime->now(time_zone => 'Asia/Shanghai') or return;
49+
} elsif (/next\syear('s)?/) {
50+
$year_gregorian = DateTime->now(time_zone => 'Asia/Shanghai')->add(years => 1) or return;
51+
} elsif (/last\syear('s)?/) {
52+
$year_gregorian = DateTime->now(time_zone => 'Asia/Shanghai')->subtract(years => 1) or return;
53+
54+
#If no relative year was supplied, look for an explicit year
55+
# DateTime::Event::SolarTerm only supports 1900--2069, so
56+
# return nothing if the user provides a year outside this range
57+
} elsif (/\b(\d+)\b/) {
58+
return unless $1 >= 1900 && $1 <= 2069;
59+
$year_gregorian = DateTime->new(year => $1, month => 6, time_zone => 'Asia/Shanghai');
60+
61+
#Otherwise, default to now if it seems like the user is
62+
# asking a question about the current zodiac animal
63+
} elsif (/(what|which|year|animal|current|now|today|this)/) {
64+
$year_gregorian = DateTime->now(time_zone => 'Asia/Shanghai') or return;
65+
66+
#Don't want to show instant answer if user is just looking for
67+
# general information on the chinese zodiac
68+
} else {
69+
return;
70+
}
71+
72+
#Find the Chinese year that aligns
73+
# with the query (presumed Gregorian) year
74+
my $year_chinese = DateTime::Calendar::Chinese->from_object(object => $year_gregorian);
75+
76+
#Get the inclusive Gregorian date range for the Chinese year
77+
#Note that returned dates will be for the 'Asia/Shanghai'
78+
# time zone (China Standard Time/CST) as this is where
79+
# Chinese New Year is calculated
80+
my $year_start = chinese_new_year_before($year_gregorian)->set_time_zone('Asia/Shanghai');
81+
my $year_end = chinese_new_year_after($year_gregorian)->subtract(days => 1)->set_time_zone('Asia/Shanghai');
82+
83+
my $animal = $year_chinese->zodiac_animal;
84+
my $english = $animal_to_language{$animal}{'en'};
85+
my $character = $animal_to_language{$animal}{'zh'};
86+
87+
my $statement = 'Chinese zodiac animal for ' . format_datetime($year_start) . "\x{2013}" . format_datetime($year_end);
88+
89+
return answer => $english, html => wrap_html($character, $english, $statement);
90+
};
91+
92+
sub format_datetime {
93+
my $dt = shift;
94+
my $formatted = $dt->strftime('%b %e, %Y');
95+
$formatted =~ s/\s\s/ /g;
96+
return $formatted;
97+
}
98+
99+
# This function adds some HTML and styling to our output
100+
# so that we can make it prettier (copied from the Conversions
101+
# goodie)
102+
sub append_css {
103+
my $html = shift;
104+
state $css = share("style.css")->slurp;
105+
return "<style type='text/css'>$css</style>$html";
106+
}
107+
108+
sub wrap_html {
109+
my ($character, $english, $statement) = @_;
110+
return append_css("<div class='zci--chinesezodiac'><div class='zodiaccharacter'>$character ($english)</div><span class='statement'>$statement</span></div>");
111+
}
112+
113+
1;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.zci--answer .zci--chinesezodiac {
2+
padding-top: .25em;
3+
padding-bottom: .25em;
4+
}
5+
6+
.zci--answer .zci--chinesezodiac .zodiaccharacter {
7+
font-size: 1.7em;
8+
}
9+
10+
.zci--answer .zci--chinesezodiac .statement {
11+
color: #747474;
12+
}

t/ChineseZodiac.t

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env perl
2+
3+
use strict;
4+
use warnings;
5+
use Test::More;
6+
use DDG::Test::Goodie;
7+
use utf8;
8+
9+
zci answer_type => 'chinesezodiac';
10+
zci is_cached => 1;
11+
12+
ddg_goodie_test(
13+
[qw(
14+
DDG::Goodie::ChineseZodiac
15+
)],
16+
17+
#Primary example
18+
'chinese zodiac for 1969' => test_zci('Rooster', html => qr/Rooster/),
19+
20+
#Secondary examples
21+
'2004 chinese zodiac animal' => test_zci('Monkey', html => qr/Monkey/),
22+
'what was the chinese zodiac animal in 1992' => test_zci('Monkey', html => qr/Monkey/),
23+
'what will the chinese zodiac animal be for 2056' => test_zci('Rat', html => qr/Rat/),
24+
'last year\'s chinese zodiac' => test_zci(qr/./, html => qr/./),
25+
26+
#Primary example with different query formats
27+
'1969 chinese zodiac animal' => test_zci('Rooster', html => qr/Rooster/),
28+
'what was the chinese zodiac animal for 1969' => test_zci('Rooster', html => qr/Rooster/),
29+
'what will the chinese zodiac animal be for people born in the year 1969' => test_zci('Rooster', html => qr/Rooster/),
30+
'chinese zodiac for a person born in 1969' => test_zci('Rooster', html => qr/Rooster/),
31+
'chinese zodiac of 1969' => test_zci('Rooster', html => qr/Rooster/),
32+
33+
#Alternative triggers
34+
'1969 shēngxiào' => test_zci('Rooster', html => qr/Rooster/),
35+
'shengxiao animal 1969' => test_zci('Rooster', html => qr/Rooster/),
36+
'shēng xiào for 1969' => test_zci('Rooster', html => qr/Rooster/),
37+
'i was born in 1969 what is my sheng xiao' => test_zci('Rooster', html => qr/Rooster/),
38+
39+
#Test some different years
40+
# Taken from http://www.chinesezodiac.com/calculator.php
41+
'chinese zodiac animal for 1924' => test_zci('Rat', html => qr/Rat/),
42+
'chinese zodiac animal for 1929' => test_zci('Snake', html => qr/Snake/),
43+
'chinese zodiac animal for 1934' => test_zci('Dog', html => qr/Dog/),
44+
'chinese zodiac animal for 1939' => test_zci('Rabbit', html => qr/Rabbit/),
45+
'chinese zodiac animal for 1944' => test_zci('Monkey', html => qr/Monkey/),
46+
'chinese zodiac animal for 1949' => test_zci('Ox', html => qr/Ox/),
47+
'chinese zodiac animal for 1954' => test_zci('Horse', html => qr/Horse/),
48+
'chinese zodiac animal for 1959' => test_zci('Pig', html => qr/Pig/),
49+
'chinese zodiac animal for 1964' => test_zci('Dragon', html => qr/Dragon/),
50+
'chinese zodiac animal for 1969' => test_zci('Rooster', html => qr/Rooster/),
51+
'chinese zodiac animal for 1974' => test_zci('Tiger', html => qr/Tiger/),
52+
'chinese zodiac animal for 2027' => test_zci('Goat', html => qr/Goat/),
53+
'chinese zodiac animal for 2040' => test_zci('Monkey', html => qr/Monkey/),
54+
55+
#Test for correct date ranges
56+
# Taken from http://www.chinesezodiac.com/calculator.php
57+
'chinese zodiac animal for 1925' => test_zci('Ox', html => qr/Jan\s24,\s1925.Feb\s12,\s1926/),
58+
'chinese zodiac animal for 1937' => test_zci('Ox', html => qr/Feb\s11,\s1937.Jan\s30,\s1938/),
59+
'chinese zodiac animal for 1953' => test_zci('Snake', html => qr/Feb\s14,\s1953.Feb\s2,\s1954/),
60+
'chinese zodiac animal for 1973' => test_zci('Ox', html => qr/Feb\s3,\s1973.Jan\s22,\s1974/),
61+
'chinese zodiac animal for 1997' => test_zci('Ox', html => qr/Feb\s7,\s1997.Jan\s27,\s1998/),
62+
'chinese zodiac animal for 2013' => test_zci('Snake', html => qr/Feb\s10,\s2013.Jan\s30,\s2014/),
63+
'chinese zodiac animal for 2017' => test_zci('Rooster', html => qr/Jan\s28,\s2017.Feb\s15,\s2018/),
64+
'chinese zodiac animal for 2041' => test_zci('Rooster', html => qr/Feb\s1,\s2041.Jan\s21,\s2042/),
65+
66+
#Should not trigger
67+
'wikipedia chinese zodiac' => undef,
68+
'what is my zodiac sign' => undef,
69+
'what is the chinese word for duck' => undef,
70+
'buy an inflatable zodiac chinese online store' => undef,
71+
'chinese zodiac 20 march 1997' => undef,
72+
'chinese zodiac 1997-03-20' => undef,
73+
'what was the chinese zodiac animal on the 3rd of april 1945' => undef,
74+
75+
#No support currently for years outside 1900--2069
76+
'chinese zodiac 1899' => undef,
77+
'chinese zodiac 1900' => test_zci('Rat', html => qr/Rat/),
78+
'chinese zodiac 2069' => test_zci('Ox', html => qr/Ox/),
79+
'chinese zodiac 2070' => undef,
80+
'chinese zodiac 2000000000000' => undef,
81+
82+
);
83+
84+
done_testing;
85+

0 commit comments

Comments
 (0)