Skip to content

Commit

Permalink
Merge pull request #12 from rufer7/11-create-demo-app
Browse files Browse the repository at this point in the history
Create initial version of demo app
  • Loading branch information
R4ffi authored Jan 24, 2025
2 parents 2843d69 + 59ec2ea commit 709dc77
Show file tree
Hide file tree
Showing 16 changed files with 369 additions and 0 deletions.
37 changes: 37 additions & 0 deletions demo-app/ConsoleApp/ChatService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using RecommenderLogic;

namespace ConsoleApp;

internal sealed class ChatService
(
ILogger<ChatService> Logger,
IHostApplicationLifetime AppLifetime,
IChatInterface ChatInterface
)
{
public void StartAndCompleteConversation()
{
try
{
Console.WriteLine(ChatInterface.StartConversation());

while (!AppLifetime.ApplicationStopping.IsCancellationRequested)
{
var message = Console.ReadLine();
AppLifetime.ApplicationStopping.ThrowIfCancellationRequested();
var answer = ChatInterface.GetAnswerForMessage(message);
Console.Write(answer);
}
}
catch (OperationCanceledException ex)
{
Logger.LogDebug(ex, "Application stopping");
}
catch (Exception ex)
{
Logger.LogError(ex, "Unhandled exception!");
}
}
}
18 changes: 18 additions & 0 deletions demo-app/ConsoleApp/ConsoleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\RecommenderLogic\RecommenderLogic.csproj" />
</ItemGroup>

</Project>
38 changes: 38 additions & 0 deletions demo-app/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using RecommenderLogic.Extensions;

namespace ConsoleApp;

internal static class Program
{
public static async Task Main(string[] args)
{
using (var host = Host.CreateDefaultBuilder(args).ConfigureServices(AddServices).Build())
{
await host.StartAsync();
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
var chatService = host.Services.GetRequiredService<ChatService>();

chatService.StartAndCompleteConversation();

lifetime.StopApplication();
await host.WaitForShutdownAsync();
}
}

private static void AddServices(HostBuilderContext hostContext, IServiceCollection services)
{
services.AddLogging(
builder =>
{
builder.AddConsole();
builder.AddDebug();
}
);

services.AddTravelAgency();
services.AddTransient<ChatService>();
}
}
22 changes: 22 additions & 0 deletions demo-app/RecommenderLogic.Test/RecommenderLogic.Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="FluentAssertions" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions demo-app/RecommenderLogic.Test/UnitTest1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using FluentAssertions;

namespace RecommenderLogic.Test;

public class UnitTest1
{
[Fact]
public void Test1()
{
true.Should().BeTrue();
}
}
5 changes: 5 additions & 0 deletions demo-app/RecommenderLogic/Destinations/Destination.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using RecommenderLogic.Parameters;

namespace RecommenderLogic.Destinations;

internal readonly record struct Destination(string Name, string Description, AgeCategory AgeCategory, Companion Companion, Reason Reason);
42 changes: 42 additions & 0 deletions demo-app/RecommenderLogic/Destinations/DestinationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using RecommenderLogic.Parameters;

namespace RecommenderLogic.Destinations;

internal class DestinationProvider
{
private readonly List<Destination> destinations =
[
new("Bali", "Lounge on beaches, devour Nasi Goreng, and perfect the art of doing nothing.", AgeCategory.YoungAdult, Companion.Solo, Reason.EatSleepRepeat),
new("Santorini", "Whitewashed walls and sunsets so pretty your Instagram will crash.", AgeCategory.YoungAdult, Companion.Solo, Reason.FlexOnSocialMedia),
new("Chiang Mai", "Co-working cafes by day, street food by night; pretend you’re 'finding yourself' while working.", AgeCategory.YoungAdult, Companion.Solo, Reason.SecretlyWorking),
new("Maldives", "Water villas and private dinners; say 'Sorry, no Wi-Fi!' with your partner.", AgeCategory.YoungAdult, Companion.Partner, Reason.EatSleepRepeat),
new("Positano", "Hold hands, drink wine, and make your friends cry over your Amalfi Coast views.", AgeCategory.YoungAdult, Companion.Partner, Reason.FlexOnSocialMedia),
new("Lisbon", "Cute alleys for selfies and Wi-Fi cafes to sneakily check your emails.", AgeCategory.YoungAdult, Companion.Partner, Reason.SecretlyWorking),
new("Orlando", "Theme parks for the kids, buffet lines for you. Mickey Mouse is your spirit animal.", AgeCategory.YoungAdult, Companion.Family, Reason.EatSleepRepeat),
new("Dubai", "Indoor skiing, luxury malls, and family photos that scream 'we made it!'", AgeCategory.YoungAdult, Companion.Family, Reason.FlexOnSocialMedia),
new("Phuket", "Juggle beach time with kids and secret Zoom calls from your balcony.", AgeCategory.YoungAdult, Companion.Family, Reason.SecretlyWorking),

new("Tuscany", "Sip wine, eat pasta, and nap under the Italian sun like royalty.", AgeCategory.Adult, Companion.Solo, Reason.EatSleepRepeat),
new("Machu Picchu", "Post selfies on ancient ruins and claim you 'hiked' all the way up.", AgeCategory.Adult, Companion.Solo, Reason.FlexOnSocialMedia),
new("Kyoto", "Zen gardens for inner peace and coworking spaces to crush deadlines.", AgeCategory.Adult, Companion.Solo, Reason.SecretlyWorking),
new("Seychelles", "Forget your problems while sipping cocktails in paradise with your partner.", AgeCategory.Adult, Companion.Partner, Reason.EatSleepRepeat),
new("Paris", "Kiss by the Eiffel Tower and hashtag #NoFilter for maximum envy points.", AgeCategory.Adult, Companion.Partner, Reason.FlexOnSocialMedia),
new("Barcelona", "Squeeze in emails between tapas bars and Gaudí architecture tours.", AgeCategory.Adult, Companion.Partner, Reason.SecretlyWorking),
new("Cancún", "All-inclusive resorts mean zero effort while the kids are busy with sandcastles.", AgeCategory.Adult, Companion.Family, Reason.EatSleepRepeat),
new("Reykjavík", "Post your whole crew under the Northern Lights and wait for 'wow' comments.", AgeCategory.Adult, Companion.Family, Reason.FlexOnSocialMedia),
new("Hawaii", "Take calls while the kids surf. Your background is too stunning for anyone to care.", AgeCategory.Adult, Companion.Family, Reason.SecretlyWorking),

new("Provence", "Lavender fields, slow strolls, and cheese that pairs perfectly with naps.", AgeCategory.Senior, Companion.Solo, Reason.EatSleepRepeat),
new("Banff", "Snap pictures of turquoise lakes and post, 'Oh, just another quiet day.'", AgeCategory.Senior, Companion.Solo, Reason.FlexOnSocialMedia),
new("Amsterdam", "Cycle along canals and schedule Zoom calls under the guise of 'slow travel.'", AgeCategory.Senior, Companion.Solo, Reason.SecretlyWorking),
new("Venice", "Gondola rides, endless gelato, and leisurely people-watching with your partner.", AgeCategory.Senior, Companion.Partner, Reason.EatSleepRepeat),
new("Kyoto", "Cherry blossoms, kimonos, and a feed full of serene beauty.", AgeCategory.Senior, Companion.Partner, Reason.FlexOnSocialMedia),
new("Dubrovnik", "Claim you’re exploring 'Game of Thrones' spots while answering emails.", AgeCategory.Senior, Companion.Partner, Reason.SecretlyWorking),
new("Scottsdale", "Spas for you, golf for them, and a schedule that’s empty as your inbox.", AgeCategory.Senior, Companion.Family, Reason.EatSleepRepeat),
new("Sydney", "Opera House, Bondi Beach, and family photos that scream 'global explorers.'", AgeCategory.Senior, Companion.Family, Reason.FlexOnSocialMedia),
new("Cape Town", "Safari by day, sneak in emails by night—just don’t let the lions know.", AgeCategory.Senior, Companion.Family, Reason.SecretlyWorking),
];

public Destination GetDestination(AgeCategory ageCategory, Companion companion, Reason reason) =>
destinations.FirstOrDefault(d => d.AgeCategory == ageCategory && d.Companion == companion && d.Reason == reason);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using RecommenderLogic.Destinations;

namespace RecommenderLogic.Extensions;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddTravelAgency(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);
services.AddTransient<IChatInterface, TravelAgent>();
services.AddTransient<DestinationProvider>();
return services;
}
}
7 changes: 7 additions & 0 deletions demo-app/RecommenderLogic/IChatInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace RecommenderLogic;

public interface IChatInterface
{
string StartConversation();
string GetAnswerForMessage(string? message);
}
53 changes: 53 additions & 0 deletions demo-app/RecommenderLogic/Outputs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace RecommenderLogic;

internal readonly record struct Outputs
{
public const string EmptyLine = @"
";
public const string Header = @"
_______ _
|__ __| | |
| |_ __ __ ___ _____| |
| | '__/ _` \ \ / / _ \ |
| | | | (_| |\ V / __/ |
__|_|_| \__,_| \_/ \___|_| _ _
| __ \ | | (_) | | (_)
| | | | ___ ___| |_ _ _ __ __ _| |_ _ ___ _ __
| | | |/ _ \/ __| __| | '_ \ / _` | __| |/ _ \| '_ \
| |__| | __/\__ \ |_| | | | | (_| | |_| | (_) | | | |
|_____/ \___||___/\__|_|_| |_|\__,_|\__|_|\___/|_| |_| _
| __ \ | |
| |__) |___ ___ ___ _ __ ___ _ __ ___ ___ _ __ __| | ___ _ __
| _ // _ \/ __/ _ \| '_ ` _ \| '_ ` _ \ / _ \ '_ \ / _` |/ _ \ '__|
| | \ \ __/ (_| (_) | | | | | | | | | | | __/ | | | (_| | __/ |
|_| \_\___|\___\___/|_| |_| |_|_| |_| |_|\___|_| |_|\__,_|\___|_|
";
public const string WelcomeMessage = @"
The Travel Destination Recommender is an innovative system that utilises
advanced algorithms to provide recommendations for travel destinations,
taking into account a range of factors.
";
public const string AgeQuestion = @"
What is the age of the primary traveller?
[1] 18-30
[2] 30-60
[3] 60+
";
public const string CompanionQuestion = @"
Who will be accompanying the primary traveller?
[1] No one
[2] Partner
[3] Family
";
public const string ReasonQuestion = @"
What is the primary motivation for travelling?
[1] Eat, Sleep, Repeat: Do nothing but gain weight
[2] Flex on Social Media: All others should be jealous
[3] Secretly Working: My boss forces me to go on vacation, but I work anyway
";
public const string UnknownAnswer = "I'm sorry, I didn't understand that. Please try again.";
public static string Concat(params string[] strings) => string.Join(EmptyLine, strings);
}
10 changes: 10 additions & 0 deletions demo-app/RecommenderLogic/Parameters/AgeCategory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace RecommenderLogic.Parameters;

internal enum AgeCategory
{
Unknown = 0,
YoungAdult = 1,
Adult = 2,
Midlife = 3,
Senior = 4,
}
10 changes: 10 additions & 0 deletions demo-app/RecommenderLogic/Parameters/Companion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace RecommenderLogic.Parameters;

internal enum Companion
{
Unknown = 0,
Solo = 1,
Partner = 2,
Family = 3,
Friends = 4
}
10 changes: 10 additions & 0 deletions demo-app/RecommenderLogic/Parameters/Reason.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace RecommenderLogic.Parameters;

internal enum Reason
{
Unknown = 0,
EatSleepRepeat = 1,
HideFromReality = 2,
SecretlyWorking = 3,
FlexOnSocialMedia = 4,
}
13 changes: 13 additions & 0 deletions demo-app/RecommenderLogic/RecommenderLogic.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
</ItemGroup>

</Project>
43 changes: 43 additions & 0 deletions demo-app/RecommenderLogic/TravelAgent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using RecommenderLogic.Destinations;
using RecommenderLogic.Parameters;

namespace RecommenderLogic;

internal class TravelAgent(DestinationProvider DestinationProvider) : IChatInterface
{
private AgeCategory ageCategory = AgeCategory.Unknown;
private Companion companion = Companion.Unknown;
private Reason reason = Reason.Unknown;

public string StartConversation() => Outputs.Concat(Outputs.Header, Outputs.WelcomeMessage, Outputs.AgeQuestion);

public string GetAnswerForMessage(string? message)
{
if (ageCategory == AgeCategory.Unknown)
{
return Enum.TryParse(message, out ageCategory)
? Outputs.CompanionQuestion
: Outputs.Concat(Outputs.UnknownAnswer, Outputs.AgeQuestion);
}

if (companion == Companion.Unknown)
{
return Enum.TryParse(message, out companion)
? Outputs.ReasonQuestion
: Outputs.Concat(Outputs.UnknownAnswer, Outputs.CompanionQuestion);
}

if (reason == Reason.Unknown)
{
if (Enum.TryParse(message, out reason))
{
var destination = DestinationProvider.GetDestination(ageCategory, companion, reason);
return Outputs.Concat(destination.Name, destination.Description);
}

return Outputs.Concat(Outputs.UnknownAnswer, Outputs.ReasonQuestion);
}

return "Press Ctrl+C to shut down";
}
}
34 changes: 34 additions & 0 deletions demo-app/TravelDestinationRecommender.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.12.35707.178
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{E73D0338-E8BD-44FD-BA9D-798343CF3085}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RecommenderLogic", "RecommenderLogic\RecommenderLogic.csproj", "{8A711A80-AC79-4C9C-BA67-03EC1B2051C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RecommenderLogic.Test", "RecommenderLogic.Test\RecommenderLogic.Test.csproj", "{69277E3C-EC3C-4BE8-8ACB-E7F687D63784}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E73D0338-E8BD-44FD-BA9D-798343CF3085}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E73D0338-E8BD-44FD-BA9D-798343CF3085}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E73D0338-E8BD-44FD-BA9D-798343CF3085}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E73D0338-E8BD-44FD-BA9D-798343CF3085}.Release|Any CPU.Build.0 = Release|Any CPU
{8A711A80-AC79-4C9C-BA67-03EC1B2051C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A711A80-AC79-4C9C-BA67-03EC1B2051C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A711A80-AC79-4C9C-BA67-03EC1B2051C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A711A80-AC79-4C9C-BA67-03EC1B2051C2}.Release|Any CPU.Build.0 = Release|Any CPU
{69277E3C-EC3C-4BE8-8ACB-E7F687D63784}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69277E3C-EC3C-4BE8-8ACB-E7F687D63784}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69277E3C-EC3C-4BE8-8ACB-E7F687D63784}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69277E3C-EC3C-4BE8-8ACB-E7F687D63784}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

0 comments on commit 709dc77

Please sign in to comment.