Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions drinksInfo.0lcm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Drinks Info
This is a learning project created for and following the requirments set out in [this](https://thecsharpacademy.com/project/15/drinks) webpage.
This app deals with getting data from [The Cocktail Db's API](https://www.thecocktaildb.com/api.php) and presents it to the user in a nicer Ui.

# Features
* Features searching mechanics for drinks and drink ingredients, both by name, or by id.
![rum search results part 1](https://i.imgur.com/P8Nt0WG.png) ![rum search results part 2](https://i.imgur.com/QLhxm7B.png)
* The random cocktail option gives users a way to randomize their searches.
* When searching drinks an image taken from the API is presented to the user, along with the drink's info. This is also toggleable in settings.
![image of a piña colada](https://i.imgur.com/3PtPbhH.png)
* The max image width setting allows the user to change the size of images to as small or as big as they could want.
![halloween punch image part 1](https://i.imgur.com/86jrqA7.png) ![halloween punch image part 2](https://i.imgur.com/XA2gfM4.png) ![halloween punch image part 3](https://i.imgur.com/0m6e3Na.png)
* The favorites feature allows users to save their favorite drinks for quick lookup later.
![favorite drink list](https://i.imgur.com/0xBwRx3.png)
* The view count leaderboard offers an easy way for the user to see which drinks have been viewed the most. Both storing view counts, and the amount of drinks shown on the leaderboard are toggleable in settings.
![view count leaderboard](https://i.imgur.com/WAz9AXN.png)
* The settings menu allows the user to change a variety of settings, or reset them to defaults with just a few clicks.
![settings menu](https://i.imgur.com/yWqycv2.png)
* Settings, favorites, and view counts are all stored locally accross app sessions. favorites and view counts are stored in an sqlite file using ef core, and settings are written to a .json file.

# Resources used
[.NET (10.0)](https://learn.microsoft.com/en-us/dotnet/)
[Spectre.Console (0.54.1-alpha.0.68)](https://spectreconsole.net/cli) - Ui
[Spectre.Console.ImageSharp (0.54.1-alpha.0.63)](https://spectreconsole.net/cli) - Image creation
[Microsoft.Extensions.Hosting (11.0.0-preview.1.26104.118)](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting?view=net-10.0-pp)
[Microsoft.Extensions.Http (11.0.0-preview.1.26104.118)](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.http?view=net-10.0-pp)
[Microsoft.EntityFrameworkCore.Sqlite (10.0.0)](https://learn.microsoft.com/en-us/ef/core/providers/sqlite/?tabs=dotnet-core-cli)
[Microsoft.EntityFrameworkCore.Design (10.0.0)](https://learn.microsoft.com/en-us/ef/core/cli/services)
[Microsoft.Extensions.Logging (11.0.0-preview.1.26104.118)](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/overview?tabs=command-line) - Logging
[Microsoft.Extensions.Logging.Abstractions (11.0.0-preview.1.26104.118)](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.abstractions?view=net-10.0-pp)
[Microsoft.Extensions.Logging.Console (11.0.0-preview.1.26104.118)](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.console?view=net-8.0-pp)

# Personal thoughts
This project took longer than I had hoped to finish, but I finally got everything up and working and I can finally submit it. Working with APIs for the first time was definitly
something I hadn't done before, it took a little bit of time to figure it out but afterwards I enjoyed working with it. Seeing my app actually get real world data from a real
website was really exciting, and it made me feel almost suprised as if I was getting closer to being able to make something real. I think the realization that I wasn't working
with test data, or small local data, but real data made me think that what I was making really was evolving and progressing as I move forward and learn more. I'm sure as I continue
to learn this project will start to feel small or of bad quality in comparison to what I'll learn later, but it's nice to see that the things you're making are becoming bigger,
more complex, and can actually show signs of progress, signs that even if you have to take small steps one day you will reach where you need to be. I also really enjoyed the fact
that I didn't have to actually write a thousand different descriptions for drinks. Being able to get the entire backstory of rum or being able to see all the drinks made with a
certain glass just with a single get request was very cool to experience. I also liked making the settings menu, which was one of the things that I dont think was a requirment,
but I just wanted to make it to try and improve my project. Learning how to save the user's settings to a local .json file was intresting too. I also used entity framework core
for handling an sqlite file of the favorites and view counts, as well as trying to improve the dependancy injection in my Program.cs, which were both really intresting things to
learn. It took me about a week and a half, maybe two weeks to finish this project, which wasn't exactly what I was hoping for, but I'm glad I got it done. I'm excitied to see
what the next project will be, and what I'll be able to learn from that project as well.
16 changes: 16 additions & 0 deletions drinksInfo.0lcm/drinksInfo.0lcm.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "drinksInfo.0lcm", "drinksInfo.0lcm\drinksInfo.0lcm.csproj", "{50D33F9E-68B9-459A-BC3E-1734271B1C53}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{50D33F9E-68B9-459A-BC3E-1734271B1C53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50D33F9E-68B9-459A-BC3E-1734271B1C53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50D33F9E-68B9-459A-BC3E-1734271B1C53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50D33F9E-68B9-459A-BC3E-1734271B1C53}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
27 changes: 27 additions & 0 deletions drinksInfo.0lcm/drinksInfo.0lcm/Configuration/AppDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using drinksInfo._0lcm.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

namespace drinksInfo._0lcm.Configuration;

public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
public DbSet<FavoritedDrink> FavoritedDrinks { get; set; }
public DbSet<ViewedDrink> ViewedDrinks { get; set; }
}

public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var dbPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"drinksInfo.0lcm",
"app.db");

var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
optionsBuilder.UseSqlite($"Data Source={dbPath}");

return new AppDbContext(optionsBuilder.Options);
}
}
13 changes: 13 additions & 0 deletions drinksInfo.0lcm/drinksInfo.0lcm/Configuration/AppSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace drinksInfo._0lcm.Configuration;

public class AppSettings
{
public bool ShowImages { get; set; } = true;
public bool ShowImagesDefault { get; } = true;
public int MaxImageWidth { get; set; } = 25;
public int MaxImageWidthDefault { get; } = 25;
public bool StoreDrinkCounts { get; set; } = true;
public bool StoreDrinkCountsDefault { get; } = true;
public int MaxLeaderboardSpots { get; set; } = 20;
public int MaxLeaderboardSpotsDefault { get; } = 20;
}
29 changes: 29 additions & 0 deletions drinksInfo.0lcm/drinksInfo.0lcm/Configuration/SettingsManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Text.Json;

namespace drinksInfo._0lcm.Configuration;

public class SettingsManager
{
private static readonly string SettingsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"drinksInfo.0lcm",
"settings.json");

public AppSettings Load()
{
if (!File.Exists(SettingsPath))
return new AppSettings();

var json = File.ReadAllText(SettingsPath);
return JsonSerializer.Deserialize<AppSettings>(json) ?? new AppSettings();
}

public void Save(AppSettings settings)
{
var directory = Path.GetDirectoryName(SettingsPath)!;
Directory.CreateDirectory(directory);

var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(SettingsPath, json);
}
}
15 changes: 15 additions & 0 deletions drinksInfo.0lcm/drinksInfo.0lcm/Enums/EnumExtender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.RegularExpressions;

namespace drinksInfo._0lcm.Enums;

internal static class EnumExtender
{
internal static string ToDisplayString(this Enum value)
{
return Regex.Replace(
value.ToString(),
"([a-z])([A-Z])",
"$1 $2"
);
}
}
59 changes: 59 additions & 0 deletions drinksInfo.0lcm/drinksInfo.0lcm/Enums/Enums.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
namespace drinksInfo._0lcm.Enums;

internal static class Enums
{
internal enum MainMenuOption
{
LookupBySpecifics,
FilterByCategories,
ReviewCategories,
SeeFavoritesAndViewCounts,
Settings,
Exit
}

internal enum ReviewCategoriesOption
{
SeeDrinks,
SeeGlasses,
SeeIngredients,
SeeAlcoholicTypes,
Back
}

internal enum FilterCategoriesOption
{
FilterByAlcoholContent,
FilterByDrinkCategory,
FilterByIngredient,
FilterByGlassType,
Back
}

internal enum LookupSpecificsOption
{
LookupCocktailByName,
LookupIngredientByName,
LookupCocktailById,
LookupIngredientById,
LookupRandomCocktail,
Back
}

internal enum SettingsMenuOption
{
ToggleShowImages,
ChangeImageMaxWidth,
ToggleStoreViewCounts,
ChangeMaxLeaderboardSpots,
ResetSettings,
Back
}

internal enum FavoritesAndLeaderboardMenuOption
{
ShowFavorites,
ShowViewLeaderboard,
Back
}
}
73 changes: 73 additions & 0 deletions drinksInfo.0lcm/drinksInfo.0lcm/Logging/Logging.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;

namespace drinksInfo._0lcm.Logging;

internal class CustomFormatter : ConsoleFormatter
{
public CustomFormatter() : base("customFormatter")
{
}

public override void Write<TState>(
in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter textWriter
)
{
var message = logEntry.Formatter(logEntry.State, logEntry.Exception);
if (string.IsNullOrEmpty(message)) return;

var originalColor = Console.ForegroundColor;
try
{
Console.ForegroundColor = GetLogLevelColor(logEntry.LogLevel);

textWriter.Write($"[{DateTimeOffset.Now:HH:mm:ss}]");

textWriter.Write($"[{logEntry.LogLevel,-12}]");

textWriter.Write($"[{logEntry.Category}]");

textWriter.Write(message);

if (logEntry.Exception != null) textWriter.Write(logEntry.Exception.ToString());
}
finally
{
Console.ForegroundColor = originalColor;
}
}

private static ConsoleColor GetLogLevelColor(LogLevel logLevel)
{
return logLevel switch
{
LogLevel.Trace => ConsoleColor.Gray,
LogLevel.Debug => ConsoleColor.Gray,
LogLevel.Information => ConsoleColor.Green,
LogLevel.Warning => ConsoleColor.Yellow,
LogLevel.Error => ConsoleColor.Red,
LogLevel.Critical => ConsoleColor.Magenta,
_ => ConsoleColor.White
};
}
}

internal class AppLogger
{
private static readonly ILoggerFactory AppLoggerFactory =
LoggerFactory.Create(builder =>
{
builder
.SetMinimumLevel(LogLevel.Debug)
.AddConsole(options => { options.FormatterName = "customFormatter"; })
.AddConsoleFormatter<CustomFormatter, ConsoleFormatterOptions>();
});

internal static ILogger CreateLogger<T>()
{
return AppLoggerFactory.CreateLogger<T>();
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading