[685] | 1 | # needed for os specific separators
|
---|
| 2 | import os
|
---|
| 3 | # selenium is needed for browser manipulation
|
---|
| 4 | from selenium import webdriver
|
---|
| 5 | from selenium.common.exceptions import NoSuchElementException
|
---|
| 6 | from selenium.common.exceptions import NoSuchWindowException
|
---|
| 7 | from selenium.webdriver.common.by import By
|
---|
| 8 | from selenium.webdriver.support.ui import WebDriverWait, Select
|
---|
| 9 | from selenium.webdriver.support import expected_conditions as EC
|
---|
| 10 | from selenium.webdriver.common.keys import Keys
|
---|
| 11 | # urllib is needed to obtain the xml
|
---|
| 12 | import urllib2
|
---|
| 13 | # xmlparser
|
---|
| 14 | from lxml import etree
|
---|
| 15 | from StringIO import StringIO
|
---|
| 16 | import lxml.html
|
---|
| 17 | import time
|
---|
| 18 |
|
---|
| 19 |
|
---|
| 20 | class MavaSimbriefIntegrator():
|
---|
| 21 | """Implements the integration with the excellent Simbrief pilot briefing
|
---|
| 22 | system for MALEV Virtual."""
|
---|
| 23 |
|
---|
| 24 | def __init__(self,
|
---|
| 25 | plan,
|
---|
| 26 | driver=None,
|
---|
| 27 | simbrief_query_settings=None,
|
---|
| 28 | mava_simbrief_url=None,
|
---|
| 29 | xml_link_fix_part=None):
|
---|
| 30 | """Init the integrator with settings that are typical of our use.
|
---|
| 31 | @param: plan - flightplan dictionary
|
---|
| 32 | @param: webdriver - a selenium webdriver
|
---|
| 33 | @param: simbrief_query_settings - a dictionary of query settings
|
---|
| 34 | @param: mava_simbrief_url - url to the form that is sent to simbrief
|
---|
| 35 | on the mava server
|
---|
| 36 | @param: xml_link_fix_part = url to the simbrief website under which the
|
---|
| 37 | xml is to be found"""
|
---|
| 38 | self.plan = plan
|
---|
| 39 | if simbrief_query_settings is None:
|
---|
| 40 | self.simbrief_query_settings = {
|
---|
| 41 | 'navlog': True,
|
---|
| 42 | 'etops': True,
|
---|
| 43 | 'stepclimbs': True,
|
---|
| 44 | 'tlr': True,
|
---|
| 45 | 'notams': True,
|
---|
| 46 | 'firnot': True,
|
---|
| 47 | 'maps': 'Simple',
|
---|
| 48 | }
|
---|
| 49 | else:
|
---|
| 50 | self.simbrief_query_settings = simbrief_query_settings
|
---|
| 51 |
|
---|
| 52 | if driver is None:
|
---|
| 53 | self.driver = webdriver.Firefox()
|
---|
| 54 | else:
|
---|
| 55 | self.driver = driver
|
---|
| 56 |
|
---|
| 57 | if mava_simbrief_url is None:
|
---|
| 58 | self.mava_simbrief_url = "http://flare.privatedns.org/" \
|
---|
| 59 | "mava_simbrief/simbrief_form.html"
|
---|
| 60 | else:
|
---|
| 61 | self.mava_simbrief_url = mava_simbrief_url
|
---|
| 62 |
|
---|
| 63 | if xml_link_fix_part is None:
|
---|
| 64 | self.xml_link_fix_part = "http://www.simbrief.com/ofp/" \
|
---|
| 65 | "flightplans/xml/"
|
---|
| 66 | else:
|
---|
| 67 | self.xml_link_fix_part = xml_link_fix_part
|
---|
| 68 |
|
---|
| 69 | def fill_form(self,
|
---|
| 70 | plan,
|
---|
| 71 | simbrief_query_settings):
|
---|
| 72 | """Fills the form of the webpage using the paramteres that the class
|
---|
| 73 | has been initialized with.
|
---|
| 74 | @param: driver - a selenium webdriver
|
---|
| 75 | @param: plan - dictionary containing plan details
|
---|
| 76 | @param: simbrief_query_settings - dictionary containing plan settings"""
|
---|
| 77 | for plan_input_field in plan.iterkeys():
|
---|
| 78 | self.driver.find_element_by_name(plan_input_field).send_keys(
|
---|
| 79 | plan[plan_input_field])
|
---|
| 80 | for option_checkbox in simbrief_query_settings.iterkeys():
|
---|
| 81 | if (isinstance(simbrief_query_settings[option_checkbox], bool) and
|
---|
| 82 | simbrief_query_settings[option_checkbox]):
|
---|
| 83 | # if setting is a boolean type and true
|
---|
| 84 | self.driver.find_element_by_name(option_checkbox).click()
|
---|
| 85 | elif isinstance(simbrief_query_settings[option_checkbox], str):
|
---|
| 86 | # if setting is a select
|
---|
| 87 | Select(self.driver.find_element_by_name(option_checkbox)).\
|
---|
| 88 | select_by_visible_text(simbrief_query_settings[
|
---|
| 89 | option_checkbox])
|
---|
| 90 |
|
---|
| 91 | def get_xml_link(self,
|
---|
| 92 | get_credentials, update_progress,
|
---|
| 93 | local_xml_debug=False,
|
---|
| 94 | local_html_debug=False):
|
---|
| 95 | """Obtains the link of the xml to be processed.
|
---|
| 96 | @param get_credentials - a function, which should return a pair of the
|
---|
| 97 | user name and password to log in to SimBrief. It gets an integer which
|
---|
| 98 | is the number of times it has been called so far.
|
---|
| 99 | @param local_xml_debug - if True then not the real location will be
|
---|
| 100 | checked but the current working dir will be searched for 'xml.xml'
|
---|
| 101 | @param local_html_debug - if True then not real mava_simbrief_url will
|
---|
| 102 | be used but the current working dir will be searched for
|
---|
| 103 | 'mava_simbrief.html'
|
---|
| 104 | @returns: the string of the xml link if it could be obtained and None
|
---|
| 105 | otherwise"""
|
---|
| 106 | # Finding xml_link
|
---|
| 107 | is_briefing_available = False
|
---|
| 108 | if local_xml_debug:
|
---|
| 109 | # set link for locally debugging an existing xml file
|
---|
| 110 | xml_link = ("file://"
|
---|
| 111 | + os.getcwd()
|
---|
| 112 | + os.sep
|
---|
| 113 | + "xml.xml")
|
---|
| 114 | is_briefing_available = True
|
---|
| 115 | else:
|
---|
| 116 | update_progress("Looking for a browser...", False)
|
---|
| 117 | # There must be window whose title is 'SimBrief' so that we
|
---|
| 118 | # could find our one among several
|
---|
| 119 | if self._find_window_by_title("SimBrief") is None:
|
---|
| 120 | print "No SimBrief window was found!"
|
---|
| 121 | return None
|
---|
| 122 |
|
---|
| 123 | # normal operation with a real xml file
|
---|
| 124 | update_progress("Retrieving form...", False)
|
---|
| 125 | if local_html_debug:
|
---|
| 126 | self.driver.get(
|
---|
| 127 | "file://" + os.getcwd() + os.sep + "simbrief_form.html")
|
---|
| 128 | is_briefing_available = True
|
---|
| 129 | else:
|
---|
| 130 | self.driver.get(self.mava_simbrief_url)
|
---|
| 131 |
|
---|
| 132 | main_handle = self._find_window_by_title("Malev Virtual Simbrief Integration System")
|
---|
| 133 | if main_handle is None:
|
---|
| 134 | print "No SimBrief Integration window was found!"
|
---|
| 135 | return None
|
---|
| 136 |
|
---|
| 137 | # Make a copy of the window handles before submitting the form,
|
---|
| 138 | # so that we could find the popup
|
---|
| 139 | handles = self.driver.window_handles[:]
|
---|
| 140 |
|
---|
| 141 | # Entering form data
|
---|
| 142 | update_progress("Filling form...", False)
|
---|
| 143 | self.driver.switch_to_window(main_handle)
|
---|
| 144 | self.fill_form(self.plan,
|
---|
| 145 | self.simbrief_query_settings)
|
---|
| 146 |
|
---|
| 147 | # Loading page
|
---|
| 148 | button = self.driver.find_element_by_name("submitform")
|
---|
| 149 | button.send_keys(Keys.RETURN)
|
---|
| 150 |
|
---|
| 151 | update_progress("Waiting for login...", False)
|
---|
| 152 | popup_handle = self._find_popup(handles)
|
---|
| 153 | if popup_handle is None:
|
---|
| 154 | print "No popup window was found!"
|
---|
| 155 | return None
|
---|
| 156 |
|
---|
| 157 | login_count = 0
|
---|
| 158 | end_time = time.time() + 120.0
|
---|
| 159 | while not is_briefing_available and end_time > time.time():
|
---|
| 160 | try:
|
---|
| 161 | self.driver.switch_to.window(popup_handle)
|
---|
| 162 |
|
---|
| 163 | userElement = self.driver.find_element_by_name("user")
|
---|
| 164 |
|
---|
| 165 | if userElement is not None:
|
---|
| 166 | update_progress("Logging in...", False)
|
---|
| 167 | (userName, password) = get_credentials(login_count)
|
---|
| 168 | userElement.send_keys(userName)
|
---|
| 169 | self.driver.find_element_by_name("pass").send_keys(password)
|
---|
| 170 | self.driver.find_element_by_name("staylogged").click()
|
---|
| 171 |
|
---|
| 172 | self.driver.find_element(By.XPATH,
|
---|
| 173 | "//input[@value='Login']").send_keys(Keys.RETURN)
|
---|
| 174 | login_count += 1
|
---|
| 175 | except NoSuchElementException:
|
---|
| 176 | pass
|
---|
| 177 | except NoSuchWindowException:
|
---|
| 178 | pass
|
---|
| 179 |
|
---|
| 180 | update_progress("Waiting for the result...", False)
|
---|
| 181 | self.driver.switch_to.window(main_handle)
|
---|
| 182 | try:
|
---|
| 183 | if self.driver.find_element_by_name("hidden_is_briefing_available") is not None:
|
---|
| 184 | is_briefing_available = True
|
---|
| 185 | xml_link_element = self.driver.find_element_by_name(
|
---|
| 186 | 'hidden_link')
|
---|
| 187 | xml_link_generated_part = xml_link_element.get_attribute(
|
---|
| 188 | 'value')
|
---|
| 189 | xml_link = self.xml_link_fix_part + xml_link_generated_part + '.xml'
|
---|
| 190 | print(xml_link)
|
---|
| 191 | except NoSuchElementException:
|
---|
| 192 | pass
|
---|
| 193 |
|
---|
| 194 | self.driver.quit()
|
---|
| 195 |
|
---|
| 196 | if is_briefing_available:
|
---|
| 197 | return xml_link
|
---|
| 198 | else:
|
---|
| 199 | return None
|
---|
| 200 |
|
---|
| 201 | def get_results(self,
|
---|
| 202 | xml_link,
|
---|
| 203 | html_file_path = None):
|
---|
| 204 | """Parses the xml for information.
|
---|
| 205 | @param xml_link - a path to the xml file
|
---|
| 206 | @return a dictionary of the found information"""
|
---|
| 207 | # Setup variables
|
---|
| 208 | ## Holds analysis data not used
|
---|
| 209 | available_info = {}
|
---|
| 210 | ## Holds analysis data to be used
|
---|
| 211 | flight_info = {}
|
---|
| 212 | ## Holds notams
|
---|
| 213 | notams_list = []
|
---|
| 214 | ## Notam counter
|
---|
| 215 | i = 0
|
---|
| 216 | # Obtaining the xml
|
---|
| 217 | response = urllib2.urlopen(xml_link)
|
---|
| 218 | xml_content = response.read()
|
---|
| 219 | # Processing xml
|
---|
| 220 | tree = etree.parse(StringIO(xml_content))
|
---|
| 221 | context = etree.iterparse(StringIO(xml_content))
|
---|
| 222 | for action, element in context:
|
---|
| 223 | # Processing tags that occur multiple times
|
---|
| 224 | ## NOTAMS
|
---|
| 225 | if element.tag == 'notamdrec':
|
---|
| 226 | notams_element_list = list(element)
|
---|
| 227 | notam_dict = {}
|
---|
| 228 | for notam in notams_element_list:
|
---|
| 229 | notam_dict[notam.tag] = notam.text
|
---|
| 230 | notams_list.append(notam_dict)
|
---|
| 231 | i += 1
|
---|
| 232 | ## WEATHER
|
---|
| 233 | elif element.tag == 'weather':
|
---|
| 234 | weather_element_list = list(element)
|
---|
| 235 | for weather in weather_element_list:
|
---|
| 236 | flight_info[weather.tag] = weather.text
|
---|
| 237 | else:
|
---|
| 238 | available_info[element.tag] = element.text
|
---|
| 239 | # Processing plan_html
|
---|
| 240 | ## Obtaining chart links
|
---|
| 241 | image_links = []
|
---|
| 242 | for image_link_a_element in lxml.html.find_class(
|
---|
| 243 | available_info['plan_html'], 'ofpmaplink'):
|
---|
| 244 | for image_link_tuple in image_link_a_element.iterlinks():
|
---|
| 245 | if image_link_tuple[1] == 'src':
|
---|
| 246 | image_links.append(image_link_tuple[2])
|
---|
| 247 | flight_info['image_links'] = image_links
|
---|
| 248 | print(sorted(available_info.keys()))
|
---|
| 249 | if html_file_path is None:
|
---|
| 250 | html_file_path = 'simbrief_plan.html'
|
---|
| 251 | with open(html_file_path, 'w') as f:
|
---|
| 252 | f.write(available_info['plan_html'])
|
---|
| 253 | return flight_info
|
---|
| 254 |
|
---|
| 255 | def _find_window_by_title(self, title, timeout = 10.0):
|
---|
| 256 | """Find the window with the given title.
|
---|
| 257 |
|
---|
| 258 | Switch to that window and return its handle."""
|
---|
| 259 | def predicate(handle):
|
---|
| 260 | self.driver.switch_to.window(handle)
|
---|
| 261 | return self.driver.title == title
|
---|
| 262 |
|
---|
| 263 | return self._find_window(predicate, timeout = timeout)
|
---|
| 264 |
|
---|
| 265 | def _find_popup(self, handles, timeout = 10.0):
|
---|
| 266 | """Find a popup, i.e. a new window being created.
|
---|
| 267 |
|
---|
| 268 | handles is a list of window handles that existed before the popup was
|
---|
| 269 | expected to be created. If a new handle is found, that is assumed to be
|
---|
| 270 | the popup window."""
|
---|
| 271 | return self._find_window(lambda handle: handle not in handles,
|
---|
| 272 | timeout = timeout)
|
---|
| 273 |
|
---|
| 274 | def _find_window(self, predicate, timeout = 10.0):
|
---|
| 275 | """Find a window that fulfills the given predicate."""
|
---|
| 276 | window_handle = None
|
---|
| 277 | end_time = time.time() + timeout
|
---|
| 278 | while window_handle is None and end_time > time.time():
|
---|
| 279 | for handle in self.driver.window_handles:
|
---|
| 280 | if predicate(handle):
|
---|
| 281 | window_handle = handle
|
---|
| 282 | break
|
---|
| 283 |
|
---|
| 284 | return window_handle
|
---|
| 285 |
|
---|
| 286 | if __name__ == "__main__":
|
---|
| 287 | mava_simbrief_url = "http://flare.privatedns.org/" \
|
---|
| 288 | "mava_simbrief/simbrief_form.html"
|
---|
| 289 | xml_link_fix_part = "http://www.simbrief.com/ofp/" \
|
---|
| 290 | "flightplans/xml/"
|
---|
| 291 | plan = {
|
---|
| 292 | 'airline': 'MAH',
|
---|
| 293 | 'fltnum': '764',
|
---|
| 294 | 'type': 'B738',
|
---|
| 295 | 'orig': 'LHBP',
|
---|
| 296 | 'dest': 'LSZH',
|
---|
| 297 | 'date': '25FEB15',
|
---|
| 298 | 'deph': '20',
|
---|
| 299 | 'depm': '00',
|
---|
| 300 | 'route': 'GILEP DCT ARSIN UL851 SITNI UL856 NEGRA',
|
---|
| 301 | 'steh': '22',
|
---|
| 302 | 'stem': '05',
|
---|
| 303 | 'reg': 'HA-LOC',
|
---|
| 304 | 'fin': 'LOC',
|
---|
| 305 | 'selcal': 'XXXX',
|
---|
| 306 | 'pax': '100',
|
---|
| 307 | 'altn': 'LSGG',
|
---|
| 308 | 'fl': '36000',
|
---|
| 309 | 'cpt': 'BALINT SZEBENYI',
|
---|
| 310 | 'pid': 'P008',
|
---|
| 311 | 'fuelfactor': 'P000',
|
---|
| 312 | 'manualzfw': '42.7',
|
---|
| 313 | 'addedfuel': '2.5',
|
---|
| 314 | 'contpct': '0.05',
|
---|
| 315 | 'resvrule': '45',
|
---|
| 316 | 'taxiout': '10',
|
---|
| 317 | 'taxiin': '4',
|
---|
| 318 | 'cargo': '5.0',
|
---|
| 319 | 'origrwy': '31L',
|
---|
| 320 | 'destrwy': '34',
|
---|
| 321 | 'climb': '250/300/78',
|
---|
| 322 | 'descent': '80/280/250',
|
---|
| 323 | 'cruise': 'LRC',
|
---|
| 324 | 'civalue': 'AUTO',
|
---|
| 325 | }
|
---|
| 326 | integrator = MavaSimbriefIntegrator(plan=plan)
|
---|
| 327 | link = integrator.get_xml_link(local_xml_debug=False,
|
---|
| 328 | local_html_debug=False)
|
---|
| 329 | flight_info = integrator.get_results(link)
|
---|
| 330 | print(flight_info)
|
---|
| 331 |
|
---|