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.

236 lines
7.0 KiB

  1. <?php
  2. namespace App\Console\Commands;
  3. use App\SearchDisplace\Ingest\SendDocument;
  4. use App\SearchDisplace\SearchAndDisplaceOriginalDocument;
  5. use Illuminate\Console\Command;
  6. use Illuminate\Support\Carbon;
  7. use Illuminate\Support\Facades\Redis;
  8. use Illuminate\Support\Facades\Storage;
  9. class RunSearchDisplace extends Command
  10. {
  11. /**
  12. * The name and signature of the console command.
  13. *
  14. * @var string
  15. */
  16. protected $signature = 'sd:run
  17. {path : The document path}
  18. {filters* : The filters which will be applied to the search}
  19. {--original : Whether the operation will recreate the original document format}';
  20. /**
  21. * The console command description.
  22. *
  23. * @var string
  24. */
  25. protected $description = 'Run search and displace on document with filters.';
  26. /**
  27. * Create a new command instance.
  28. *
  29. * @return void
  30. */
  31. public function __construct()
  32. {
  33. parent::__construct();
  34. // @TODO Add way to handle 'displace'.. Right now we are doing the 'replace' with ':'.
  35. }
  36. /**
  37. * Execute the console command.
  38. *
  39. */
  40. public function handle()
  41. {
  42. $documentPath = $this->argument('path');
  43. $searchers = $this->argument('filters');
  44. $original = $this->option('original');
  45. try {
  46. $this->bootAnalyzer($documentPath);
  47. if ( ! $original) {
  48. $this->runMarkdownOperation($documentPath, $searchers);
  49. } else {
  50. $this->runOriginalDocumentOperation($documentPath, $searchers);
  51. }
  52. $this->info('Processing document..');
  53. $this->info('After the processing will be done the result will show up at the same path as the input.');
  54. } catch (\Exception $exception) {
  55. $this->error('Something went wrong. (' . $exception->getMessage() . ')');
  56. }
  57. }
  58. /**
  59. * @param $documentPath
  60. * @param $searchers
  61. * @throws \Exception
  62. */
  63. protected function runMarkdownOperation($documentPath, $searchers)
  64. {
  65. $id = md5(uniqid(rand(), true));
  66. $pathDetails = pathinfo($documentPath);
  67. $resultedDocumentPath = $pathDetails['dirname'] . '/' . $pathDetails['filename'] . '-displaced.md';
  68. $this->storeSearchers($id, $searchers, $resultedDocumentPath);
  69. $sendToIngest = new SendDocument();
  70. $sendToIngest->execute($id, [
  71. 'path' => $documentPath,
  72. 'name' => $pathDetails['basename'],
  73. 'type' => $this->getFileMimeType($documentPath),
  74. ]);
  75. }
  76. /**
  77. * @param $documentPath
  78. * @param $searchers
  79. * @throws \Exception
  80. */
  81. protected function runOriginalDocumentOperation($documentPath, $searchers)
  82. {
  83. $handler = new SearchAndDisplaceOriginalDocument();
  84. $handler->start($documentPath, $this->getListOfSearchersAndActions($searchers));
  85. }
  86. protected function storeSearchers($id, $searchers, $storeResultPath)
  87. {
  88. $data = [
  89. 'searchers' => $this->getSearchers($searchers),
  90. 'document_path' => $storeResultPath,
  91. ];
  92. $storage = Storage::disk('local');
  93. $storage->put("searchers/$id.json", json_encode($data));
  94. }
  95. protected function bootAnalyzer($filePath)
  96. {
  97. $redis = Redis::connection();
  98. $redis->set('analyze_performance_time', Carbon::now()->format('U'));
  99. $redis->set('analyze_performance_path', pathinfo($filePath,PATHINFO_DIRNAME));
  100. }
  101. protected function getSearchers($searchers)
  102. {
  103. if (count($searchers) === 1 && str_contains($searchers[0], '.json')) {
  104. return $this->getSearchersFromFile($searchers[0]);
  105. }
  106. return $this->getSearchersFromList($searchers);
  107. }
  108. protected function getSearchersFromList($searchers)
  109. {
  110. $storage = Storage::disk('local');
  111. $list = [];
  112. foreach ($this->getListOfSearchersAndActions($searchers) as $searcherInfo) {
  113. $searcherPath = 'searchers/' . $searcherInfo['key'] . '.json';
  114. if ( ! $storage->exists($searcherPath)) {
  115. throw new \Exception('Searcher "' . $searcherInfo['key'] . '" does not exist');
  116. }
  117. $list[] = [
  118. 'content' => json_decode($storage->get($searcherPath), true),
  119. 'type' => $searcherInfo['type'],
  120. 'value' => $searcherInfo['value'],
  121. ];
  122. }
  123. return $list;
  124. }
  125. protected function getSearchersFromFile($argument)
  126. {
  127. $searchersList = $this->getListOfSearchersAndActions([$argument]);
  128. $searcherInfo = $searchersList[0];
  129. $contents = file_get_contents($searcherInfo['key']);
  130. if ( ! $contents) {
  131. throw new \Exception('There is no data in the searcher JSON file.');
  132. }
  133. return [
  134. [
  135. 'content' => json_decode($contents),
  136. 'type' => $searcherInfo['type'],
  137. 'value' => $searcherInfo['value'],
  138. ],
  139. ];
  140. }
  141. protected function getListOfSearchersAndActions($searchers)
  142. {
  143. $searchersList = [];
  144. foreach ($searchers as $searcher) {
  145. $replaceActionResult = explode(':', $searcher);
  146. $searcherKey = $replaceActionResult[0];
  147. $type = 'replace';
  148. $value = '';
  149. if (count($replaceActionResult) === 1) {
  150. $displaceActionResult = explode('+', $searcher);
  151. if (count($displaceActionResult) > 1) {
  152. $searcherKey = $displaceActionResult[0];
  153. $type = 'displace';
  154. $value = $displaceActionResult[1];
  155. }
  156. } else {
  157. $value = $replaceActionResult[1];
  158. }
  159. $searchersList[] = [
  160. 'key' => $searcherKey,
  161. 'type' => $type,
  162. 'value' => $value,
  163. ];
  164. }
  165. return $searchersList;
  166. }
  167. protected function getFileMimeType($file) {
  168. if (function_exists('finfo_file')) {
  169. $finfo = finfo_open(FILEINFO_MIME_TYPE);
  170. $type = finfo_file($finfo, $file);
  171. finfo_close($finfo);
  172. } else {
  173. require_once 'upgradephp/ext/mime.php';
  174. $type = mime_content_type($file);
  175. }
  176. if (!$type || in_array($type, array('application/octet-stream', 'text/plain'))) {
  177. $secondOpinion = exec('file -b --mime-type ' . escapeshellarg($file), $foo, $returnCode);
  178. if ($returnCode === 0 && $secondOpinion) {
  179. $type = $secondOpinion;
  180. }
  181. }
  182. if (!$type || in_array($type, array('application/octet-stream', 'text/plain'))) {
  183. require_once 'upgradephp/ext/mime.php';
  184. $exifImageType = exif_imagetype($file);
  185. if ($exifImageType !== false) {
  186. $type = image_type_to_mime_type($exifImageType);
  187. }
  188. }
  189. return $type;
  190. }
  191. }