From 1c2da57916ce99e6d48523c8b697dded455b1ad9 Mon Sep 17 00:00:00 2001 From: sipakov Date: Fri, 14 Apr 2023 17:35:40 +0300 Subject: [PATCH] #8 Integrated a geoadmin search library into the bot --- .../ApplicationDbContext.cs | 12 +- .../Core/Repositories/SpaceRepository.cs | 3 +- .../Controllers/BoatController.cs | 39 +++--- .../Controllers/Interfaces/IBoatController.cs | 2 + .../Controllers/MainController.cs | 113 +++++++++++++++++- 5 files changed, 131 insertions(+), 38 deletions(-) diff --git a/AlgoTecture.Data.Persistence.Ef/ApplicationDbContext.cs b/AlgoTecture.Data.Persistence.Ef/ApplicationDbContext.cs index 084d5f6..f0daa69 100644 --- a/AlgoTecture.Data.Persistence.Ef/ApplicationDbContext.cs +++ b/AlgoTecture.Data.Persistence.Ef/ApplicationDbContext.cs @@ -95,11 +95,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) SpaceId = 1, Name = "Santa María", SpacePropertyId = Guid.Parse("4c4f455c-bc98-47da-9f4b-9dcc25a17fe5"), - Description = "Description" + Description = "best boat in the world" }; modelBuilder.Entity().HasData(new Space { - Id = 1, Latitude = 38.705022, Longitude = -9.145460, SpaceAddress = "Lisbon, Lisboa-Cacilhas", + Id = 1, Latitude = 47.361812591552734, Longitude = 8.5370702743530273, SpaceAddress = "Mythenquai 7, 8002 Zürich", SpaceProperty = JsonConvert.SerializeObject(newSpaceProperty1), UtilizationTypeId = 12 }); var newSpaceProperty2 = new SpaceProperty @@ -107,11 +107,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) SpaceId = 2, Name = "Niña", SpacePropertyId = Guid.Parse("7d2dc2f3-4f52-4244-8ade-73eba2772a51"), - Description = "Description" + Description = "best boat in the world" }; modelBuilder.Entity().HasData(new Space { - Id = 2, Latitude = 38.705022, Longitude = -9.145460, SpaceAddress = "Lisbon, Lisboa-Cacilhas", + Id = 2, Latitude = 47.36164855957031, Longitude = 8.5366735458374023, SpaceAddress = "Mythenquai 9, 8002 Zürich", SpaceProperty = JsonConvert.SerializeObject(newSpaceProperty2), UtilizationTypeId = 12 }); var newSpaceProperty3 = new SpaceProperty @@ -119,11 +119,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) SpaceId = 3, Name = "Pinta", SpacePropertyId = Guid.Parse("a5f8e388-0c2f-491c-82ff-d4c92da97aaa"), - Description = "Description" + Description = "best boat in the world" }; modelBuilder.Entity().HasData(new Space { - Id = 3, Latitude = 38.705022, Longitude = -9.145460, SpaceAddress = "Lisbon, Lisboa-Cacilhas", + Id = 3, Latitude = 47.3613166809082, Longitude = 8.5362958908081055, SpaceAddress = "Mythenquai 25, 8002 Zürich", SpaceProperty = JsonConvert.SerializeObject(newSpaceProperty3), UtilizationTypeId = 12 }); diff --git a/AlgoTecture.Data.Persistence/Core/Repositories/SpaceRepository.cs b/AlgoTecture.Data.Persistence/Core/Repositories/SpaceRepository.cs index 3520865..1983f57 100644 --- a/AlgoTecture.Data.Persistence/Core/Repositories/SpaceRepository.cs +++ b/AlgoTecture.Data.Persistence/Core/Repositories/SpaceRepository.cs @@ -25,7 +25,8 @@ public override async Task> All() public async Task GetByCoordinates(double latitude, double longitude) { - return await dbSet.FirstOrDefaultAsync(x=>Math.Abs(x.Latitude - latitude) < 0.000000001 && Math.Abs(x.Longitude - longitude) < 0.000000001); + return await dbSet.Include(x=>x.UtilizationType) + .FirstOrDefaultAsync(x=>Math.Abs(x.Latitude - latitude) < 0.000000001 && Math.Abs(x.Longitude - longitude) < 0.000000001); } public async Task> GetByType(int utilizationTypeId) diff --git a/AlgoTecture.TelegramBot/Controllers/BoatController.cs b/AlgoTecture.TelegramBot/Controllers/BoatController.cs index 9acef4a..0a119c7 100644 --- a/AlgoTecture.TelegramBot/Controllers/BoatController.cs +++ b/AlgoTecture.TelegramBot/Controllers/BoatController.cs @@ -25,7 +25,8 @@ public class BoatController : BotController, IBoatController private readonly IReservationService _reservationService; private readonly ILogger _logger; - public BoatController(ISpaceGetter spaceGetter, IServiceProvider serviceProvider, IUnitOfWork unitOfWork, IReservationService reservationService, ILogger logger) + public BoatController(ISpaceGetter spaceGetter, IServiceProvider serviceProvider, IUnitOfWork unitOfWork, IReservationService reservationService, + ILogger logger) { _spaceGetter = spaceGetter ?? throw new ArgumentNullException(nameof(spaceGetter)); _serviceProvider = serviceProvider; @@ -37,6 +38,13 @@ public BoatController(ISpaceGetter spaceGetter, IServiceProvider serviceProvider [Action] public async Task PressToMainBookingPage(BotState botState) { + //only for demo + const int boatTargetOfSpaceId = 12; + if (botState.UtilizationTypeId !=boatTargetOfSpaceId) + { + return; + } + RowButton("See available boats", Q(PressToRentTargetUtilizationButton, botState, true)); RowButton("Make a reservation", Q(PressToEnterTheStartEndTime, botState, RentTimeState.None, null)); @@ -61,10 +69,8 @@ private async Task PressToRentTargetUtilizationButton(BotState botState, bool is botState.SpaceId = default; botState.SpaceName = default; } - - const int boatTargetOfSpaceId = 12; - - var targetSpaces = await _spaceGetter.GetByType(boatTargetOfSpaceId); + + var targetSpaces = await _spaceGetter.GetByType(botState.UtilizationTypeId); foreach (var space in targetSpaces) { @@ -90,7 +96,7 @@ private async Task PressToChooseTheDate(BotState botState, RentTimeState rentTim } [Action] - private async Task PressToEnterTheStartEndTime(BotState botState, RentTimeState rentTimeState, DateTime? dateTime) + public async Task PressToEnterTheStartEndTime(BotState botState, RentTimeState rentTimeState, DateTime? dateTime) { var chatId = Context.GetSafeChatId(); if (!chatId.HasValue) return; @@ -148,12 +154,12 @@ private async Task PressToEnterTheStartEndTime(BotState botState, RentTimeState if (string.IsNullOrEmpty(time)) { - PushL("Reservation"); + PushL("Reservation the boat"); await SendOrUpdate(); } else { - await Send("Reservation"); + await Send("Reservation the boat"); } } @@ -306,21 +312,4 @@ private async Task PressMakeAReservation(BotState botState) throw; } } - - [On(Handle.Exception)] - public async Task Exception() - { - PushL("Ooops"); - - await SendOrUpdate(); - } - - [On(Handle.Unknown)] - public async Task Unknown() - { - PushL( - "I'm sorry, but I'm not yet able to understand natural language requests at the moment. Please provide specific instructions using the AlgoTecture bot " + - "interface for me to execute tasks."); - await SendOrUpdate(); - } } \ No newline at end of file diff --git a/AlgoTecture.TelegramBot/Controllers/Interfaces/IBoatController.cs b/AlgoTecture.TelegramBot/Controllers/Interfaces/IBoatController.cs index 499554a..fdd8a96 100644 --- a/AlgoTecture.TelegramBot/Controllers/Interfaces/IBoatController.cs +++ b/AlgoTecture.TelegramBot/Controllers/Interfaces/IBoatController.cs @@ -5,4 +5,6 @@ namespace AlgoTecture.TelegramBot.Controllers.Interfaces; public interface IBoatController { Task PressToMainBookingPage(BotState botState); + + Task PressToEnterTheStartEndTime(BotState botState, RentTimeState rentTimeState, DateTime? dateTime); } \ No newline at end of file diff --git a/AlgoTecture.TelegramBot/Controllers/MainController.cs b/AlgoTecture.TelegramBot/Controllers/MainController.cs index b9b14c3..a5575b8 100644 --- a/AlgoTecture.TelegramBot/Controllers/MainController.cs +++ b/AlgoTecture.TelegramBot/Controllers/MainController.cs @@ -1,10 +1,14 @@ using AlgoTecture.Data.Persistence.Core.Interfaces; +using AlgoTecture.Domain.Models; using AlgoTecture.Domain.Models.Dto; +using AlgoTecture.Libraries.GeoAdminSearch; +using AlgoTecture.Libraries.Spaces.Interfaces; using AlgoTecture.Libraries.UtilizationTypes; using AlgoTecture.TelegramBot.Controllers.Interfaces; using AlgoTecture.TelegramBot.Interfaces; using AlgoTecture.TelegramBot.Models; using Deployf.Botf; +using Newtonsoft.Json; namespace AlgoTecture.TelegramBot.Controllers; @@ -14,16 +18,26 @@ public class MainController : BotController, IMainController private readonly IUtilizationTypeGetter _utilizationTypeGetter; private readonly IUnitOfWork _unitOfWork; private readonly ILogger _logger; + private readonly IGeoAdminSearcher _geoAdminSearcher; + private readonly ITelegramToAddressResolver _telegramToAddressResolver; + private readonly ISpaceGetter _spaceGetter; + private readonly IServiceProvider _serviceProvider; private readonly IBoatController _boatController; - public MainController(ITelegramUserInfoService telegramUserInfoService, IBoatController boatController, IUtilizationTypeGetter utilizationTypeGetter, IUnitOfWork unitOfWork, ILogger logger) + public MainController(ITelegramUserInfoService telegramUserInfoService, IBoatController boatController, IUtilizationTypeGetter utilizationTypeGetter, + IUnitOfWork unitOfWork, ILogger logger, IGeoAdminSearcher geoAdminSearcher, ITelegramToAddressResolver telegramToAddressResolver, + ISpaceGetter spaceGetter, IServiceProvider serviceProvider) { _telegramUserInfoService = telegramUserInfoService; _boatController = boatController; _utilizationTypeGetter = utilizationTypeGetter; _unitOfWork = unitOfWork; _logger = logger; + _geoAdminSearcher = geoAdminSearcher; + _telegramToAddressResolver = telegramToAddressResolver; + _spaceGetter = spaceGetter; + _serviceProvider = serviceProvider; } [Action("/start", "start the bot")] @@ -56,7 +70,9 @@ public async Task Start() [Action] public async Task PressToRentButton() { - var utilizationTypes = (await _utilizationTypeGetter.GetAll()).Skip(6).ToList(); + //only for demo + var utilizationTypes = (await _utilizationTypeGetter.GetAll()).Where(x=> (new List(){"Residential", "Parking", "Boat"}) + .Contains(x.Name)).ToList(); foreach (var utilizationType in utilizationTypes) { @@ -66,7 +82,7 @@ public async Task PressToRentButton() Id = utilizationType.Id }; - var botState = new BotState { UtilizationTypeId = utilizationType.Id, MessageId = default(int) }; + var botState = new BotState { UtilizationTypeId = utilizationType.Id, MessageId = default }; RowButton(utilizationTypeOut.Name, Q(_boatController.PressToMainBookingPage, botState)); } @@ -92,15 +108,16 @@ public async Task PressToFindReservationsButton() var reservationToTelegram = new ReservationToTelegramOut() { Id = reservation.Id, - DateTimeFrom = $"{reservation.ReservationFromUtc.Value:dd MM yyyy HH:mm} utc", - DateTimeTo = $"{reservation.ReservationToUtc.Value:dd MM yyyy HH:mm} utc", + DateTimeFrom = $"{reservation.ReservationFromUtc.Value:dd-MM-yyyy HH:mm} utc", + DateTimeTo = $"{reservation.ReservationToUtc.Value:dd-MM-yyyy HH:mm} utc", Description = reservation.Description, TotlaPrice = reservation.TotalPrice, PriceCurrency = reservation.PriceSpecification.PriceCurrency }; reservationList.Add(reservationToTelegram); var description = - $"{reservationToTelegram.Description}, {reservationToTelegram.DateTimeFrom} - {reservationToTelegram.DateTimeTo}, {reservationToTelegram.TotlaPrice} {reservationToTelegram.PriceCurrency.ToUpper()}"; + $"{reservationToTelegram.Description}, \n{reservationToTelegram.DateTimeFrom}, {reservationToTelegram.TotlaPrice} \n" + + $"{reservationToTelegram.PriceCurrency.ToUpper()}"; RowButton(description, Q(PressToManageContract)); } RowButton("Go Back", Q(Start)); @@ -114,4 +131,88 @@ public async Task PressToManageContract() { } + + [Action] + private async Task PressAddressToRentButton(string geoAdminFeatureId) + { + var chatId = Context.GetSafeChatId(); + if (!chatId.HasValue) return; + + var targetAddress = _telegramToAddressResolver.TryGetAddressListByChatId(chatId.Value).FirstOrDefault(x => x.FeatureId == geoAdminFeatureId); + + //var user = await _unitOfWork.Users.GetByTelegramChatId(chatId.Value); + var targetSpace = await _spaceGetter.GetByCoordinates(targetAddress.latitude, targetAddress.longitude); + + _telegramToAddressResolver.RemoveAddressListByChatId(chatId.Value); + + if (targetSpace == null) + { + await Start(); + await Send("No spaces found at this address"); + } + else + { + var targetSpaceProperty = JsonConvert.DeserializeObject(targetSpace.SpaceProperty); + + var boatControllerService = _serviceProvider.GetRequiredService(); + + RowButton("Rent!", Q(boatControllerService.PressToEnterTheStartEndTime, new BotState{SpaceId = targetSpace.Id, + SpaceName = targetSpaceProperty.Name}, RentTimeState.None, null)); + RowButton("Go to main", Q(Start)); + + PushL($"Found! {targetSpace.UtilizationType.Name}: {targetSpaceProperty?.Name}. {targetSpaceProperty?.Description}"); + } + } + + [On(Handle.Exception)] + public async Task Exception(Exception ex) + { + _logger.LogError(ex, "Handle.Exception on telegram-bot"); + + PushL("Ooops. An error has occurred"); + await Start(); + await SendOrUpdate(); + } + + [On(Handle.Unknown)] + public async Task Unknown() + { + var chatId = Context.GetSafeChatId(); + if (!chatId.HasValue) return; + + PushL( + "I'm sorry, but I'm not yet able to understand natural language requests at the moment. Enter an address to search for the space"); + await Send(); + + var address = await AwaitText(); + + var telegramToAddressList = new List(); + + + var labels = (await _geoAdminSearcher.GetAddress(address)).ToList(); + + foreach (var label in labels) + { + var telegramToAddressModel = new TelegramToAddressModel + { + FeatureId = label.featureId, + latitude = label.lat, + longitude = label.lon, + Address = label.label + }; + telegramToAddressList.Add(telegramToAddressModel); + RowButton(label.label, Q(PressAddressToRentButton, label.featureId)); + } + + if (!labels.Any()) + { + RowButton("Try again"); + await Send("Nothing found"); + } + else + { + _telegramToAddressResolver.TryAddCurrentAddressList(chatId.Value, telegramToAddressList); + await Send("Choose the right address"); + } + } } \ No newline at end of file