ФЭНДОМ


На досуге нарисовал парсер CSV данных на текщий момент и сегодняшний день с worldwheateronline с превращением их в текстовый и приятный для festival формат. 

Это первая версия. Возможно буду продолжать улучшение. 

Имеется:

  • 16 русских направлений ветра
  • Склонение чисел
  • Машинный перевод состояний погоды полученных по API

Особеннности: 

  • Скрипт только генерирует текст. Для воспроизведения можно использовать конструкцию на bash ./saytext_ru.sh "`./wheater.pl`"
  • Весь текст формируется в виде одного принта в конце файла.
  • Все переменные для принта описываются чуть выше.

Благодарности:

Текущие проблемы:

  • Отсутствие адекватного перевода для состояний погоды
  • Неявное конструирование текста.


ИнструкцияПравить

Аналогично статье Получение актуальной погоды требуется получить API-key и ввести его в скрипт. Так же необходимо указать координаты своего города до четвёртого знака.

Для использования склонятора есть 2 функции 

  1. litnum($num, $pad, $gen) .  - преобразование числа в текст с учётом падежа и пола. возвращает число
  1. [" $one "," $two "," $three "]->[numdecl($num)]; - выбор версии текста в зависимости от числа. возвращает слово
  2. rus_int_word(ceil($temp),'i','m',"градус","градуса","градусов"); - комбинированная версия 2х вышеописанных функций. Возвращает число слово.
  • Весь текст формируется в виде одного принта в конце файла.
  • Все переменные для принта описываются чуть выше.

СкриптПравить

#!/usr/bin/perl
# Free License 
# Nikita I Shapovalov
# n.shapovalov@immap.ru
# Thanks for: 
# http://mail.pm.org/pipermail/moscow-pm/2010-October/009101.html - For dig2text function
# http://developer.worldweatheronline.com - for Free wheater API
# https://translate.yandex.net/api/v1.5/tr.json/translate - For Translate API for wheater strings
#


	use POSIX;
	use LWP::Simple;
	use Time::Piece;

### PUT HERE YOU API KEY FROM developer.worldweatheronline.com ###
$api_key="developer.worldweatheronline";                       ###
###       А сюда координаты                                    ###
###         Нижний Новгород                                    ###
$shir = '56.1937';                                             ###
$dolg = '44.0027';                                             ###
##################################################################


###
my %lit = (
	'i' => {
		lit100 => [undef, 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот'],
		lit10 => [undef, undef, 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто'],
		zero => 'ноль',
		ths => ['тысяча', 'тысячи', 'тысяч'],
		mln => ['миллион', 'миллиона', 'миллионов'],
		bln => ['миллиард', 'миллиарда', 'миллиардов'],
	},
	'r' => {
		lit100 => [undef, 'ста', 'двухсот', 'трёхсот', 'четырёхсот', 'пятисот', 'шестисот', 'семисот', 'восьмисот', 'девятисот'],
		lit10 => [undef, undef, 'двадцати', 'тридцати', 'сорока', 'пятидесяти', 'шестидесяти', 'семидесяти', 'восьмидесяти', 'девяноста'],
		zero => 'нуля',
		ths => ['тысячи', 'тысяч', 'тысяч'],
		mln => ['миллиона', 'миллионов', 'миллионов'],
		bln => ['миллиарда', 'миллиардов', 'миллиардов'],
	},
	'd' => {
		lit100 => [undef, 'ста', 'двумстам', 'трёмстам', 'четырёмстам', 'пятистам', 'шестистам', 'семистам', 'восьмистам', 'девятистам'],
		lit10 => [undef, undef, 'двадцати', 'тридцати', 'сорока', 'пятидесяти', 'шестидесяти', 'семидесяти', 'восьмидесяти', 'девяноста'],
		zero => 'нулю',
		ths => ['тысяче', 'тысячам', 'тысячам'],
		mln => ['миллиону', 'миллионам', 'миллионам'],
		bln => ['миллиарду', 'миллиардам', 'миллиардам'],
	},
	'v' => {
		lit100 => [undef, 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот'],
		lit10 => [undef, undef, 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто'],
		zero => 'ноль',
		ths => ['тысячу', 'тысячи', 'тысяч'],
		mln => ['миллион', 'миллиона', 'миллионов'],
		bln => ['миллиард', 'миллиарда', 'миллиардов'],
	},
	't' => {
		lit100 => [undef, 'ста', 'двумястами', 'тремястами', 'четыремястами', 'пятистами', 'шестистами', 'семистами', 'восемьюстами', 'девятьюстами'],
		lit10 => [undef, undef, 'двадцатью', 'тридцатью', 'сорока', 'пятидесятью', 'шестидесятью', 'семидесятью', 'восьмидесятью', 'девяноста'],
		zero => 'нулём',
		ths => ['тысячью', 'тысячами', 'тысячами'],
		mln => ['миллионом', 'миллионами', 'миллионами'],
		bln => ['миллиардом', 'миллиардами', 'миллиардами'],
	},
	'p' => {
		lit100 => [undef, 'ста', 'двухстах', 'трёхстах', 'четырёхстах', 'пятистах', 'шестистах', 'семистах', 'восьмистах', 'девятистах'],
		lit10 => [undef, undef, 'двадцати', 'тридцати', 'сорока', 'пятидесяти', 'шестидесяти', 'семидесяти', 'восьмидесяти', 'девяноста'],
		zero => 'нуле',
		ths => ['тысяче', 'тысячах', 'тысячах'],
		mln => ['миллионе', 'миллионах', 'миллионах'],
		bln => ['миллиарде', 'миллиардах', 'миллиардах'],
	},
	'im' => {
		lit1 => [undef, 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять', 'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать'],
	},
	'if' => {
		lit1 => [undef, 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять', 'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать'],
	},
	'rm' => {
		lit1 => [undef, 'одного', 'двух', 'трёх', 'четырёх', 'пяти', 'шести', 'семи', 'восьми', 'девяти', 'десяти', 'одиннадцати', 'двенадцати', 'тринадцати', 'четырнадцати', 'пятнадцати', 'шестнадцати', 'семнадцати', 'восемнадцати', 'девятнадцати'],
	},
	'rf' => {
		lit1 => [undef, 'одной', 'двух', 'трёх', 'четырёх', 'пяти', 'шести', 'семи', 'восьми', 'девяти', 'десяти', 'одиннадцати', 'двенадцати', 'тринадцати', 'четырнадцати', 'пятнадцати', 'шестнадцати', 'семнадцати', 'восемнадцати', 'девятнадцати'],
	},
	'dm' => {
		lit1 => [undef, 'одному', 'двум', 'трём', 'четырём', 'пяти', 'шести', 'семи', 'восьми', 'девяти', 'десяти', 'одиннадцати', 'двенадцати', 'тринадцати', 'четырнадцати', 'пятнадцати', 'шестнадцати', 'семнадцати', 'восемнадцати', 'девятнадцати'],
	},
	'df' => {
		lit1 => [undef, 'одной', 'двум', 'трём', 'четырём', 'пяти', 'шести', 'семи', 'восьми', 'девяти', 'десяти', 'одиннадцати', 'двенадцати', 'тринадцати', 'четырнадцати', 'пятнадцати', 'шестнадцати', 'семнадцати', 'восемнадцати', 'девятнадцати'],
	},
	'vm' => {
		lit1 => [undef, 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять', 'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать'],
	},
	'vf' => {
		lit1 => [undef, 'одну', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять', 'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать'],
	},
	'tm' => {
		lit1 => [undef, 'одним', 'двумя', 'тремя', 'четырьмя', 'пятью', 'шестью', 'семью', 'восемью', 'девятью', 'десятью', 'одиннадцатью', 'двенадцатью', 'тринадцатью', 'четырнадцатью', 'пятнадцатью', 'шестнадцатью', 'семнадцатью', 'восемнадцатью', 'девятнадцатью'],
	},
	'tf' => {
		lit1 => [undef, 'одной', 'двумя', 'тремя', 'четырьмя', 'пятью', 'шестью', 'семью', 'восемью', 'девятью', 'десятью', 'одиннадцатью', 'двенадцатью', 'тринадцатью', 'четырнадцатью', 'пятнадцатью', 'шестнадцатью', 'семнадцатью', 'восемнадцатью', 'девятнадцатью'],
	},
	'pm' => {
		lit1 => [undef, 'одном', 'двух', 'трёх', 'четырёх', 'пяти', 'шести', 'семи', 'восьми', 'девяти', 'десяти', 'одиннадцати', 'двенадцати', 'тринадцати', 'четырнадцати', 'пятнадцати', 'шестнадцати', 'семнадцати', 'восемнадцати', 'девятнадцати'],
	},
	'pf' => {
		lit1 => [undef, 'одной', 'двух', 'трёх', 'четырёх', 'пяти', 'шести', 'семи', 'восьми', 'девяти', 'десяти', 'одиннадцати', 'двенадцати', 'тринадцати', 'четырнадцати', 'пятнадцати', 'шестнадцати', 'семнадцати', 'восемнадцати', 'девятнадцати'],
	},
);

sub litnum
{
	my $n = int(shift);
	my $p = shift || 'i';
	my $g = shift || 'm';
	return 'минус ' . litnum(abs($n), $p) if $n < 0;
	$lit{$p} or die "Invalid parameter: p=$p";
	$lit{"$p$g"} or die "Invalid parameter: g=$g";
	my $res = ;
	if ($n >= 1000000000) {
		$res .= ' ' if $res;
		my $int = int($n / 1000000000);
		$res .= litnum($int, $p, 'm') . ' ' . $lit{($int % 1000) ? $p : 'r'}->{bln}->[numdecl($int)];
		$n -= $int * 1000000000;
	}
	if ($n >= 1000000) {
		$res .= ' ' if $res;
		my $int = int($n / 1000000);
		$res .= litnum($int, $p, 'm') . ' ' . $lit{($int % 1000) ? $p : 'r'}->{mln}->[numdecl($int)];
		$n -= $int * 1000000;
	}
	if ($n >= 1000) {
		$res .= ' ' if $res;
		my $int = int($n / 1000);
		$res .= litnum($int, $p, 'f') . ' ' . $lit{$p}->{ths}->[numdecl($int)];
		$n -= $int * 1000;
	}
	if ($n >= 100) {
		$res .= ' ' if $res;
		my $int = int($n / 100);
		$res .= $lit{$p}->{lit100}->[$int];
		$n -= $int * 100;
	}
	if ($n >= 20) {
		$res .= ' ' if $res;
		my $int = int($n / 10);
		$res .= $lit{$p}->{lit10}->[$int];
		$n -= $int * 10;
	}
	if ($n > 0) {
		$res .= ' ' if $res;
		$res .= $lit{"$p$g"}->{lit1}->[$n];
	}
	return $res || $lit{$p}->{zero};
}

sub numdecl
{
$_[0] = abs($_[0]);
	return
		(($_[0] % 100) >= 10 && ($_[0] % 100) <= 20) ? 2 :
		(($_[0] % 10) >= 2 && ($_[0] % 10) <= 4) ? 1 :
		(($_[0] % 10) == 1) ? 0 :
		2;
}

sub rus_int_word{
	($num,$pad,$gen,$one,$two,$three) = @_;
	return litnum($num, $pad, $gen) . [" $one "," $two "," $three "]->[numdecl($num)];
}


$url = "http://api.worldweatheronline.com/free/v1/weather.ashx?q=$shir%2C$dolg&format=csv&show_comments=no&extra=localObsTime&key=$api_key";
$Hour = localtime->strftime('%-H');
$day = localtime->strftime('%-d');
$month = localtime->strftime('%-m');
$year = litnum(localtime->strftime('%Y'),'i','m');
$minute = localtime->strftime('%-M');

if(($Hour >= 6) && ($Hour < 10)){$grettings = "Доброе утро!"}
if(($Hour >= 10) && ($Hour < 17)){$grettings = "Добрый день!"}
if(($Hour >= 17) && ($Hour < 23)){$grettings = "Добрый вечер!"}
if(($Hour < 6) || ($Hour >= 23)){$grettings = "Доброй ночи!"}

@days = ("Дни месяца","первое","второе","третье","четвёртое","пятое","шестое","седьмое","восьмое","девятое","десятое","одинадцатое","двенадцатое","тринадцатое","четырнадцатое","пятнадцатое","шестнадцатое","семнадцатое","восемнадцатое","девятнадцатое","двадцатое","двадцать первое","двадцать второе","двадцать третье","двадцать четвёртое","двадцать пятое","двадцать шестое","двадцать седьмое","двадцать восьмое","двадцать девятое","тридцатое","тридцать первое");
@months = ("Месяца","января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря");
%windTo = ("N" => "Северный","NNE" => "Северо Северо Восточный","NE" => "Северо Восточный","ENE" => "востоко северо восточный","E" => "восточный","ESE" => "востоко юго восточный","SE" => "юго восточный","SSE" => "юго юго восточный","S" => "южный","SSW" => "юго юго западный","SW" => "юго западный","WSW" => "западо юго западный","W" => "западный","WNW" => "западо северо западный","NW" => "северо западный","NNW" => "северо северо западный");
# Тут весьма плохой перевод погод согласно кода возвращаемого апи / возможно в будущем будет доработано, а пока поржём.
%rusDesk = ("395" => "Умеренный или сильный снегопад в районе с громом","392" => "Пятнистый свет снега в районе с громом","389" => "Умеренный или сильный дождь с громом курения","386" => "Пятнистый небольшой дождь с громом курения","377" => "Умеренные или сильные ливни частиц льда","374" => "Свет души гранулы льда","371" => "Умеренный или сильный снег","368" => "Свет снег","365" => "Умеренный или сильный мокрый снег душевые","362" => "Местами небольшой мокрый снег душевые","359" => "Проливной дождь Душ","356" => "Умеренный или сильный дождь Душ","353" => "Небольшой дождь Душ","350" => "Гроза","338" => "Сильный снег","335" => "Пятнистый снег","332" => "Умеренный снег","329" => "Пятнистый умеренный снег","326" => "Легкий снег","323" => "Пятнистый свет снег","320" => "Умеренный или сильный мокрый снег","317" => "Небольшой мокрый снег","314" => "Умеренный или сильный дождь","311" => "Свет ледяной дождь","308" => "Проливной дождь","305" => "Сильный дождь порой","302" => "Умеренный дождь","299" => "Умеренный дождь порой","296" => "Небольшой дождь","293" => "Пятнистый небольшой дождь","284" => "Тяжелые замораживания полить","281" => "Замораживание полить","266" => "Морось","263" => "Пятнистый морось","260" => "Холодный туман","248" => "Туман","230" => "Метель","227" => "Метель","200" => "Грозовой вспышек в ближайших","185" => "Пятнистый замораживания полить поблизости","182" => "Пятнистый мокрый снег рядом","179" => "Пятнистый снег неподалеку","176" => "Пятнистый среди дождя","143" => "Туман","122" => "Сплошная облачность","119" => "Облачно","116" => "Небольшая Облачность","113" => "Ясно");

$contents = get $url or die;
@strings = split "\n" , $contents;
@params = split "," , $strings[0];
@dayparams = split "," , $strings[1];
($utctime, $temp, $wheaterCode, $image, $desk, $windSpeedmh, $windSpeedkmph, $winddigr, $winddir16, $precip, $humidity, $visibility, $pressure, $clousure) = @params;
($utcdate, $maxTempC, $maxTempF, $minTempC, $minTempF, $windSpeedmhDay, $windSpeedkmphDay, $winddigrDay, $winddigr16, $wheaterCodeDay, $imageDay, $wheaterDeskDay, $precipDay) = @dayparams;
$windSpeed = $windSpeedkmph*1000/3600;
$windSpeedDay = $windSpeedkmphDay*1000/3600;
$hours = rus_int_word($Hour,'i','m',"час","часа","часов");
$minutes = rus_int_word($minute,'i','m',"минута","минуты","минут");
$metres = rus_int_word(ceil($windSpeed),'i','m',"метр","метра","метров");
$vypalo = [" выпал "," выпало "," выпало "]->[numdecl(ceil($precip))];
$milimetres = rus_int_word(ceil($precip),'i','m',"милиметр","милиметра","милиметров");
$graduses = rus_int_word(ceil($temp),'i','m',"градус","градуса","градусов");
$procents = rus_int_word(ceil($humidity),'i','m',"процент","процента","процентов");
$metresDay = rus_int_word(ceil($windSpeedDay),'r','m',"метров","метров","метров");
$milimetresDay = rus_int_word(ceil($precipDay),'i','m',"милиметр","милиметра","милиметров");
$gradusesDayMin = rus_int_word(ceil($minTempC),'r','m',"градусов","градусов","градусов");
$gradusesDayMax = rus_int_word(ceil($maxTempC),'r','m',"градусов","градусов","градусов");
$procentsDay = rus_int_word(ceil($humidityDay),'i','m',"процент","процента","процентов");

if($minTempC < 0){
	$fromto = "от $gradusesDayMax до $gradusesDayMin";
}else{
	$fromto = "от $gradusesDayMin до $gradusesDayMax";
}

### You generated message here.
print "
$grettings Сегодня $days[$day] $months[$month] год $year 
 cейчас $hours $minutes 
 За бортом $graduses по цельсию $rusDesk{$wheaterCode} 
 Ветер $windTo{$winddir16} $metres в секунду 
 Влажность  $procents На данный момент $vypalo $milimetres осадков
 В течении дня ожидается температура $fromto по цельсию $rusDesk{$wheaterCodeDay}
 Ветер $windTo{$winddigr16} до $metresDay в секунду Ожидаемое количество осадков $milimetresDay \n";