Skip to content

Commit a1394ca

Browse files
authored
Merge pull request #3716 from AtlasOfLivingAustralia/feature/issue3715
Fix thumbnail creation for homepage fieldcapture#3715
2 parents 240a444 + 0b6f9a3 commit a1394ca

File tree

3 files changed

+187
-3
lines changed

3 files changed

+187
-3
lines changed

grails-app/services/au/org/ala/merit/DocumentService.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,8 @@ class DocumentService {
243243

244244
url
245245
}
246+
247+
String buildDownloadUrl(Map document) {
248+
buildDownloadUrl(document.filepath, document.filename)
249+
}
246250
}

grails-app/services/au/org/ala/merit/ReportService.groovy

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package au.org.ala.merit
22

3+
import au.org.ala.ecodata.forms.EcpWebService
34
import au.org.ala.merit.config.EmailTemplate
45
import au.org.ala.merit.config.ProgramConfig
56
import au.org.ala.merit.config.ReportConfig
@@ -9,6 +10,7 @@ import au.org.ala.merit.reports.ReportOwner
910
import grails.plugin.cache.Cacheable
1011
import groovy.util.logging.Slf4j
1112
import org.apache.commons.io.FilenameUtils
13+
import org.apache.http.HttpStatus
1214
import org.joda.time.DateTime
1315
import org.joda.time.DateTimeZone
1416
import org.joda.time.Period
@@ -34,7 +36,7 @@ class ReportService {
3436
}
3537

3638
def grailsApplication
37-
def webService
39+
EcpWebService webService
3840
def userService
3941
def projectService
4042
def authService
@@ -836,14 +838,28 @@ class ReportService {
836838
private Map createCustomThumbnail(Map document) {
837839
String thumbName = document.documentId+'-thumb-500.'+FilenameUtils.getExtension(document.filename)
838840
File homePageThumb = new File(imageService.fullPath(thumbName))
841+
File tmpFullSizeImage = null
839842
try {
840-
if (!homePageThumb.exists() && document.url) {
841-
imageService.createThumbnail(new URL(document.url).openStream(), homePageThumb, document.contentType, HOME_PAGE_IMAGE_SIZE)
843+
if (!homePageThumb.exists() && document.url) {
844+
String documentUrl = documentService.buildDownloadUrl(document)
845+
tmpFullSizeImage = File.createTempFile(document.documentId+"-tmp", '.'+FilenameUtils.getExtension(document.filename))
846+
Map resp = null
847+
tmpFullSizeImage.withOutputStream {
848+
resp = webService.readToStream(documentUrl, it, true)
849+
}
850+
if (resp?.statusCode == HttpStatus.SC_OK) {
851+
imageService.createThumbnail(tmpFullSizeImage, homePageThumb, document.contentType, HOME_PAGE_IMAGE_SIZE)
852+
}
842853
}
843854
}
844855
catch (Exception e) {
845856
log.warn("Unable to create thumbnail: ${homePageThumb}")
846857
}
858+
finally {
859+
if (tmpFullSizeImage?.exists()) {
860+
tmpFullSizeImage.delete()
861+
}
862+
}
847863

848864
[thumbnailFile:homePageThumb, fileName:thumbName]
849865
}

src/test/groovy/au/org/ala/merit/ReportServiceSpec.groovy

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class ReportServiceSpec extends Specification implements ServiceUnitTest<ReportS
1919
def emailService = Mock(EmailService)
2020
def userService = Mock(UserService)
2121
def authService = Mock(AuthService)
22+
def documentService = Mock(DocumentService)
23+
def imageService = Mock(ImageService)
24+
def grailsLinkGenerator = Mock(grails.web.mapping.LinkGenerator)
2225

2326
def setup() {
2427

@@ -30,6 +33,9 @@ class ReportServiceSpec extends Specification implements ServiceUnitTest<ReportS
3033
service.grailsApplication = grailsApplication
3134
service.userService = userService
3235
service.authService = authService
36+
service.documentService = documentService
37+
service.imageService = imageService
38+
service.grailsLinkGenerator = grailsLinkGenerator
3339
}
3440

3541
/**
@@ -549,4 +555,162 @@ class ReportServiceSpec extends Specification implements ServiceUnitTest<ReportS
549555
1 * webService.doPost({it.endsWith('search/targetsReportForScoreIds')}, expectedParams) >> [statusCode:200, resp:targetsData]
550556
results.scores[0].target == 100
551557
}
558+
559+
def "homePageImages returns image details with thumbnail URLs when thumbnails exist"() {
560+
setup:
561+
File tmpDir = File.createTempDir()
562+
File thumb1 = new File(tmpDir, 'd1-thumb-500.jpg')
563+
thumb1.text = 'thumb1'
564+
File thumb2 = new File(tmpDir, 'd2-thumb-500.png')
565+
thumb2.text = 'thumb2'
566+
567+
List documents = [
568+
[documentId:'d1', name:'Image 1', attribution:'Author 1', projectId:'p1', filename:'image1.jpg', url:'http://original/image1.jpg', contentType:'image/jpeg'],
569+
[documentId:'d2', name:'Image 2', attribution:'Author 2', projectId:'p2', filename:'image2.png', url:'http://original/image2.png', contentType:'image/png']
570+
]
571+
572+
when:
573+
List result = service.homePageImages()
574+
575+
then:
576+
1 * documentService.search({it.max == 1}) >> [count:10]
577+
1 * documentService.search({it.max == 5}) >> [documents:documents]
578+
projectService.search([projectId:['p1', 'p2']]) >> [resp:[projects:[[projectId:'p1', name:'Project 1'], [projectId:'p2', name:'Project 2']]]]
579+
1 * imageService.fullPath('d1-thumb-500.jpg') >> thumb1.absolutePath
580+
1 * imageService.fullPath('d2-thumb-500.png') >> thumb2.absolutePath
581+
1 * grailsLinkGenerator.link([controller:'image', id:'d1-thumb-500.jpg']) >> '/image/d1-thumb-500.jpg'
582+
1 * grailsLinkGenerator.link([controller:'image', id:'d2-thumb-500.png']) >> '/image/d2-thumb-500.png'
583+
584+
and:
585+
result.size() == 2
586+
result[0].name == 'Image 1'
587+
result[0].attribution == 'Author 1'
588+
result[0].projectName == 'Project 1'
589+
result[0].url == '/image/d1-thumb-500.jpg'
590+
result[0].projectId == 'p1'
591+
result[1].name == 'Image 2'
592+
result[1].projectName == 'Project 2'
593+
result[1].url == '/image/d2-thumb-500.png'
594+
595+
cleanup:
596+
tmpDir.deleteDir()
597+
}
598+
599+
def "homePageImages downloads and creates a thumbnail when one does not already exist"() {
600+
setup:
601+
File tmpDir = File.createTempDir()
602+
String thumbPath = new File(tmpDir, 'd1-thumb-500.jpg').absolutePath
603+
604+
List documents = [
605+
[documentId:'d1', name:'Image 1', attribution:'Author 1', projectId:'p1', filename:'image1.jpg', filepath:'2024-01', url:'http://original/image1.jpg', contentType:'image/jpeg']
606+
]
607+
608+
when:
609+
List result = service.homePageImages()
610+
611+
then:
612+
1 * documentService.search({it.max == 1}) >> [count:10]
613+
1 * documentService.search({it.max == 5}) >> [documents:documents]
614+
projectService.search(_) >> [resp:[projects:[[projectId:'p1', name:'Project 1']]]]
615+
1 * imageService.fullPath('d1-thumb-500.jpg') >> thumbPath
616+
1 * documentService.buildDownloadUrl(documents[0]) >> 'http://ecodata/document/download/2024-01/image1.jpg'
617+
1 * webService.readToStream('http://ecodata/document/download/2024-01/image1.jpg', _, true) >> [statusCode:200]
618+
1 * imageService.createThumbnail(_, {it.absolutePath == thumbPath}, 'image/jpeg', 500) >> { File src, File dest, String ct, int sz ->
619+
dest.text = 'thumbnail'
620+
true
621+
}
622+
1 * grailsLinkGenerator.link([controller:'image', id:'d1-thumb-500.jpg']) >> '/image/d1-thumb-500.jpg'
623+
624+
and:
625+
result.size() == 1
626+
result[0].url == '/image/d1-thumb-500.jpg'
627+
628+
cleanup:
629+
tmpDir.deleteDir()
630+
}
631+
632+
def "homePageImages keeps original URL when thumbnail download fails"() {
633+
setup:
634+
File tmpDir = File.createTempDir()
635+
String thumbPath = new File(tmpDir, 'd1-thumb-500.jpg').absolutePath
636+
637+
List documents = [
638+
[documentId:'d1', name:'Image 1', attribution:'Author 1', projectId:'p1', filename:'image1.jpg', filepath:'2024-01', url:'http://original/image1.jpg', contentType:'image/jpeg']
639+
]
640+
641+
when:
642+
List result = service.homePageImages()
643+
644+
then:
645+
1 * documentService.search({it.max == 1}) >> [count:10]
646+
1 * documentService.search({it.max == 5}) >> [documents:documents]
647+
projectService.search(_) >> [resp:[projects:[[projectId:'p1', name:'Project 1']]]]
648+
1 * imageService.fullPath('d1-thumb-500.jpg') >> thumbPath
649+
1 * documentService.buildDownloadUrl(documents[0]) >> 'http://ecodata/document/download/2024-01/image1.jpg'
650+
1 * webService.readToStream(_, _, true) >> [statusCode:500]
651+
0 * imageService.createThumbnail(_, _, _, _)
652+
0 * grailsLinkGenerator.link(_)
653+
654+
and:
655+
result.size() == 1
656+
result[0].url == 'http://original/image1.jpg'
657+
658+
cleanup:
659+
tmpDir.deleteDir()
660+
}
661+
662+
def "homePageImages handles exceptions during thumbnail creation gracefully"() {
663+
setup:
664+
File tmpDir = File.createTempDir()
665+
String thumbPath = new File(tmpDir, 'd1-thumb-500.jpg').absolutePath
666+
667+
List documents = [
668+
[documentId:'d1', name:'Image 1', attribution:'Author 1', projectId:'p1', filename:'image1.jpg', filepath:'2024-01', url:'http://original/image1.jpg', contentType:'image/jpeg']
669+
]
670+
671+
when:
672+
List result = service.homePageImages()
673+
674+
then:
675+
1 * documentService.search({it.max == 1}) >> [count:10]
676+
1 * documentService.search({it.max == 5}) >> [documents:documents]
677+
projectService.search(_) >> [resp:[projects:[[projectId:'p1', name:'Project 1']]]]
678+
1 * imageService.fullPath('d1-thumb-500.jpg') >> thumbPath
679+
1 * documentService.buildDownloadUrl(documents[0]) >> { throw new RuntimeException("connection error") }
680+
0 * grailsLinkGenerator.link(_)
681+
682+
and: "no exception is thrown and the original URL is preserved"
683+
result.size() == 1
684+
result[0].url == 'http://original/image1.jpg'
685+
686+
cleanup:
687+
tmpDir.deleteDir()
688+
}
689+
690+
def "homePageImages returns empty project name when project is not found"() {
691+
setup:
692+
File tmpDir = File.createTempDir()
693+
File thumb1 = new File(tmpDir, 'd1-thumb-500.jpg')
694+
thumb1.text = 'thumb1'
695+
696+
List documents = [
697+
[documentId:'d1', name:'Image 1', attribution:'Author 1', projectId:'p1', filename:'image1.jpg', url:'http://original/image1.jpg', contentType:'image/jpeg']
698+
]
699+
700+
when:
701+
List result = service.homePageImages()
702+
703+
then:
704+
1 * documentService.search({it.max == 1}) >> [count:10]
705+
1 * documentService.search({it.max == 5}) >> [documents:documents]
706+
projectService.search(_) >> [resp:[projects:[]]]
707+
1 * imageService.fullPath('d1-thumb-500.jpg') >> thumb1.absolutePath
708+
1 * grailsLinkGenerator.link([controller:'image', id:'d1-thumb-500.jpg']) >> '/image/d1-thumb-500.jpg'
709+
710+
and:
711+
result[0].projectName == ''
712+
713+
cleanup:
714+
tmpDir.deleteDir()
715+
}
552716
}

0 commit comments

Comments
 (0)