Skip to content

Commit

Permalink
Merge pull request #55 from Thomas-Valkenburg/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Thomas-Valkenburg authored Jun 17, 2024
2 parents 308b143 + 7fe4724 commit 37bfc39
Show file tree
Hide file tree
Showing 26 changed files with 2,092 additions and 103 deletions.
110 changes: 52 additions & 58 deletions Talpa Api/Algorithms/SimilarityCheck.cs
Original file line number Diff line number Diff line change
@@ -1,74 +1,68 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Talpa_Api.Models;
using Talpa_Api.Contexts;
namespace Talpa_Api.Algorithms;

namespace Talpa_Api.Algorithms
public abstract class SimilarityCheck
{
public abstract class SimilarityCheck
{
public readonly struct ObjectWithSimilarity(int suggestionId, string title, double similarity)
{
public int Id { get; init; } = suggestionId;
public string Title { get; init; } = title;
public double Similarity { get; init; } = similarity;
}
public readonly struct ObjectWithSimilarity(int suggestionId, string title, double similarity)
{
public int Id { get; init; } = suggestionId;
public string Title { get; init; } = title;
public double Similarity { get; init; } = similarity;
}

private static double CalculateSimilarityPercentage(string string1, string string2)
{
var pairs1 = WordLetterPairs(string1.ToUpper());
var pairs2 = WordLetterPairs(string2.ToUpper());
private static double CalculateSimilarityPercentage(string string1, string string2)
{
var pairs1 = WordLetterPairs(string1.ToUpper());
var pairs2 = WordLetterPairs(string2.ToUpper());

var intersection = 0;
var union = pairs1.Count + pairs2.Count;
var intersection = 0;
var union = pairs1.Count + pairs2.Count;

foreach (var pair1 in pairs1)
{
for (var number = 0; number < pairs2.Count; number++)
{
if (pair1 != pairs2[number]) continue;
foreach (var pair1 in pairs1)
{
for (var number = 0; number < pairs2.Count; number++)
{
if (pair1 != pairs2[number]) continue;

intersection++;
pairs2.RemoveAt(number);
break;
}
}
intersection++;
pairs2.RemoveAt(number);
break;
}
}

// return the percentage of similarity
return 2.0 * intersection * 100 / union;
}
// return the percentage of similarity
return 2.0 * intersection * 100 / union;
}

private static List<string> WordLetterPairs(string str)
{
var allPairs = new List<string>();
var words = str.Split(' ');
private static List<string> WordLetterPairs(string str)
{
var allPairs = new List<string>();
var words = str.Split(' ');

foreach (var word in words)
{
for (var number = 0; number < word.Length - 1; number++)
{
allPairs.Add(word.Substring(number, 2));
}
}
foreach (var word in words)
{
for (var number = 0; number < word.Length - 1; number++)
{
allPairs.Add(word.Substring(number, 2));
}
}

return allPairs;
}
return allPairs;
}

public static (List<ObjectWithSimilarity> objects, double max) GetObjectWithSimilarity(string title, dynamic dbSet)
{
var objectWithSimilarity = new List<ObjectWithSimilarity>();
var maxSimilarity = 0.0;
public static (List<ObjectWithSimilarity> objects, double max) GetObjectWithSimilarity(string title, dynamic dbSet)
{
var objectWithSimilarity = new List<ObjectWithSimilarity>();
var maxSimilarity = 0.0;

foreach (var obj in dbSet)
{
var sim = CalculateSimilarityPercentage(obj.Title, title);
foreach (var obj in dbSet)
{
var sim = CalculateSimilarityPercentage(obj.Title, title);

if (sim > maxSimilarity) maxSimilarity = sim;
if (sim > maxSimilarity) maxSimilarity = sim;

if (sim > 50) objectWithSimilarity.Add(new ObjectWithSimilarity(obj.Id, obj.Title, sim));
}
if (sim > 50) objectWithSimilarity.Add(new ObjectWithSimilarity(obj.Id, obj.Title, sim));
}

return (objectWithSimilarity, maxSimilarity);
}
}
return (objectWithSimilarity, maxSimilarity);
}
}
10 changes: 10 additions & 0 deletions Talpa Api/Contexts/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class Context(DbContextOptions<Context> options) : DbContext(options)

public DbSet<Vote> Votes { get; init; }

public DbSet<Customization> Customization { get; init; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Suggestion>()
Expand All @@ -34,5 +36,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.WithOne(p => p.Team)
.HasForeignKey<Poll>()
.OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<Poll>()
.HasMany(p => p.Dates)
.WithOne(d => d.Poll);

modelBuilder.Entity<Vote>()
.HasMany(v => v.Dates)
.WithMany(d => d.Votes);
}
}
99 changes: 99 additions & 0 deletions Talpa Api/Controllers/Api/CustomizationController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using Microsoft.AspNetCore.Mvc;
using Talpa_Api.Contexts;
using Talpa_Api.Models;

namespace Talpa_Api.Controllers.Api;

[Route("api/[controller]")]
[ApiController]
public class CustomizationController(Context context) : ControllerBase
{
private readonly List<string> AllowedExtensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"];

[HttpGet]
public IActionResult GetCustomization()
{
return Ok(context.Customization.FirstOrDefault());
}

[HttpPost]
public IActionResult UpdateCustomization(string? name, bool? gradient, string? color1, string? color2, string? color3,
IFormFile? image)
{
var currentCustomization = context.Customization.FirstOrDefault();

if (image is not null)
{
if (!AllowedExtensions.Contains(Path.GetExtension(image.FileName).ToLowerInvariant()))
{
return BadRequest();
}

var path = Path.Combine("wwwroot", "logo", image.FileName);

try
{
if (!Directory.Exists(Path.Combine("wwwroot", "logo"))) Directory.CreateDirectory(Path.Combine("wwwroot", "logo"));

var stream = new FileStream(path, FileMode.Create);

image.CopyTo(stream);
}
catch (Exception ex)
{
return StatusCode(503, "Image write unavailable: " + ex);
}
}

if (currentCustomization is null)
{
if (!gradient.HasValue || color1 is null || image is null ||
(gradient is true && (color2 is null || color3 is null)))
{
return BadRequest();
}

var customization = new Customization(name ?? "Talpa", gradient!.Value, color1, color2, color3, Path.Combine(HttpContext.Request.Host.Value, "logo", image.FileName));

context.Customization.Add(customization);
context.SaveChanges();

return Created();
}

if (name is not null)
{
currentCustomization.Name = name;
}

if (gradient is not null)
{
currentCustomization.Gradient = gradient.Value;
}

if (color1 is not null)
{
currentCustomization.Color1 = color1;
}

if (color2 is not null)
{
currentCustomization.Color2 = color2;
}

if (color3 is not null)
{
currentCustomization.Color3 = color3;
}

if (image is not null)
{
currentCustomization.LogoPath = Path.Combine(HttpContext.Request.Host.Value, "logo", image.FileName);
}

context.Customization.Update(currentCustomization);
context.SaveChanges();

return NoContent();
}
}
86 changes: 68 additions & 18 deletions Talpa Api/Controllers/Api/SuggestionsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,99 @@ namespace Talpa_Api.Controllers.Api;
[ApiController]
public class SuggestionsController(Context context, IStringLocalizer<LocalizationStrings> localizer) : ControllerBase
{
public class SuggestionData(Suggestion suggestion)
{
public int Id { get; } = suggestion.Id;

public string Title { get; } = suggestion.Title;

public string Description { get; } = suggestion.Description;

public string ImagePath { get; } = suggestion.ImagePath;

public SuggestionCreatorData? Creator { get; } = suggestion.Creator is null ? null : new SuggestionCreatorData(suggestion.Creator);

public List<SuggestionTagData> Tags { get; } = suggestion.Tags.Select(tag => new SuggestionTagData(tag)).ToList();

public class SuggestionCreatorData(User user)
{
public string Id { get; } = user.Id;

public int Points { get; } = user.Points;
}

public class SuggestionTagData(Tag tag)
{
public int Id { get; } = tag.Id;

public string Title { get; } = tag.Title;

public bool Restrictive { get; } = tag.Restrictive;
}
}

public class SuggestionFormData
{
public List<int>? Tags { get; set; }
public IFormFile? Image { get; set; }
}

private static readonly string[] AllowedExtensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"];

[HttpGet]
public async Task<ActionResult<List<Suggestion>>> GetSuggestions()
public async Task<ActionResult<List<SuggestionData>>> GetSuggestions()
{
return await context.Suggestions
.Include(suggestion => suggestion.Creator)
.Include(suggestion => suggestion.Tags)
.ToListAsync();
var suggestions = await context.Suggestions
.Include(suggestion => suggestion.Creator)
.Include(suggestion => suggestion.Tags)
.ToListAsync();

return Ok(suggestions.Select(suggestion => new SuggestionData(suggestion)));
}

[HttpPost]
public async Task<ActionResult<List<SimilarityCheck.ObjectWithSimilarity>>> CreateSuggestion(string title, string description, string creatorId, IFormFile? image, bool overrideSimilarity = false)
public async Task<ActionResult<List<SimilarityCheck.ObjectWithSimilarity>>> CreateSuggestion(string title, string description, string creatorId, [FromForm] SuggestionFormData formData, bool overrideSimilarity = false)
{
var user = await context.Users.FindAsync(creatorId);
if (user is null) return NotFound(localizer["UserNotFound"].Value);

var imagePath = "images/default.png";

if (image != null)
if (formData.Image != null)
{
if (image.Length > 3 * 1000000) return StatusCode(413, localizer["ImageTooLarge"].Value); // Request Entity Too Large / Payload Too Large (image too big).
if (formData.Image.Length > 3 * 1000000) return StatusCode(413, localizer["ImageTooLarge"].Value); // Request Entity Too Large / Payload Too Large (image too big).

imagePath = await SaveImage(image);
imagePath = await SaveImage(formData.Image);

if (string.IsNullOrEmpty(imagePath))
return BadRequest(localizer["ImageInvalid"].Value);
}
var similarity = SimilarityCheck.GetObjectWithSimilarity(title, context.Suggestions);

var (objects, max) = SimilarityCheck.GetObjectWithSimilarity(title, context.Suggestions);

if (similarity.max >= 90)
if (max >= 90)
return Conflict(localizer["SuggestionTooSimilar"].Value);

if (!overrideSimilarity && similarity.objects.Count > 0 && similarity.max > 70)
return Accepted(similarity.objects.OrderByDescending(x => x.Similarity).ToList());
if (!overrideSimilarity && objects.Count > 0 && max > 70)
return Accepted(objects.OrderByDescending(x => x.Similarity).ToList());

List<Tag> tags = [];

if (formData.Tags is not null)
{
if (formData.Tags.Any(tag => context.Tags.Find(tag) is null))
return NotFound(localizer["TagNotFound"].Value);

tags = await context.Tags.Where(tag => formData.Tags.Contains(tag.Id)).ToListAsync();
}

context.Suggestions.Add(new Suggestion
{
Title = title,
Description = description,
ImagePath = imagePath,
Creator = user
Title = title,
Description = description,
ImagePath = imagePath,
Creator = user,
Tags = tags
});

await context.SaveChangesAsync();
Expand Down
Loading

0 comments on commit 37bfc39

Please sign in to comment.