execute.mako 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. ## Licensed to Cloudera, Inc. under one
  2. ## or more contributor license agreements. See the NOTICE file
  3. ## distributed with this work for additional information
  4. ## regarding copyright ownership. Cloudera, Inc. licenses this file
  5. ## to you under the Apache License, Version 2.0 (the
  6. ## "License"); you may not use this file except in compliance
  7. ## with the License. You may obtain a copy of the License at
  8. ##
  9. ## http://www.apache.org/licenses/LICENSE-2.0
  10. ##
  11. ## Unless required by applicable law or agreed to in writing, software
  12. ## distributed under the License is distributed on an "AS IS" BASIS,
  13. ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. ## See the License for the specific language governing permissions and
  15. ## limitations under the License.
  16. <%!
  17. from desktop.lib.django_util import extract_field_data
  18. from desktop.views import commonheader, commonfooter
  19. %>
  20. <%namespace name="comps" file="beeswax_components.mako" />
  21. <%namespace name="layout" file="layout.mako" />
  22. <%namespace name="util" file="util.mako" />
  23. <%def name="query()">
  24. <h1>Hive Query</h1>
  25. <fieldset>
  26. % if design and not design.is_auto and design.name:
  27. <legend>${design.name}</legend>
  28. % if design.desc:
  29. <p>${design.desc}</p>
  30. % endif
  31. % else:
  32. <legend>Query</legend>
  33. % endif
  34. <div class="clearfix">
  35. <div class="input">
  36. <textarea class="span9" rows="9" placeholder="Example: SELECT * FROM tablename" name="${form.query["query"].html_name | n}" id="queryField">${extract_field_data(form.query["query"]) or ''}</textarea>
  37. <div id="validationResults">
  38. % if len(form.query["query"].errors):
  39. ${unicode(form.query["query"].errors) | n}
  40. % endif
  41. </div>
  42. </div>
  43. </div>
  44. </fieldset>
  45. <div class="actions">
  46. <a id="executeQuery" class="btn primary">Execute</a>
  47. % if design and not design.is_auto and design.name:
  48. <a id="saveQuery" class="btn">Save</a>
  49. % endif
  50. <a id="saveQueryAs" class="btn">Save as...</a>
  51. <a id="explainQuery" class="btn">Explain</a>
  52. &nbsp; or <a href="${ url('beeswax.views.execute_query') }">create a new query</a>
  53. </div>
  54. </%def>
  55. ${commonheader("Hive Query", "beeswax", "100px")}
  56. ${layout.menubar(section='query')}
  57. <div class="container-fluid">
  58. <div class="row-fluid">
  59. <div class="span3">
  60. <div class="well sidebar-nav">
  61. <form id="advancedSettingsForm" action="${action}" method="POST" class="form form-horizontal noPadding">
  62. <ul class="nav nav-list">
  63. <li class="nav-header">Hive settings</li>
  64. <li>
  65. % for i, f in enumerate(form.settings.forms):
  66. <div class="param">
  67. <div class="remove">
  68. ${comps.field(f['_deleted'], tag="button", button_text="x", notitle=True, attrs=dict(
  69. type="submit",
  70. title="Delete this setting",
  71. klass="btn btn-mini settingsDelete"
  72. ), value=True)}
  73. </div>
  74. <div class="control-group">
  75. ${comps.label(f['key'])}
  76. ${comps.field(f['key'], attrs=dict(
  77. placeholder="mapred.reduce.tasks",
  78. klass="settingsField span8"
  79. ))}
  80. </div>
  81. <div class="control-group">
  82. ${comps.label(f['value'])}
  83. ${comps.field(f['value'], attrs=dict(
  84. placeholder="1",
  85. klass="span8"
  86. ))}
  87. </div>
  88. </div>
  89. ${comps.field(f['_exists'], hidden=True)}
  90. % endfor
  91. <div class="control-group">
  92. <a class="btn btn-small" data-form-prefix="settings">Add</a>
  93. </div>
  94. </li>
  95. <li class="nav-header">File Resources</li>
  96. <li>
  97. % for i, f in enumerate(form.file_resources.forms):
  98. <div class="param">
  99. <div class="remove">
  100. ${comps.field(f['_deleted'], tag="button", button_text="x", notitle=True, attrs=dict(
  101. type="submit",
  102. title="Delete this setting",
  103. klass="btn btn-mini file_resourcesDelete"
  104. ), value=True)}
  105. </div>
  106. <div class="control-group">
  107. ${comps.label(f['type'])}
  108. ${comps.field(f['type'], render_default=True, attrs=dict(
  109. klass="span8"
  110. ))}
  111. </div>
  112. <div class="control-group">
  113. ${comps.label(f['path'])}
  114. ${comps.field(f['path'], attrs=dict(
  115. placeholder="/user/foo/udf.jar",
  116. klass="input-small file_resourcesField",
  117. data_filters=f['path'].html_name
  118. ))}
  119. </div>
  120. </div>
  121. ${comps.field(f['_exists'], hidden=True)}
  122. % endfor
  123. <div class="control-group">
  124. <a class="btn btn-small" data-form-prefix="file_resources">Add</a>
  125. </div>
  126. </li>
  127. <li class="nav-header">User-defined Functions</li>
  128. <li>
  129. % for i, f in enumerate(form.functions.forms):
  130. <div class="param">
  131. <div class="remove">
  132. ${comps.field(f['_deleted'], tag="button", button_text="x", notitle=True, attrs=dict(
  133. type="submit",
  134. title="Delete this setting",
  135. klass="btn btn-mini file_resourcesDelete"
  136. ), value=True)}
  137. </div>
  138. <div class="control-group">
  139. ${comps.label(f['name'])}
  140. ${comps.field(f['name'], attrs=dict(
  141. placeholder="myFunction",
  142. klass="span8 functionsField"
  143. ))}
  144. </div>
  145. <div class="control-group">
  146. ${comps.label(f['class_name'])}
  147. ${comps.field(f['class_name'], attrs=dict(
  148. placeholder="com.acme.example",
  149. klass="span8"
  150. ))}
  151. </div>
  152. </div>
  153. ${comps.field(f['_exists'], hidden=True)}
  154. % endfor
  155. <div class="control-group">
  156. <a class="btn btn-small" data-form-prefix="functions">Add</a>
  157. </div>
  158. </li>
  159. <li class="nav-header">Parametrization</li>
  160. <li>
  161. ${comps.field(form.query["is_parameterized"],
  162. notitle = True,
  163. tag = "checkbox",
  164. button_text = "Enable Parameterization",
  165. help = "If checked (the default), you can include parameters like $parameter_name in your query, and users will be prompted for a value when the query is run.",
  166. help_attrs= dict(
  167. data_help_direction='11'
  168. )
  169. )}
  170. </li>
  171. <li class="nav-header">Email Notification</li>
  172. <li>
  173. ${comps.field(form.query["email_notify"],
  174. notitle = True,
  175. tag = "checkbox",
  176. button_text = "Email me on complete",
  177. help = "If checked, you will receive an email notification when the query completes.",
  178. help_attrs= dict(
  179. data_help_direction='11'
  180. )
  181. )}
  182. </li>
  183. </ul>
  184. <input type="hidden" name="${form.query["query"].html_name | n}" class="query" value="" />
  185. </form>
  186. </div>
  187. </div>
  188. <div class="span9">
  189. % if error_message:
  190. <div class="alert alert-error">
  191. <p><strong>Your query has the following error(s):</strong></p>
  192. <p>${error_message}</p>
  193. % if log:
  194. <small>click the <b>Error Log</b> tab below for details</small>
  195. % endif
  196. </div>
  197. % endif
  198. % if on_success_url:
  199. <input type="hidden" name="on_success_url" value="${on_success_url}"/>
  200. % endif
  201. % if error_messages or log:
  202. <ul class="nav nav-tabs">
  203. <li class="active">
  204. <a href="#queryPane" data-toggle="tab">Query</a>
  205. </li>
  206. % if error_message or log:
  207. <li>
  208. <a href="#errorPane" data-toggle="tab">
  209. % if log:
  210. Error Log
  211. % else:
  212. &nbsp;
  213. % endif
  214. </a>
  215. </li>
  216. % endif
  217. </ul>
  218. <div class="tab-content">
  219. <div class="active tab-pane" id="queryPane">
  220. ${query()}
  221. </div>
  222. % if error_message or log:
  223. <div class="tab-pane" id="errorPane">
  224. % if log:
  225. <pre>${log | h}</pre>
  226. % endif
  227. </div>
  228. % endif
  229. </div>
  230. % else:
  231. ${query()}
  232. % endif
  233. <br/>
  234. </div>
  235. </div>
  236. </div>
  237. <div id="chooseFile" class="modal hide fade">
  238. <div class="modal-header">
  239. <a href="#" class="close" data-dismiss="modal">&times;</a>
  240. <h3>Choose a file</h3>
  241. </div>
  242. <div class="modal-body">
  243. <div id="filechooser">
  244. </div>
  245. </div>
  246. <div class="modal-footer">
  247. </div>
  248. </div>
  249. <div id="saveAs" class="modal hide fade">
  250. <div class="modal-header">
  251. <a href="#" class="close" data-dismiss="modal">&times;</a>
  252. <h3>Choose a name</h3>
  253. </div>
  254. <div class="modal-body">
  255. <div class="clearfix">
  256. <label>Name</label>
  257. ${comps.field(form.saveform['name'])}
  258. </div>
  259. <div class="clearfix">
  260. <label>Description</label>
  261. ${comps.field(form.saveform['desc'])}
  262. </div>
  263. </div>
  264. <div class="modal-footer">
  265. <button id="saveAsNameBtn" class="btn primary">Save</button>
  266. </div>
  267. </div>
  268. <style>
  269. #filechooser {
  270. min-height:100px;
  271. overflow-y:scroll;
  272. }
  273. .control-group label {
  274. float: left;
  275. padding-top: 5px;
  276. text-align: left;
  277. width: 40px;
  278. }
  279. .nav-list {
  280. padding:0;
  281. }
  282. .param {
  283. background:#FDFDFD;
  284. padding: 8px 8px 1px 8px;
  285. border-radius: 4px;
  286. -webkit-border-radius: 4px;
  287. -moz-border-radius: 4px;
  288. margin-bottom:5px;
  289. border:1px solid #EEE;
  290. }
  291. .remove {
  292. float:right;
  293. }
  294. .file_resourcesField {
  295. border-radius: 3px 0 0 3px;
  296. border-right:0;
  297. }
  298. .fileChooserBtn {
  299. border-radius: 0 3px 3px 0;
  300. }
  301. </style>
  302. <script type="text/javascript" charset="utf-8">
  303. $(document).ready(function(){
  304. $("*[rel=tooltip]").tooltip({
  305. placement: 'right'
  306. });
  307. // hack!!!
  308. $("select").addClass("span8");
  309. $("a[data-form-prefix]").each(function(){
  310. var _prefix = $(this).attr("data-form-prefix");
  311. var _nextID = 0;
  312. if ($("."+_prefix+"Field").length){
  313. _nextID= ($("."+_prefix+"Field").last().attr("name").substr(_prefix.length+1).split("-")[0]*1)+1;
  314. }
  315. $("<input>").attr("type","hidden").attr("name",_prefix+"-next_form_id").attr("value",_nextID).appendTo($("#advancedSettingsForm"));
  316. $("."+_prefix+"Delete").click(function(e){
  317. e.preventDefault();
  318. $("input[name="+_prefix+"-add]").attr("value","");
  319. $("<input>").attr("type","hidden").attr("name", $(this).attr("name")).attr("value","True").appendTo($("#advancedSettingsForm"));
  320. checkAndSubmit();
  321. });
  322. });
  323. $("a[data-form-prefix]").click(function(){
  324. var _prefix = $(this).attr("data-form-prefix");
  325. $("<input>").attr("type","hidden").attr("name",_prefix+"-add").attr("value","True").appendTo($("#advancedSettingsForm"));
  326. checkAndSubmit();
  327. });
  328. $(".file_resourcesField").each(function(){
  329. var self = $(this);
  330. self.after(getFileBrowseButton(self));
  331. });
  332. function getFileBrowseButton(inputElement) {
  333. return $("<button>").addClass("btn").addClass("fileChooserBtn").text("..").click(function(e){
  334. e.preventDefault();
  335. $("#filechooser").jHueFileChooser({
  336. onFileChoose: function(filePath) {
  337. inputElement.val(filePath);
  338. $("#chooseFile").modal("hide");
  339. },
  340. createFolder: false
  341. });
  342. $("#chooseFile").modal("show");
  343. })
  344. }
  345. $("#executeQuery").click(function(){
  346. $("<input>").attr("type","hidden").attr("name","button-submit").attr("value","Execute").appendTo($("#advancedSettingsForm"));
  347. checkAndSubmit();
  348. });
  349. $("#saveQuery").click(function(){
  350. $("<input>").attr("type","hidden").attr("name","saveform-name")
  351. .attr("value", "${extract_field_data(form.saveform["name"])}").appendTo($("#advancedSettingsForm"));
  352. $("<input>").attr("type","hidden").attr("name","saveform-desc")
  353. .attr("value", "${extract_field_data(form.saveform["desc"])}").appendTo($("#advancedSettingsForm"));
  354. $("<input>").attr("type","hidden").attr("name","saveform-save").attr("value","Save").appendTo($("#advancedSettingsForm"));
  355. checkAndSubmit();
  356. });
  357. $("#saveQueryAs").click(function(){
  358. $("<input>").attr("type","hidden").attr("name","saveform-saveas").attr("value","Save As...").appendTo($("#advancedSettingsForm"));
  359. $("#saveAs").modal("show");
  360. });
  361. $("#saveAsNameBtn").click(function(){
  362. $("<input>").attr("type","hidden").attr("name","saveform-name")
  363. .attr("value", $("input[name=saveform-name]").val()).appendTo($("#advancedSettingsForm"));
  364. $("<input>").attr("type","hidden").attr("name","saveform-desc")
  365. .attr("value", $("input[name=saveform-desc]").val()).appendTo($("#advancedSettingsForm"));
  366. checkAndSubmit();
  367. });
  368. $("#explainQuery").click(function(){
  369. $("<input>").attr("type","hidden").attr("name","button-explain").attr("value","Explain").appendTo($("#advancedSettingsForm"));
  370. checkAndSubmit();
  371. });
  372. $("#queryField").change(function(){
  373. $(".query").val($(this).val());
  374. });
  375. $("#queryField").focus(function(){
  376. $(this).removeClass("fieldError");
  377. $("#validationResults").empty();
  378. });
  379. function checkAndSubmit(){
  380. // TODO: client side validation
  381. $(".query").val($("#queryField").val());
  382. $("#advancedSettingsForm").submit();
  383. }
  384. });
  385. </script>
  386. ${commonfooter()}