@@ -2,47 +2,66 @@ package data
22
33import com .gu .atom .data .PreviewDynamoDataStoreV2
44import com .gu .media .CapiAccess
5- import com .gu .media .model .{ContentChangeDetails , Image , MediaAtom }
5+ import com .gu .media .model .Platform .{Url , Youtube }
6+ import com .gu .media .model .VideoPlayerFormat .Loop
7+ import com .gu .media .model .{
8+ ContentChangeDetails ,
9+ Image ,
10+ MediaAtom ,
11+ Platform ,
12+ VideoPlayerFormat
13+ }
614import com .gu .media .util .TestFilters
715import model .commands .CommandExceptions .AtomDataStoreError
816import model .{MediaAtomList , MediaAtomSummary }
17+ import play .api .Logging
918import play .api .libs .json .{JsArray , JsValue }
1019
1120trait AtomListStore {
1221 def getAtoms (
1322 search : Option [String ],
1423 limit : Option [Int ],
1524 shouldUseCreatedDateForSort : Boolean ,
16- mediaPlatform : Option [String ]
25+ platformFilter : Option [String ],
26+ orderByOldest : Boolean
1727 ): MediaAtomList
1828}
1929
20- class CapiBackedAtomListStore (capi : CapiAccess ) extends AtomListStore {
30+ class CapiBackedAtomListStore (capi : CapiAccess )
31+ extends AtomListStore
32+ with Logging {
33+
34+ // CAPI max page size is 200
35+ val CapiMaxPageSize = 200
36+
2137 override def getAtoms (
2238 search : Option [String ],
2339 limit : Option [Int ],
2440 shouldUseCreatedDateForSort : Boolean ,
25- mediaPlatform : Option [String ]
41+ platformFilter : Option [String ],
42+ orderByOldest : Boolean
2643 ): MediaAtomList = {
27- // CAPI max page size is 200
28- val cappedLimit : Option [Int ] = limit.map(Math .min(200 , _))
44+ val pagination = Pagination .option(CapiMaxPageSize , limit)
2945
3046 val dateSorter = shouldUseCreatedDateForSort match {
3147 case true => Map (" order-date" -> " first-publication" )
3248 case false => Map .empty
3349 }
3450
35- val mediaPlatformFilter = mediaPlatform match {
51+ val mediaPlatformFilter = platformFilter match {
3652 case Some (mPlatform) => Map (" media-platform" -> mPlatform)
3753 case _ => Map .empty
3854 }
3955
40- val base : Map [String , String ] = Map (
41- " types" -> " media" ,
42- " order-by" -> " newest"
43- ) ++
56+ val orderBy = orderByOldest match {
57+ case true => Map (" order-by" -> " oldest" )
58+ case false => Map (" order-by" -> " newest" )
59+ }
60+
61+ val base : Map [String , String ] = Map (" types" -> " media" ) ++
4462 dateSorter ++
45- mediaPlatformFilter
63+ mediaPlatformFilter ++
64+ orderBy
4665
4766 val baseWithSearch = search match {
4867 case Some (q) =>
@@ -53,20 +72,57 @@ class CapiBackedAtomListStore(capi: CapiAccess) extends AtomListStore {
5372 case None => base
5473 }
5574
56- val baseWithSearchAndLimit = cappedLimit match {
57- case Some (pageSize) =>
75+ val baseWithSearchAndLimit = pagination match {
76+ case Some (Pagination ( pageSize, _) ) =>
5877 baseWithSearch ++ Map (
5978 " page-size" -> pageSize.toString
6079 )
6180 case None => baseWithSearch
6281 }
6382
64- val response = capi.capiQuery(" atoms" , baseWithSearchAndLimit)
83+ val nPages = pagination.map(_.pageCount).getOrElse(1 )
84+
85+ val (total, _, atoms) =
86+ (1 to nPages).foldLeft(0 , nPages, List .empty[MediaAtomSummary ]) {
87+ case ((prevTotal, prevMaxPage, prevAtoms), page) =>
88+ val pageNumber = pagination match {
89+ case Some (_) => Map (" page" -> page.toString)
90+ case None => Map .empty
91+ }
92+ // make sure we don't request beyond the last page
93+ val (total, maxPage, atoms) = if (page <= prevMaxPage) {
94+ getCapiAtoms(baseWithSearchAndLimit ++ pageNumber)
95+ } else {
96+ (prevTotal, prevMaxPage, Nil )
97+ }
98+ (
99+ total,
100+ maxPage,
101+ prevAtoms ++ atoms
102+ )
103+ }
104+
105+ val limitedAtoms = limit match {
106+ case Some (limit) => atoms.take(limit)
107+ case None => atoms
108+ }
109+
110+ logger.info(s " total $total, atoms: ${limitedAtoms.size}" )
111+ MediaAtomList (total, limitedAtoms)
112+ }
65113
114+ private [data] def getCapiAtoms (
115+ query : Map [String , String ]
116+ ): (Int , Int , List [MediaAtomSummary ]) = {
117+ val response = capi.capiQuery(" atoms" , query)
66118 val total = (response \ " response" \ " total" ).as[Int ]
119+ val maxPage = (response \ " response" \ " pages" ).as[Int ]
67120 val results = (response \ " response" \ " results" ).as[JsArray ]
68-
69- MediaAtomList (total, results.value.flatMap(fromJson).toList)
121+ (
122+ total,
123+ maxPage,
124+ results.value.flatMap(fromJson).toList
125+ )
70126 }
71127
72128 private def fromJson (wrapper : JsValue ): Option [MediaAtomSummary ] = {
@@ -94,38 +150,42 @@ class CapiBackedAtomListStore(capi: CapiAccess) extends AtomListStore {
94150 (asset \ " version" ).as[Long ]
95151 }
96152
97- val mediaPlatforms = (atom \ " assets" )
98- .as[JsArray ]
99- .value
100- .flatMap { asset =>
101- (asset \ " platform" ).asOpt[String ].map(_.toLowerCase)
102- }
103- .toList
104- .distinct
153+ val atomPlatform =
154+ (atom \ " platform" ).asOpt[Platform ]
105155
106- val currentAsset = (atom \ " assets" ).as[JsArray ].value.find { asset =>
156+ val activeAsset = (atom \ " assets" ).as[JsArray ].value.find { asset =>
107157 val assetVersion = (asset \ " version" ).as[Long ]
108158 activeVersion.contains(assetVersion)
109159 }
110160
111- val currentMediaPlatform = currentAsset.flatMap { asset =>
112- (asset \ " platform" ).asOpt[ String ].map(_.toLowerCase)
161+ val activeAssetPlatform = activeAsset.map { asset =>
162+ (asset \ " platform" ).as[ Platform ]
113163 }
114164
115- // sort media platforms so the current one is first
116- val sortedMediaPlatforms = currentMediaPlatform match {
117- case Some (current) => current :: mediaPlatforms.filter(_ != current)
118- case None => mediaPlatforms
119- }
165+ val firstAssetPlatform =
166+ (atom \ " assets" ).as[JsArray ].value.headOption.map { asset =>
167+ (asset \ " platform" ).as[Platform ]
168+ }
169+
170+ val platform = Platform .getPlatform(
171+ atomPlatform,
172+ activeAssetPlatform,
173+ firstAssetPlatform
174+ )
175+
176+ val videoPlayerFormat =
177+ (atom \ " metadata" \ " selfHost" \ " videoPlayerFormat" )
178+ .asOpt[VideoPlayerFormat ]
179+ .orElse(if (platform == Url ) Some (Loop ) else None )
120180
121181 Some (
122182 MediaAtomSummary (
123183 id,
124184 title,
125185 posterImage,
126186 contentChangeDetails,
127- sortedMediaPlatforms ,
128- currentMediaPlatform
187+ platform ,
188+ videoPlayerFormat
129189 )
130190 )
131191 }
@@ -138,7 +198,8 @@ class DynamoBackedAtomListStore(store: PreviewDynamoDataStoreV2)
138198 search : Option [String ],
139199 limit : Option [Int ],
140200 shouldUseCreatedDateForSort : Boolean ,
141- mediaPlatform : Option [String ]
201+ mediaPlatform : Option [String ],
202+ orderByOldest : Boolean
142203 ): MediaAtomList = {
143204 // We must filter the entire list of atoms rather than use Dynamo limit to ensure stable iteration order.
144205 // Without it, the front page will shuffle around when clicking the Load More button.
@@ -157,7 +218,11 @@ class DynamoBackedAtomListStore(store: PreviewDynamoDataStoreV2)
157218 .map(MediaAtom .fromThrift)
158219 .toList
159220 .sortBy(sortField(_).map(_.date.getMillis))
160- .reverse // newest atoms first
221+
222+ val mediaAtomsSorted = orderByOldest match {
223+ case true => mediaAtoms
224+ case false => mediaAtoms.reverse
225+ }
161226
162227 val searchTermFilter = search match {
163228 case Some (str) => Some ((atom : MediaAtom ) => atom.title.contains(str))
@@ -177,7 +242,7 @@ class DynamoBackedAtomListStore(store: PreviewDynamoDataStoreV2)
177242 val filters = List (searchTermFilter, mediaPlatformFilter).flatten
178243
179244 val filteredAtoms =
180- filters.foldLeft(mediaAtoms )((atoms, f) => atoms.filter(f))
245+ filters.foldLeft(mediaAtomsSorted )((atoms, f) => atoms.filter(f))
181246
182247 val limitedAtoms = limit match {
183248 case Some (l) => filteredAtoms.take(l)
@@ -189,27 +254,13 @@ class DynamoBackedAtomListStore(store: PreviewDynamoDataStoreV2)
189254 }
190255
191256 private def fromAtom (atom : MediaAtom ): MediaAtomSummary = {
192- val versions = atom.assets.map(_.version).toSet
193- val currentAsset = atom.assets.find(asset =>
194- asset.version == atom.activeVersion.getOrElse(versions.max)
195- )
196- val mediaPlatforms = atom.assets.map(_.platform.name.toLowerCase).distinct
197- val currentMediaPlatform =
198- currentAsset.map(_.platform.name).map(_.toLowerCase)
199-
200- // sort media platforms so the current one is first
201- val sortedMediaPlatforms = currentMediaPlatform match {
202- case Some (current) => current :: mediaPlatforms.filter(_ != current)
203- case None => mediaPlatforms
204- }
205-
206257 MediaAtomSummary (
207258 atom.id,
208259 atom.title,
209260 atom.posterImage,
210261 atom.contentChangeDetails,
211- sortedMediaPlatforms ,
212- currentMediaPlatform
262+ atom.platform.getOrElse( Youtube ) ,
263+ atom.videoPlayerFormat
213264 )
214265 }
215266}
@@ -225,3 +276,15 @@ object AtomListStore {
225276 case _ => new CapiBackedAtomListStore (capi)
226277 }
227278}
279+
280+ case class Pagination (pageSize : Int , pageCount : Int )
281+
282+ object Pagination {
283+ def option (maxPageSize : Int , limit : Option [Int ]): Option [Pagination ] = {
284+ limit.map { limit =>
285+ val pageSize = Math .min(maxPageSize, limit)
286+ val pageCount = Math .ceil(1.0 * limit / maxPageSize).toInt
287+ Pagination (pageSize, pageCount)
288+ }
289+ }
290+ }
0 commit comments