source: src/mlx/gui/mava_simbrief.py@ 694:63fee61a34d5

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

Added an extra progress message (re #279).

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