source: src/mlx/gui/mava_simbrief.py@ 693:1fa45b7d33f4

cef
Last change on this file since 693:1fa45b7d33f4 was 693:1fa45b7d33f4, checked in by István Váradi <ivaradi@…>, 9 years ago

Removed quitting the driver to make reusable (re #279).

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