RelatedRorController.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. namespace Drupal\related_ror\Controller;
  3. use Drupal\Core\Controller\ControllerBase;
  4. use Symfony\Component\HttpFoundation\Response;
  5. use Symfony\Component\HttpFoundation\JsonResponse;
  6. use Drupal\Core\Cache\CacheableJsonResponse;
  7. use Drupal\Core\Cache\CacheableMetadata;
  8. use Symfony\Component\DependencyInjection\ContainerInterface;
  9. use Drupal\Core\Template\TwigEnvironment;
  10. use Drupal\node\NodeInterface;
  11. class RelatedRorController extends ControllerBase {
  12. protected $state;
  13. protected $twig;
  14. public function __construct($state, TwigEnvironment $twig) {
  15. $this->state = $state;
  16. $this->twig = $twig;
  17. }
  18. public static function create(ContainerInterface $container) {
  19. return new static(
  20. $container->get('state'),
  21. $container->get('twig')
  22. );
  23. }
  24. private function nodeToLinkdata($node_entity) {
  25. $arr = $node_entity->toArray();
  26. return array(
  27. 'nid' => intval($arr['nid'] [0] ['value']),
  28. 'title' => $arr['title'][0]['value'],
  29. //'body' => $arr['body'][0] ['value'],
  30. 'summary' => htmlspecialchars(substr(
  31. html_entity_decode(strip_tags($arr['body'][0]['value'])), 0, 3500), ENT_XML1, 'UTF-8'),
  32. 'url' => $arr['path'][0] ['alias']
  33. );
  34. }
  35. private function queryNearInTime(int $time, int $days = 7, int $limit = 0): array {
  36. $arg = array('ror_news', 'redazionali', 'news_trasmissioni');
  37. $query = \Drupal::entityQuery('node');
  38. $query = \Drupal::database()->select('node', 'n');
  39. $query->addJoin('INNER', 'node__field_tx_date', 'dt', 'n.nid=dt.entity_id');
  40. $query->addJoin('INNER', 'node_field_data', 'field', 'n.nid=field.nid');
  41. $query ->addField('n', 'nid');
  42. $query->addExpression("CAST(UNIX_TIMESTAMP(field_tx_date_value) as UNSIGNED)", 'ts');
  43. $query->addExpression("ABS(cast(UNIX_TIMESTAMP(field_tx_date_value) as signed) - $time)", 'dist');
  44. $query->condition('field.status', '1');
  45. $query->condition('n.type', $arg, 'IN');
  46. $query->where('CAST(UNIX_TIMESTAMP(dt.field_tx_date_value) as UNSIGNED) > :from', array('from' => $time - 3600*24*$days));
  47. $query->where('CAST(UNIX_TIMESTAMP(dt.field_tx_date_value) as UNSIGNED) < :to', array('to' => $time + 3600*24*$days));
  48. $query->orderBy('dist', 'ASC');
  49. if($limit > 0) {
  50. $query->range(0, $limit);
  51. }
  52. if($query->preExecute() !== TRUE) {
  53. return null;
  54. }
  55. $nids = $query->execute()->fetchCol(0);
  56. return $nids;
  57. }
  58. private function presentNids(array $nids) {
  59. $nodes_e = \Drupal\node\Entity\Node::loadMultiple($nids);
  60. $data = ['nodes' => []];
  61. foreach($nodes_e as $nid => $node) {
  62. $nodedata = $this->nodeToLinkdata($node);
  63. array_push($data['nodes'], $nodedata);
  64. }
  65. return $data;
  66. }
  67. public function relatedTime() {
  68. $nid = \Drupal::request()->query->get('nid');
  69. if($nid == null) {
  70. return new Response("Must supply a NID", 400, array('Content-Type' => 'text/plain'));
  71. }
  72. if(!is_numeric($nid)) {
  73. return new Response("NID must be integer, not `$nid`", 400, array('Content-Type' => 'text/plain'));
  74. }
  75. $nid = intval($nid);
  76. $node = \Drupal\node\Entity\Node::load($nid);
  77. if($node == null) {
  78. return new Response("Node not found", 400, array('Content-Type' => 'text/plain'));
  79. }
  80. $time = intval($node->getCreatedTime());
  81. $content .= "\ntime={$time}";
  82. $nearnodes = $this->presentNids(array_filter(
  83. $this->queryNearInTime($time, $days=4, $limit=20),
  84. function ($n) use ($nid): bool { return intval($n) != $nid; }));
  85. $resp = $this->cachedJsonResp(array('time' => $nearnodes));
  86. return $resp;
  87. }
  88. public function relatedTrx() {
  89. // TODO: get node time
  90. // TODO: queryNearInTime($time)
  91. $nid = \Drupal::request()->query->get('nid');
  92. if($nid == null) {
  93. return new Response("Must supply a NID", 400, array('Content-Type' => 'text/plain'));
  94. }
  95. $content = "asd $nid";
  96. $resp = new Response($content, 500, array( 'Content-Type' => 'text/plain'));
  97. return $resp;
  98. }
  99. private function getTermWeight(int $termid): int {
  100. $term = \Drupal\node\Entity\Term::load($termid);
  101. $query = \Drupal::database()->select('taxonomy_index', 'ti');
  102. $query->fields('ti', ['nid']);
  103. $query->condition('ti.tid', $term_id);
  104. $cnt = $query->execute()->rowCount();
  105. if($cnt < 100) {
  106. return 2;
  107. }
  108. return 1;
  109. }
  110. private function getSimilarity(array $orig, array $other): int {
  111. $orig_tags = array_map(function($t) { return $t['target_id']; }, $orig['field_tags']);
  112. $other_tags = array_map(function($t) { return $t['target_id']; }, $other['field_tags']);
  113. $both = array_intersect($orig_tags, $other_tags);
  114. $weighted = array_map($this->getTermWeight, $both);
  115. return array_sum($weighted);
  116. }
  117. private function cachedJsonResp(array $data) {
  118. $data['#cache'] = [
  119. 'max-age' => 600,
  120. 'contexts' => [
  121. 'url',
  122. ]];
  123. $resp = new CacheableJsonResponse($data);
  124. $resp->addCacheableDependency(CacheableMetadata::createFromRenderArray($data));
  125. return $resp;
  126. }
  127. public function relatedTopic() {
  128. // TODO: get node time
  129. // TODO: queryNearInTime($time)
  130. $nid = \Drupal::request()->query->get('nid');
  131. if($nid == null) {
  132. return new Response("Must supply a NID", 400, array('Content-Type' => 'text/plain'));
  133. }
  134. if(!is_numeric($nid)) {
  135. return new Response("NID must be integer, not `$nid`", 400, array('Content-Type' => 'text/plain'));
  136. }
  137. $nid = intval($nid);
  138. $node = \Drupal\node\Entity\Node::load($nid);
  139. if($node == null) {
  140. return new Response("Node not found", 400, array('Content-Type' => 'text/plain'));
  141. }
  142. $orig_arr = $node->toArray();
  143. $time = intval($node->getCreatedTime());
  144. $goodnids = [];
  145. $scores = [];
  146. $other_e = \Drupal\node\Entity\Node::loadMultiple($this->queryNearInTime($time, $days=30, $limit=100));
  147. foreach($other_e as $other_nid => $other_node) {
  148. if(intval($other_nid) === $nid) {
  149. continue;
  150. }
  151. $score = $this->getSimilarity($orig_arr, $other_node->toArray());
  152. if($score > 0) {
  153. $scores[$other_nid] = $score;
  154. }
  155. }
  156. arsort($scores);
  157. foreach($scores as $nid => $score) {
  158. array_push($goodnids, $nid);
  159. }
  160. $resp = $this->cachedJsonResp(['topic' => $this->presentNids($goodnids)]);
  161. return $resp;
  162. }
  163. }