From 2777f15e00fb307b42d9b84299d45a5a22fb625f Mon Sep 17 00:00:00 2001 From: Eugene Zadyra Date: Wed, 12 Dec 2018 16:35:48 +0100 Subject: [PATCH] #10 Added offset support --- server/build.gradle | 1 + server/src/main/resources/application.yml | 10 ++--- .../01-add-offset-columns-to-settings.yml | 28 ++++++++++++++ .../02-add-offset-columns-to-monitoring.yml | 18 +++++++++ .../03-add-rebook-column-to-monitoring.yml | 24 ++++++++++++ .../main/resources/db/liquibase-changelog.yml | 10 +++++ .../com/lbs/server/conversation/Book.scala | 38 ++++++++++++++----- .../lbs/server/conversation/Settings.scala | 32 ++++++++++++++-- .../main/scala/com/lbs/server/lang/En.scala | 6 +++ .../main/scala/com/lbs/server/lang/Lang.scala | 6 +++ .../com/lbs/server/lang/Localization.scala | 8 ++-- .../main/scala/com/lbs/server/lang/Ua.scala | 6 +++ .../server/repository/model/Monitoring.scala | 8 +++- .../server/repository/model/Settings.scala | 12 +++++- .../server/service/MonitoringService.scala | 7 ++-- .../scala/com/lbs/server/util/package.scala | 7 +++- 16 files changed, 192 insertions(+), 29 deletions(-) create mode 100644 server/src/main/resources/db/changelog/01-add-offset-columns-to-settings.yml create mode 100644 server/src/main/resources/db/changelog/02-add-offset-columns-to-monitoring.yml create mode 100644 server/src/main/resources/db/changelog/03-add-rebook-column-to-monitoring.yml create mode 100644 server/src/main/resources/db/liquibase-changelog.yml 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 {