Added round parameter. Closes #1

This commit is contained in:
Erik C. Thauvin 2021-05-30 17:40:03 -07:00
parent d99218f43b
commit 6cabd2e723
7 changed files with 102 additions and 93 deletions

View file

@ -22,7 +22,17 @@ To get the estimated reading time in seconds use the `calcReadingTimeInSec()` fu
- View [Kotlin](https://github.com/ethauvin/readingtime/blob/master/examples/src/main/kotlin/com/example/ReadingTimeExample.kt) or [Java](https://github.com/ethauvin/readingtime/blob/master/examples/src/main/java/com/example/ReadingTimeSample.java) Examples. - View [Kotlin](https://github.com/ethauvin/readingtime/blob/master/examples/src/main/kotlin/com/example/ReadingTimeExample.kt) or [Java](https://github.com/ethauvin/readingtime/blob/master/examples/src/main/java/com/example/ReadingTimeSample.java) Examples.
### Gradle, Maven, etc.
To use with [Gradle](https://gradle.org/), include the following dependency in your [build](https://github.com/ethauvin/readingtime/blob/master/examples/build.gradle.kts) file:
```gradle
dependencies {
implementation("net.thauvin.erik:readingtime:0.9.1")
}
```
Instructions for using with Maven, Ivy, etc. can be found on [Maven Central](https://search.maven.org/artifact/net.thauvin.erik/readingtime/0.9.0/jar).
### Properties ### Properties
@ -30,24 +40,26 @@ The following properties are available:
```kotlin ```kotlin
ReadingTime( ReadingTime(
text = "some_text", text,
wpm = 275, wpm = 275,
postfix = "min read", postfix = "min read",
plural = "min read", plural = "min read",
excludeImages = false, excludeImages = false,
extra = 0 extra = 0,
round = RoundingMode.HALF_DOWN
) )
``` ```
Property | Description Property | Description
:-------------------------- |:------------------------------------------------------------------- :-------------------------- |:-----------------------------------------------------------------------------------------------------------------------
`text` | The text to be evaluated. `text` | The text to be evaluated. (Required)
`wpm` | The words per minute reading average. `wpm` | The words per minute reading average.
`postfix` | The value to be appended to the reading time. `postfix` | The value to be appended to the reading time.
`plural` | The value to be appended if the reading time is more than 1 minute. `plural` | The value to be appended if the reading time is more than 1 minute.
`excludeImages` | Images are excluded from the reading time when set. `excludeImages` | Images are excluded from the reading time when set.
`extra` | Additional seconds to be added to the total reading time. `extra` | Additional seconds to be added to the total reading time.
`round` | The [rounding mode](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/RoundingMode.html) to apply.
### Functions ### Functions
@ -73,15 +85,3 @@ A JSP tag is also available for easy incorporation into web applications:
``` ```
None of the attributes are required. None of the attributes are required.
### Gradle, Maven, etc.
To use with [Gradle](https://gradle.org/), include the following dependency in your [build](https://github.com/ethauvin/readingtime/blob/master/examples/build.gradle.kts) file:
```gradle
dependencies {
implementation("net.thauvin.erik:readingtime:0.9.0")
}
```
Instructions for using with Maven, Ivy, etc. can be found on [Maven Central](https://search.maven.org/artifact/net.thauvin.erik/readingtime/0.9.0/jar).

View file

@ -6,16 +6,16 @@ import java.io.FileInputStream
import java.util.* import java.util.*
plugins { plugins {
id("com.github.ben-manes.versions") version "0.38.0" id("com.github.ben-manes.versions") version "0.39.0"
id("io.gitlab.arturbosch.detekt") version "1.17.1" id("io.gitlab.arturbosch.detekt") version "1.17.1"
id("jacoco")
id("java")
id("java-library")
id("maven-publish")
id("org.jetbrains.dokka") version "1.4.32" id("org.jetbrains.dokka") version "1.4.32"
id("org.jetbrains.kotlin.jvm") version "1.5.0"
id("org.sonarqube") version "3.2.0" id("org.sonarqube") version "3.2.0"
java id("signing")
`java-library` kotlin("jvm") version "1.5.10"
`maven-publish`
jacoco
signing
} }
description = "Estimated Reading Time for Blog Posts, Articles, etc." description = "Estimated Reading Time for Blog Posts, Articles, etc."
@ -34,12 +34,12 @@ repositories {
} }
dependencies { dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation(platform(kotlin("bom")))
implementation("org.jsoup:jsoup:1.13.1") implementation("org.jsoup:jsoup:1.13.1")
testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation(kotlin("test"))
testImplementation("org.jetbrains.kotlin:kotlin-test-junit") testImplementation(kotlin("test-junit"))
} }
java { java {

View file

@ -1,11 +1,11 @@
<?xml version="1.0" ?> <?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline> <SmellBaseline>
<ManuallySuppressedIssues/> <ManuallySuppressedIssues/>
<CurrentIssues> <CurrentIssues>
<ID>LongParameterList:ReadingTime.kt$ReadingTime$( text: String, wpm: Int = 275, var postfix: String = "min read", var plural: String = "min read", excludeImages: Boolean = false, extra: Int = 0, var round: RoundingMode = RoundingMode.HALF_DOWN )</ID>
<ID>MagicNumber:ReadingTime.kt$ReadingTime$10</ID> <ID>MagicNumber:ReadingTime.kt$ReadingTime$10</ID>
<ID>MagicNumber:ReadingTime.kt$ReadingTime$12</ID> <ID>MagicNumber:ReadingTime.kt$ReadingTime$12</ID>
<ID>MagicNumber:ReadingTime.kt$ReadingTime$3</ID> <ID>MagicNumber:ReadingTime.kt$ReadingTime$3</ID>
<ID>MagicNumber:ReadingTime.kt$ReadingTime$60</ID>
<ID>MagicNumber:ReadingTime.kt$ReadingTime$60.0</ID> <ID>MagicNumber:ReadingTime.kt$ReadingTime$60.0</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View file

@ -1,7 +1,7 @@
plugins { plugins {
id("org.jetbrains.kotlin.jvm") version "1.5.0" id("application")
id("com.github.ben-manes.versions") version "0.38.0" id("com.github.ben-manes.versions") version "0.39.0"
application kotlin("jvm") version "1.5.10"
} }
// ./gradlew run // ./gradlew run

View file

@ -40,7 +40,7 @@
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-bom</artifactId> <artifactId>kotlin-bom</artifactId>
<version>1.5.0</version> <version>1.5.10</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
@ -50,7 +50,7 @@
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId> <artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.5.0</version> <version>1.5.10</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>

View file

@ -49,6 +49,7 @@ import java.math.RoundingMode
* @param plural The value to be appended if the reading time is more than 1 minute. * @param plural The value to be appended if the reading time is more than 1 minute.
* @param excludeImages Images are excluded from the reading time when set. * @param excludeImages Images are excluded from the reading time when set.
* @param extra Additional seconds to be added to the total reading time. * @param extra Additional seconds to be added to the total reading time.
* @param round The [RoundingMode] to apply. Default is [RoundingMode.HALF_DOWN].
*/ */
class ReadingTime @JvmOverloads constructor( class ReadingTime @JvmOverloads constructor(
text: String, text: String,
@ -56,7 +57,8 @@ class ReadingTime @JvmOverloads constructor(
var postfix: String = "min read", var postfix: String = "min read",
var plural: String = "min read", var plural: String = "min read",
excludeImages: Boolean = false, excludeImages: Boolean = false,
extra: Int = 0 extra: Int = 0,
var round: RoundingMode = RoundingMode.HALF_DOWN
) { ) {
companion object { companion object {
private const val INVALID: Double = -1.0 private const val INVALID: Double = -1.0
@ -109,6 +111,8 @@ class ReadingTime @JvmOverloads constructor(
/** /**
* Calculates and returns the reading time in seconds. * Calculates and returns the reading time in seconds.
*
* `((word count / wpm) * 60) + images + extra`
*/ */
fun calcReadingTimeInSec(): Double { fun calcReadingTimeInSec(): Double {
if (readTime == INVALID) { if (readTime == INVALID) {
@ -121,9 +125,11 @@ class ReadingTime @JvmOverloads constructor(
/** /**
* Calculates and returns the reading time. (eg. 1 min read) * Calculates and returns the reading time. (eg. 1 min read)
*
* `(reading time in sec / 60) + postfix`
*/ */
fun calcReadingTime(): String { fun calcReadingTime(): String {
val time = BigDecimal((calcReadingTimeInSec() / 60.0)).setScale(0, RoundingMode.HALF_DOWN) val time = BigDecimal((calcReadingTimeInSec() / 60.0)).setScale(0, round)
return if (time.compareTo(BigDecimal.ONE) == 1) { return if (time.compareTo(BigDecimal.ONE) == 1) {
"$time $plural".trim() "$time $plural".trim()
} else { } else {
@ -131,6 +137,10 @@ class ReadingTime @JvmOverloads constructor(
} }
} }
/**
* 12 seconds for the first image, 11 for the second, and minus an additional second for each subsequent image.
* Any images after the tenth image are counted at 3 seconds.
*/
private fun calcImgReadingTime(): Int { private fun calcImgReadingTime(): Int {
var time = 0 var time = 0
val imgCount = imgCount(text) val imgCount = imgCount(text)

View file

@ -33,28 +33,16 @@
package net.thauvin.erik.readingtime package net.thauvin.erik.readingtime
import java.io.File import java.io.File
import java.math.RoundingMode
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class ReadingTimeTest { class ReadingTimeTest {
private val rt = ReadingTime("This is a <b>test</b>.\nWith an image: <img src=\"#\">") private val img = """<img src="#">"""
private val rt = ReadingTime("This is a <b>test</b>.\nWith an image: $img")
private val blogPost = File("src/test/resources/post.html").readText() private val blogPost = File("src/test/resources/post.html").readText()
private val mediumPost = File("src/test/resources/medium.html").readText() private val mediumPost = File("src/test/resources/medium.html").readText()
private val twoSeventyFive = """one two three four five six seven eight nine ten one two three four five six seven private val twoSeventyFive = "word ".repeat(275)
eight nine ten one two three four five six seven eight nine ten one two three four five six seven eight nine
ten one two three four five six seven eight nine ten one two three four five six seven eight nine ten one two
three four five six seven eight nine ten one two three four five six seven eight nine ten one two three four
five six seven eight nine ten one two three four five six seven eight nine ten one two three four five six
seven eight nine ten one two three four five six seven eight nine ten one two three four five six seven eight
nine ten one two three four five six seven eight nine ten one two three four five six seven eight nine ten
one two three four five six seven eight nine ten one two three four five six seven eight nine ten one two
three four five six seven eight nine ten one two three four five six seven eight nine ten one two three four
five six seven eight nine ten one two three four five six seven eight nine ten one two three four five six
seven eight nine ten one two three four five six seven eight nine ten one two three four five six seven eight
nine ten one two three four five six seven eight nine ten one two three four five six seven eight nine ten
one two three four five six seven eight nine ten one two three four five"""
private val tenImages = "<img src=\"#\"> <img src=\"#\"> <img src=\"#\"> <img src=\"#\"> <img src=\"#\"> " +
"<img src=\"#\"> <img src=\"#\"> <img src=\"#\"> <img src=\"#\"> <img src=\"#\">"
private fun calcImgTime(imgCount: Int): Double { private fun calcImgTime(imgCount: Int): Double {
var time = 0.0 var time = 0.0
@ -75,100 +63,111 @@ one two three four five six seven eight nine ten one two three four five"""
@Test @Test
fun testWordCount() { fun testWordCount() {
assertEquals(0, ReadingTime.wordCount(" ")) assertEquals(0, ReadingTime.wordCount(" "), "empty")
assertEquals(3, ReadingTime.wordCount("one two three")) assertEquals(3, ReadingTime.wordCount("one two three"), "one two three")
assertEquals(2, ReadingTime.wordCount(" one two ")) assertEquals(2, ReadingTime.wordCount(" one two "), "one two")
assertEquals(7, ReadingTime.wordCount(rt.text)) assertEquals(7, ReadingTime.wordCount(rt.text), "text")
assertEquals(505, ReadingTime.wordCount(blogPost)) assertEquals(505, ReadingTime.wordCount(blogPost), "blogPost")
assertEquals(391, ReadingTime.wordCount(mediumPost)) assertEquals(391, ReadingTime.wordCount(mediumPost), "mediumPost")
assertEquals(275, ReadingTime.wordCount(twoSeventyFive)) assertEquals(275, ReadingTime.wordCount(twoSeventyFive), "275")
assertEquals(275, ReadingTime.wordCount("$twoSeventyFive <img src=\"#\"")) assertEquals(275, ReadingTime.wordCount("$twoSeventyFive $img"), "275 + image")
} }
@Test @Test
fun testImgCount() { fun testImgCount() {
assertEquals(1, ReadingTime.imgCount(rt.text)) assertEquals(1, ReadingTime.imgCount(rt.text), "text")
assertEquals(11, ReadingTime.imgCount(blogPost)) assertEquals(11, ReadingTime.imgCount(blogPost), "blogPost")
assertEquals(3, ReadingTime.imgCount(mediumPost)) assertEquals(3, ReadingTime.imgCount(mediumPost), "mediumPost")
assertEquals(1, ReadingTime.imgCount("$twoSeventyFive <img src=\"#\"")) assertEquals(1, ReadingTime.imgCount("$twoSeventyFive $img"), "275 + image")
assertEquals(2, ReadingTime.imgCount("$twoSeventyFive <img src=\"#\"> <img src=\"#\">")) assertEquals(2, ReadingTime.imgCount("$twoSeventyFive $img $img"), "275 + 2 images")
} }
@Test @Test
fun testReadingTimeInSec() { fun testReadingTimeInSec() {
assertEquals(calcReadingTime(rt.text, rt.wpm) + calcImgTime(1), rt.calcReadingTimeInSec()) assertEquals(calcReadingTime(rt.text, rt.wpm) + calcImgTime(1), rt.calcReadingTimeInSec(), "text + image")
rt.text = "<img src=\"#\"> <IMG src=\"#\">" rt.text = "$img ${img.uppercase()}"
assertEquals(calcImgTime(2), rt.calcReadingTimeInSec()) assertEquals(calcImgTime(2), rt.calcReadingTimeInSec(), "2 images")
rt.excludeImages = true rt.excludeImages = true
assertEquals(0.0, rt.calcReadingTimeInSec()) assertEquals(0.0, rt.calcReadingTimeInSec(), "image uppercase")
rt.excludeImages = false rt.excludeImages = false
rt.text = blogPost rt.text = blogPost
assertEquals( assertEquals(
calcReadingTime(rt.text, rt.wpm) + calcImgTime(11), rt.calcReadingTimeInSec() calcReadingTime(rt.text, rt.wpm) + calcImgTime(11), rt.calcReadingTimeInSec(), "blogPost"
) )
rt.excludeImages = true rt.excludeImages = true
assertEquals(calcReadingTime(rt.text, rt.wpm), rt.calcReadingTimeInSec()) assertEquals(calcReadingTime(rt.text, rt.wpm), rt.calcReadingTimeInSec(), "exclude images")
rt.extra = 60 rt.extra = 60
assertEquals(calcReadingTime(rt.text, rt.wpm) + 60L, rt.calcReadingTimeInSec()) assertEquals(calcReadingTime(rt.text, rt.wpm) + 60L, rt.calcReadingTimeInSec(), "extra 60")
rt.extra = 0 rt.extra = 0
rt.excludeImages = false rt.excludeImages = false
rt.text = mediumPost rt.text = mediumPost
rt.wpm = 300 rt.wpm = 300
assertEquals(calcReadingTime(rt.text, 300) + calcImgTime(3), rt.calcReadingTimeInSec()) assertEquals(calcReadingTime(rt.text, 300) + calcImgTime(3), rt.calcReadingTimeInSec(), "mediumPost 300 wpm")
rt.wpm = 275 rt.wpm = 275
rt.text = "This is a test" rt.text = "This is a test"
assertEquals(0.0, rt.calcReadingTimeInSec()) assertEquals(0.0, rt.calcReadingTimeInSec(), "test")
rt.text = twoSeventyFive rt.text = twoSeventyFive
assertEquals(60.0, rt.calcReadingTimeInSec()) assertEquals(60.0, rt.calcReadingTimeInSec(), "275")
rt.text = "$twoSeventyFive <img src=\"#\">" rt.text = "$twoSeventyFive $img"
assertEquals(72.0, rt.calcReadingTimeInSec()) assertEquals(72.0, rt.calcReadingTimeInSec(), "275 + image")
rt.text = "$twoSeventyFive <img src=\"#\"> <img src=\"#\">" rt.text = "$twoSeventyFive $img $img"
assertEquals(83.0, rt.calcReadingTimeInSec()) assertEquals(83.0, rt.calcReadingTimeInSec(), "275 + 2 images")
rt.text = "$twoSeventyFive $tenImages" rt.text = "$twoSeventyFive ${img.repeat(10)}"
assertEquals(60.0 + 12 + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3, rt.calcReadingTimeInSec()) assertEquals(60.0 + 12 + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3, rt.calcReadingTimeInSec(), "10 images")
rt.text = "$twoSeventyFive $tenImages <img src=\"#\">" rt.text = "$twoSeventyFive ${img.repeat(10)} $img"
assertEquals(135.0 + 3, rt.calcReadingTimeInSec()) assertEquals(135.0 + 3, rt.calcReadingTimeInSec(), "11 images")
rt.text = "$twoSeventyFive $twoSeventyFive" rt.text = "$twoSeventyFive $twoSeventyFive"
assertEquals(120.0, rt.calcReadingTimeInSec()) assertEquals(120.0, rt.calcReadingTimeInSec(), "275*2")
rt.text = "" rt.text = ""
assertEquals(0.0, rt.calcReadingTimeInSec()) assertEquals(0.0, rt.calcReadingTimeInSec(), "empty")
} }
@Test @Test
fun testReadingTime() { fun testReadingTime() {
rt.text = blogPost rt.text = blogPost
assertEquals("2 min read", rt.calcReadingTime()) assertEquals("2 min read", rt.calcReadingTime(), "blogPost")
rt.plural = "mins read" rt.plural = "mins read"
assertEquals("2 mins read", rt.calcReadingTime()) assertEquals("2 mins read", rt.calcReadingTime(), "plural")
rt.text = mediumPost rt.text = mediumPost
rt.plural = "" rt.plural = ""
assertEquals("2", rt.calcReadingTime()) assertEquals("2", rt.calcReadingTime(), "mediumPost")
rt.text = "This is a test." rt.text = "This is a test."
rt.postfix = "" rt.postfix = ""
assertEquals("0", rt.calcReadingTime()) assertEquals("0", rt.calcReadingTime(), "test")
rt.text = "" rt.text = ""
assertEquals("0", rt.calcReadingTime()) assertEquals("0", rt.calcReadingTime(), "empty")
rt.text = twoSeventyFive rt.text = twoSeventyFive
assertEquals("1", rt.calcReadingTime()) assertEquals("1", rt.calcReadingTime(), "275")
rt.text = "$twoSeventyFive $twoSeventyFive" rt.text = "$twoSeventyFive $twoSeventyFive"
assertEquals("2", rt.calcReadingTime()) assertEquals("2", rt.calcReadingTime(), "275 * 2")
}
@Test
fun testRoundingMode() {
rt.text = blogPost
rt.round = RoundingMode.UP
assertEquals("3 min read", rt.calcReadingTime(), "UP")
rt.text = mediumPost
rt.round = RoundingMode.DOWN
assertEquals("1 min read", rt.calcReadingTime(), "DOWN")
} }
} }