mirror of
https://github.com/dyrkin/luxmed-bot.git
synced 2026-01-02 11:07:25 +01:00
#10 Added offset support
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
10
server/src/main/resources/db/liquibase-changelog.yml
Normal file
10
server/src/main/resources/db/liquibase-changelog.yml
Normal 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
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 = "👍 Візит не було скасовано"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user