Repo for the search and displace core module including the interface to select files and search and displace operations to run on them. https://searchanddisplace.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
8.3 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. import marked from 'marked';
  2. import {Vue, Component, Prop, Watch} from 'vue-property-decorator';
  3. import {FileData} from '@/interfaces/FileData';
  4. @Component
  5. export default class ProcessFile extends Vue {
  6. /**
  7. * Props
  8. */
  9. // The data for the file we are processing
  10. @Prop({default: {id: -1, file: '', path: ''}})
  11. public readonly file!: FileData;
  12. // The list of available searchers
  13. @Prop({default: []})
  14. public readonly searchers!: [];
  15. /**
  16. * Class members
  17. */
  18. // The id of the interval used to query the file status
  19. private intervalId!: any;
  20. // The content of the file we are processing
  21. private fileContent: string = '';
  22. // The processed document content
  23. private processedFileContent: string = '';
  24. private processedFileContentPreview: string = '';
  25. private documentDiffIndexes: { [key: string]: Array<{ start: number; end: number; }>; } = {};
  26. // Flag to determine whether the text is processing or not
  27. private processing: boolean = false;
  28. // Toggles the visibility of the selected searchers sidebar
  29. private searchersSidebarVisible: boolean = false;
  30. // Toggles the visibility of the available searchers dialog
  31. private searchersDialogVisible: boolean = false;
  32. // Toggles the visibility of the document upload dialog
  33. private uploadDialogVisible: boolean = false;
  34. // The list of filters/searchers in a format usable by the datatable
  35. // private searchersData: Array<{ id: string; name: string; type: string; }> = [];
  36. // The list of filters applied to the selected searchers
  37. private searchersFilters: any = [];
  38. // The list of selected filters/searchers
  39. private selectedSearchers: any = {};
  40. //The list of expanded rows in the selected filters/searchers table
  41. private expandedRows: Array<any> = [];
  42. // The list of options applied to the searchers (for the moment, only replace_with)
  43. private searchersOptions: { [key: string]: string } = {};
  44. // Flag to determine whether or not we will show the diff highlights
  45. private showDiffHighlight: boolean = false;
  46. private newlySelectedSearchers: Array<{ id: string; name: string; }> = [];
  47. /**
  48. *
  49. */
  50. created() {
  51. this.intervalId = setInterval(this.waitForFile, 3000);
  52. }
  53. /**
  54. * MD-to-HTML compiled file content
  55. */
  56. get compiledFileContent(): string {
  57. return marked(this.fileContent);
  58. }
  59. /**
  60. * MD-to-HTML compiled processed file content
  61. */
  62. get compiledProcessedFileContent(): string {
  63. return marked(this.processedFileContent);
  64. }
  65. /**
  66. * MD-to-HTML compiled processed file content with diff highlight
  67. */
  68. get compiledProcessedFileContentPreview(): string {
  69. return marked(this.processedFileContentPreview);
  70. }
  71. /**
  72. * Toggle the sidebar containing the searchers
  73. */
  74. private toggleSearchersSidebar() {
  75. this.searchersSidebarVisible = !this.searchersSidebarVisible;
  76. }
  77. /**
  78. * Toggle the menu containing the list of available searchers
  79. *
  80. * @param {string} newValue The new value for the dialog visibility
  81. */
  82. private toggleSearchersDialog(newValue?: boolean) {
  83. if (typeof newValue !== 'undefined') {
  84. this.searchersDialogVisible = newValue;
  85. } else {
  86. this.searchersDialogVisible = !this.searchersDialogVisible;
  87. }
  88. if ( ! this.searchersDialogVisible) {
  89. for (let selectedSearcher of this.newlySelectedSearchers) {
  90. // this.selectedSearchers[selectedSearcher.id] = selectedSearcher;
  91. this.$set(this.selectedSearchers, selectedSearcher.id, selectedSearcher);
  92. this.expandedRows = Object.values(this.selectedSearchers).filter((p: any) => p.id);
  93. }
  94. this.newlySelectedSearchers = [];
  95. }
  96. }
  97. /**
  98. * Toggle the dialog which lets the user upload a new document
  99. *
  100. * @param {boolean} newValue
  101. */
  102. toggleUploadDialog(newValue?: boolean) {
  103. if (typeof newValue !== 'undefined') {
  104. this.uploadDialogVisible = newValue;
  105. } else {
  106. this.uploadDialogVisible = !this.searchersDialogVisible;
  107. }
  108. }
  109. /**
  110. * A method which uploads the files to the server for processing
  111. *
  112. * @param event The event containing the uploaded files information
  113. */
  114. public async uploadFile(event: any): Promise<void> {
  115. this.$confirm.require({
  116. message: 'You will lose any progress on the current uploaded document. Are you sure you want to proceed?',
  117. header: 'Confirmation',
  118. icon: 'pi pi-exclamation-triangle',
  119. accept: () => {
  120. this.fileContent = this.processedFileContent = '';
  121. let file = event.files[0];
  122. this.toggleUploadDialog(false);
  123. this.$emit('newFile', file);
  124. },
  125. reject: () => {
  126. // TODO: Show a message to the user that the action was cancelled.
  127. }
  128. });
  129. }
  130. /**
  131. * Wait for the file to be processed in ingest
  132. */
  133. private async waitForFile() {
  134. const response = await this.$api.getFileData(this.file.id);
  135. if (response.content !== null) {
  136. if (response.ingest_status === 'fail') {
  137. this.$toast.add({
  138. severity: 'error',
  139. summary: 'File error',
  140. detail: 'THere was an error processing the file in ingest',
  141. life: 3000
  142. });
  143. } else {
  144. this.fileContent = response.content;
  145. this.$toast.add({
  146. severity: 'success',
  147. summary: 'File loaded',
  148. detail: 'The file has been processed by ingest.',
  149. life: 3000
  150. });
  151. clearInterval(this.intervalId);
  152. }
  153. }
  154. }
  155. /**
  156. *
  157. * @param $event
  158. */
  159. private onSelectedSearchersReorder($event: any) {
  160. Object.assign({}, this.selectedSearchers, $event.value);
  161. }
  162. private confirmDeleteProduct(searcher: any) {
  163. this.$delete(this.selectedSearchers, searcher.id);
  164. }
  165. /**
  166. * Run the searchers
  167. */
  168. private async runSearchers() {
  169. this.processing = true;
  170. this.processedFileContent = '';
  171. let searchers: Array<{ key: string; replace_with: string; }> = [];
  172. Object.values(this.selectedSearchers).forEach((searcher: any) => {
  173. searchers.push({
  174. 'key': searcher.id,
  175. 'replace_with': this.searchersOptions[searcher.id] || ''
  176. });
  177. });
  178. const response = await this.$api.filterDocument(this.fileContent, searchers);
  179. this.processedFileContent = response.content;
  180. this.documentDiffIndexes = response.indexes;
  181. this.createDiffPreview();
  182. this.processing = false;
  183. }
  184. /**
  185. * Create the diff preview for the document
  186. */
  187. private createDiffPreview() {
  188. console.log('CREATING DIFF PREVIEW: ', this.processedFileContent);
  189. this.processedFileContentPreview = this.processedFileContent;
  190. let indexes: Array<{ start: number; end: number }> = [];
  191. for (let searcher in this.documentDiffIndexes) {
  192. const searcherIndexes = this.documentDiffIndexes[searcher];
  193. searcherIndexes.forEach(index => {
  194. indexes.push(index);
  195. });
  196. }
  197. indexes.sort((a, b) => {
  198. return b.start - a.start;
  199. });
  200. this.processedFileContentPreview = indexes.reduce(
  201. (r, a) => {
  202. r[a.start] = '<mark>' + r[a.start];
  203. r[a.end] += '</mark>';
  204. return r;
  205. },
  206. this.processedFileContent.split('')
  207. ).join('');
  208. }
  209. /**
  210. * Download the document in ODT format
  211. */
  212. private async downloadOdt() {
  213. let response = await this.$api.convertFile(this.processedFileContent);
  214. window.open(`${window.location.origin}/file/download/` + response.path);
  215. }
  216. /**
  217. * Watch the `showDiffHighlight` property for changes
  218. *
  219. * @param {boolean} newValue
  220. * @param {boolean} oldValue
  221. */
  222. @Watch('showDiffHighlight')
  223. private onDiffHighlightChanged(newValue: boolean, oldValue: boolean): void {
  224. //
  225. }
  226. }