Add new/flexible "download results" feature
This commit is contained in:
		
							parent
							
								
									43472c7eb6
								
							
						
					
					
						commit
						922cbe4451
					
				
					 3 changed files with 553 additions and 3 deletions
				
			
		|  | @ -143,6 +143,155 @@ | |||
| 
 | ||||
| <%def name="grid_tools()"> | ||||
| 
 | ||||
|   ## download search results | ||||
|   % if master.results_downloadable and master.has_perm('download_results'): | ||||
|       % if use_buefy: | ||||
|           <b-button type="is-primary" | ||||
|                     icon-pack="fas" | ||||
|                     icon-left="fas fa-download" | ||||
|                     @click="showDownloadResultsDialog = true" | ||||
|                     :disabled="!total"> | ||||
|             Download Results | ||||
|           </b-button> | ||||
| 
 | ||||
|           ${h.form(url('{}.download_results'.format(route_prefix)), ref='download_results_form')} | ||||
|           ${h.csrf_token(request)} | ||||
|           <input type="hidden" name="fmt" :value="downloadResultsFormat" /> | ||||
|           <input type="hidden" name="fields" :value="downloadResultsFieldsIncluded" /> | ||||
|           ${h.end_form()} | ||||
| 
 | ||||
|           <b-modal :active.sync="showDownloadResultsDialog"> | ||||
|             <div class="card"> | ||||
| 
 | ||||
|               <div class="card-content"> | ||||
|                 <p> | ||||
|                   There are | ||||
|                   <span class="is-size-4 has-text-weight-bold"> | ||||
|                     {{ total.toLocaleString('en') }} ${model_title_plural} | ||||
|                   </span> | ||||
|                   matching your current filters. | ||||
|                 </p> | ||||
|                 <p> | ||||
|                   You may download this set as a single data file if you like. | ||||
|                 </p> | ||||
|                 <br /> | ||||
| 
 | ||||
|                 <b-notification type="is-warning" :closable="false" | ||||
|                                 v-if="downloadResultsFormat == 'xlsx' && total >= 1000"> | ||||
|                   Excel downloads for large data sets can take a long time to | ||||
|                   generate, and bog down the server in the meantime.  You are | ||||
|                   encouraged to choose CSV for a large data set, even though | ||||
|                   the end result (file size) may be larger with CSV. | ||||
|                 </b-notification> | ||||
| 
 | ||||
|                 <div style="display: flex;"> | ||||
| 
 | ||||
|                   <div style="flex-grow: 1;"> | ||||
|                     <b-field horizontal label="Format"> | ||||
|                       <b-select v-model="downloadResultsFormat"> | ||||
|                         % for key, label in six.iteritems(master.download_results_supported_formats()): | ||||
|                         <option value="${key}">${label}</option> | ||||
|                         % endfor | ||||
|                       </b-select> | ||||
|                     </b-field> | ||||
|                   </div> | ||||
| 
 | ||||
|                   <div> | ||||
|                     <div v-show="downloadResultsFieldsMode != 'choose'" | ||||
|                          class="has-text-right"> | ||||
|                       <p v-if="downloadResultsFieldsMode == 'default'"> | ||||
|                         Will use DEFAULT fields. | ||||
|                       </p> | ||||
|                       <p v-if="downloadResultsFieldsMode == 'all'"> | ||||
|                         Will use ALL fields. | ||||
|                       </p> | ||||
|                     </div> | ||||
|                     <br /> | ||||
|                     <div class="buttons"> | ||||
|                       <b-button type="is-primary" | ||||
|                                 v-show="downloadResultsFieldsMode != 'default'" | ||||
|                                 @click="downloadResultsUseDefaultFields()"> | ||||
|                         Use Default Fields | ||||
|                       </b-button> | ||||
|                       <b-button type="is-primary" | ||||
|                                 v-show="downloadResultsFieldsMode != 'all'" | ||||
|                                 @click="downloadResultsUseAllFields()"> | ||||
|                         Use All Fields | ||||
|                       </b-button> | ||||
|                       <b-button type="is-primary" | ||||
|                                 v-show="downloadResultsFieldsMode != 'choose'" | ||||
|                                 @click="downloadResultsFieldsMode = 'choose'"> | ||||
|                         Choose Fields | ||||
|                       </b-button> | ||||
|                     </div> | ||||
|                   </div> | ||||
| 
 | ||||
|                 </div> | ||||
|                 <br /> | ||||
| 
 | ||||
|                 <div v-show="downloadResultsFieldsMode == 'choose'"> | ||||
|                   <div style="display: flex;"> | ||||
|                     <div> | ||||
|                       <b-field label="Excluded Fields"> | ||||
|                         <b-select multiple native-size="8" | ||||
|                                   expanded | ||||
|                                   ref="downloadResultsExcludedFields"> | ||||
|                           <option v-for="field in downloadResultsFieldsAvailable" | ||||
|                                   v-if="!downloadResultsFieldsIncluded.includes(field)" | ||||
|                                   :key="field" | ||||
|                                   :value="field"> | ||||
|                             {{ field }} | ||||
|                           </option> | ||||
|                         </b-select> | ||||
|                       </b-field> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                       <br /><br /> | ||||
|                       <b-button style="margin: 0.5rem;" | ||||
|                                 @click="downloadResultsExcludeFields()"> | ||||
|                         < | ||||
|                       </b-button> | ||||
|                       <br /> | ||||
|                       <b-button style="margin: 0.5rem;" | ||||
|                                 @click="downloadResultsIncludeFields()"> | ||||
|                         > | ||||
|                       </b-button> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                       <b-field label="Included Fields"> | ||||
|                         <b-select multiple native-size="8" | ||||
|                                   expanded | ||||
|                                   ref="downloadResultsIncludedFields"> | ||||
|                           <option v-for="field in downloadResultsFieldsIncluded" | ||||
|                                   :key="field" | ||||
|                                   :value="field"> | ||||
|                             {{ field }} | ||||
|                           </option> | ||||
|                         </b-select> | ||||
|                       </b-field> | ||||
|                     </div> | ||||
|                   </div> | ||||
|                 </div> | ||||
| 
 | ||||
|               </div> <!-- card-content --> | ||||
| 
 | ||||
|               <footer class="modal-card-foot"> | ||||
|                 <b-button @click="showDownloadResultsDialog = false"> | ||||
|                   Cancel | ||||
|                 </b-button> | ||||
|                 <once-button type="is-primary" | ||||
|                              @click="downloadResultsSubmit()" | ||||
|                              icon-pack="fas" | ||||
|                              icon-left="fas fa-download" | ||||
|                              :disabled="!downloadResultsFieldsIncluded.length" | ||||
|                              text="Download Results"> | ||||
|                 </once-button> | ||||
|               </footer> | ||||
|             </div> | ||||
|           </b-modal> | ||||
|       % endif | ||||
|   % endif | ||||
| 
 | ||||
|   ## merge 2 objects | ||||
|   % if master.mergeable and request.has_perm('{}.merge'.format(permission_prefix)): | ||||
| 
 | ||||
|  | @ -256,6 +405,14 @@ | |||
| </%def> | ||||
| 
 | ||||
| <%def name="page_content()"> | ||||
| 
 | ||||
|   % if download_results_path: | ||||
|       <b-notification type="is-info"> | ||||
|         Your download should start automatically, or you can | ||||
|         ${h.link_to("click here", '{}?filename={}'.format(url('{}.download_results'.format(route_prefix)), h.os.path.basename(download_results_path)))} | ||||
|       </b-notification> | ||||
|   % endif | ||||
| 
 | ||||
|   <${grid.component} :csrftoken="csrftoken" | ||||
|      % if master.deletable and request.has_perm('{}.delete'.format(permission_prefix)) and master.delete_confirm == 'simple': | ||||
|      @deleteActionClicked="deleteObject" | ||||
|  | @ -295,6 +452,19 @@ | |||
|   ${parent.modify_this_page_vars()} | ||||
|   <script type="text/javascript"> | ||||
| 
 | ||||
|     ## maybe auto-redirect to download latest results file | ||||
|     % if download_results_path and use_buefy: | ||||
|         ThisPage.methods.downloadResultsRedirect = function() { | ||||
|             location.href = '${url('{}.download_results'.format(route_prefix))}?filename=${h.os.path.basename(download_results_path)}'; | ||||
|         } | ||||
|         ThisPage.mounted = function() { | ||||
|             // we give this 1 second before attempting the redirect; otherwise | ||||
|             // the FontAwesome icons do not seem to load properly.  so this way | ||||
|             // the page should fully render before redirecting | ||||
|             window.setTimeout(this.downloadResultsRedirect, 1000) | ||||
|         } | ||||
|     % endif | ||||
| 
 | ||||
|     ## TODO: stop checking for buefy here once we only have the one session.pop() | ||||
|     % if use_buefy and request.session.pop('{}.results_csv.generated'.format(route_prefix), False): | ||||
|         ThisPage.mounted = function() { | ||||
|  | @ -318,6 +488,83 @@ | |||
|         } | ||||
|     % endif | ||||
| 
 | ||||
|     ## download results | ||||
|     % if master.results_downloadable and master.has_perm('download_results'): | ||||
| 
 | ||||
|         ${grid.component_studly}Data.downloadResultsFormat = '${master.download_results_default_format()}' | ||||
|         ${grid.component_studly}Data.showDownloadResultsDialog = false | ||||
|         ${grid.component_studly}Data.downloadResultsFieldsMode = 'default' | ||||
|         ${grid.component_studly}Data.downloadResultsFieldsAvailable = ${json.dumps(download_results_fields_available)|n} | ||||
|         ${grid.component_studly}Data.downloadResultsFieldsDefault = ${json.dumps(download_results_fields_default)|n} | ||||
|         ${grid.component_studly}Data.downloadResultsFieldsIncluded = ${json.dumps(download_results_fields_default)|n} | ||||
| 
 | ||||
|         ${grid.component_studly}.computed.downloadResultsFieldsExcluded = function() { | ||||
|             let excluded = [] | ||||
|             this.downloadResultsFieldsAvailable.forEach(field => { | ||||
|                 if (!this.downloadResultsFieldsIncluded.includes(field)) { | ||||
|                     excluded.push(field) | ||||
|                 } | ||||
|             }, this) | ||||
|             return excluded | ||||
|         } | ||||
| 
 | ||||
|         ${grid.component_studly}.methods.downloadResultsExcludeFields = function() { | ||||
|             let selected = this.$refs.downloadResultsIncludedFields.selected | ||||
|             if (!selected) { | ||||
|                 return | ||||
|             } | ||||
|             selected = Array.from(selected) | ||||
|             selected.forEach(field => { | ||||
| 
 | ||||
|                 // de-select the entry within "included" field input | ||||
|                 let index = this.$refs.downloadResultsIncludedFields.selected.indexOf(field) | ||||
|                 if (index > -1) { | ||||
|                     this.$refs.downloadResultsIncludedFields.selected.splice(index, 1) | ||||
|                 } | ||||
| 
 | ||||
|                 // remove field from official "included" list | ||||
|                 index = this.downloadResultsFieldsIncluded.indexOf(field) | ||||
|                 if (index > -1) { | ||||
|                     this.downloadResultsFieldsIncluded.splice(index, 1) | ||||
|                 } | ||||
|             }, this) | ||||
|         } | ||||
| 
 | ||||
|         ${grid.component_studly}.methods.downloadResultsIncludeFields = function() { | ||||
|             let selected = this.$refs.downloadResultsExcludedFields.selected | ||||
|             if (!selected) { | ||||
|                 return | ||||
|             } | ||||
|             selected = Array.from(selected) | ||||
|             selected.forEach(field => { | ||||
| 
 | ||||
|                 // de-select the entry within "excluded" field input | ||||
|                 let index = this.$refs.downloadResultsExcludedFields.selected.indexOf(field) | ||||
|                 if (index > -1) { | ||||
|                     this.$refs.downloadResultsExcludedFields.selected.splice(index, 1) | ||||
|                 } | ||||
| 
 | ||||
|                 // add field to official "included" list | ||||
|                 this.downloadResultsFieldsIncluded.push(field) | ||||
| 
 | ||||
|             }, this) | ||||
|         } | ||||
| 
 | ||||
|         ${grid.component_studly}.methods.downloadResultsUseDefaultFields = function() { | ||||
|             this.downloadResultsFieldsIncluded = Array.from(this.downloadResultsFieldsDefault) | ||||
|             this.downloadResultsFieldsMode = 'default' | ||||
|         } | ||||
| 
 | ||||
|         ${grid.component_studly}.methods.downloadResultsUseAllFields = function() { | ||||
|             this.downloadResultsFieldsIncluded = Array.from(this.downloadResultsFieldsAvailable) | ||||
|             this.downloadResultsFieldsMode = 'all' | ||||
|         } | ||||
| 
 | ||||
|         ${grid.component_studly}.methods.downloadResultsSubmit = function() { | ||||
|             this.$refs.download_results_form.submit() | ||||
|         } | ||||
|     % endif | ||||
| 
 | ||||
|     ## enable / disable selected objects | ||||
|     % if master.supports_set_enabled_toggle and master.has_perm('enable_disable_set'): | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar