#10 Added offset support

This commit is contained in:
Eugene Zadyra
2018-12-12 16:35:48 +01:00
parent 68eb0c2fe8
commit 2777f15e00
16 changed files with 192 additions and 29 deletions

View File

@@ -25,6 +25,7 @@ dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.liquibase:liquibase-core')
compile('org.jasypt:jasypt:1.9.2')
compile('org.postgresql:postgresql:42.2.1.jre7')

View File

@@ -6,13 +6,13 @@ spring:
jpa:
properties:
hibernate:
ddl-auto: none
temp:
use_jdbc_metadata_defaults: "false"
database-platform: "org.hibernate.dialect.PostgreSQL9Dialect"
generate-ddl: "true"
# hibernate:
# ddl-auto: "create-update"
# ddl-auto: "create-drop"
generate-ddl: "false"
liquibase:
change-log: classpath:db/liquibase-changelog.yml
banner:
location: "classpath:/banner.txt"
@@ -21,8 +21,6 @@ logging:
file: /lbs/log/app.log
level:
com.lbs: DEBUG
# org.hibernate.SQL: DEBUG
# org.hibernate.type.descriptor.sql.BasicBinder: TRACE
pattern:
file: "%d{yyyy-MM-dd HH:mm:ss} %logger{25} - %msg%n"
console: "%d{yyyy-MM-dd HH:mm:ss} %logger{25} - %msg%n"

View File

@@ -0,0 +1,28 @@
databaseChangeLog:
- changeSet:
id: 01
author: dyrkin
changes:
- addColumn:
tableName: settings
columns:
- column:
name: default_offset
type: int
defaultValue: 0
- column:
name: always_ask_offset
type: boolean
defaultValue: false
- addNotNullConstraint:
tableName: settings
columnName: default_offset
columnDataType: int
defaultNullValue: 0
- addNotNullConstraint:
tableName: settings
columnName: always_ask_offset
columnDataType: boolean
defaultNullValue: false

View File

@@ -0,0 +1,18 @@
databaseChangeLog:
- changeSet:
id: 02
author: dyrkin
changes:
- addColumn:
tableName: monitoring
columns:
- column:
name: offset
type: int
defaultValue: 0
- addNotNullConstraint:
tableName: monitoring
columnName: offset
columnDataType: int
defaultNullValue: 0

View File

@@ -0,0 +1,24 @@
databaseChangeLog:
- changeSet:
id: 03
author: dyrkin
preConditions:
onFail: MARK_RAN
not:
columnExists:
tableName: monitoring
columnName: rebook_if_exists
changes:
- addColumn:
tableName: monitoring
columns:
- column:
name: rebook_if_exists
type: boolean
defaultValue: false
- addNotNullConstraint:
tableName: monitoring
columnName: offset
columnDataType: int
defaultNullValue: 0

View File

@@ -0,0 +1,10 @@
databaseChangeLog:
- include:
file: changelog/01-add-offset-columns-to-settings.yml
relativeToChangelogFile: true
- include:
file: changelog/02-add-offset-columns-to-monitoring.yml
relativeToChangelogFile: true
- include:
file: changelog/03-add-rebook-column-to-monitoring.yml
relativeToChangelogFile: true

View File

@@ -16,7 +16,7 @@ import com.lbs.server.conversation.base.Conversation
import com.lbs.server.lang.{Localizable, Localization}
import com.lbs.server.repository.model.Monitoring
import com.lbs.server.service.{ApiService, DataService, MonitoringService}
import com.lbs.server.util.MessageExtractors.{BooleanString, CallbackCommand}
import com.lbs.server.util.MessageExtractors.{BooleanString, CallbackCommand, IntString, TextCommand}
import com.lbs.server.util.ServerModelConverters._
class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: DataService, monitoringService: MonitoringService,
@@ -147,7 +147,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
case Left(ex) =>
warn(s"Service [${bookingData.serviceId.name}] is already booked. Ask to update term", ex)
bot.sendMessage(userId.source, lang.visitAlreadyExists,
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.no, Tags.RebookNo), Button(lang.yes, Tags.RebookYes))))
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.no, Tags.No), Button(lang.yes, Tags.Yes))))
goto(awaitRebookDecision) using bookingData.copy(term = Some(term))
case Right((temporaryReservation, valuations)) =>
bot.sendMessage(userId.source, lang.confirmAppointment(term, valuations),
@@ -166,13 +166,20 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
case Msg(CallbackCommand(Tags.ModifyDate), bookingData) =>
goto(requestDateFrom) using bookingData.copy(dateFrom = ZonedDateTime.now(),
dateTo = ZonedDateTime.now().plusDays(1L))
case Msg(CallbackCommand(Tags.CreateMonitoring), _) =>
goto(askMonitoringAutobookOption)
case Msg(CallbackCommand(Tags.CreateMonitoring), bookingData) =>
val settingsMaybe = dataService.findSettings(userId.userId)
val (defaultOffset, askOffset) = settingsMaybe match {
case Some(settings) => (settings.defaultOffset, settings.alwaysAskOffset)
case None => (0, false)
}
val newData = bookingData.copy(offset = defaultOffset)
if(askOffset) goto(askMonitoringOffsetOption) using newData
else goto(askMonitoringAutobookOption) using newData
}
private def awaitRebookDecision: Step =
monologue {
case Msg(CallbackCommand(Tags.RebookYes), bookingData: BookingData) =>
case Msg(CallbackCommand(Tags.Yes), bookingData: BookingData) =>
apiService.updateReservedVisit(userId.accountId, bookingData.term.get) match {
case Right(success) =>
debug(s"Successfully confirmed: $success")
@@ -182,7 +189,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
bot.sendMessage(userId.source, ex.getMessage)
}
end()
case Msg(CallbackCommand(Tags.RebookNo), _) =>
case Msg(CallbackCommand(Tags.No), _) =>
info("User doesn't want to change term")
end()
}
@@ -219,6 +226,17 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
}
}
private def askMonitoringOffsetOption: Step =
ask { _ =>
bot.sendMessage(userId.source, lang.pleaseSpecifyOffset,
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.no, Tags.No))))
} onReply {
case Msg(TextCommand(IntString(offset)), bookingData: BookingData) =>
goto(askMonitoringAutobookOption) using bookingData.copy(offset = offset)
case Msg(CallbackCommand(BooleanString(false)), _) =>
goto(askMonitoringAutobookOption)
}
private def askMonitoringAutobookOption: Step =
ask { _ =>
bot.sendMessage(userId.source, lang.chooseTypeOfMonitoring,
@@ -233,7 +251,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
private def askMonitoringRebookOption: Step =
ask { _ =>
bot.sendMessage(userId.source, lang.rebookIfExists,
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.no, Tags.RebookNo), Button(lang.yes, Tags.RebookYes))))
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.no, Tags.No), Button(lang.yes, Tags.Yes))))
} onReply {
case Msg(CallbackCommand(BooleanString(rebookIfExists)), bookingData: BookingData) =>
goto(createMonitoring) using bookingData.copy(rebookIfExists = rebookIfExists)
@@ -276,7 +294,7 @@ object Book {
dateTo: ZonedDateTime = ZonedDateTime.now().plusDays(1L), timeFrom: LocalTime = LocalTime.of(7, 0),
timeTo: LocalTime = LocalTime.of(21, 0), autobook: Boolean = false, rebookIfExists: Boolean = false,
term: Option[AvailableVisitsTermPresentation] = None, temporaryReservationId: Option[Long] = None,
valuations: Option[ValuationsResponse] = None)
valuations: Option[ValuationsResponse] = None, offset: Int = 0)
object Tags {
val Cancel = "cancel"
@@ -286,8 +304,8 @@ object Book {
val CreateMonitoring = "create_monitoring"
val BookManually = "false"
val BookByApplication = "true"
val RebookYes = "true"
val RebookNo = "false"
val Yes = "true"
val No = "false"
}
}

View File

@@ -9,6 +9,8 @@ import com.lbs.server.conversation.Settings._
import com.lbs.server.conversation.base.Conversation
import com.lbs.server.lang.{Lang, Localizable, Localization}
import com.lbs.server.service.DataService
import com.lbs.server.util.MessageExtractors.{CallbackCommand, IntString}
import com.lbs.server.repository.model
class Settings(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization)(val actorSystem: ActorSystem) extends Conversation[Unit] with Localizable {
@@ -17,10 +19,13 @@ class Settings(val userId: UserId, bot: Bot, dataService: DataService, val local
def askForAction: Step =
ask { _ =>
bot.sendMessage(userId.source, lang.settingsHeader, inlineKeyboard =
createInlineKeyboard(Seq(Button(lang.language, Tags.Language))))
createInlineKeyboard(Seq(Button(lang.language, Tags.Language),
Button(lang.offset, Tags.Offset)), columns = 1))
} onReply {
case Msg(Command(_, _, Some(Tags.Language)), _) =>
goto(askLanguage)
case Msg(Command(_, _, Some(Tags.Offset)), _) =>
goto(showOffsetOptions)
}
def askLanguage: Step =
@@ -28,18 +33,39 @@ class Settings(val userId: UserId, bot: Bot, dataService: DataService, val local
bot.sendMessage(userId.source, lang.chooseLanguage,
inlineKeyboard = createInlineKeyboard(Lang.Langs.map(l => Button(l.label, l.id)), columns = 1))
} onReply {
case Msg(Command(_, _, Some(langIdStr)), _) =>
val langId = langIdStr.toInt
case Msg(CallbackCommand(IntString(langId)), _) =>
localization.updateLanguage(userId.userId, Lang(langId))
bot.sendMessage(userId.source, lang.languageUpdated)
end()
}
def showOffsetOptions: Step = {
ask { _ =>
val settingsMaybe = dataService.findSettings(userId.userId)
val (defaultOffset, askOffset) = settingsMaybe match {
case Some(settings) => (settings.defaultOffset, settings.alwaysAskOffset)
case None => (0, false)
}
bot.sendMessage(userId.source, lang.configureOffset,
inlineKeyboard = createInlineKeyboard(Seq(Button(s"${if(askOffset) "✅" else " "} Always ask offset", Tags.AskOffset)), columns = 1))
} onReply {
case Msg(cmd@CallbackCommand(Tags.AskOffset), _) =>
val settings = dataService.findSettings(userId.userId).getOrElse(model.Settings(userId.userId, lang.id, 0, alwaysAskOffset = false))
settings.alwaysAskOffset = !settings.alwaysAskOffset
dataService.saveSettings(settings)
bot.sendEditMessage(userId.source, cmd.message.messageId,
inlineKeyboard = createInlineKeyboard(Seq(Button(s"${if(settings.alwaysAskOffset) "✅" else " "} Always ask offset", Tags.AskOffset)), columns = 1))
stay()
}
}
}
object Settings {
object Tags {
val Language = "language"
val Offset = "offset"
val AskOffset = "ask_offset"
}
}

View File

@@ -97,6 +97,8 @@ object En extends Lang {
override def rebookIfExists: String = "<b>➡</b> Do you want to update term if reservation already exists?"
override def pleaseSpecifyOffset: String = "<b>➡</b> Please send me offset in hours or press No button"
override def visitAlreadyExists: String = "<b>➡</b> The same service is already booked. Do you want to update term?"
override def city: String = "city"
@@ -305,8 +307,12 @@ object En extends Lang {
override def language: String = "🌐 Change language"
override def offset: String = "⏱ Offset"
override def chooseLanguage: String = "<b>➡</b> Please choose a language"
override def configureOffset: String = "<b>➡</b> Please specify offset options"
override def languageUpdated: String = "👍 Language was successfully changed!"
override def appointmentWasNotCancelled: String = "👍 Appointment was not cancelled"

View File

@@ -92,6 +92,8 @@ trait Lang {
def rebookIfExists: String
def pleaseSpecifyOffset: String
def visitAlreadyExists: String
def city: String
@@ -192,8 +194,12 @@ trait Lang {
def language: String
def offset: String
def chooseLanguage: String
def configureOffset: String
def languageUpdated: String
def appointmentWasNotCancelled: String

View File

@@ -31,10 +31,10 @@ class Localization {
def updateLanguage(userId: Long, lang: Lang): Unit = {
cachedLangs.put(userId, lang)
val settings = dataService.findSettings(userId) match {
case Some(exists) =>
exists.setLang(lang.id)
exists
case None => model.Settings(userId, lang.id)
case Some(existingSettings) =>
existingSettings.setLang(lang.id)
existingSettings
case None => model.Settings(userId, lang.id, 0, alwaysAskOffset = false)
}
dataService.saveSettings(settings)
}

View File

@@ -97,6 +97,8 @@ object Ua extends Lang {
override def rebookIfExists: String = "<b>➡</b> Чи хотіли би ви змінити термін в разі, якщо резервація вже існує?"
override def pleaseSpecifyOffset: String = "<b>➡</b> Будь ласка, надішліть мені зміщення в годинах, або натисніть Ні"
override def visitAlreadyExists: String = "<b>➡</b> Резервація для такого сервісу вже існує. Чі хотіли би ви змінити термін?"
override def city: String = "місто"
@@ -304,8 +306,12 @@ object Ua extends Lang {
override def language: String = "🌐 Змінити мову"
override def offset: String = "⏱ Зміщення"
override def chooseLanguage: String = "<b>➡</b> Будь ласка, оберіть мову"
override def configureOffset: String = "<b>➡</b> Будь ласка, сконфігуруйте зміщення"
override def languageUpdated: String = "👍 Мову успішно змінено!"
override def appointmentWasNotCancelled: String = "👍 Візит не було скасовано"

View File

@@ -89,13 +89,18 @@ class Monitoring extends RecordId {
@BeanProperty
@Column(nullable = false)
var active: Boolean = true
@BeanProperty
@Column(nullable = false)
var offset: Int = 0
}
object Monitoring {
def apply(userId: Long, accountId: Long, chatId: String, sourceSystemId: Long, cityId: Long, cityName: String, clinicId: Option[Long], clinicName: String,
serviceId: Long, serviceName: String, doctorId: Option[Long], doctorName: String, dateFrom: ZonedDateTime,
dateTo: ZonedDateTime, autobook: Boolean = false, rebookIfExists: Boolean = false, created: ZonedDateTime = ZonedDateTime.now(), timeFrom: LocalTime, timeTo: LocalTime,
active: Boolean = true): Monitoring = {
active: Boolean = true, offset: Int): Monitoring = {
val monitoring = new Monitoring
monitoring.userId = userId
monitoring.accountId = accountId
@@ -117,6 +122,7 @@ object Monitoring {
monitoring.rebookIfExists = rebookIfExists
monitoring.created = created
monitoring.active = active
monitoring.offset = offset
monitoring
}
}

View File

@@ -15,13 +15,23 @@ class Settings extends RecordId {
@BeanProperty
@Column(nullable = false)
var lang: Int = 0 //En by default
@BeanProperty
@Column(nullable = false)
var defaultOffset: Int = 0
@BeanProperty
@Column(nullable = false)
var alwaysAskOffset: Boolean = false
}
object Settings {
def apply(userId: Long, lang: Int): Settings = {
def apply(userId: Long, lang: Int, defaultOffset: Int, alwaysAskOffset: Boolean): Settings = {
val settings = new Settings
settings.userId = userId
settings.lang = lang
settings.defaultOffset = defaultOffset
settings.alwaysAskOffset = alwaysAskOffset
settings
}
}

View File

@@ -66,7 +66,7 @@ class MonitoringService extends Logger {
private def monitor(monitoring: Monitoring): Unit = {
debug(s"Looking for available terms. Monitoring [#${monitoring.recordId}]")
val dateFrom = optimizeDateFrom(monitoring.dateFrom)
val dateFrom = optimizeDateFrom(monitoring.dateFrom, monitoring.offset)
val termsEither = apiService.getAvailableTerms(monitoring.accountId, monitoring.cityId, monitoring.clinicId, monitoring.serviceId,
monitoring.doctorId, dateFrom, Some(monitoring.dateTo), timeFrom = monitoring.timeFrom, timeTo = monitoring.timeTo)
termsEither match {
@@ -93,9 +93,10 @@ class MonitoringService extends Logger {
}
}
private def optimizeDateFrom(date: ZonedDateTime) = {
private def optimizeDateFrom(date: ZonedDateTime, offset: Int) = {
val dateWithOffset = date.plusHours(offset)
val now = ZonedDateTime.now()
if (date.isBefore(now)) now else date
if (dateWithOffset.isBefore(now)) now else dateWithOffset
}
private def initializeMonitorings(allMonitorings: Seq[Monitoring]): Unit = {

View File

@@ -44,7 +44,8 @@ package object util {
timeFrom = bookingData.timeFrom,
timeTo = bookingData.timeTo,
autobook = bookingData.autobook,
rebookIfExists = bookingData.rebookIfExists
rebookIfExists = bookingData.rebookIfExists,
offset = bookingData.offset
)
}
}
@@ -123,6 +124,10 @@ package object util {
def unapply(string: String): Option[Boolean] = Try(string.toBoolean).toOption
}
object IntString {
def unapply(string: String): Option[Int] = Try(string.toInt).toOption
}
}
object DateTimeUtil {