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.

293 lines
8.7 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
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. window.addEventListener('beforeunload', async (event) => {
  53. // Cancel the event as stated by the standard.
  54. event.preventDefault();
  55. const response = await this.$api.discardFile(this.file.id);
  56. return event;
  57. });
  58. }
  59. /**
  60. * MD-to-HTML compiled file content
  61. */
  62. get compiledFileContent(): string {
  63. return marked(this.fileContent);
  64. }
  65. /**
  66. * MD-to-HTML compiled processed file content
  67. */
  68. get compiledProcessedFileContent(): string {
  69. return marked(this.processedFileContent);
  70. }
  71. /**
  72. * MD-to-HTML compiled processed file content with diff highlight
  73. */
  74. get compiledProcessedFileContentPreview(): string {
  75. return marked(this.processedFileContentPreview);
  76. }
  77. /**
  78. * Toggle the sidebar containing the searchers
  79. */
  80. private toggleSearchersSidebar() {
  81. this.searchersSidebarVisible = !this.searchersSidebarVisible;
  82. }
  83. /**
  84. * Toggle the menu containing the list of available searchers
  85. *
  86. * @param {string} newValue The new value for the dialog visibility
  87. */
  88. private toggleSearchersDialog(newValue?: boolean) {
  89. if (typeof newValue !== 'undefined') {
  90. this.searchersDialogVisible = newValue;
  91. } else {
  92. this.searchersDialogVisible = !this.searchersDialogVisible;
  93. }
  94. if ( ! this.searchersDialogVisible) {
  95. for (let selectedSearcher of this.newlySelectedSearchers) {
  96. // this.selectedSearchers[selectedSearcher.id] = selectedSearcher;
  97. this.$set(this.selectedSearchers, selectedSearcher.id, selectedSearcher);
  98. this.expandedRows = Object.values(this.selectedSearchers).filter((p: any) => p.id);
  99. }
  100. this.newlySelectedSearchers = [];
  101. }
  102. }
  103. /**
  104. * Toggle the dialog which lets the user upload a new document
  105. *
  106. * @param {boolean} newValue
  107. */
  108. toggleUploadDialog(newValue?: boolean) {
  109. if (typeof newValue !== 'undefined') {
  110. this.uploadDialogVisible = newValue;
  111. } else {
  112. this.uploadDialogVisible = !this.searchersDialogVisible;
  113. }
  114. }
  115. /**
  116. * A method which uploads the files to the server for processing
  117. *
  118. * @param event The event containing the uploaded files information
  119. */
  120. public async uploadFile(event: any): Promise<void> {
  121. this.$confirm.require({
  122. message: 'You will lose any progress on the current uploaded document. Are you sure you want to proceed?',
  123. header: 'Confirmation',
  124. icon: 'pi pi-exclamation-triangle',
  125. accept: () => {
  126. this.fileContent = this.processedFileContent = '';
  127. let file = event.files[0];
  128. this.toggleUploadDialog(false);
  129. this.$api.discardFile(this.file.id);
  130. this.$emit('newFile', file);
  131. },
  132. reject: () => {
  133. // TODO: Show a message to the user that the action was cancelled.
  134. }
  135. });
  136. }
  137. /**
  138. * Wait for the file to be processed in ingest
  139. */
  140. private async waitForFile() {
  141. const response = await this.$api.getFileData(this.file.id);
  142. if (response.content !== null) {
  143. if (response.ingest_status === 'fail') {
  144. this.$toast.add({
  145. severity: 'error',
  146. summary: 'File error',
  147. detail: 'THere was an error processing the file in ingest',
  148. life: 3000
  149. });
  150. } else {
  151. this.fileContent = response.content;
  152. this.$toast.add({
  153. severity: 'success',
  154. summary: 'File loaded',
  155. detail: 'The file has been processed by ingest.',
  156. life: 3000
  157. });
  158. clearInterval(this.intervalId);
  159. }
  160. }
  161. }
  162. /**
  163. *
  164. * @param $event
  165. */
  166. private onSelectedSearchersReorder($event: any) {
  167. Object.assign({}, this.selectedSearchers, $event.value);
  168. }
  169. private confirmDeleteProduct(searcher: any) {
  170. this.$delete(this.selectedSearchers, searcher.id);
  171. }
  172. /**
  173. * Run the searchers
  174. */
  175. private async runSearchers() {
  176. this.processing = true;
  177. this.processedFileContent = '';
  178. let searchers: Array<{ key: string; replace_with: string; }> = [];
  179. Object.values(this.selectedSearchers).forEach((searcher: any) => {
  180. searchers.push({
  181. 'key': searcher.id,
  182. 'replace_with': this.searchersOptions[searcher.id] || ''
  183. });
  184. });
  185. const response = await this.$api.filterDocument(this.fileContent, searchers);
  186. this.processedFileContent = response.content;
  187. this.documentDiffIndexes = response.indexes;
  188. this.createDiffPreview();
  189. this.processing = false;
  190. }
  191. /**
  192. * Create the diff preview for the document
  193. */
  194. private createDiffPreview() {
  195. console.log('CREATING DIFF PREVIEW: ', this.processedFileContent);
  196. this.processedFileContentPreview = this.processedFileContent;
  197. let indexes: Array<{ start: number; end: number }> = [];
  198. for (let searcher in this.documentDiffIndexes) {
  199. const searcherIndexes = this.documentDiffIndexes[searcher];
  200. searcherIndexes.forEach(index => {
  201. indexes.push(index);
  202. });
  203. }
  204. indexes.sort((a, b) => {
  205. return b.start - a.start;
  206. });
  207. this.processedFileContentPreview = indexes.reduce(
  208. (r, a) => {
  209. r[a.start] = '<mark>' + r[a.start];
  210. r[a.end] += '</mark>';
  211. return r;
  212. },
  213. this.processedFileContent.split('')
  214. ).join('');
  215. }
  216. /**
  217. * Download the document in ODT format
  218. */
  219. private async downloadOdt() {
  220. let response = await this.$api.convertFile(this.processedFileContent, this.file.id);
  221. window.open(`${window.location.origin}/file/download/` + response.path);
  222. }
  223. /**
  224. * Watch the `showDiffHighlight` property for changes
  225. *
  226. * @param {boolean} newValue
  227. * @param {boolean} oldValue
  228. */
  229. @Watch('showDiffHighlight')
  230. private onDiffHighlightChanged(newValue: boolean, oldValue: boolean): void {
  231. //
  232. }
  233. }