source: src/mlx/gui/mava_simbrief.py@ 701:8a1cb08e0099

cef
Last change on this file since 701:8a1cb08e0099 was 701:8a1cb08e0099, checked in by István Váradi <ivaradi@…>, 7 years ago

The end time for acquiring the result is always lengthened when the credentials are queried to account for the dialog being displayed for a longer time (re #279).

File size: 15.4 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 end_time = time.time() + 120.0
221 if userName is None or password is None:
222 update_progress(MavaSimbriefIntegrator.PROGRESS_WAITING_LOGIN,
223 MavaSimbriefIntegrator.RESULT_ERROR_LOGIN_FAILED, None)
224 return None
225
226 userElement.send_keys(userName)
227 self.driver.find_element_by_name("pass").send_keys(password)
228 self.driver.find_element_by_name("staylogged").click()
229
230 self.driver.find_element(By.XPATH,
231 "//input[@value='Login']").send_keys(Keys.RETURN)
232 login_count += 1
233 except NoSuchElementException:
234 pass
235 except NoSuchWindowException:
236 pass
237
238 update_progress(MavaSimbriefIntegrator.PROGRESS_WAITING_RESULT,
239 MavaSimbriefIntegrator.RESULT_NONE, None)
240 self.driver.switch_to.window(main_handle)
241 try:
242 if self.driver.find_element_by_name("hidden_is_briefing_available") is not None:
243 is_briefing_available = True
244 xml_link_element = self.driver.find_element_by_name(
245 'hidden_link')
246 xml_link_generated_part = xml_link_element.get_attribute(
247 'value')
248 xml_link = self.xml_link_fix_part + xml_link_generated_part + '.xml'
249 print(xml_link)
250 except NoSuchElementException:
251 pass
252
253 if is_briefing_available:
254 return xml_link
255 else:
256 update_progress(MavaSimbriefIntegrator.PROGRESS_WAITING_RESULT,
257 MavaSimbriefIntegrator.RESULT_ERROR_OTHER, None)
258 return None
259
260 def get_results(self,
261 xml_link,
262 html_file_path = None):
263 """Parses the xml for information.
264 @param xml_link - a path to the xml file
265 @return a dictionary of the found information"""
266 # Setup variables
267 ## Holds analysis data not used
268 available_info = {}
269 ## Holds analysis data to be used
270 flight_info = {}
271 ## Holds notams
272 notams_list = []
273 ## Notam counter
274 i = 0
275 # Obtaining the xml
276 response = urllib2.urlopen(xml_link)
277 xml_content = response.read()
278 # Processing xml
279 tree = etree.parse(StringIO(xml_content))
280 context = etree.iterparse(StringIO(xml_content))
281 for action, element in context:
282 # Processing tags that occur multiple times
283 ## NOTAMS
284 if element.tag == 'notamdrec':
285 notams_element_list = list(element)
286 notam_dict = {}
287 for notam in notams_element_list:
288 notam_dict[notam.tag] = notam.text
289 notams_list.append(notam_dict)
290 i += 1
291 ## WEATHER
292 elif element.tag == 'weather':
293 weather_element_list = list(element)
294 for weather in weather_element_list:
295 flight_info[weather.tag] = weather.text
296 else:
297 available_info[element.tag] = element.text
298 # Processing plan_html
299 ## Obtaining chart links
300 image_links = []
301 for image_link_a_element in lxml.html.find_class(
302 available_info['plan_html'], 'ofpmaplink'):
303 for image_link_tuple in image_link_a_element.iterlinks():
304 if image_link_tuple[1] == 'src':
305 image_links.append(image_link_tuple[2])
306 flight_info['image_links'] = image_links
307 print(sorted(available_info.keys()))
308 if html_file_path is None:
309 html_file_path = 'simbrief_plan.html'
310 with open(html_file_path, 'w') as f:
311 f.write(available_info['plan_html'])
312 return flight_info
313
314 def _find_window_by_title(self, title, timeout = 10.0):
315 """Find the window with the given title.
316
317 Switch to that window and return its handle."""
318 def predicate(handle):
319 self.driver.switch_to.window(handle)
320 return self.driver.title == title
321
322 return self._find_window(predicate, timeout = timeout)
323
324 def _find_popup(self, handles, timeout = 10.0):
325 """Find a popup, i.e. a new window being created.
326
327 handles is a list of window handles that existed before the popup was
328 expected to be created. If a new handle is found, that is assumed to be
329 the popup window."""
330 return self._find_window(lambda handle: handle not in handles,
331 timeout = timeout)
332
333 def _find_window(self, predicate, timeout = 10.0):
334 """Find a window that fulfills the given predicate."""
335 window_handle = None
336 end_time = time.time() + timeout
337 while window_handle is None and end_time > time.time():
338 for handle in self.driver.window_handles:
339 if predicate(handle):
340 window_handle = handle
341 break
342
343 return window_handle
344
345if __name__ == "__main__":
346 mava_simbrief_url = "http://flare.privatedns.org/" \
347 "mava_simbrief/simbrief_form.html"
348 xml_link_fix_part = "http://www.simbrief.com/ofp/" \
349 "flightplans/xml/"
350 plan = {
351 'airline': 'MAH',
352 'fltnum': '764',
353 'type': 'B738',
354 'orig': 'LHBP',
355 'dest': 'LSZH',
356 'date': '25FEB15',
357 'deph': '20',
358 'depm': '00',
359 'route': 'GILEP DCT ARSIN UL851 SITNI UL856 NEGRA',
360 'steh': '22',
361 'stem': '05',
362 'reg': 'HA-LOC',
363 'fin': 'LOC',
364 'selcal': 'XXXX',
365 'pax': '100',
366 'altn': 'LSGG',
367 'fl': '36000',
368 'cpt': 'BALINT SZEBENYI',
369 'pid': 'P008',
370 'fuelfactor': 'P000',
371 'manualzfw': '42.7',
372 'addedfuel': '2.5',
373 'contpct': '0.05',
374 'resvrule': '45',
375 'taxiout': '10',
376 'taxiin': '4',
377 'cargo': '5.0',
378 'origrwy': '31L',
379 'destrwy': '34',
380 'climb': '250/300/78',
381 'descent': '80/280/250',
382 'cruise': 'LRC',
383 'civalue': 'AUTO',
384 }
385 integrator = MavaSimbriefIntegrator(plan=plan)
386 link = integrator.get_xml_link(local_xml_debug=False,
387 local_html_debug=False)
388 flight_info = integrator.get_results(link)
389 print(flight_info)
390
Note: See TracBrowser for help on using the repository browser.