mirror of
https://github.com/dyrkin/luxmed-bot.git
synced 2025-12-24 06:28:37 +01:00
create a reminder for a booked appointment
This commit is contained in:
75
server/src/main/resources/db/changelog/06-reminders.yml
Normal file
75
server/src/main/resources/db/changelog/06-reminders.yml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
databaseChangeLog:
|
||||||
|
- changeSet:
|
||||||
|
id: 06
|
||||||
|
author: dyrkin
|
||||||
|
preConditions:
|
||||||
|
onFail: MARK_RAN
|
||||||
|
not:
|
||||||
|
tableExists:
|
||||||
|
tableName: reminder
|
||||||
|
changes:
|
||||||
|
- createTable:
|
||||||
|
tableName: reminder
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
autoIncrement: true
|
||||||
|
constraints:
|
||||||
|
primaryKey: true
|
||||||
|
primaryKeyName: bug_pkey
|
||||||
|
name: record_id
|
||||||
|
type: BIGSERIAL
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: user_id
|
||||||
|
type: BIGINT
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: account_id
|
||||||
|
type: BIGINT
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: chat_id
|
||||||
|
type: BIGINT
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: source_system_id
|
||||||
|
type: BIGINT
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: city_name
|
||||||
|
type: VARCHAR(255)
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: clinic_name
|
||||||
|
type: VARCHAR(255)
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: service_name
|
||||||
|
type: VARCHAR(255)
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: doctor_name
|
||||||
|
type: VARCHAR(255)
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: appointment_time
|
||||||
|
type: TIME WITHOUT TIME ZONE
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
name: remind_at_time
|
||||||
|
type: TIME WITHOUT TIME ZONE
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: true
|
||||||
|
name: active
|
||||||
|
type: BOOLEAN
|
||||||
@@ -17,3 +17,6 @@ databaseChangeLog:
|
|||||||
- include:
|
- include:
|
||||||
file: changelog/05-drop-bugs-table.yml
|
file: changelog/05-drop-bugs-table.yml
|
||||||
relativeToChangelogFile: true
|
relativeToChangelogFile: true
|
||||||
|
- include:
|
||||||
|
file: changelog/06-reminders.yml
|
||||||
|
relativeToChangelogFile: true
|
||||||
@@ -7,7 +7,7 @@ import com.lbs.bot.telegram.TelegramBot
|
|||||||
import com.lbs.server.conversation._
|
import com.lbs.server.conversation._
|
||||||
import com.lbs.server.lang.Localization
|
import com.lbs.server.lang.Localization
|
||||||
import com.lbs.server.repository.model.Monitoring
|
import com.lbs.server.repository.model.Monitoring
|
||||||
import com.lbs.server.service.{ApiService, DataService, MonitoringService}
|
import com.lbs.server.service.{ApiService, DataService, MonitoringService, ReminderService}
|
||||||
import org.jasypt.util.text.{StrongTextEncryptor, TextEncryptor}
|
import org.jasypt.util.text.{StrongTextEncryptor, TextEncryptor}
|
||||||
import org.springframework.beans.factory.annotation.{Autowired, Value}
|
import org.springframework.beans.factory.annotation.{Autowired, Value}
|
||||||
import org.springframework.context.annotation.{Bean, Configuration}
|
import org.springframework.context.annotation.{Bean, Configuration}
|
||||||
@@ -29,6 +29,9 @@ class BootConfig {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private var monitoringService: MonitoringService = _
|
private var monitoringService: MonitoringService = _
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private var reminderService: ReminderService = _
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private var localization: Localization = _
|
private var localization: Localization = _
|
||||||
|
|
||||||
@@ -123,8 +126,8 @@ class BootConfig {
|
|||||||
userId =>
|
userId =>
|
||||||
new Chat(
|
new Chat(
|
||||||
userId,
|
userId,
|
||||||
dataService,
|
|
||||||
monitoringService,
|
monitoringService,
|
||||||
|
reminderService,
|
||||||
bookFactory,
|
bookFactory,
|
||||||
helpFactory,
|
helpFactory,
|
||||||
monitoringsFactory,
|
monitoringsFactory,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import com.lbs.bot.model.Command
|
|||||||
import com.lbs.server.conversation.Chat._
|
import com.lbs.server.conversation.Chat._
|
||||||
import com.lbs.server.conversation.Login.UserId
|
import com.lbs.server.conversation.Login.UserId
|
||||||
import com.lbs.server.conversation.base.{Conversation, Interactional}
|
import com.lbs.server.conversation.base.{Conversation, Interactional}
|
||||||
import com.lbs.server.service.{DataService, MonitoringService}
|
import com.lbs.server.service.{MonitoringService, ReminderService}
|
||||||
import com.lbs.server.util.MessageExtractors._
|
import com.lbs.server.util.MessageExtractors._
|
||||||
import com.typesafe.scalalogging.StrictLogging
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@ import scala.util.matching.Regex
|
|||||||
|
|
||||||
class Chat(
|
class Chat(
|
||||||
val userId: UserId,
|
val userId: UserId,
|
||||||
dataService: DataService,
|
|
||||||
monitoringService: MonitoringService,
|
monitoringService: MonitoringService,
|
||||||
|
reminderService: ReminderService,
|
||||||
bookingFactory: UserIdTo[Book],
|
bookingFactory: UserIdTo[Book],
|
||||||
helpFactory: UserIdTo[Help],
|
helpFactory: UserIdTo[Help],
|
||||||
monitoringsFactory: UserIdTo[Monitorings],
|
monitoringsFactory: UserIdTo[Monitorings],
|
||||||
@@ -127,12 +127,12 @@ class Chat(
|
|||||||
case Msg(cmd @ TextCommand("/accounts"), _) =>
|
case Msg(cmd @ TextCommand("/accounts"), _) =>
|
||||||
self ! cmd
|
self ! cmd
|
||||||
goto(accountChat)
|
goto(accountChat)
|
||||||
case Msg(TextCommand(ReserveTerm(monitoringIdStr, scheduleIdStr, timeStr)), _) =>
|
case Msg(TextCommand(ReserveTermRegex(LongString(monitoringId), LongString(scheduleId), LongString(time))), _) =>
|
||||||
val monitoringId = monitoringIdStr.toLong
|
|
||||||
val scheduleId = scheduleIdStr.toLong
|
|
||||||
val time = timeStr.toLong
|
|
||||||
monitoringService.bookAppointmentByScheduleId(userId.accountId, monitoringId, scheduleId, time)
|
monitoringService.bookAppointmentByScheduleId(userId.accountId, monitoringId, scheduleId, time)
|
||||||
stay()
|
stay()
|
||||||
|
case Msg(CallbackCommand(RemindRegexp(LongString(reminderId), LongString(time))), _) =>
|
||||||
|
reminderService.activateReminder(userId.accountId, reminderId, time)
|
||||||
|
stay()
|
||||||
case Msg(cmd: Command, _) =>
|
case Msg(cmd: Command, _) =>
|
||||||
interactional ! cmd
|
interactional ! cmd
|
||||||
stay()
|
stay()
|
||||||
@@ -151,5 +151,6 @@ class Chat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Chat {
|
object Chat {
|
||||||
val ReserveTerm: Regex = s"/reserve_(\\d+)_(\\d+)_(\\d+)".r
|
val ReserveTermRegex: Regex = "/reserve_(\\d+)_(\\d+)_(\\d+)".r
|
||||||
|
val RemindRegexp: Regex = "remind_at_(\\d+)_(\\d+)".r
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.lbs.server.lang
|
|||||||
import com.lbs.api.json.model.{Event, TermExt}
|
import com.lbs.api.json.model.{Event, TermExt}
|
||||||
import com.lbs.server.conversation.Book
|
import com.lbs.server.conversation.Book
|
||||||
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
||||||
import com.lbs.server.repository.model.Monitoring
|
import com.lbs.server.repository.model.{Monitoring, Reminder}
|
||||||
import com.lbs.server.util.DateTimeUtil._
|
import com.lbs.server.util.DateTimeUtil._
|
||||||
|
|
||||||
import java.time.{LocalDateTime, LocalTime}
|
import java.time.{LocalDateTime, LocalTime}
|
||||||
@@ -173,7 +173,7 @@ object En extends Lang {
|
|||||||
|
|
||||||
override def help: String =
|
override def help: String =
|
||||||
s"""ℹ Non official bot for <b>Portal Pacjenta LUX MED (v.${Lang.version})</b>.
|
s"""ℹ Non official bot for <b>Portal Pacjenta LUX MED (v.${Lang.version})</b>.
|
||||||
|It can help you to book a visit to a doctor, create term monitoring, view upcoming appointments and visit history.
|
|The bot can help you book a visit to a doctor, create term monitorings, view upcoming appointments and visit history.
|
||||||
|
|
|
|
||||||
|<b>➡</b> Supported commands
|
|<b>➡</b> Supported commands
|
||||||
|/book - reserve a visit, or create a monitoring
|
|/book - reserve a visit, or create a monitoring
|
||||||
@@ -377,4 +377,17 @@ object En extends Lang {
|
|||||||
override def canNotDetectPayer(error: String): String = s"Can't determine payer. Reason: $error"
|
override def canNotDetectPayer(error: String): String = s"Can't determine payer. Reason: $error"
|
||||||
|
|
||||||
override def pleaseChoosePayer: String = "<b>➡</b> Can't determine default payer. Please choose one"
|
override def pleaseChoosePayer: String = "<b>➡</b> Can't determine default payer. Please choose one"
|
||||||
|
|
||||||
|
override def youHaveAppointmentAt(reminder: Reminder): String =
|
||||||
|
s"""👍 You have an appointment at ⏱ <b>${formatTime(reminder.appointmentTime.toLocalTime)}</b>!
|
||||||
|
|
|
||||||
|
|${capitalize(doctor)}: ${reminder.doctorName}
|
||||||
|
|${capitalize(service)}: ${reminder.serviceName}
|
||||||
|
|${capitalize(clinic)}: ${reminder.clinicName}
|
||||||
|
|${capitalize(city)}: ${reminder.cityName}""".stripMargin
|
||||||
|
|
||||||
|
override def remindAt(time: LocalDateTime): String = s"⏱ Remind at ${formatTime(time.toLocalTime)}"
|
||||||
|
|
||||||
|
override def appointmentIsOutdated(appointmentTime: LocalDateTime): String =
|
||||||
|
s"Your appointment has already taken place at ${formatDateTime(appointmentTime, locale)}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.lbs.server.lang
|
|||||||
import com.lbs.api.json.model.{Event, TermExt}
|
import com.lbs.api.json.model.{Event, TermExt}
|
||||||
import com.lbs.server.conversation.Book.BookingData
|
import com.lbs.server.conversation.Book.BookingData
|
||||||
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
||||||
import com.lbs.server.repository.model.Monitoring
|
import com.lbs.server.repository.model.{Monitoring, Reminder}
|
||||||
|
|
||||||
import java.time.{LocalDateTime, LocalTime}
|
import java.time.{LocalDateTime, LocalTime}
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@@ -235,4 +235,10 @@ trait Lang {
|
|||||||
def pleaseChooseAccount(currentAccountName: String): String
|
def pleaseChooseAccount(currentAccountName: String): String
|
||||||
|
|
||||||
def accountSwitched(username: String): String
|
def accountSwitched(username: String): String
|
||||||
|
|
||||||
|
def youHaveAppointmentAt(reminder: Reminder): String
|
||||||
|
|
||||||
|
def remindAt(time: LocalDateTime): String
|
||||||
|
|
||||||
|
def appointmentIsOutdated(appointmentTime: LocalDateTime): String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.lbs.server.lang
|
|||||||
import com.lbs.api.json.model.{Event, TermExt}
|
import com.lbs.api.json.model.{Event, TermExt}
|
||||||
import com.lbs.server.conversation.Book
|
import com.lbs.server.conversation.Book
|
||||||
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
||||||
import com.lbs.server.repository.model.Monitoring
|
import com.lbs.server.repository.model.{Monitoring, Reminder}
|
||||||
import com.lbs.server.util.DateTimeUtil._
|
import com.lbs.server.util.DateTimeUtil._
|
||||||
|
|
||||||
import java.time.{LocalDateTime, LocalTime}
|
import java.time.{LocalDateTime, LocalTime}
|
||||||
@@ -379,4 +379,17 @@ object Pl extends Lang {
|
|||||||
override def canNotDetectPayer(error: String): String = s"Nie udało się ustalić płatnika. Powód: $error"
|
override def canNotDetectPayer(error: String): String = s"Nie udało się ustalić płatnika. Powód: $error"
|
||||||
|
|
||||||
override def pleaseChoosePayer: String = "<b>➡</b> Nie udało się ustalić domyślnego płatnika, wybierz jakiegoś."
|
override def pleaseChoosePayer: String = "<b>➡</b> Nie udało się ustalić domyślnego płatnika, wybierz jakiegoś."
|
||||||
|
|
||||||
|
override def youHaveAppointmentAt(reminder: Reminder): String =
|
||||||
|
s"""👍 Macie wizytę o ⏱ <b>${formatTime(reminder.appointmentTime.toLocalTime)}</b>!
|
||||||
|
|
|
||||||
|
|${capitalize(doctor)}: ${reminder.doctorName}
|
||||||
|
|${capitalize(service)}: ${reminder.serviceName}
|
||||||
|
|${capitalize(clinic)}: ${reminder.clinicName}
|
||||||
|
|${capitalize(city)}: ${reminder.cityName}""".stripMargin
|
||||||
|
|
||||||
|
override def remindAt(time: LocalDateTime): String = s"⏱ Przypomnij o ${formatTime(time.toLocalTime)}"
|
||||||
|
|
||||||
|
override def appointmentIsOutdated(appointmentTime: LocalDateTime): String =
|
||||||
|
s"Twoja wizyta już się odbyła o godzine ${formatDateTime(appointmentTime, locale)}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.lbs.server.lang
|
|||||||
import com.lbs.api.json.model.{Event, TermExt}
|
import com.lbs.api.json.model.{Event, TermExt}
|
||||||
import com.lbs.server.conversation.Book
|
import com.lbs.server.conversation.Book
|
||||||
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
import com.lbs.server.conversation.StaticData.StaticDataConfig
|
||||||
import com.lbs.server.repository.model.Monitoring
|
import com.lbs.server.repository.model.{Monitoring, Reminder}
|
||||||
import com.lbs.server.util.DateTimeUtil._
|
import com.lbs.server.util.DateTimeUtil._
|
||||||
|
|
||||||
import java.time.{LocalDateTime, LocalTime}
|
import java.time.{LocalDateTime, LocalTime}
|
||||||
@@ -378,4 +378,17 @@ object Ua extends Lang {
|
|||||||
|
|
||||||
override def pleaseChoosePayer: String =
|
override def pleaseChoosePayer: String =
|
||||||
"<b>➡</b> Не можу визначити платника за замовчуванням. Будь ласка, виберіть платника"
|
"<b>➡</b> Не можу визначити платника за замовчуванням. Будь ласка, виберіть платника"
|
||||||
|
|
||||||
|
override def youHaveAppointmentAt(reminder: Reminder): String =
|
||||||
|
s"""👍 У вас є візит о ⏱ <b>${formatTime(reminder.appointmentTime.toLocalTime)}</b>!
|
||||||
|
|
|
||||||
|
|${capitalize(doctor)}: ${reminder.doctorName}
|
||||||
|
|${capitalize(service)}: ${reminder.serviceName}
|
||||||
|
|${capitalize(clinic)}: ${reminder.clinicName}
|
||||||
|
|${capitalize(city)}: ${reminder.cityName}""".stripMargin
|
||||||
|
|
||||||
|
override def remindAt(time: LocalDateTime): String = s"⏱ Remind at ${formatTime(time.toLocalTime)}"
|
||||||
|
|
||||||
|
override def appointmentIsOutdated(appointmentTime: LocalDateTime): String =
|
||||||
|
s"Your appointment has already taken place at ${formatDateTime(appointmentTime, locale)}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.lbs.server.repository
|
package com.lbs.server.repository
|
||||||
|
|
||||||
import com.lbs.server.repository.model.{CityHistory, ClinicHistory, Credentials, DoctorHistory, JLong, Monitoring, ServiceHistory, Settings, Source, SystemUser}
|
import com.lbs.server.repository.model.{CityHistory, ClinicHistory, Credentials, DoctorHistory, JLong, Monitoring, Reminder, ServiceHistory, Settings, Source, SystemUser}
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
@@ -95,6 +95,15 @@ class DataRepository(@Autowired em: EntityManager) {
|
|||||||
.toSeq
|
.toSeq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getActiveReminders: Seq[Reminder] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select reminder from Reminder reminder where reminder.active = true""".stripMargin,
|
||||||
|
classOf[Reminder]
|
||||||
|
).getResultList
|
||||||
|
.asScala
|
||||||
|
.toSeq
|
||||||
|
}
|
||||||
|
|
||||||
def getActiveMonitoringsCount(accountId: Long): JLong = {
|
def getActiveMonitoringsCount(accountId: Long): JLong = {
|
||||||
em.createQuery(
|
em.createQuery(
|
||||||
"""select count(monitoring) from Monitoring monitoring where monitoring.active = true
|
"""select count(monitoring) from Monitoring monitoring where monitoring.active = true
|
||||||
@@ -177,6 +186,18 @@ class DataRepository(@Autowired em: EntityManager) {
|
|||||||
.headOption
|
.headOption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def findReminder(accountId: Long, reminderId: Long): Option[Reminder] = {
|
||||||
|
em.createQuery(
|
||||||
|
"""select reminder from Reminder reminder where reminder.accountId = :accountId
|
||||||
|
| and reminder.recordId = :reminderId""".stripMargin,
|
||||||
|
classOf[Reminder]
|
||||||
|
).setParameter("accountId", accountId)
|
||||||
|
.setParameter("reminderId", reminderId)
|
||||||
|
.getResultList
|
||||||
|
.asScala
|
||||||
|
.headOption
|
||||||
|
}
|
||||||
|
|
||||||
def findSettings(userId: Long): Option[Settings] = {
|
def findSettings(userId: Long): Option[Settings] = {
|
||||||
em.createQuery("select settings from Settings settings where settings.userId = :userId", classOf[Settings])
|
em.createQuery("select settings from Settings settings where settings.userId = :userId", classOf[Settings])
|
||||||
.setParameter("userId", userId)
|
.setParameter("userId", userId)
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.lbs.server.repository.model
|
||||||
|
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.persistence.{Access, AccessType, Column, Entity}
|
||||||
|
import scala.beans.BeanProperty
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Access(AccessType.FIELD)
|
||||||
|
class Reminder extends RecordId {
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
var userId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "account_id", nullable = false)
|
||||||
|
var accountId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "chat_id", nullable = false)
|
||||||
|
var chatId: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "source_system_id", nullable = false)
|
||||||
|
var sourceSystemId: JLong = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "city_name", nullable = false)
|
||||||
|
var cityName: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "clinic_name", nullable = false)
|
||||||
|
var clinicName: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "service_name", nullable = false)
|
||||||
|
var serviceName: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "doctor_name", nullable = false)
|
||||||
|
var doctorName: String = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "appointment_time", nullable = false)
|
||||||
|
var appointmentTime: LocalDateTime = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(name = "remind_at_time", nullable = true)
|
||||||
|
var remindAt: LocalDateTime = _
|
||||||
|
|
||||||
|
@BeanProperty
|
||||||
|
@Column(nullable = false)
|
||||||
|
var active: Boolean = false
|
||||||
|
}
|
||||||
|
|
||||||
|
object Reminder {
|
||||||
|
def apply(
|
||||||
|
userId: Long,
|
||||||
|
accountId: Long,
|
||||||
|
chatId: String,
|
||||||
|
sourceSystemId: Long,
|
||||||
|
cityName: String,
|
||||||
|
clinicName: String,
|
||||||
|
serviceName: String,
|
||||||
|
doctorName: String,
|
||||||
|
appointmentTime: LocalDateTime,
|
||||||
|
remindAt: LocalDateTime,
|
||||||
|
active: Boolean
|
||||||
|
): Reminder = {
|
||||||
|
val reminder = new Reminder
|
||||||
|
reminder.userId = userId
|
||||||
|
reminder.accountId = accountId
|
||||||
|
reminder.chatId = chatId
|
||||||
|
reminder.sourceSystemId = sourceSystemId
|
||||||
|
reminder.cityName = cityName
|
||||||
|
reminder.clinicName = clinicName
|
||||||
|
reminder.serviceName = serviceName
|
||||||
|
reminder.doctorName = doctorName
|
||||||
|
reminder.appointmentTime = appointmentTime
|
||||||
|
reminder.remindAt = remindAt
|
||||||
|
reminder.active = active
|
||||||
|
reminder
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,6 +48,15 @@ class DataService {
|
|||||||
dataRepository.saveEntity(monitoring)
|
dataRepository.saveEntity(monitoring)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
def saveReminder(reminder: Reminder): Reminder = {
|
||||||
|
dataRepository.saveEntity(reminder)
|
||||||
|
}
|
||||||
|
|
||||||
|
def getActiveReminders: Seq[Reminder] = {
|
||||||
|
dataRepository.getActiveReminders
|
||||||
|
}
|
||||||
|
|
||||||
def getActiveMonitorings: Seq[Monitoring] = {
|
def getActiveMonitorings: Seq[Monitoring] = {
|
||||||
dataRepository.getActiveMonitorings
|
dataRepository.getActiveMonitorings
|
||||||
}
|
}
|
||||||
@@ -76,6 +85,10 @@ class DataService {
|
|||||||
dataRepository.findMonitoring(accountId, monitoringId)
|
dataRepository.findMonitoring(accountId, monitoringId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def findReminder(accountId: Long, reminderId: Long): Option[Reminder] = {
|
||||||
|
dataRepository.findReminder(accountId, reminderId)
|
||||||
|
}
|
||||||
|
|
||||||
def findSettings(userId: Long): Option[Settings] = {
|
def findSettings(userId: Long): Option[Settings] = {
|
||||||
dataRepository.findSettings(userId)
|
dataRepository.findSettings(userId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package com.lbs.server.service
|
|||||||
|
|
||||||
import com.lbs.api.exception.InvalidLoginOrPasswordException
|
import com.lbs.api.exception.InvalidLoginOrPasswordException
|
||||||
import com.lbs.api.json.model._
|
import com.lbs.api.json.model._
|
||||||
import com.lbs.bot.Bot
|
import com.lbs.bot.model.{Button, MessageSource, MessageSourceSystem}
|
||||||
import com.lbs.bot.model.{MessageSource, MessageSourceSystem}
|
import com.lbs.bot.{Bot, createInlineKeyboard}
|
||||||
import com.lbs.common.Scheduler
|
import com.lbs.common.Scheduler
|
||||||
import com.lbs.server.lang.Localization
|
import com.lbs.server.lang.Localization
|
||||||
import com.lbs.server.repository.model._
|
import com.lbs.server.repository.model._
|
||||||
@@ -13,7 +13,7 @@ import com.typesafe.scalalogging.StrictLogging
|
|||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
import java.time.{LocalDateTime, ZonedDateTime}
|
import java.time.{LocalDateTime, ZoneId, ZonedDateTime}
|
||||||
import java.util.concurrent.ScheduledFuture
|
import java.util.concurrent.ScheduledFuture
|
||||||
import javax.annotation.PostConstruct
|
import javax.annotation.PostConstruct
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
@@ -30,6 +30,8 @@ class MonitoringService extends StrictLogging {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private var apiService: ApiService = _
|
private var apiService: ApiService = _
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private var reminderService: ReminderService = _
|
||||||
|
@Autowired
|
||||||
private var localization: Localization = _
|
private var localization: Localization = _
|
||||||
|
|
||||||
private var activeMonitorings = mutable.Map.empty[JLong, (Monitoring, ScheduledFuture[_])]
|
private var activeMonitorings = mutable.Map.empty[JLong, (Monitoring, ScheduledFuture[_])]
|
||||||
@@ -195,8 +197,23 @@ class MonitoringService extends StrictLogging {
|
|||||||
} yield response
|
} yield response
|
||||||
bookingResult match {
|
bookingResult match {
|
||||||
case Right(_) =>
|
case Right(_) =>
|
||||||
bot.sendMessage(monitoring.source, lang(monitoring.userId).appointmentIsBooked(term, monitoring))
|
|
||||||
deactivateMonitoring(monitoring.accountId, monitoring.recordId)
|
deactivateMonitoring(monitoring.accountId, monitoring.recordId)
|
||||||
|
val reminder = reminderService.createInactiveReminder((term.term, monitoring).mapTo[Reminder])
|
||||||
|
val remind1h = term.term.dateTimeFrom.get.minusHours(1)
|
||||||
|
val remind1hMillis = remind1h.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli
|
||||||
|
val remind2h = term.term.dateTimeFrom.get.minusHours(2)
|
||||||
|
val remind2hMillis = remind2h.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli
|
||||||
|
val messages = lang(monitoring.userId)
|
||||||
|
bot.sendMessage(
|
||||||
|
monitoring.source,
|
||||||
|
messages.appointmentIsBooked(term, monitoring),
|
||||||
|
inlineKeyboard = createInlineKeyboard(
|
||||||
|
Seq(
|
||||||
|
Button(messages.remindAt(remind1h), s"remind_at_${reminder.recordId}_$remind1hMillis"),
|
||||||
|
Button(messages.remindAt(remind2h), s"remind_at_${reminder.recordId}_$remind2hMillis")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
case Left(ex) =>
|
case Left(ex) =>
|
||||||
logger.error(s"Unable to book appointment by monitoring [${monitoring.recordId}]", ex)
|
logger.error(s"Unable to book appointment by monitoring [${monitoring.recordId}]", ex)
|
||||||
}
|
}
|
||||||
@@ -206,7 +223,7 @@ class MonitoringService extends StrictLogging {
|
|||||||
accountId: Long,
|
accountId: Long,
|
||||||
xsrfToken: XsrfToken,
|
xsrfToken: XsrfToken,
|
||||||
temporaryReservationId: Long,
|
temporaryReservationId: Long,
|
||||||
fn: (Long) => Either[Throwable, T]
|
fn: Long => Either[Throwable, T]
|
||||||
): Either[Throwable, T] = {
|
): Either[Throwable, T] = {
|
||||||
fn(accountId) match {
|
fn(accountId) match {
|
||||||
case r @ Left(_) =>
|
case r @ Left(_) =>
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.lbs.server.service
|
||||||
|
|
||||||
|
import com.lbs.bot.Bot
|
||||||
|
import com.lbs.bot.model.{MessageSource, MessageSourceSystem}
|
||||||
|
import com.lbs.common.Scheduler
|
||||||
|
import com.lbs.server.lang.Localization
|
||||||
|
import com.lbs.server.repository.model._
|
||||||
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
import java.time.{Instant, LocalDateTime, ZoneId}
|
||||||
|
import javax.annotation.PostConstruct
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ReminderService extends StrictLogging {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private var bot: Bot = _
|
||||||
|
@Autowired
|
||||||
|
private var dataService: DataService = _
|
||||||
|
@Autowired
|
||||||
|
private var localization: Localization = _
|
||||||
|
|
||||||
|
private val dbChecker = new Scheduler(1)
|
||||||
|
|
||||||
|
private def deactivateReminder(accountId: JLong, reminderId: JLong): Unit = {
|
||||||
|
dataService.findReminder(accountId, reminderId).foreach { reminder =>
|
||||||
|
reminder.active = false
|
||||||
|
dataService.saveReminder(reminder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def remindUserAboutAppointment(reminder: Reminder): Unit = {
|
||||||
|
deactivateReminder(reminder.accountId, reminder.recordId)
|
||||||
|
|
||||||
|
val messages = lang(reminder.userId)
|
||||||
|
val message = messages.youHaveAppointmentAt(reminder)
|
||||||
|
|
||||||
|
bot.sendMessage(reminder.source, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkReminders(): Unit = {
|
||||||
|
logger.debug(s"Looking for active reminders")
|
||||||
|
|
||||||
|
val activeReminders = dataService.getActiveReminders
|
||||||
|
logger.debug(s"Found [${activeReminders.size}] active reminders")
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
|
||||||
|
activeReminders.foreach {
|
||||||
|
case reminder if reminder.remindAt.isAfter(now) =>
|
||||||
|
logger.debug(s"Notifying user [${reminder.userId}] about appointment at [${reminder.appointmentTime}]")
|
||||||
|
remindUserAboutAppointment(reminder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def initializeDbChecker(): Unit = {
|
||||||
|
dbChecker.schedule(checkReminders(), 20.seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
def createInactiveReminder(reminder: Reminder): Reminder = {
|
||||||
|
reminder.active = false
|
||||||
|
dataService.saveReminder(reminder)
|
||||||
|
}
|
||||||
|
|
||||||
|
def activateReminder(accountId: Long, reminderId: Long, timeMillis: Long): Unit = {
|
||||||
|
val time = Instant.ofEpochMilli(timeMillis).atZone(ZoneId.systemDefault()).toLocalDateTime
|
||||||
|
val reminderMaybe = dataService.findReminder(accountId, reminderId)
|
||||||
|
|
||||||
|
reminderMaybe.foreach { reminder =>
|
||||||
|
if (reminder.appointmentTime.isBefore(time)) {
|
||||||
|
reminder.active = true
|
||||||
|
reminder.remindAt = time
|
||||||
|
dataService.saveReminder(reminder)
|
||||||
|
} else {
|
||||||
|
val messages = lang(reminder.userId)
|
||||||
|
val message = messages.appointmentIsOutdated(reminder.appointmentTime)
|
||||||
|
bot.sendMessage(reminder.source, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class ReminderAsSource(reminder: Reminder) {
|
||||||
|
def source: MessageSource = MessageSource(MessageSourceSystem(reminder.sourceSystemId), reminder.chatId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def lang(userId: Long) = localization.lang(userId)
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private def initialize(): Unit = {
|
||||||
|
initializeDbChecker()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,12 @@ import com.lbs.bot.model.Command
|
|||||||
import com.lbs.common.ModelConverters
|
import com.lbs.common.ModelConverters
|
||||||
import com.lbs.server.conversation.Book.BookingData
|
import com.lbs.server.conversation.Book.BookingData
|
||||||
import com.lbs.server.conversation.Login.UserId
|
import com.lbs.server.conversation.Login.UserId
|
||||||
import com.lbs.server.repository.model.{History, Monitoring}
|
import com.lbs.server.repository.model.{History, Monitoring, Reminder}
|
||||||
|
|
||||||
import java.time._
|
import java.time._
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import scala.language.{higherKinds, implicitConversions}
|
import scala.language.implicitConversions
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
package object util {
|
package object util {
|
||||||
@@ -109,6 +109,24 @@ package object util {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implicit val TermAndMonitoringToReminder: ObjectConverter[(Term, Monitoring), Reminder] =
|
||||||
|
(data: (Term, Monitoring)) => {
|
||||||
|
val (term, monitoring) = data
|
||||||
|
Reminder(
|
||||||
|
userId = monitoring.userId,
|
||||||
|
accountId = monitoring.accountId,
|
||||||
|
chatId = monitoring.chatId,
|
||||||
|
sourceSystemId = monitoring.sourceSystemId,
|
||||||
|
cityName = monitoring.cityName,
|
||||||
|
clinicName = monitoring.clinicName,
|
||||||
|
serviceName = monitoring.serviceName,
|
||||||
|
doctorName = monitoring.doctorName,
|
||||||
|
appointmentTime = term.dateTimeFrom.get,
|
||||||
|
remindAt = null,
|
||||||
|
active = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
implicit val HistoryToIdNameConverter: ObjectConverter[History, IdName] =
|
implicit val HistoryToIdNameConverter: ObjectConverter[History, IdName] =
|
||||||
(history: History) => IdName(history.id, history.name)
|
(history: History) => IdName(history.id, history.name)
|
||||||
}
|
}
|
||||||
@@ -167,6 +185,8 @@ package object util {
|
|||||||
|
|
||||||
def formatDateTime(date: LuxmedFunnyDateTime, locale: Locale): String = date.get.format(DateTimeFormat(locale))
|
def formatDateTime(date: LuxmedFunnyDateTime, locale: Locale): String = date.get.format(DateTimeFormat(locale))
|
||||||
|
|
||||||
|
def formatDateTime(date: LocalDateTime, locale: Locale): String = date.format(DateTimeFormat(locale))
|
||||||
|
|
||||||
private val EpochMinutesTillBeginOf2022: Long = epochMinutes(LocalDateTime.of(2022, 1, 1, 0, 0, 0, 0))
|
private val EpochMinutesTillBeginOf2022: Long = epochMinutes(LocalDateTime.of(2022, 1, 1, 0, 0, 0, 0))
|
||||||
|
|
||||||
def epochMinutes(time: LocalDateTime): Long = time.toInstant(ZonedDateTime.now().getOffset).getEpochSecond / 60
|
def epochMinutes(time: LocalDateTime): Long = time.toInstant(ZonedDateTime.now().getOffset).getEpochSecond / 60
|
||||||
|
|||||||
Reference in New Issue
Block a user