Dan Brown

Added Popular books list with relevant tests

...@@ -42,8 +42,9 @@ class BookController extends Controller ...@@ -42,8 +42,9 @@ class BookController extends Controller
42 public function index() 42 public function index()
43 { 43 {
44 $books = $this->bookRepo->getAllPaginated(10); 44 $books = $this->bookRepo->getAllPaginated(10);
45 - $recents = $this->signedIn ? $this->bookRepo->getRecentlyViewed(10, 0) : false; 45 + $recents = $this->signedIn ? $this->bookRepo->getRecentlyViewed(4, 0) : false;
46 - return view('books/index', ['books' => $books, 'recents' => $recents]); 46 + $popular = $this->bookRepo->getPopular(4, 0);
47 + return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular]);
47 } 48 }
48 49
49 /** 50 /**
......
...@@ -77,6 +77,11 @@ class BookRepo ...@@ -77,6 +77,11 @@ class BookRepo
77 return Views::getUserRecentlyViewed($count, $page, $this->book); 77 return Views::getUserRecentlyViewed($count, $page, $this->book);
78 } 78 }
79 79
80 + public function getPopular($count = 10, $page = 0)
81 + {
82 + return Views::getPopular($count, $page, $this->book);
83 + }
84 +
80 /** 85 /**
81 * Get a book by slug 86 * Get a book by slug
82 * @param $slug 87 * @param $slug
......
...@@ -44,6 +44,29 @@ class ViewService ...@@ -44,6 +44,29 @@ class ViewService
44 return 1; 44 return 1;
45 } 45 }
46 46
47 +
48 + /**
49 + * Get the entities with the most views.
50 + * @param int $count
51 + * @param int $page
52 + * @param bool|false $filterModel
53 + */
54 + public function getPopular($count = 10, $page = 0, $filterModel = false)
55 + {
56 + $skipCount = $count * $page;
57 + $query = $this->view->select('id', 'viewable_id', 'viewable_type', \DB::raw('SUM(views) as view_count'))
58 + ->groupBy('viewable_id', 'viewable_type')
59 + ->orderBy('view_count', 'desc');
60 +
61 + if($filterModel) $query->where('viewable_type', '=', get_class($filterModel));
62 +
63 + $views = $query->with('viewable')->skip($skipCount)->take($count)->get();
64 + $viewedEntities = $views->map(function ($item) {
65 + return $item->viewable()->getResults();
66 + });
67 + return $viewedEntities;
68 + }
69 +
47 /** 70 /**
48 * Get all recently viewed entities for the current user. 71 * Get all recently viewed entities for the current user.
49 * @param int $count 72 * @param int $count
......
...@@ -34,12 +34,23 @@ ...@@ -34,12 +34,23 @@
34 @endif 34 @endif
35 </div> 35 </div>
36 <div class="col-sm-4 col-sm-offset-1"> 36 <div class="col-sm-4 col-sm-offset-1">
37 - <div class="margin-top large">&nbsp;</div> 37 + <div id="recents">
38 @if($recents) 38 @if($recents)
39 + <div class="margin-top large">&nbsp;</div>
39 <h3>Recently Viewed</h3> 40 <h3>Recently Viewed</h3>
40 @include('partials/entity-list', ['entities' => $recents]) 41 @include('partials/entity-list', ['entities' => $recents])
41 @endif 42 @endif
42 </div> 43 </div>
44 + <div class="margin-top large">&nbsp;</div>
45 + <div id="popular">
46 + <h3>Popular Books</h3>
47 + @if(count($popular) > 0)
48 + @include('partials/entity-list', ['entities' => $popular])
49 + @else
50 + <p class="text-muted">The most popular books will appear here.</p>
51 + @endif
52 + </div>
53 + </div>
43 </div> 54 </div>
44 </div> 55 </div>
45 56
......
1 1
2 @if(count($entities) > 0) 2 @if(count($entities) > 0)
3 - @foreach($entities as $entity) 3 + @foreach($entities as $index => $entity)
4 @if($entity->isA('page')) 4 @if($entity->isA('page'))
5 @include('pages/list-item', ['page' => $entity]) 5 @include('pages/list-item', ['page' => $entity])
6 @elseif($entity->isA('book')) 6 @elseif($entity->isA('book'))
...@@ -8,7 +8,11 @@ ...@@ -8,7 +8,11 @@
8 @elseif($entity->isA('chapter')) 8 @elseif($entity->isA('chapter'))
9 @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true]) 9 @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true])
10 @endif 10 @endif
11 +
12 + @if($index !== count($entities) - 1)
11 <hr> 13 <hr>
14 + @endif
15 +
12 @endforeach 16 @endforeach
13 @else 17 @else
14 <p class="text-muted"> 18 <p class="text-muted">
......
1 +<?php
2 +
3 +use Illuminate\Foundation\Testing\WithoutMiddleware;
4 +use Illuminate\Foundation\Testing\DatabaseMigrations;
5 +use Illuminate\Foundation\Testing\DatabaseTransactions;
6 +
7 +class ActivityTrackingTest extends TestCase
8 +{
9 +
10 + public function testRecentlyViewedBooks()
11 + {
12 + $books = \BookStack\Book::all()->take(10);
13 +
14 + $this->asAdmin()->visit('/books')
15 + ->dontSeeInElement('#recents', $books[0]->name)
16 + ->dontSeeInElement('#recents', $books[1]->name)
17 + ->visit($books[0]->getUrl())
18 + ->visit($books[1]->getUrl())
19 + ->visit('/books')
20 + ->seeInElement('#recents', $books[0]->name)
21 + ->seeInElement('#recents', $books[1]->name);
22 + }
23 +
24 + public function testPopularBooks()
25 + {
26 + $books = \BookStack\Book::all()->take(10);
27 +
28 + $this->asAdmin()->visit('/books')
29 + ->dontSeeInElement('#popular', $books[0]->name)
30 + ->dontSeeInElement('#popular', $books[1]->name)
31 + ->visit($books[0]->getUrl())
32 + ->visit($books[1]->getUrl())
33 + ->visit($books[0]->getUrl())
34 + ->visit('/books')
35 + ->seeInNthElement('#popular .book', 0, $books[0]->name)
36 + ->seeInNthElement('#popular .book', 1, $books[1]->name);
37 + }
38 +}
...@@ -48,4 +48,31 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase ...@@ -48,4 +48,31 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
48 $settings->put($key, $value); 48 $settings->put($key, $value);
49 } 49 }
50 } 50 }
51 +
52 + /**
53 + * Assert that a given string is seen inside an element.
54 + *
55 + * @param bool|string|null $element
56 + * @param integer $position
57 + * @param string $text
58 + * @param bool $negate
59 + * @return $this
60 + */
61 + protected function seeInNthElement($element, $position, $text, $negate = false)
62 + {
63 + $method = $negate ? 'assertNotRegExp' : 'assertRegExp';
64 +
65 + $rawPattern = preg_quote($text, '/');
66 +
67 + $escapedPattern = preg_quote(e($text), '/');
68 +
69 + $content = $this->crawler->filter($element)->eq($position)->html();
70 +
71 + $pattern = $rawPattern == $escapedPattern
72 + ? $rawPattern : "({$rawPattern}|{$escapedPattern})";
73 +
74 + $this->$method("/$pattern/i", $content);
75 +
76 + return $this;
77 + }
51 } 78 }
......