diff --git a/server/build.gradle b/server/build.gradle
index 51e246c..3693d3a 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -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')
diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml
index f6d90d9..e7a872e 100644
--- a/server/src/main/resources/application.yml
+++ b/server/src/main/resources/application.yml
@@ -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"
diff --git a/server/src/main/resources/db/changelog/01-add-offset-columns-to-settings.yml b/server/src/main/resources/db/changelog/01-add-offset-columns-to-settings.yml
new file mode 100644
index 0000000..479ba18
--- /dev/null
+++ b/server/src/main/resources/db/changelog/01-add-offset-columns-to-settings.yml
@@ -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
diff --git a/server/src/main/resources/db/changelog/02-add-offset-columns-to-monitoring.yml b/server/src/main/resources/db/changelog/02-add-offset-columns-to-monitoring.yml
new file mode 100644
index 0000000..cef41d6
--- /dev/null
+++ b/server/src/main/resources/db/changelog/02-add-offset-columns-to-monitoring.yml
@@ -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
diff --git a/server/src/main/resources/db/changelog/03-add-rebook-column-to-monitoring.yml b/server/src/main/resources/db/changelog/03-add-rebook-column-to-monitoring.yml
new file mode 100644
index 0000000..ddb113a
--- /dev/null
+++ b/server/src/main/resources/db/changelog/03-add-rebook-column-to-monitoring.yml
@@ -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
diff --git a/server/src/main/resources/db/liquibase-changelog.yml b/server/src/main/resources/db/liquibase-changelog.yml
new file mode 100644
index 0000000..1efaba6
--- /dev/null
+++ b/server/src/main/resources/db/liquibase-changelog.yml
@@ -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
\ No newline at end of file
diff --git a/server/src/main/scala/com/lbs/server/conversation/Book.scala b/server/src/main/scala/com/lbs/server/conversation/Book.scala
index 7a9d395..ab9ae80 100644
--- a/server/src/main/scala/com/lbs/server/conversation/Book.scala
+++ b/server/src/main/scala/com/lbs/server/conversation/Book.scala
@@ -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"
}
}
diff --git a/server/src/main/scala/com/lbs/server/conversation/Settings.scala b/server/src/main/scala/com/lbs/server/conversation/Settings.scala
index b576c04..12966d7 100644
--- a/server/src/main/scala/com/lbs/server/conversation/Settings.scala
+++ b/server/src/main/scala/com/lbs/server/conversation/Settings.scala
@@ -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"
}
}
\ No newline at end of file
diff --git a/server/src/main/scala/com/lbs/server/lang/En.scala b/server/src/main/scala/com/lbs/server/lang/En.scala
index b249790..71d5a12 100644
--- a/server/src/main/scala/com/lbs/server/lang/En.scala
+++ b/server/src/main/scala/com/lbs/server/lang/En.scala
@@ -97,6 +97,8 @@ object En extends Lang {
override def rebookIfExists: String = "➡ Do you want to update term if reservation already exists?"
+ override def pleaseSpecifyOffset: String = "➡ Please send me offset in hours or press No button"
+
override def visitAlreadyExists: String = "➡ 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 = "➡ Please choose a language"
+ override def configureOffset: String = "➡ Please specify offset options"
+
override def languageUpdated: String = "👍 Language was successfully changed!"
override def appointmentWasNotCancelled: String = "👍 Appointment was not cancelled"
diff --git a/server/src/main/scala/com/lbs/server/lang/Lang.scala b/server/src/main/scala/com/lbs/server/lang/Lang.scala
index 36ee34b..a1c0d04 100644
--- a/server/src/main/scala/com/lbs/server/lang/Lang.scala
+++ b/server/src/main/scala/com/lbs/server/lang/Lang.scala
@@ -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
diff --git a/server/src/main/scala/com/lbs/server/lang/Localization.scala b/server/src/main/scala/com/lbs/server/lang/Localization.scala
index c81bb28..7458fbc 100644
--- a/server/src/main/scala/com/lbs/server/lang/Localization.scala
+++ b/server/src/main/scala/com/lbs/server/lang/Localization.scala
@@ -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)
}
diff --git a/server/src/main/scala/com/lbs/server/lang/Ua.scala b/server/src/main/scala/com/lbs/server/lang/Ua.scala
index a8b453a..22eb806 100644
--- a/server/src/main/scala/com/lbs/server/lang/Ua.scala
+++ b/server/src/main/scala/com/lbs/server/lang/Ua.scala
@@ -97,6 +97,8 @@ object Ua extends Lang {
override def rebookIfExists: String = "➡ Чи хотіли би ви змінити термін в разі, якщо резервація вже існує?"
+ override def pleaseSpecifyOffset: String = "➡ Будь ласка, надішліть мені зміщення в годинах, або натисніть Ні"
+
override def visitAlreadyExists: String = "➡ Резервація для такого сервісу вже існує. Чі хотіли би ви змінити термін?"
override def city: String = "місто"
@@ -304,8 +306,12 @@ object Ua extends Lang {
override def language: String = "🌐 Змінити мову"
+ override def offset: String = "⏱ Зміщення"
+
override def chooseLanguage: String = "➡ Будь ласка, оберіть мову"
+ override def configureOffset: String = "➡ Будь ласка, сконфігуруйте зміщення"
+
override def languageUpdated: String = "👍 Мову успішно змінено!"
override def appointmentWasNotCancelled: String = "👍 Візит не було скасовано"
diff --git a/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala b/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala
index 20478ab..e26f61c 100644
--- a/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala
+++ b/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala
@@ -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
}
}
\ No newline at end of file
diff --git a/server/src/main/scala/com/lbs/server/repository/model/Settings.scala b/server/src/main/scala/com/lbs/server/repository/model/Settings.scala
index f8af9c2..f6e5139 100644
--- a/server/src/main/scala/com/lbs/server/repository/model/Settings.scala
+++ b/server/src/main/scala/com/lbs/server/repository/model/Settings.scala
@@ -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
}
}
\ No newline at end of file
diff --git a/server/src/main/scala/com/lbs/server/service/MonitoringService.scala b/server/src/main/scala/com/lbs/server/service/MonitoringService.scala
index 893a294..fcfe5a0 100644
--- a/server/src/main/scala/com/lbs/server/service/MonitoringService.scala
+++ b/server/src/main/scala/com/lbs/server/service/MonitoringService.scala
@@ -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 = {
diff --git a/server/src/main/scala/com/lbs/server/util/package.scala b/server/src/main/scala/com/lbs/server/util/package.scala
index 7ddc78f..edcf313 100644
--- a/server/src/main/scala/com/lbs/server/util/package.scala
+++ b/server/src/main/scala/com/lbs/server/util/package.scala
@@ -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 {