[HomeWallet] Logika

Czas podsumować tydzień prac nad Home Wallet. Zapraszam do czytania.

 


Statystyki produktu

 

Zgodnie z przewidywaniami w tym tygodniu zajmowałem się statystykami konkretnego produktu.

Samo wymyślenie ich było wyzwaniem, ostatecznie wpadłem na takie:

  • kwota wydana łącznie na dany produkt
  • ile razy kupiliśmy produkt (na ilu paragonach wystąpił)
  • ile sztuk produktu kupiliśmy
  • średnia ilość sztuk kupowanych za razem
  • średni koszt jednej sztuki
  • ostatnia data zakupu
  • ile sztuk najwięcej kupiliśmy na raz
  • ile sztuk produktu kupiliśmy w danym sklepie
  • średnia cena w danym sklepie
  • ile procentowo przeznaczyliśmy na dany produkt wobec założonych planów

 

Wszystkie te statystyki można ustalić dla konkretnego okresu. W planach jest zrobienie trzech przycisków/kart: ostatni rok, ostatni miesiąc, dowolne.

W tym tygodniu udało mi się napisać pełną logikę do wszystkich statystyk. Uwzględnia ona już także przedziały czasowe, które nie są jedynie zaimplementowane od strony klienta.

Cała logika została zawarta w jednej statycznej klasie, aby uniknąć bardzo przeciążonych kontrolerów. Powoli zaczynam bardzo doceniać ten styl pisania, gdyż przy tej ilości kodu ma to zdecydowanie same zalety.

Każda z metod tej klasy przyjmuje praktycznie takie same parametry: id produktu, context oraz datę początkową i końcową.

 

Przykładowy kod:

 

public static double GetTotalSpent(int id, ApplicationDbContext context, DateTime startDate, DateTime endDate)
{
       var receipts = context.ReceiptProducts.Where(
                              rp => rp.ProductID == id 
                              && rp.Receipt.PurchaseDate >= startDate 
                              && rp.Receipt.PurchaseDate <= endDate);
       double result = 0;
       foreach (var receipt in receipts)
       {
           result += receipt.Amount * receipt.Price;
       }
       return Math.Round(result,1);
}

 

Wykorzystuję w tych metodach bardzo dużo LINQ i również zaczynam je naprawdę doceniać. Jest to fantastyczne narzędzie które zdecydowanie ułatwia pracę dla programisty. Przykładowo aby wyznaczyć ile sztuk produktu kupiliśmy najwięcej na raz nie musiałem używać niczego innego poza LINQ. Normalnie byłoby tam prawdopodobnie kilka pętli.

 

public static double GetMostBoughtAtOnce(int id, ApplicationDbContext context, DateTime startDate, DateTime endDate)
{
       var receipts = context.ReceiptProducts.Where(
             rp => rp.ProductID == id
                    && rp.Receipt.PurchaseDate >= startDate
                    && rp.Receipt.PurchaseDate <= endDate)
             .Distinct()
             .OrderByDescending(p=>p.Amount)
             .Select(p=>p.Amount)
             .Take(1)
             .ToList();
       var result = receipts.ElementAt(0);
       return Math.Round(result,1);
}

 

Wszystkie te dane na razie przedstawiam w takim samym stylu jak statystyki planu oraz konkretnego dnia. Coraz mniej mi się to podoba, jednak jak na razie nie mam innego pomysłu, więc zostaje jak jest.

 

 

To, co należy dopracować, to sensowne nazwy angielskie oraz oczywiście dobre opisanie wykresów, które aktualnie są wzięte prosto z przykładowego kodu. Dodatkowo muszę znaleźć ładny sposób na przedstawienie daty.

 


Podsumowanie

 

Udało się zrealizować wszystkie statystyki dla produktów. W następnym tygodniu planuję to samo przenieść na sklepy oraz kategorie. Będzie o tyle łatwiej, że wiele kodu będzie się powtarzać.

 

Dziękuję za odwiedziny.

Do zobaczenia, cześć!

1 komentarz

  1. Dużo lepszym pomysłem wydaje się być stworzenie jakiegoś dao albo repozytorium skoro są to dane wyciągane z bazy. Wtedy w konstruktorze klasy przekazywać ApplicationDbContext z którego będzie mogła korzystać każda metoda w niej zaimplementowana. Oprócz tego w metodzie GetTotalSpent dane z bazy wyciągasz dopiero podczas przechodzenia pętli foreach. Jeśli zastosowany jest LazyLoading to najprawdopodobniej każdy element wyciągany jest oddzielnie, a co za tym idzie zachodzie problem N+1. Nie mówiąc już o tym, że wyciągany jest cały obiekt ReceiptProducts, a można by w zapytaniu od razu wyciągać receipt.Amount * receipt.Price albo i nawet całkowitą sumę używając select-a. Zaś w drugim zapytaniu można by się ograniczyć do samego take który zwróci nam jeden element, bez toList, a nawet „wyciągnięcia” elementu przez FirstOrDefault.

Dodaj komentarz