XML сервисы, часть вторая
Недавно мы рассматривали создание приложения на платформе App Engine, которое позволяет IP телефонам Cisco получать информацию о прогнозе погоды, новостях и курсе валют.
К сожалению, пользователи, которые же в первые дни появились у этого сервиса, обнаружили в нем два существенных недостатка:
- Мы отображаем только погоду только по Москве, хотя сервис у нас глобальный
- Существуют проблемы на разных версиях прошивок для телефонов, которые ожидают данные на русском языке в кодировке cp-1251.
Первый недочет решено было исправить, добавив опциональный параметр city к URL сервиса, равный коду города на сайте ГисМетео, на котором мы берем данные погоды. Таким образом строка для телефонов Питера выглядит как http://xmlphones.appspot.com/?city=26063. Второй аналогично будет называться encode и содержать при необходимости задание кодировки windows-1251.
Перекодировка в Python делается элегантно. Допустим, у нас через URLFetch получены данные в UTF-8, а вывод требуется в windows-1251:
resp = unicode(result.content, 'utf-8') resp = resp.encode("cp1251")
Ну и наоборот:
resp = unicode(result.content, 'cp1251')
В первом случае мы из байтового массива конструируем объект unicode, который затем кодируем с указанием кодека cp1251. Во втором случае одним действием из байтового массива конструируем также объект unicode с заданием кодека cp1251, но далее просто отдаем его в таком виде платформе App Engine (подразумевается что весь вывод по умолчанию в кодировке UTF-8).
Все кажется замечательным, но тут опять обнаруживается криворукость писателей прошивок некоторых моделей IP телефонов, заключающаяся в том, что телефоны сами добавляют к параметрам запросов дополнительные переменные вида ?locale=English_United_States&name=SEP001B541415EE, что приводит к проблемам обработке меню сами же телефонами.
Проведенный небольшой эксперимент показал, что если, например, параметры задавать с помощью многоуровневых URL, то все работает отлично. Допустим, кодировку windows-1251 мы будем задавать как http://xmlphones.appspot.com/windows-1251/ (подразумевая, по умолчанию корневой адрес аналогичный http://xmlphones.appspot.com/utf-8/), а город как http://xmlphones.appspot.com/utf-8/city/26063 (и опять это Питер).
Выкидываем из кода несколько обработчиков, привязанных к URL, и заменяем их одним Dispatcher, который будет решать куда направить тот или иной запрос. Так как нашим сервисом уже пользуются люди, старые параметры ?encode и ?city также оставляем для совместимости, но делаем их второстепенными по сравнению со схемой параметров, заданных через URL.
Вот что получилось в итоге:
# coding=UTF-8 # -*- coding: utf-8 -*- import wsgiref.handlers from google.appengine.ext import webapp from google.appengine.api import urlfetch import os from google.appengine.ext.webapp import template from xml.dom import minidom import re import time WEATHER_URL = 'http://informer.gismeteo.ru/rss/%s.xml' def get_city(self): city = self.request.get("city") if city == '': # Москва city = '27612' ccode = re.search('/.+/meteo/(.+)', self.request.path) if ccode != None: return ccode.group(1) else: return city def get_encode_templ(self): encode = self.request.get("encode") if encode == 'windows-1251': return encode else: return 'utf-8' def render_template(self, templ, template_values={}, encode='utf-8'): path = os.path.join(os.path.dirname(__file__), 'templates/'+templ) resp = template.render(path, template_values) if encode == 'windows-1251': # Перекодируем из UTF-8 в CP1251 resp = unicode(resp, 'utf-8') resp = resp.encode("cp1251") self.response.out.write(resp) def is_ipphone(self): if re.match('Allegro', self.request.headers.get('User-Agent', '')) == None: return False else: return True def parse_rss_data(content): result = '' dom = minidom.parseString(content) for item in dom.getElementsByTagName('item'): result = result + item.getElementsByTagName('title')[0].firstChild.nodeValue + '\n' + item.getElementsByTagName('description')[0].firstChild.nodeValue + '\n\n' return result def rss_to_text(self, url): result = urlfetch.fetch(url) if result.status_code == 200: resp = parse_rss_data(result.content) charset = get_uri_encode(self) if charset == 'windows-1251': # Перекодируем из UTF-8 в CP1251 resp = resp.encode("cp1251") self.response.headers['Content-type']='text/plain; charset='+charset self.response.out.write(resp) else: self.redirect('/error.xml') def cbrf(self): charset = get_uri_encode(self) self.response.headers['Content-type']='text/plain; charset='+charset result = urlfetch.fetch('http://www.cbr.ru/currency_base/D_print.asp?date_req='+time.strftime('%d.%m.%Y', time.localtime())) if result.status_code == 200: content = unicode(result.content, 'windows-1251') data=re.split('<td align="right" >036</td>', content)[1] for block in re.split('<tr bgcolor="#ffffff">', data): block = re.sub('\r\n', '', block) cells = re.search('<td align="left" > (.*?)</td><td align="right" >(.*?)</td><td> (.*?)</td><td align="right">(.*?)</td>', block) if cells != None: resp = cells.group(2)+' '+cells.group(3)+' = '+cells.group(4)+'\n' if charset == 'windows-1251': # Перекодируем из UTF-8 в CP1251 resp = resp.encode("cp1251") self.response.out.write(resp) else: self.redirect('/error.xml') def error(self): self.response.headers['Content-type]']='text/plain' self.response.out.write('Произошла ошибка при работе сервиса') def gismeteo(self): city = get_city(self) rss_to_text(self, WEATHER_URL % city) def yandex_news(self): rss_to_text(self, 'http://news.yandex.ru/Russia/index.rss') def get_uri_encode(self, default = 'utf-8'): urcode = re.search('/windows-1251/.*', self.request.url) if (urcode != None): return 'windows-1251' else: return default def main_page(self): if is_ipphone(self): citytmpl = get_city(self) encode = get_encode_templ(self) urlcity = re.search('/(.+)/city/(.+)', self.request.path) if (urlcity != None): citytmpl = urlcity.group(2) encode = get_uri_encode(self, encode) self.response.headers['Content-type']='text/xml; charset='+encode render_template(self, 'main.xml', { 'city': citytmpl, 'encode': encode }, encode=encode) else: self.response.headers['Content-type']='text/html; charset=utf-8' render_template(self, 'main.html') class Dispatcher(webapp.RequestHandler): def get(self): # Анализируем только URI до знака ? url = self.request.url.split('?')[0] if re.search('/$', url): main_page(self) return if re.search('/.+/city/.+', url): main_page(self) return if re.search('/utf-8/$', url): main_page(self) return if re.search('/windows-1251/$', url): main_page(self) return if re.search('/(.+)/yandex_news.xml', url): yandex_news(self) return if re.search('/(.+)/meteo.+', url): gismeteo(self) return if re.search('/(.+)/cbrf.xml', url): cbrf(self) return error(self) def main(): application = webapp.WSGIApplication( [('/.*', Dispatcher) ], debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == '__main__': main()
Какие еще будут предложения по добавлению новых сервисов? Напишите комментарий к этой статье.
[...] Перейти к второй части этой темы. [...]
Скажите, возможен ли сервис статистики звонков из личного кабинета sipnet.ru?
Возможно все
Напишите кратко откуда нужно получать информацию и в каком виде выдавать.
У вас как я понимаю, CCM подключен к sipnet.ru?
к sipnet.ru подключен Cisco 7941G.
Информацию нужно получить из https://customer.sipnet.ru/cabinet/do_calls (требуется авторизация). Выдать:
1 29/08/08 12:57 тел:90216ххххххх Turkey Istanbul время зв.1:47
Собственно все.
Спасибо все работает. А как можно добавить в меню прогноза погоды несколько городов? Есть вообще такая возможность?
Подскажит как можно добавить в список прогноза погоды несколько городов? Это возможно?
Как насчет для каждой группы телефонов иметь собственный Services URL?
Можно конечно, но тогда будет прогноз только для того города, который я укажу в собственной ссылке, если правильно понимаю. А хотелось бы под рукой иметь прогноз по 3-м городам. Специфика такая что сотрудники курсируют постоянно между этими направлениями.
Можно ли добавить возможность задавать сайт с курсами валют? мы не из России….
Какие именно нужны?
Вот отсюда – http://www.nationalbank.kz/?docid=460&uid=9DD8DEC5-802C-E8F0-E08CEC6C341E9A4F
Это то, что требуется?
Валютаның ресми (нарықтық) бағамы 26-09-2008
USD / KZT TOD 119.78 0.04
EUR / KZT TOD 176.39 0.67
RUB / KZT TOD 4.8 0.02
да, только на русском
Добрый день!
Подскажите, пожалуйста, как победить ошибку HTTP [403]??
Железо: UC 500, CUCME 4.2, CP-7940G, Application load P00308000500.
Какой указан URL сервиса в настройках телефона?
http://66.249.93.118/windows-1251/city/26063/
http://66.249.93.118/
Результат одинаковый…
Во-первых, почему хост задан через IP?
Во-вторых, какие DNS серверы указываются в настройках телефона?
В качестве DNS-сервера указан соответствующий сервер в локалке. IP-телефон показывает его IP-шник. ПК, который подключен через телефон, показывает тот же IP-шник DNS-сервера. С ПК я спокойно пингую «xmlphones.appspot.com», а если в качестве «url service» пропишу «http://xmlphones.appspot.com», то получу ошибку «Host not found»…
Попробовали работу сервиса с CUCME 7.0, в блоке telephony-service задан параметр:
url services http://xmlphones.appspot.com/
Телефон 7941G имеет доступ в Интернет через NAT.
К сожалению, при нажатии на кнопку Services выскакивает сначала вспомогательное меню, состоящее с трех пунктов:
1) CME Service URL
2) Extension Mobility
3) My Phone Apps
А при выборе первого, собственно отображаются сами XML сервисы. Видимо, такова логика работы CUME.
На 7945, что в utf, что в windows, всегда вместо букв знаки ???.
А на 7911 на том же CME все нормально.
то же самое с 7965 (собссно, прошивки-то у них одинаковые с 7945)
интересно другое: заголовки выдаются нормально: Новости Яндекса, Прогноз погоды, Курсы валют, а вот при заходе в подменю – вопросики вместо всех русских символов
Всё работает под ССМ 6.1, русская локаль стоит. Но на курсе валют ошибка 500
Подтверждаю проблему с 7941 – символы «?» вместо букв, в обоих кодировках. Локаль телефона win-1251.
to allarm.
а какая версия локали\прошивки стоит на 7941 ?
у меня
локаль po-locale-ru_RU-8.3.3.1000-1.cop.sgn
прошивка SCCP41.8-3-4SR1S
с этими всё работает, правда…курс валют выдаёт ошибку
По поводу дополнительных сервисов….
Учитывая популярность некоторых сайтов, осталось добавить «Яндекс пробки» и котировки акций с с сайта Финам (www.finam.ru) . ))))
У меня работа телефонов построена через сквид. В настройках указываешь ИП адрес проксей и всё работает. Сами прокси так-же работают через NAT.
У меня все работает на ура. Подскажите, есть ли возможность выводить информацию и меню на английском. И как можно загрузить новости и курс валют другой страны.
С середины июня где-то курсы валют не работают, видимо какие-то изменения на сервере цб рф произошли на самих страницах, откуда данные забираются…
К сожалению, этот досадный глюк только что был исправен
а на английском это получить никак нельзя?
подскажите я указываю код города Тюмень по gismeteo
url services http://xmlphones.appspot.com/windows-1251/city/4501
так как код четырехзначный, телефон выплевывает traceback , как исправить?
пробовал url services http://xmlphones.appspot.com/windows-1251/city/04501
не помогает, подскажите пожалуйста
что покчавает телефон для себя чтобы пользоваться вашими сервисами, какую xml? что с курсом валют(только австралийский доллар)?
[...] статей, но уже устаревшие, и не работают. p.s. это вот они http://techwork.ru/2008/08/28/xml-services-part-2/ и http://techwork.ru/2008/08/09/xml-services-for-ip-phones/Заранее [...]