...

суббота, 7 декабря 2013 г.

Flightstats API: Пишем свое табло прилетов с Боингами и Аэробусами

Введение




Всем читающим этот пост — здравствуйте.

Авиация — мое хобби, я об этом уже писал. Я готов часами стоять и фотографировать самолеты, смотреть видео о них, читать блоги летчиков, следить за трафиком на fr24.com. А еще мне нравится то, что в авиации кругом одни сокращения: ECAM, CDA, ACESS, APU и так далее. Вообщем, магия. А вот почти все люди из моего окружения к авиации равнодушны: «Ну самолет, как самолет. Большой, да. Что? Boeing 777-300ER? Ну ясно, ясно...». Но ничего не поделаешь, на вкус и цвет все фломастеры разные.

Как ко мне пришла идея поста? Так получилось, что я живу в 20 минутах езды от аэропорта Шереметьево. Как-то у меня выдался свободный час и приехал немного пофотографировать. За то время, пока я там был, мимо меня пролетело около 10 самолетов. Почти все — Аэрофлот. Я не спорю, у Аэрофлота есть интересные борты. Например, Добролет или Хохлома. Но в тот день мне не повезло, ничего подобного я не увидел. И тогда я подумал, что было бы очень полезно планировать подобные выезды. Вот так вот и родилась идея поста. Мне хотелось иметь следующий функционал: таблица вылетов — прилетов для выбранного аэропорта, выделение цветом как интересных, так и не очень ботов, экспорт в pdf.

Начинаем!


Итак, первым делом необходимо зайти на https://developer.flightstats.com, зарегистрироваться, перейти в Dashboard и нажать на кнопку «Create a new application». Это необходимо для получения связки AppId + AppKey, без которой доступ к API невозможен. Вообще, оно платное, но присутствует и бесплатный тарифный план — "Evaluation Plan", его возможностей для наших нужд хватит сполна. После этого смело идем "Get Started" -> "Flex API Reference" -> "Flight Status & Track API" -> "Flight Status & Track by Airport". В нижней части страницы есть раздел "Interactive Documentation", выбираем "Airport status (departures)". В данном запросе есть 7 обязательных полей, которые необходимо заполнить следующим образом:




















appIdappKeyairportyearmonthdayhourOfDay
Ваш appIdВаш appKeySVO201312710

Через пару секунд появится ответ.



Иными словами мы попросили выдать нам информацию о рейсах, которые вылетят 7 декабря 2014 года после 10 часов из аэропорта Шереметьево. Да, SVO — Шереметьево. А еще UUEE — тоже Шереметьево. Помните, чуть выше я говорил о сокращениях? Вот, мы наткнулись на первое.


Коды аэропортов. IATA vs. ICAO




IATA — Международная ассоциация воздушного транспорта, ИАТА (англ. International Air Transport Association) международная неправительственная организация. Ассоциация выступает координатором и представителем интересов авиатранспортной отрасли в таких областях как обеспечение безопасности полетов, производство полетов, тарифная политика, техобслуживание, авиационная безопасность, разработка международных стандартов совместно с ИКАО и т. д.

ICAO — Международная организация гражданской авиации (International Civil Aviation Organization) — специализированное учреждение ООН, устанавливающее международные нормы гражданской авиации и координирующее её развитие с целью повышения безопасности и эффективности.


И у ИАТА и у ИКАО есть свои коды аэропортов. Они различны, поскольку коды ИАТА выбираются созвучными с названием аэропорта, а код ИКАО основан на том, где находится аэропорт. Именно поэтому у Шереметево код ИАТА SVO, а ИКАО — UUEE, для Пулково, например, LED и ULLI соответственно. Исключение составляют лишь аэропорты США (добавляется «K» к коду ИАТА: Лос-Анджелес — LAX — KLAX) и Канады (добавляется «С»: Торонто — YYZ — CYYZ).


Ответ flightstats




При данном запросе ответ имеет следующую структуру:

{
посланный запрос
}
"appendix":
{
"airlines": {...}
"airports": {...}
"equipments": {...}
"flightStatuses": {...}
}




Секции airlines, airports и equipments содержат в себе описание авиакомпаний, аэропортов и типов самолетов, которые присутствуют в секции flightStatuses.

Секция «airlines» предельно проста:

"airlines": [
{
"fs": "SU",
"iata": "SU",
"icao": "AFL",
"name": "Aeroflot",
"active": true
},
...




Поле «fs» содержит в себе код авиакомпании в базе flightStats. Почти всегда он совпадает с кодом IATA.

Секция «airports» посложнее:



"airports": [
{
"fs": "BUD",
"iata": "BUD",
"icao": "LHBP",
"name": "Liszt Ferenc International Airport",
"city": "Budapest",
"cityCode": "BUD",
"countryCode": "HU",
"countryName": "Hungary",
"regionName": "Europe",
"timeZoneRegionName": "Europe/Budapest",
"localTime": "2013-12-06T20:51:56.974",
"utcOffsetHours": 1,
"latitude": 47.433037,
"longitude": 19.261621,
"elevationFeet": 495,
"classification": 2,
"active": true,
"delayIndexUrl": "https://api.flightstats.com/flex/delayindex/rest/v1/json/airports/BUD?codeType=fs",
"weatherUrl": "https://api.flightstats.com/flex/weather/rest/v1/json/all/BUD?codeType=fs"
},
...




Здесь содержится вся необходимая информация, кроме погоды и коэффициента задержки, которые надо запрашивать отдельно.

Секция «equipments».



"equipments": [
{
"iata": "319",
"name": "Airbus Industrie A319",
"turboProp": false,
"jet": true,
"widebody": false,
"regional": false
},
...




Описывает базовые характеристики самолета.

Отвлечемся вновь от API.


Учимся различать типы самолетов




Это совсем не сложно. Я подготовил небольшую схему, которая поможет легко сориентироваться в мире летающих машин.



И теперь подтвержение моих слов:

Airbus A380 vs. Boeing 747


Airbus A340


Boeing 737 vs. Airbus A320


Boeing 757 vs. Boeing 767


Airbus A330 vs. Boeing 777


McDonnel Douglas MD-11


Разбираем flightStatus




Срдержимое flightStatus. Длинное, поэтому скрыто


{
"flightId": 317846653,
"carrierFsCode": "SU",
"flightNumber": "2030",
"departureAirportFsCode": "SVO",
"arrivalAirportFsCode": "BUD",
"departureDate": {
"dateLocal": "2013-12-07T10:50:00.000",
"dateUtc": "2013-12-07T06:50:00.000Z"
},
"arrivalDate": {
"dateLocal": "2013-12-07T10:35:00.000",
"dateUtc": "2013-12-07T09:35:00.000Z"
},
"status": "L",
"schedule": {
"flightType": "J",
"serviceClasses": "RJY",
"restrictions": ""
},
"operationalTimes": {
"publishedDeparture": {
"dateLocal": "2013-12-07T10:50:00.000",
"dateUtc": "2013-12-07T06:50:00.000Z"
},
"publishedArrival": {
"dateLocal": "2013-12-07T10:35:00.000",
"dateUtc": "2013-12-07T09:35:00.000Z"
},
"scheduledGateDeparture": {
"dateLocal": "2013-12-07T10:50:00.000",
"dateUtc": "2013-12-07T06:50:00.000Z"
},
"estimatedGateDeparture": {
"dateLocal": "2013-12-07T10:50:00.000",
"dateUtc": "2013-12-07T06:50:00.000Z"
},
"actualGateDeparture": {
"dateLocal": "2013-12-07T11:27:00.000",
"dateUtc": "2013-12-07T07:27:00.000Z"
},
"scheduledGateArrival": {
"dateLocal": "2013-12-07T10:35:00.000",
"dateUtc": "2013-12-07T09:35:00.000Z"
},
"estimatedGateArrival": {
"dateLocal": "2013-12-07T11:12:00.000",
"dateUtc": "2013-12-07T10:12:00.000Z"
},
"actualGateArrival": {
"dateLocal": "2013-12-07T10:43:00.000",
"dateUtc": "2013-12-07T09:43:00.000Z"
}
},
"delays": {
"departureGateDelayMinutes": 37,
"arrivalGateDelayMinutes": 8
},
"flightDurations": {
"scheduledBlockMinutes": 165,
"blockMinutes": 136
},
"airportResources": {
"departureTerminal": "D",
"departureGate": "28",
"arrivalTerminal": "2"
},
"flightEquipment": {
"scheduledEquipmentIataCode": "320",
"actualEquipmentIataCode": "A320",
"tailNumber": "VP-BWI"
}
},
...







Назначение полей в большинстве случаев очевидно. Я подробно расскажу лишь о тех, содержимое которых не совсем очевидно. А почему? Правильно, потому что сокращения.

Вот эта часть ответа:

"status": "L",
"schedule": {
"flightType": "J",
"serviceClasses": "RJY",
"restrictions": ""
},

























ПолеОписание
statusТекущий статус рейса

A — Active

C — Canceled

D — Diverted — Была произведена смена пункта назначения (например, по метео-условиям)

DN — Data source needed — Неоткуда получить информацию о статусе

L — Landed

NO — Not Operational

R — Redirected

S — Scheduled

U — Unknown
flightTypeТип рейса. Всего их существует 23 штуки. Например,

J — Scheduled Passanger — Пассажириский по расписанию

M — Scheduled Cargo/Mail(MailOnly) — Грузовой, но только с письмами.

W — Military — Военный
serviceClassesВарианты сервиса, предусмотренные на рейсе по классификации IATA. Подробнее тут — http://en.wikipedia.org/wiki/IATA_class_codes
restrictionsОграничения по классификации IATA. Подробнее — http://www.flyerguide.com/wiki/index.php/Traffic_Restriction_Codes_(AA)

Программирование




На данный момент я использую python 2.7, urllib2 и simplejson.

Первое, что нужно сделать — подключить необходимые библиотеки и проинициализировать переменные.



import urllib2
import simplejson

appId = "Ваш appId тут"
appKey = "Ваш appKey тут"

# Название аэропорта. Может быть запрошен как по внутреннему коду flightstats, так и по кодам ICAO или IATA
requestedAirport = "SVO"

# Какие рейсы нам нужны. arr - прибывающие, dep - отбывающие
flightsType = "arr"

# Дата
requestedDate = "2013/12/7"

# Время, с которого мы хотим получить список рейсов
requestedHour = "15"

# Количество часов, за которые будет составлен список
requestedNumHours = "6"


Следующий шаг — упаковываем эти переменные в url, отправляем запрос и ждем ответа.



# Заготовка для запроса
url = "https://api.flightstats.com/flex/flightstatus/rest/v2/json/" \
"airport/status/%s/%s/%s/%s?appId=%s&appKey=%s&utc=false&numHours=%s"

# Подставляем нужные значения в запрос
url = url %(requestedAirport, flightsType, requestedDate, requestedHour, appId, appKey, requestedNumHours)

# Шлем запрос и получаем JSON-ответ
req = urllib2.Request(url, None)
opener = urllib2.build_opener()
f = opener.open(req)
response = simplejson.load(f)


Затем парсим вспомогательные поля. Они нам нужны для того, чтобы подставлять развернутые названия самолетов и аэропотов в список.



# Сохраняем ветку с аэропортами
airports = response["appendix"]["airports"]

# Данные по аэропортам будут храниться в словаре (dictionary)
airportsDict = dict()

# Для каждого аэропорта записываем пару [код flightstats]:[название]
for airport in airports:
airportsDict[airport["fs"]] = airport["name"]

# Аналогично поступаем для типов бортов...
equipments = response["appendix"]["equipments"]
equipmentsDict = dict()
for equipment in equipments:
equipmentsDict[equipment["iata"]] = equipment["name"], equipment["iata"]

#... и для авиакомпаний
airlines = response["appendix"]["airlines"]
airlinesDict = dict()
for airline in airlines:
airlinesDict[airline["fs"]] = airline["name"]


Результатом работы нашего кода должна быть вот такая таблица:



























FlightCarrierEquipmentRegistrationFromSTD ATD ToSTA STD
XQ114SunExpressBoeing 737-800 PassengerD-ASXAAntalya15:00:00.000---CGN17:55:00.000---

Выводить данные будем в HTML.



# Заготовка для страницы
webPage = "<html><body><table border=\"1\"> \
<tr><th>Flight</th><th>Carrier</th><th>Equipment</th><th>Registration</th><th>From</th><th>STD</th> \
<th>ATD</th><th>To</th><th>STA</th><th>ETA</th></tr>"

# Заготовка для строки таблицы
templateRow = "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td> \
<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>"

f = open("./list.html", "w")


Далее необходимо написать вспомогательную функцию.

Нужные нам значения лежат на разной глубине. Например, carrierFsCode, код аэропорта, на нулевой глубине. А чтобы добыть время фактическое время вылета, нужно опуститься на вторую глубину: operationalTimes --> actualGateDeparture --> dateLocal". Для этого нужна первая вспомогательная функция.



def getProperty(status, propertyNames):
# Cохраняем все содержимое status
property = status

# Пытаемся найти нужный ключ
try:
# Перебираем каждый ключ из propertyNames
for propertyName in propertyNames:
# Отсекаем ненужное
property = property[propertyName]

# Нужный ключ найден!
return property
except
# А если нет, то возвращаем заглушку
return "---"


Теперь самое интересное: необходимо выбрать то, что вас наиболее интересует в трафике. Это содержится в трех массивах:



interestingCarriers = ["RU", # AirBridgeCargo
"CU", # Cubana de Aviacion
"ME", # China Eastern Airlines
"KE", # Korean Air Lines
]
interestingEquipments = ["SU9"] # Sukhoi Superjet 100

interestingTailNumbers = ["VP-BGB"] # Номер первого Boeing 777-300ER для Аэрофлота


А теперь, собственно, парсер:



for flightStatus in flightStatuses:
newRow = templateRow %(getProperty(flightStatus, ["carrierFsCode"]) + getProperty(flightStatus, ["flightNumber"]),
airlinesDict[getProperty(flightStatus, ["carrierFsCode"])],
getProperty(flightStatus, ["flightEquipment", "scheduledEquipmentIataCode"]),
getProperty(flightStatus, ["flightEquipment", "tailNumber"]),
str(airportsDict[getProperty(flightStatus, ["departureAirportFsCode"])]).replace("Airport", ""),
str(getProperty(flightStatus, ["departureDate", "dateLocal"])).split("T")[-1],
str(getProperty(flightStatus, ["operationalTimes", "actualGateDeparture", "dateLocal"])).split("T")[-1],
str(airportsDict[getProperty(flightStatus, ["arrivalAirportFsCode"])]).replace("Airport", "")
str(getProperty(flightStatus, ["arrivalDate", "dateLocal"])).split("T")[-1],
str(getProperty(flightStatus, ["operationalTimes", "estimatedGateArrival", "dateLocal"])).split("T")[-1])

# Подсвечиваем необходимую строку
if (getProperty(flightStatus, ["carrierFsCode"]) in interestingCarriers) or \
(getProperty(flightStatus, ["flightEquipment", "scheduledEquipmentIataCode"]) in interestingEquipments) or \
(getProperty(flightStatus, ["flightEquipment", "tailNumber"]) in interestingTailNumbers):
newRow = newRow.replace("<tr>", "<tr bgcolor=\"#FF0000\">")

# Добавляем ее к странице
webPage += newRow


Завершающий штрих — дописываем теги в конец страницы и закрываем файл.



webPage = webPage + "</table></body></html>"
f.write(webPage)
f.close()




Результат работы































































































































































































































































































































































































































































































































































































































































































































































































































































































































































FlightCarrierEquipmentRegistrationFromSTDATDToSTAETA
SU155Aeroflot332VQ-BBECancun International12:30:00.00013:17:00.000Sheremetyevo International10:30:00.00011:03:00.000
DL466Delta Air Lines76W---John F. Kennedy International16:15:00.00016:14:00.000Sheremetyevo International10:50:00.00010:12:00.000
SU111Aeroflot332VP-BLXMiami International17:35:00.00018:35:00.000Sheremetyevo International13:45:00.00013:46:00.000
SU103Aeroflot333VP-BDEJohn F. Kennedy International19:05:00.000---Sheremetyevo International13:25:00.00013:34:00.000
UN576Transaero Airlines744EI-XLJPunta Cana International19:55:00.00021:18:00.000Sheremetyevo International14:50:00.00015:35:00.000
RU566AirBridgeCargo74Y---Frankfurt am Main04:45:00.000---Sheremetyevo International11:00:00.000---
RU498AirBridgeCargo74N---Shanghai Pudong International05:00:00.000---Sheremetyevo International10:45:00.000---
SU233Aeroflot332---Indira Gandhi International05:05:00.00005:26:00.000Sheremetyevo International10:10:00.00010:13:00.000
RU506AirBridgeCargo74N---Milano Malpensa05:30:00.000---Sheremetyevo International12:00:00.000---
SU1827Aeroflot320VQ-BAZSimferopol06:00:00.00006:25:00.000Sheremetyevo International10:15:00.00010:40:00.000
SU2437Aeroflot320VP-BLHDusseldorf International06:05:00.00006:27:00.000Sheremetyevo International12:25:00.00012:24:00.000
RU440AirBridgeCargo74NVP-BIMHong Kong International06:15:00.00006:15:00.000Sheremetyevo International12:25:00.000---
KE529Korean Air Lines74YHL7466Incheon International06:25:00.00007:07:00.000Sheremetyevo International10:40:00.000---
JU650Jat Airways733---Belgrad Nikola Tesla06:45:00.00006:45:00.000Sheremetyevo International12:35:00.00012:39:00.000
PS561UIA73NUR-GAPKiev/Kyiv — Borispol07:00:00.00007:00:00.000Sheremetyevo International10:35:00.00010:35:00.000
SU1009Aeroflot321VQ-BEDKaliningrad07:10:00.00007:36:00.000Sheremetyevo International10:00:00.00010:26:00.000
AF1644Air France319F-GRHLCharles de Gaulle07:15:00.00007:13:00.000Sheremetyevo International13:55:00.00013:52:00.000
SU1867Aeroflot320VP-BQPZvartnots International08:10:00.00008:21:00.000Sheremetyevo International11:00:00.00011:11:00.000
5N502Nordavia Regional Airlines735---Syktyvkar08:20:00.00008:27:00.000Sheremetyevo International10:15:00.00010:11:00.000
KC893Air Astana320P4-KBCAstana08:40:00.00008:36:00.000Sheremetyevo International10:20:00.00010:49:00.000
SU3Aeroflot321VP-BWOPulkovo08:55:00.00009:04:00.000Sheremetyevo International10:20:00.00010:29:00.000
SU1513Aeroflot319VP-BWASurgut09:00:00.00008:59:00.000Sheremetyevo International10:35:00.00010:34:00.000
SU1293Aeroflot320VQ-BIVKazan09:00:00.00009:27:00.000Sheremetyevo International10:30:00.00010:50:00.000
SU1229Aeroflot320VP-BDKNizhniy Novgorod09:05:00.00009:21:00.000Sheremetyevo International10:25:00.00010:41:00.000
SU1309Aeroflot319VP-BDOSamara09:15:00.00009:20:00.000Sheremetyevo International10:55:00.00011:00:00.000
AY153Finnair319OH-LVIHelsinki-Vantaa09:25:00.00009:29:00.000Sheremetyevo International13:05:00.00012:57:00.000
OK892CSA319---Vaclav Havel Prague09:30:00.00009:31:00.000Sheremetyevo International15:10:00.00015:05:00.000
SU2005Aeroflot320VP-BWIJ. Paul II International Krakow-Balice09:35:00.00009:56:00.000Sheremetyevo International14:40:00.00014:49:00.000
SU1121Aeroflot320VP-BTIAdler/Sochi09:50:00.00009:55:00.000Sheremetyevo International12:20:00.00012:25:00.000
SU2685Aeroflot320VQ-BCMSchoenefeld09:50:00.00010:44:00.000Sheremetyevo International15:25:00.00016:15:00.000
SU5Aeroflot320VQ-BAXPulkovo09:55:00.00010:20:00.000Sheremetyevo International11:15:00.00011:40:00.000
SU1839AeroflotSU9RA-89010Kharkov09:55:00.00010:10:00.000Sheremetyevo International13:30:00.00013:20:00.000
SU2321Aeroflot320VQ-BHLFranz Josef Strauss10:00:00.00010:16:00.000Sheremetyevo International16:00:00.00016:16:00.000
SU1001Aeroflot320VP-BLLKaliningrad10:05:00.00010:25:00.000Sheremetyevo International12:55:00.00013:15:00.000
R25807Orenair738---Barnaul10:10:00.00010:15:00.000Sheremetyevo International11:30:00.00011:35:00.000
SU1307Aeroflot320VP-BKXTolmachevo10:15:00.00010:19:00.000Sheremetyevo International11:25:00.00011:29:00.000
SU1701Aeroflot333VQ-BNSVladivostok International10:20:00.00010:24:00.000Sheremetyevo International12:25:00.00012:29:00.000
SU1805Aeroflot321VP-BOEKiev/Kyiv — Borispol10:20:00.00011:00:00.000Sheremetyevo International13:50:00.00014:30:00.000
SU2137Aeroflot321VQ-BHKIstanbul Ataturk10:20:00.00011:03:00.000Sheremetyevo International15:15:00.00015:26:00.000
SK734SAS320OY-KAPCopenhagen10:20:00.00010:46:00.000Sheremetyevo International15:45:00.00016:02:00.000
SU7Aeroflot320---Pulkovo10:25:00.00010:43:00.000Sheremetyevo International11:45:00.00012:03:00.000
SU1813Aeroflot320VP-BRXDonetsk10:30:00.00010:31:00.000Sheremetyevo International14:25:00.00014:26:00.000
SU1831Aeroflot320---Minsk International 210:50:00.00011:40:00.000Sheremetyevo International13:15:00.00014:05:00.000
SU2107Aeroflot320VP-BZSTallinn10:50:00.00010:54:00.000Sheremetyevo International14:30:00.00014:18:00.000
SU1479Aeroflot319VP-BDMAbakan10:55:00.00010:55:00.000Sheremetyevo International11:55:00.00011:55:00.000
SU1483Aeroflot77WVP-BGBKrasnojarsk11:00:00.00011:13:00.000Sheremetyevo International11:35:00.00011:48:00.000
SU2683Aeroflot319VQ-BCORiga11:00:00.00011:24:00.000Sheremetyevo International14:35:00.00014:44:00.000
D95399Donavia319VP-BNNStavropol11:15:00.00011:17:00.000Sheremetyevo International13:30:00.00013:32:00.000
SU2035AeroflotSU9RA-89008Otopeni International11:15:00.00011:28:00.000Sheremetyevo International15:35:00.00015:33:00.000
SU11Aeroflot320---Pulkovo11:30:00.00011:49:00.000Sheremetyevo International12:45:00.00013:04:00.000
SU1139Aeroflot321VQ-BKUAdler/Sochi11:35:00.00011:55:00.000Sheremetyevo International14:00:00.00014:20:00.000
SU1211Aeroflot320VQ-BITSamara11:40:00.00012:13:00.000Sheremetyevo International13:25:00.00013:42:00.000
SU1759AeroflotSU9VP-BZQVolgograd11:45:00.00011:53:00.000Sheremetyevo International13:35:00.00013:43:00.000
SU1255Aeroflot319VP-BDNBegishevo11:50:00.00012:03:00.000Sheremetyevo International13:40:00.00013:53:00.000
SU1643Aeroflot320VQ-BIWAstrakhan11:50:00.00011:55:00.000Sheremetyevo International14:10:00.00014:15:00.000
SU1305Aeroflot320VP-BLPMineralnye Vody11:50:00.00012:08:00.000Sheremetyevo International14:15:00.00014:33:00.000
SU1761Aeroflot738VP-BRHChita11:55:00.00012:10:00.000Sheremetyevo International12:45:00.00013:00:00.000
SU1221Aeroflot320VP-BMFNizhniy Novgorod12:05:00.00012:12:00.000Sheremetyevo International13:10:00.00013:17:00.000
SU1743Aeroflot333VQ-BQXYuzhno-Sakhalinsk12:10:00.00012:20:00.000Sheremetyevo International14:05:00.00014:15:00.000
D95301Donavia734VQ-BCSRostov12:15:00.00012:28:00.000Sheremetyevo International14:15:00.00014:28:00.000
SU13Aeroflot319---Pulkovo12:20:00.00012:50:00.000Sheremetyevo International13:35:00.00014:05:00.000
5N117Nordavia Regional Airlines735---Arkhangelsk12:20:00.00012:25:00.000Sheremetyevo International14:05:00.00014:10:00.000
SU1191Aeroflot320VQ-BEAKazan12:25:00.00013:04:00.000Sheremetyevo International13:55:00.00014:34:00.000
SU1751Aeroflot738VP-BRFYakutsk12:30:00.00012:58:00.000Sheremetyevo International13:15:00.00013:43:00.000
SU1547AeroflotSU9---Anapa12:30:00.00012:50:00.000Sheremetyevo International14:45:00.00015:05:00.000
D95377Donavia319---Mineralnye Vody12:45:00.00013:03:00.000Sheremetyevo International15:10:00.00015:28:00.000
D95363Donavia319VP-BQKRostov13:05:00.00013:20:00.000Sheremetyevo International15:05:00.00015:20:00.000
SU1411Aeroflot321VQ-BOIKoltsovo International13:15:00.00013:43:00.000Sheremetyevo International13:40:00.00014:08:00.000
SU1731Aeroflot333VQ-BCQPetropavlovsk-Kamchatsky13:30:00.00013:44:00.000Sheremetyevo International14:30:00.00014:44:00.000
SU15Aeroflot320---Pulkovo13:30:00.00013:39:00.000Sheremetyevo International14:45:00.00014:52:00.000
SU1231Aeroflot320VP-BLRUfa13:55:00.00014:19:00.000Sheremetyevo International14:00:00.00014:24:00.000
SU1421Aeroflot320VP-BNLChelyabinsk13:55:00.00013:56:00.000Sheremetyevo International14:20:00.00014:21:00.000
R25803Orenair738---Irkutsk14:05:00.00014:30:00.000Sheremetyevo International14:50:00.00015:15:00.000
SU1201AeroflotSU9---Perm14:10:00.000---Sheremetyevo International14:25:00.00014:25:00.000
5N9134Nordavia Regional Airlines------Kazan14:10:00.00015:07:00.000Sheremetyevo International15:30:00.000---
SU17Aeroflot320---Pulkovo14:25:00.00014:56:00.000Sheremetyevo International15:40:00.00016:11:00.000



Future work





  • Хочу сделать более красивый вид таблицы результатами

  • Нормальный вывод в PDF, а не как печать web-страницы

  • Приложение для Android


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Комментариев нет:

Отправить комментарий