| using ContactManagementAPI.Models; |
| using ContactManagementAPI.Data; |
| using Microsoft.EntityFrameworkCore; |
|
|
| namespace ContactManagementAPI.Services |
| { |
| public class ContactStatisticsService |
| { |
| private readonly ApplicationDbContext _context; |
|
|
| public ContactStatisticsService(ApplicationDbContext context) |
| { |
| _context = context; |
| } |
|
|
| public async Task<ContactStatistics> GetStatisticsAsync() |
| { |
| var totalContacts = await _context.Contacts.CountAsync(); |
| var contactsWithEmail = await _context.Contacts.Where(c => !string.IsNullOrEmpty(c.Email)).CountAsync(); |
| var contactsWithPhone = await _context.Contacts.Where(c => |
| !string.IsNullOrEmpty(c.Mobile1) || !string.IsNullOrEmpty(c.Mobile2) || !string.IsNullOrEmpty(c.Mobile3)).CountAsync(); |
| |
| var topCities = await _context.Contacts |
| .Where(c => !string.IsNullOrEmpty(c.City)) |
| .GroupBy(c => c.City) |
| .Select(g => new CityStatistic { City = g.Key, Count = g.Count() }) |
| .OrderByDescending(x => x.Count) |
| .Take(5) |
| .ToListAsync(); |
|
|
| var contactsByGroup = await _context.Contacts |
| .Include(c => c.Group) |
| .GroupBy(c => c.Group == null ? "Unassigned" : c.Group.Name) |
| .Select(g => new GroupStatistic { GroupName = g.Key, Count = g.Count() }) |
| .OrderByDescending(x => x.Count) |
| .ToListAsync(); |
|
|
| return new ContactStatistics |
| { |
| TotalContacts = totalContacts, |
| ContactsWithEmail = contactsWithEmail, |
| ContactsWithPhone = contactsWithPhone, |
| TopCities = topCities, |
| ContactsByGroup = contactsByGroup, |
| LastUpdated = DateTime.Now |
| }; |
| } |
|
|
| |
| public async Task<List<ContactDuplicate>> FindDuplicatesAsync() |
| { |
| var duplicates = new List<ContactDuplicate>(); |
| var contacts = await _context.Contacts.ToListAsync(); |
|
|
| for (int i = 0; i < contacts.Count; i++) |
| { |
| for (int j = i + 1; j < contacts.Count; j++) |
| { |
| var similarity = CalculateSimilarity(contacts[i], contacts[j]); |
| if (similarity >= 0.7) |
| { |
| duplicates.Add(new ContactDuplicate |
| { |
| Contact1Id = contacts[i].Id, |
| Contact1Name = $"{contacts[i].FirstName} {contacts[i].LastName}", |
| Contact2Id = contacts[j].Id, |
| Contact2Name = $"{contacts[j].FirstName} {contacts[j].LastName}", |
| SimilarityScore = similarity |
| }); |
| } |
| } |
| } |
|
|
| return duplicates.OrderByDescending(x => x.SimilarityScore).ToList(); |
| } |
|
|
| private double CalculateSimilarity(Contact c1, Contact c2) |
| { |
| double score = 0; |
| int factors = 0; |
|
|
| |
| if (!string.IsNullOrEmpty(c1.FirstName) && !string.IsNullOrEmpty(c2.FirstName)) |
| { |
| if (c1.FirstName.Equals(c2.FirstName, StringComparison.OrdinalIgnoreCase)) |
| score += 1; |
| else if (LevenshteinDistance(c1.FirstName, c2.FirstName) <= 2) |
| score += 0.8; |
| factors++; |
| } |
|
|
| if (!string.IsNullOrEmpty(c1.LastName) && !string.IsNullOrEmpty(c2.LastName)) |
| { |
| if (c1.LastName.Equals(c2.LastName, StringComparison.OrdinalIgnoreCase)) |
| score += 1; |
| else if (LevenshteinDistance(c1.LastName, c2.LastName) <= 2) |
| score += 0.8; |
| factors++; |
| } |
|
|
| |
| if (!string.IsNullOrEmpty(c1.Email) && !string.IsNullOrEmpty(c2.Email)) |
| { |
| if (c1.Email.Equals(c2.Email, StringComparison.OrdinalIgnoreCase)) |
| score += 2; |
| factors++; |
| } |
|
|
| |
| if (!string.IsNullOrEmpty(c1.Mobile1) && !string.IsNullOrEmpty(c2.Mobile1)) |
| { |
| if (c1.Mobile1.Equals(c2.Mobile1)) |
| score += 2; |
| factors++; |
| } |
|
|
| return factors > 0 ? score / (factors * 1.5) : 0; |
| } |
|
|
| private int LevenshteinDistance(string s1, string s2) |
| { |
| var length1 = s1.Length; |
| var length2 = s2.Length; |
| var d = new int[length1 + 1, length2 + 1]; |
|
|
| for (int i = 0; i <= length1; i++) |
| d[i, 0] = i; |
|
|
| for (int j = 0; j <= length2; j++) |
| d[0, j] = j; |
|
|
| for (int i = 1; i <= length1; i++) |
| for (int j = 1; j <= length2; j++) |
| { |
| int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; |
| d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost); |
| } |
|
|
| return d[length1, length2]; |
| } |
| } |
|
|
| public class ContactStatistics |
| { |
| public int TotalContacts { get; set; } |
| public int ContactsWithEmail { get; set; } |
| public int ContactsWithPhone { get; set; } |
| public List<CityStatistic> TopCities { get; set; } = new(); |
| public List<GroupStatistic> ContactsByGroup { get; set; } = new(); |
| public DateTime LastUpdated { get; set; } |
| } |
|
|
| public class CityStatistic |
| { |
| public string City { get; set; } |
| public int Count { get; set; } |
| } |
|
|
| public class GroupStatistic |
| { |
| public string GroupName { get; set; } |
| public int Count { get; set; } |
| } |
|
|
| public class ContactDuplicate |
| { |
| public int Contact1Id { get; set; } |
| public string Contact1Name { get; set; } |
| public int Contact2Id { get; set; } |
| public string Contact2Name { get; set; } |
| public double SimilarityScore { get; set; } |
| } |
| } |
|
|