feat: autocompletions
This commit is contained in:
171
CarManagerV3/Util/CarCompletions.cs
Normal file
171
CarManagerV3/Util/CarCompletions.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using CarManagerV3.Classes;
|
||||
|
||||
namespace CarManagerV3.Util
|
||||
{
|
||||
internal class CarCompletions
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of URLs that should be used to fetch the json file with autocompletion data.
|
||||
/// Multiple URLs are provided to ensure that if one source is unavailable, the others can be used as a fallback. The application should attempt to fetch the data from each URL in order until it successfully retrieves the data or exhausts all options.
|
||||
/// </summary>
|
||||
private static readonly string[] carDataFileUrls =
|
||||
{
|
||||
"https://static.clsw.app/carmgm/merged-cars.json"
|
||||
};
|
||||
|
||||
private static readonly string carCompletionDataFileName = "car_data.json";
|
||||
private static DateTime lastFetchAttempt = Properties.Settings.Default.LastFetchedAutoCompletions;
|
||||
private static readonly TimeSpan fetchRetryInterval = Properties.Settings.Default.FetchAutoCompletionsInterval; // Minimum interval between fetch attempts
|
||||
|
||||
private static string getCarCompletionDataFilePath()
|
||||
{
|
||||
var userDataDir = Properties.Settings.Default.DataLocation;
|
||||
SafeManager.EnsureDirectoryExists(userDataDir);
|
||||
return Path.Combine(userDataDir, carCompletionDataFileName);
|
||||
}
|
||||
|
||||
public static List<CarManufacturer> carBrands = new List<CarManufacturer>
|
||||
{
|
||||
new CarManufacturer("Toyota") { Models = new List<string> { "Camry", "Corolla", "RAV4" } },
|
||||
new CarManufacturer("Honda") { Models = new List<string> { "Civic", "Accord", "CR-V" } },
|
||||
new CarManufacturer("Ford") { Models = new List<string> { "F-150", "Mustang", "Escape" } },
|
||||
new CarManufacturer("Chevrolet") { Models = new List<string> { "Silverado", "Malibu", "Equinox" } },
|
||||
new CarManufacturer("BMW") { Models = new List<string> { "3 Series", "5 Series", "X5" } },
|
||||
new CarManufacturer("Mercedes-Benz") { Models = new List<string> { "C-Class", "E-Class", "GLC" } },
|
||||
new CarManufacturer("Audi") { Models = new List<string> { "A4", "A6", "Q5" } },
|
||||
new CarManufacturer("Volkswagen") { Models = new List<string> { "Golf", "Passat", "Tiguan" } },
|
||||
new CarManufacturer("Nissan") { Models = new List<string> { "Altima", "Sentra", "Rogue" } },
|
||||
new CarManufacturer("Hyundai") { Models = new List<string> { "Elantra", "Sonata", "Tucson" } },
|
||||
new CarManufacturer("SEAT") { Models = new List<string> { "Ibiza", "Leon", "Ateca", "Alhambra" } },
|
||||
new CarManufacturer("Skoda") { Models = new List<string> { "Octavia", "Fabia", "Kodiaq" } },
|
||||
};
|
||||
|
||||
public static List<string> GetCarBrands()
|
||||
{
|
||||
return carBrands.Select(c => c.Name).ToList();
|
||||
}
|
||||
|
||||
public static List<string> GetCarModels(string brand)
|
||||
{
|
||||
var manufacturer = carBrands.FirstOrDefault(c => c.Name.Equals(brand, StringComparison.OrdinalIgnoreCase));
|
||||
return manufacturer != null ? manufacturer.Models : new List<string>();
|
||||
}
|
||||
|
||||
public static List<string> CommonColors = new List<string>
|
||||
{
|
||||
"Black", "White", "Gray", "Silver", "Red", "Blue", "Green", "Yellow", "Brown", "Orange"
|
||||
};
|
||||
|
||||
public static void ReadFromResourceFile()
|
||||
{
|
||||
// Read the json file from the projects resources and populate the carBrands list
|
||||
// Format is a json object with the key being the car brand and the value being a list of models
|
||||
|
||||
// Get the json string from the resources, Resource name is CarCompleteData, it is a .json file
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the car data from urls asynchronous and saves it in the users data directory for offline use.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task FetchCarCompletionDataFromUrlsAsync()
|
||||
{
|
||||
if(DateTime.Now - lastFetchAttempt < fetchRetryInterval)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Fetch attempt skipped to avoid excessive retries. Last attempt was at " + lastFetchAttempt);
|
||||
Console.WriteLine("Fetch attempt skipped to avoid excessive retries. Last attempt was at " + lastFetchAttempt);
|
||||
return;
|
||||
}
|
||||
foreach (var url in carDataFileUrls)
|
||||
{
|
||||
try
|
||||
{
|
||||
lastFetchAttempt = DateTime.Now;
|
||||
Properties.Settings.Default.LastFetchedAutoCompletions = lastFetchAttempt;
|
||||
Properties.Settings.Default.Save();
|
||||
using var httpClient = new HttpClient();
|
||||
var response = await httpClient.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var jsonData = await response.Content.ReadAsStringAsync();
|
||||
// Save the json data to a file in the user's data directory for offline use
|
||||
var filePath = getCarCompletionDataFilePath();
|
||||
await File.WriteAllTextAsync(filePath, jsonData);
|
||||
System.Diagnostics.Debug.WriteLine($"Successfully fetched car data from {url} and saved to {filePath}");
|
||||
// Optionally, you can also parse the json data and populate the carBrands list here
|
||||
break; // Exit the loop if data is successfully fetched
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the error and try the next URL
|
||||
System.Diagnostics.Debug.WriteLine($"Failed to fetch car data from {url}: {ex.Message}");
|
||||
Console.WriteLine($"Failed to fetch car data from {url}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the car data from urls and saves it in the users data directory for offline use.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Task FetchCarCompletionDataFromUrls()
|
||||
{
|
||||
// Run synchronously
|
||||
return Task.Run(() => FetchCarCompletionDataFromUrlsAsync().Wait());
|
||||
}
|
||||
|
||||
public static List<CarManufacturer> GetFromFile(bool skipOnlineUpdate = false)
|
||||
{
|
||||
if (!skipOnlineUpdate) FetchCarCompletionDataFromUrls();
|
||||
var filePath = getCarCompletionDataFilePath();
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
var jsonData = File.ReadAllText(filePath);
|
||||
// Parse the json data and populate the carBrands list
|
||||
// Format is a json object with the key being the car brand and the value being a list of models
|
||||
var carData = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(jsonData);
|
||||
List<CarManufacturer> _carBrands = carData.Select(kvp => new CarManufacturer(kvp.Key) { Models = kvp.Value }).ToList();
|
||||
return _carBrands;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Silent error.
|
||||
Console.WriteLine($"Failed to read car data from file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine("Car data file not found. Please ensure that the data has been fetched successfully at least once.");
|
||||
}
|
||||
return new List<CarManufacturer>();
|
||||
}
|
||||
|
||||
public static void UpdateCarCompletionData(bool skipOnlineUpdate = false)
|
||||
{
|
||||
var updatedCarBrands = GetFromFile(skipOnlineUpdate);
|
||||
if (updatedCarBrands.Count > 0)
|
||||
{
|
||||
carBrands = updatedCarBrands;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task UpdateCarCompletionDataAsync()
|
||||
{
|
||||
await FetchCarCompletionDataFromUrlsAsync();
|
||||
System.Diagnostics.Debug.WriteLine("Car completion data fetch attempt completed. Now updating local data.");
|
||||
UpdateCarCompletionData(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user