Skip to content

Commit

Permalink
Merge pull request #7 from Wilmoth-Technologies/public-inventory-inte…
Browse files Browse the repository at this point in the history
…grations

Public inventory integrations
  • Loading branch information
GabeWilmoth authored Nov 4, 2024
2 parents 77c466a + b0c2a2f commit 1c46fa9
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 1 deletion.
10 changes: 10 additions & 0 deletions app/dao/CosmosQuery.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ object CosmosQuery {
|AND ARRAY_LENGTH(c.imageLinks) >= 1
|OFFSET 0 LIMIT 10""".stripMargin)

def getInStockInventoryAddedInLastWeek(sevenDaysAgo: String)(collectionName: String): SqlQuerySpec =
new SqlQuerySpec(
s"""SELECT * FROM $collectionName c
|WHERE c.status != "Sold"
|AND c.status != "Pending Sale"
|AND ARRAY_LENGTH(c.imageLinks) >= 1
|AND c.creationTimeStamp >= @sevenDaysAgo""".stripMargin,
List(new SqlParameter("@sevenDaysAgo", sevenDaysAgo)): _*
)

def getNotificationWithinWindow(date: String)(collectionName: String): SqlQuerySpec = {
new SqlQuerySpec(
s"""SELECT * FROM $collectionName c
Expand Down
16 changes: 15 additions & 1 deletion app/models/MayberryMiniTrucks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,18 @@ case class ContactRequest(
description: String,
vin: String = "",
isFailedFilter: Boolean = false
)
)


case class InventoryDetailsForTemplate(
photoUrl: String,
year: String,
make: String,
model: String,
price: String,
mileage: String,
engine: String,
transmission: String,
color: String,
itemUrl: String
)
2 changes: 2 additions & 0 deletions app/modules/Injection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package modules

import com.google.inject.AbstractModule
import dao.{CosmosDb, CosmosDbBuilder}
import services.InventoryNewsLetterService


class Injection extends AbstractModule {

override def configure(): Unit = {
bind(classOf[CosmosDbBuilder]).asEagerSingleton()
bind(classOf[CosmosDb]).asEagerSingleton()
bind(classOf[InventoryNewsLetterService]).asEagerSingleton()
}
}
80 changes: 80 additions & 0 deletions app/services/InventoryNewsLetterService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package services

import akka.actor.{ActorSystem, Cancellable}
import dao.{CosmosDb, CosmosQuery}
import dao.CosmosQuery.{getAllResults, getInStockInventoryAddedInLastWeek}
import models.{Inventory, Subscribers}
import shared.AppFunctions._

import java.time.format.DateTimeFormatter
import java.time.temporal.{ChronoUnit, TemporalAdjusters}
import java.time.{DayOfWeek, Duration, Instant, ZoneId, ZonedDateTime}
import javax.inject.{Inject, Singleton}
import scala.concurrent.ExecutionContext
import scala.concurrent.duration.{DurationInt, FiniteDuration, MILLISECONDS}

@Singleton
class InventoryNewsLetterService @Inject()(actorSystem: ActorSystem,
cosmosDb: CosmosDb,
emailService: EmailService)
(implicit ec: ExecutionContext) {
private val inventoryCollection: String = CosmosQuery.inventoryCollection
private val subscriberCollection: String = CosmosQuery.subscriberCollection

// Define the task to run
def task(): Unit = {
println("Running scheduled task at " + ZonedDateTime.now(ZoneId.of("America/New_York")))
val sevenDaysAgo = Instant.now().minus(7, ChronoUnit.DAYS)
val formattedDate = DateTimeFormatter.ISO_INSTANT.format(sevenDaysAgo)

for {
inventoryList <- cosmosDb.runQuery[Inventory](getInStockInventoryAddedInLastWeek(formattedDate), inventoryCollection)
inventoryMap = inventoryList.zipWithIndex.flatMap {
case (vehicle, index) => Map(
s"photoUrl$index" -> vehicle.imageLinks.head,
s"year$index" -> vehicle.year,
s"make$index" -> vehicle.make,
s"model$index" -> vehicle.model,
s"price$index" -> formatPrice(vehicle.price),
s"mileage$index" -> formatNumberWithCommas(vehicle.mileage.toString),
s"engine$index" -> vehicle.engine,
s"transmission$index" -> vehicle.transmission,
s"color$index" -> vehicle.exteriorColor,
s"itemURL$index" -> s"http://localhost:3000/inventory/${vehicle.vin}"
)
}.toMap
subscriberList <- cosmosDb.runQuery[Subscribers](getAllResults(), subscriberCollection)
_ = subscriberList.map(subscriber =>
if(inventoryList.length >= 6) {
emailService.sendEmail(subscriber.email, "d-965b9c678e364190a1d8aef756faa080", inventoryMap)
} else if (inventoryList.length >= 3) {
emailService.sendEmail(subscriber.email, "d-287fccd69f8e479c88b1234cb078b5f1", inventoryMap)
}
)
} yield ""
}

// Calculate the initial delay until the next Wednesday at 9 AM EST
def calculateInitialDelay(): FiniteDuration = {
val now = ZonedDateTime.now(ZoneId.of("America/New_York"))
val nextRun = now.`with`(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY))
.withHour(9)
.withMinute(0)
.withSecond(0)
.withNano(0)

val initialDelay = Duration.between(now, nextRun)
FiniteDuration(initialDelay.toMillis, MILLISECONDS)
}

// Schedule the task to run weekly on Wednesdays at 9 AM EST
private val cancellable: Cancellable = actorSystem.scheduler.scheduleAtFixedRate(
calculateInitialDelay(), //Change me to 10.millis to run quicker at startup
7.days
)(new Runnable {
override def run(): Unit = task()
})

// Optionally add a stop hook for cleanup on application shutdown
def cancel(): Unit = cancellable.cancel()
}
17 changes: 17 additions & 0 deletions app/shared/AppFunctions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import play.api.mvc.{AnyContent, Request, Result}
import play.api.mvc.Results.{NotFound, _}

import java.sql.Timestamp
import java.text.NumberFormat
import java.util.Locale

object AppFunctions {
val objectMapper: JsonMapper with ClassTagExtensions = JsonMapper.builder().addModule(DefaultScalaModule).build() :: ClassTagExtensions
Expand Down Expand Up @@ -49,4 +51,19 @@ object AppFunctions {
def toSha256(message: String): String =
String.format("%064x", new java.math.BigInteger(1,
java.security.MessageDigest.getInstance("SHA-256").digest(message.getBytes("UTF-8"))))

def formatPrice(price: BigDecimal): String = {
val formatter = NumberFormat.getCurrencyInstance(Locale.US)
formatter.format(price)
}

def formatNumberWithCommas(numberString: String): String = {
try {
val number = numberString.toDouble
val formatter = NumberFormat.getInstance
formatter.format(number)
} catch {
case _: NumberFormatException => "Invalid number"
}
}
}

0 comments on commit 1c46fa9

Please sign in to comment.