CorpusController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Models\Sentence;
  4. use App\Models\Channel;
  5. use App\Models\PaliText;
  6. use App\Models\WbwTemplate;
  7. use App\Models\WbwBlock;
  8. use App\Models\Wbw;
  9. use Illuminate\Http\Request;
  10. use Illuminate\Support\Facades\Cache;
  11. use App\Http\Api\MdRender;
  12. use App\Http\Api\SuggestionApi;
  13. use App\Http\Api\ChannelApi;
  14. use Illuminate\Support\Facades\Log;
  15. class CorpusController extends Controller
  16. {
  17. protected $result = [
  18. "uid"=> '',
  19. "title"=> '',
  20. "path"=>[],
  21. "sub_title"=> '',
  22. "summary"=> '',
  23. "content"=> '',
  24. "content_type"=> "html",
  25. "status"=>30,
  26. "lang"=> "",
  27. "created_at"=> "",
  28. "updated_at"=> "",
  29. ];
  30. protected $wbwChannels = [];
  31. protected $selectCol = ['book_id','paragraph','word_start',"word_end",'channel_uid','content','updated_at'];
  32. public function __construct()
  33. {
  34. }
  35. /**
  36. * Display a listing of the resource.
  37. *
  38. * @return \Illuminate\Http\Response
  39. */
  40. public function index()
  41. {
  42. //
  43. }
  44. /**
  45. * Store a newly created resource in storage.
  46. *
  47. * @param \Illuminate\Http\Request $request
  48. * @return \Illuminate\Http\Response
  49. */
  50. public function store(Request $request)
  51. {
  52. //
  53. }
  54. /**
  55. * Display the specified resource.
  56. *
  57. * @param \App\Models\Sentence $sentence
  58. * @return \Illuminate\Http\Response
  59. */
  60. public function show(Sentence $sentence)
  61. {
  62. //
  63. }
  64. public function getSentTpl($id,$channels){
  65. $sent = [];
  66. $sentId = \explode('-',$id);
  67. $channelId = ChannelApi::getSysChannel('_System_Wbw_VRI_');
  68. if($channelId !== false){
  69. array_push($channels,$channelId);
  70. }
  71. $record = Sentence::select($this->selectCol)
  72. ->where('book_id',$sentId[0])
  73. ->where('paragraph',$sentId[1])
  74. ->where('word_start',$sentId[2])
  75. ->where('word_end',$sentId[3])
  76. ->whereIn('channel_uid',$channels)
  77. ->get();
  78. Log::info("sent count:".count($record));
  79. $channelIndex = $this->getChannelIndex($channels);
  80. $content = $this->makeContent($record,"edit",$channelIndex);
  81. return $content;
  82. }
  83. /**
  84. * Display the specified resource.
  85. *
  86. * @param string $id
  87. * @return \Illuminate\Http\Response
  88. */
  89. public function showSent($id)
  90. {
  91. //
  92. $param = \explode('_',$id);
  93. if(count($param)>1){
  94. $channels = array_slice($param,1);
  95. }else{
  96. $channels = [];
  97. }
  98. $this->result['content'] = getSentTpl($param[0],$channels);
  99. return $this->ok($this->result);
  100. }
  101. public function showSentences($type,$id,$mode='read'){
  102. $param = \explode('_',$id);
  103. $sentId = \explode('-',$param[0]);
  104. $channels = [];
  105. #获取channel类型
  106. $sentChannel = Sentence::select('channel_uid')
  107. ->where('book_id',$sentId[0])
  108. ->where('paragraph',$sentId[1])
  109. ->where('word_start',$sentId[2])
  110. ->where('word_end',$sentId[3])
  111. ->get();
  112. foreach ($sentChannel as $key => $value) {
  113. # code...
  114. $channels[] = $value->channel_uid;
  115. }
  116. $channelInfo = Channel::whereIn("uid",$channels)->select(['uid','type','name'])->get();
  117. $indexChannel = [];
  118. $channels = [];
  119. foreach ($channelInfo as $key => $value) {
  120. # code...
  121. if($value->type === $type){
  122. $indexChannel[$value->uid] = $value;
  123. $channels[] = $value->uid;
  124. }
  125. }
  126. //获取句子数据
  127. $record = Sentence::select($this->selectCol)
  128. ->where('book_id',$sentId[0])
  129. ->where('paragraph',$sentId[1])
  130. ->where('word_start',$sentId[2])
  131. ->where('word_end',$sentId[3])
  132. ->whereIn('channel_uid',$channels)
  133. ->orderBy('paragraph')
  134. ->orderBy('word_start')
  135. ->get();
  136. if(count($record) ===0){
  137. return $this->error("no data");
  138. }
  139. $this->result['content'] = $this->makeContent($record,$mode,$indexChannel);
  140. return $this->ok($this->result);
  141. }
  142. public function showChapter($id,$mode='read')
  143. {
  144. //
  145. $param = \explode('_',$id);
  146. $sentId = \explode('-',$param[0]);
  147. $channels = [];
  148. if(count($param)>1){
  149. $channels = array_slice($param,1);
  150. }
  151. if($mode === 'read'){
  152. //阅读模式加载md格式原文
  153. $channelId = ChannelApi::getSysChannel('_System_Pali_VRI_');
  154. }else{
  155. //翻译模式加载json格式原文
  156. $channelId = ChannelApi::getSysChannel('_System_Wbw_VRI_');
  157. }
  158. if($channelId !== false){
  159. $channels[] = $channelId;
  160. }
  161. $chapter = PaliText::where('book',$sentId[0])->where('paragraph',$sentId[1])->first();
  162. if(!$chapter){
  163. return $this->error("no data");
  164. }
  165. if(empty($chapter->toc)){
  166. $this->result['title'] = "unknown";
  167. }else{
  168. $this->result['title'] = $chapter->toc;
  169. $this->result['sub_title'] = $chapter->toc;
  170. $this->result['path'] = json_decode($chapter->path);
  171. }
  172. $paraFrom = $sentId[1];
  173. $paraTo = $sentId[1]+$chapter->chapter_len-1;
  174. //获取标题
  175. $heading = PaliText::select(["book","paragraph","level"])
  176. ->where('book',$sentId[0])
  177. ->whereBetween('paragraph',[$paraFrom,$paraTo])
  178. ->where('level','<',8)
  179. ->get();
  180. //将标题段落转成索引数组 以便输出标题层级
  181. $indexedHeading = [];
  182. foreach ($heading as $key => $value) {
  183. # code...
  184. $indexedHeading["{$value->book}-{$value->paragraph}"] = $value->level;
  185. }
  186. #获取channel索引表
  187. $tranChannels = [];
  188. $channelInfo = Channel::whereIn("uid",$channels)->select(['uid','type','name'])->get();
  189. $indexChannel = [];
  190. foreach ($channelInfo as $key => $value) {
  191. # code...
  192. $indexChannel[$value->uid] = $value;
  193. if($value->type==="translation" ){
  194. $tranChannels[] = $value->uid;
  195. }
  196. }
  197. //获取wbw channel
  198. //目前默认的 wbw channel 是第一个translation channel
  199. foreach ($channels as $key => $value) {
  200. # code...
  201. if($indexChannel[$value]->type==='translation'){
  202. $this->wbwChannels[] = $value;
  203. break;
  204. }
  205. }
  206. $title = Sentence::select($this->selectCol)
  207. ->where('book_id',$sentId[0])
  208. ->where('paragraph',$sentId[1])
  209. ->whereIn('channel_uid',$tranChannels)
  210. ->first();
  211. if($title){
  212. $this->result['title'] = MdRender::render($title->content,$title->channel_uid);
  213. }
  214. //获取句子数据
  215. $record = Sentence::select($this->selectCol)
  216. ->where('book_id',$sentId[0])
  217. ->whereBetween('paragraph',[$paraFrom,$paraTo])
  218. ->whereIn('channel_uid',$channels)
  219. ->orderBy('paragraph')
  220. ->orderBy('word_start')
  221. ->get();
  222. if(count($record) ===0){
  223. return $this->error("no data");
  224. }
  225. $this->result['content'] = $this->makeContent($record,$mode,$indexChannel,$indexedHeading);
  226. return $this->ok($this->result);
  227. }
  228. private function getChannelIndex($channels){
  229. #获取channel索引表
  230. $channelInfo = Channel::whereIn("uid",$channels)->select(['uid','type','name'])->get();
  231. $indexChannel = [];
  232. foreach ($channelInfo as $key => $value) {
  233. # code...
  234. $indexChannel[$value->uid] = $value;
  235. }
  236. return $indexChannel;
  237. }
  238. /**
  239. * 根据句子库数据生成文章内容
  240. * $record 句子数据
  241. * $mode read | edit | wbw
  242. * $indexChannel channel索引
  243. * $indexedHeading 标题索引 用于给段落加标题标签 <h1> ect.
  244. */
  245. private function makeContent($record,$mode,$indexChannel,$indexedHeading=[]){
  246. $content = [];
  247. $lastSent = "0-0";
  248. $sentCount = 0;
  249. foreach ($record as $key => $value) {
  250. # 遍历结果生成html文件
  251. $currSentId = $value->book_id.'-'.$value->paragraph.'-'.$value->word_start.'-'.$value->word_end;
  252. if($currSentId !== $lastSent){
  253. if($sentCount > 0){
  254. //保存上一个句子
  255. //增加标题的html标记
  256. $level = 0;
  257. if(isset($indexedHeading["{$value->book_id}-{$value->paragraph}"])){
  258. $level = $indexedHeading["{$value->book_id}-{$value->paragraph}"];
  259. }
  260. $content = $this->pushSent($content,$sent,$level,$mode);
  261. }
  262. //新建句子
  263. $sent = $this->newSent($value->book_id,$value->paragraph,$value->word_start,$value->word_end);
  264. $lastSent = $currSentId;
  265. }
  266. $sentContent=$value->content;
  267. $channelType = $indexChannel[$value->channel_uid]->type;
  268. if($indexChannel[$value->channel_uid]->type==="original" && $mode !== 'read'){
  269. //非阅读模式下。原文使用逐词解析数据。优先加载第一个translation channel 如果没有。加载默认逐词解析。
  270. $channelType = 'wbw';
  271. $html = "";
  272. if(count($this->wbwChannels)>0){
  273. //获取逐词解析数据
  274. $wbwBlock = WbwBlock::where('channel_uid',$this->wbwChannels[0])
  275. ->where('book_id',$value->book_id)
  276. ->where('paragraph',$value->paragraph)
  277. ->select('uid')
  278. ->first();
  279. if($wbwBlock){
  280. //找到逐词解析数据
  281. $wbwData = Wbw::where('block_uid',$wbwBlock->uid)
  282. ->whereBetween('wid',[$value->word_start,$value->word_end])
  283. ->select('data')
  284. ->orderBy('wid')
  285. ->get();
  286. $wbwContent = [];
  287. foreach ($wbwData as $wbwrow) {
  288. $wbw = str_replace("&nbsp;",' ',$wbwrow->data);
  289. $wbw = str_replace("<br>",' ',$wbw);
  290. $xmlString = "<root>" . $wbw . "</root>";
  291. try{
  292. $xmlWord = simplexml_load_string($xmlString);
  293. }catch(Exception $e){
  294. continue;
  295. }
  296. $wordsList = $xmlWord->xpath('//word');
  297. foreach ($wordsList as $word) {
  298. $case = \str_replace(['#','.'],['$',''],$word->case->__toString());
  299. $case = \str_replace('$$','$',$case);
  300. $case = trim($case);
  301. $case = trim($case,"$");
  302. $wbwContent[] = [
  303. 'word'=>['value'=>$word->pali->__toString(),'status'=>0],
  304. 'real'=> ['value'=>$word->real->__toString(),'status'=>0],
  305. 'meaning'=> ['value'=>\explode('$',$word->mean->__toString()) ,'status'=>0],
  306. 'type'=> ['value'=>$word->type->__toString(),'status'=>0],
  307. 'grammar'=> ['value'=>$word->gramma->__toString(),'status'=>0],
  308. 'case'=> ['value'=>\explode('$',$case),'status'=>0],
  309. 'parent'=> ['value'=>$word->parent->__toString(),'status'=>0],
  310. 'style'=> ['value'=>$word->style->__toString(),'status'=>0],
  311. 'factors'=> ['value'=>$word->org->__toString(),'status'=>0],
  312. 'factorMeaning'=> ['value'=>$word->om->__toString(),'status'=>0],
  313. 'confidence'=> 0.5
  314. ];
  315. }
  316. }
  317. $sentContent = \json_encode($wbwContent);
  318. }
  319. }
  320. }else{
  321. $html = Cache::remember("/sent/{$value->channel_uid}/{$currSentId}",10,
  322. function() use($value){
  323. return MdRender::render($value->content,$value->channel_uid);
  324. });
  325. }
  326. $newSent = [
  327. "content"=>$sentContent,
  328. "html"=> $html,
  329. "book"=> $value->book_id,
  330. "para"=> $value->paragraph,
  331. "wordStart"=> $value->word_start,
  332. "wordEnd"=> $value->word_end,
  333. "editor"=> [
  334. 'id'=>$value->editor_uid,
  335. 'nickName'=>'nickname',
  336. 'realName'=>'realName',
  337. 'avatar'=>'',
  338. ],
  339. "channel"=> [
  340. "name"=>$indexChannel[$value->channel_uid]->name,
  341. "type"=>$channelType,
  342. "id"=> $value->channel_uid,
  343. ],
  344. "updateAt"=> $value->updated_at,
  345. "suggestionCount" => SuggestionApi::getCountBySent($value->book_id,$value->paragraph,$value->word_start,$value->word_end,$value->channel_uid),
  346. ];
  347. switch ($indexChannel[$value->channel_uid]->type) {
  348. case 'original';
  349. case 'wbw';
  350. array_push($sent["origin"],$newSent);
  351. break;
  352. default:
  353. array_push($sent["translation"],$newSent);
  354. break;
  355. }
  356. $sentCount++;
  357. }
  358. $content = $this->pushSent($content,$sent,0,$mode);
  359. return \implode("",$content);
  360. }
  361. private function pushSent($result,$sent,$level=0,$mode='read'){
  362. $sentProps = base64_encode(\json_encode($sent)) ;
  363. if($mode === 'read'){
  364. $sentWidget = "<MdTpl tpl='sentread' props='{$sentProps}' />";
  365. }else{
  366. $sentWidget = "<MdTpl tpl='sentedit' props='{$sentProps}' />";
  367. }
  368. //增加标题的html标记
  369. if($level>0){
  370. $sentWidget = "<h{$level}>".$sentWidget."</h{$level}>";
  371. }
  372. array_push($result,$sentWidget);
  373. return $result;
  374. }
  375. private function newSent($book,$para,$word_start,$word_end){
  376. $sent = [
  377. "id"=>"{$book}-{$para}-{$word_start}-{$word_end}",
  378. "origin"=>[],
  379. "translation"=>[],
  380. ];
  381. #生成channel 数量列表
  382. $sentId = "{$book}-{$para}-{$word_start}-{$word_end}";
  383. $channelCount = Cache::remember("/sent1/{$sentId}/channels/count",
  384. 60,
  385. function() use($book,$para,$word_start,$word_end){
  386. $channels = Sentence::where('book_id',$book)
  387. ->where('paragraph',$para)
  388. ->where('word_start',$word_start)
  389. ->where('word_end',$word_end)
  390. ->select('channel_uid')
  391. ->groupBy('channel_uid')
  392. ->get();
  393. $channelList = [];
  394. foreach ($channels as $key => $value) {
  395. # code...
  396. $channelList[] = $value->channel_uid;
  397. }
  398. $channelInfo = Channel::whereIn("uid",$channelList)->select('type')->get();
  399. $output["tranNum"]=0;
  400. $output["nissayaNum"]=0;
  401. $output["commNum"]=0;
  402. $output["originNum"]=0;
  403. foreach ($channelInfo as $key => $value) {
  404. # code...
  405. switch($value->type){
  406. case "translation":
  407. $output["tranNum"]++;
  408. break;
  409. case "nissaya":
  410. $output["nissayaNum"]++;
  411. break;
  412. case "commentary":
  413. $output["commNum"]++;
  414. break;
  415. case "original":
  416. $output["originNum"]++;
  417. break;
  418. }
  419. }
  420. return $output;
  421. });
  422. $sent["tranNum"] = $channelCount['tranNum'];
  423. $sent["nissayaNum"] = $channelCount['nissayaNum'];
  424. $sent["commNum"] = $channelCount['commNum'];
  425. $sent["originNum"] = $channelCount['originNum'];
  426. return $sent;
  427. }
  428. private function markdownRender($input){
  429. }
  430. /**
  431. * Update the specified resource in storage.
  432. *
  433. * @param \Illuminate\Http\Request $request
  434. * @param \App\Models\Sentence $sentence
  435. * @return \Illuminate\Http\Response
  436. */
  437. public function update(Request $request, Sentence $sentence)
  438. {
  439. //
  440. }
  441. /**
  442. * Remove the specified resource from storage.
  443. *
  444. * @param \App\Models\Sentence $sentence
  445. * @return \Illuminate\Http\Response
  446. */
  447. public function destroy(Sentence $sentence)
  448. {
  449. //
  450. }
  451. }