source: src/mlx/gui/mava_simbrief.py@ 685:6391018c120b

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

Added some basic support for SimBrief (re #279)

File size: 13.0 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 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
286if __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
Note: See TracBrowser for help on using the repository browser.