Przypadki dwa…

July 13th, 2015

Nie mam czasu na pisanie, bo trzymam kredens, ale dziś się wkurwiłem i muszę wyrzucić z siebie kilka słów.

Dwa zdarzenia – dwie reakcje

Po pierwsze samobój 14-latka.
Po drugie chłopak z dziarą.

Po pierwszej akcji wszyscy (z dokładnością do kilku osób, które mają dość nieciekawe poglądy) na około pisali, że chłopak zaszczuty, że nie wolno mówić “pedał”, że on tylko rurki nosił. Prezydent Biedroń udzielał wywiadów jak to on chciał też strzelić samobója.

Po drugiej akcji internety toczą bekę z “idioty” (wyjątków nie znalazłem)… najbardziej toczą ją ludzie, którzy jeszcze niedawno tak płakali nad zaszczutym chłopakiem.

Ja nie ogarniam tej kuwety. Jeden nosił przyciasne spodnie i miał pecha, drugi chciał być jak swój idol i też miał pecha (do idola). Ocena społeczna finału obu spraw jest skrajnie różna. Jeden to “zaszczuty młody człowiek”, drugi to “idiota”, którego należy “wykastrować”.

Ktoś może mi to przetłumaczyć?

Robożółwie

June 29th, 2015

Obecnie mam jak w dowcipie
– panie taki zapierdol z tymi taczkami, że nie ma kiedy załadować…

Dlatego też mało piszę. Dziś jednak chciałem przybliżyć wam pewną grę. Robo Turtles, bo o niej mowa to gra to gra przeznaczona dla dzieciaków w wieku od 3 do 8 lat. Jeżeli tytuł kojarzy ci się z LOGOW to jesteś bardzo blisko.

Autor, Dan Shapiro, jest programistą i stworzył tą grę jako “coś nowego” dla swoich dzieci. Reguły są banalnie proste i Mati opanował większość z nich w przeciągu dwóch rozgrywek. Co prawda nie gramy jeszcze na 100% zasad, ale i tak mały ma dużo radości.

Grę polecił mi Paweł Szulc i ja też polecam ją każdemu kto chce zainteresować swoją hałastrę programowaniem.

Ku pamięci

May 25th, 2015
  • dołoży na każde dziecko 500zł
  • nie dopuści do zamknięcia ani jednej kopalni i nie zwolni żadnego górnika
  • reaktywacja Stoczni w Szczecinie
  • przywróci poprzednie widełki wieku emerytalnego zamiast 67/67
  • (ubranka dla dzieci chce 0 stawki VAT) czyli zlikwiduje VAT8% (przepisy unii ubranka muszą mieć nałożoną najniższą stopę VAT w danym kraju)
  • obniży VAT do 22%,
  • da prace młodym i mieszkania socjalne na start jeśli założy rodzinę
  • dźwignie pensje pielęgniarkom i w samej służbie zdrowia przynajmniej x2
  • przewalutuje frankowiczów,
  • zwiększy 3 krotnie kwotę wolna od podatku,
  • podniesie renty i zwiększy emerytury,
  • podniesie najniższą krajową do poziomu UE,
  • podwoi nakłady na polską naukę
  • repolonizacja banków
  • nowe opodatkowanie hipemarketów i banków
  • lekarz i dentysta w każdej szkole
  • darmowe przedszkola
  • obniżenie CIT z 19 do 15% dla najmniejszych przedsiębiorstw zatrudniających min 3 osoby
  • wycofanie się z pakietu klimatycznego
  • stworzenie systemu obrony terytorialnej kraju z organizacji paramilitarnych
  • likwidacja NFZ
  • konstytucyjna ochrona lasów państwowych

V4P na Confiturze 2015

May 19th, 2015

Wystartowało confiturowe v4p. Jest i moja prezentacja zatem zapraszam do oddania głosu na plus :)

Będzie o JSR-354, czyli java money. Będzie o typach w Javie i o tym jak to wszystko ogarnąć.

Dwa słowa o typach własnych

May 5th, 2015

Wiele osób nie lubi tworzyć własnych specyficznych dla domeny typów, które nie niosą ze sobą wartości biznesowej. Przykładowo po co tworzyć własny typ reprezentujący wiek skoro można oprzeć się o starego dobrego inta?

Z moich obserwacji wynika, że przyczyna tego zachowania leży w niechęci do pisania jakiegokolwiek dodatkowego kodu, czyli zazwyczaj testów. Zamiast tego dzielnie napieramy na dodatkowy kod przy okazji różnorodnych funkcjonalności biznesowych. Wiek to jest zresztą piękny przykład, bo za każdym razem nasze testy będą jakoś zahaczać o wartości ujemne, nulle itp. Czy nie lepiej jest napisać to raz, a porządnie?

Listing 1. Przykładowa klasa Age

class Age {

	@Min(0)
	@Max(Integer.MAX_VALUE)
	@NotNull
	private Integer value;

	public Age() {
	}

	public Age(Integer value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
// hash, equals, toString co tam trzeba
}

Do tego dopisac konwertery na potrzeby Hibernate-a i gotowe. czy to tak dużo roboty?

Dziwi mnie ta niechęć do wzorca Value Object i jednej z Ekstremalna obiektowość w praktyce – część 3 – Opakowuj wszystkie prymitywy i Stringi. Nie dużo z tym roboty, a życie łatwiejsze.

DevCrowd 2015 już za nami…

April 29th, 2015

Konferencja była już pewien czas temu. Dziś pojawiły się oficjalne wyniki glosowania na prezentacje. Chciałbym podziękować wszystkim, którzy docenili moje wygłupy. A tych, którzy nie byli zapraszam do glosowania na prezentację by można było spotkać się na Confiturze.
Trochę obok – dziś grała Legia z Pogonią, czyli dla mnie taki mały wyznacznik terminu konferencji. Po raz pierwszy od kilku lat nie wylegitymowali mnie na dworcu w Szczecinie na okazję przybycia kibiców Legii.

Teraz do rzeczy. O prezentacjach powiem tak. Z rzeczy naprawdę ciekawych to prezentacja Marcina Matuszaka o nieblokującym pisaniu z Javą 8 i popychacz do tego w wykonaniu Adama Biena. Cała reszta na której byłe to było mało technicznie, ale to pewien trend na naszych javowych konferencjach. Mi osobiście średnio to odpowiada, ale taki a nie inny wybór tematów nie jest przypadkowy.

Organizacja w tym roku zaliczyła jeden mały zonk z wodą dla prelegentów :( Poza tym super było. After był i co działo się na afterze niech zostanie na afterze 😉

Tyle na dziś. Pewno zauważyliście, że blog trochę umarł. Po prostu mało czasu, a mam kilka fajnych tematów. Obiecuję będzie lepiej.

Throw to taki inny return…

April 15th, 2015

Przynajmniej w Javie. Jak już mamy Javę 8 to możemy pokusić się o takie podejście pod pewnymi warunkami.

Przypadek

Klient ma Sonara i nie zawahał się go użyć. Rzecz w tym, że ocenie podlega tylko kod z zewnątrz organizacji, czyli nasz, a ten wewnętrzny może być chujowy. My przejęliśmy ten chujowy kod i teraz naprawiamy…
Jednym z elementów jest maszynka do zamiany jsonów na obiekty i na odwrót. W niej jest kawałek kodu do pracy z datami. Wygląda on mniej więcej tak

Listing 1. Konwerter dat

public class DateConverting {

	public static void main(String[] args) {
		String dateJson = "1"; // 1-1-1970 😀

		Date date = convertStringToDate(dateJson);
		System.out.println(date.toString());
	}

	private static Date convertStringToDate(String s) {
		if (s == null || s.trim().isEmpty()) return null;
		try {
			return new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS").parse(s);
		} catch (ParseException e) {
		}
		try {
			return new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss").parse(s);
		} catch (ParseException e) {
		}
		try {
			return new SimpleDateFormat("yyyy-MM-dd'T'hh:mm").parse(s);
		} catch (ParseException e) {
		}
		try {
			return new SimpleDateFormat("yyyy-MM-dd").parse(s);
		} catch (ParseException e) {
		}
		return new Date(Long.valueOf(s));
	}
}

Mamy cztery formaty daty i jeżeli String nie pasuje do żadnego z nich to traktujemy jak zwykły long. Jeżeli jest pusty albo jest nullem to zwracamy null. Straszna paskuda. Sonar też to stwierdził i strasznie czerwony raport. Klient kręci germańską główką i szwargocze… Jak to można naprawić.

Wersja prosta

Na razie rzecz prosta i bez udziwnień. Generalnie mamy sobie dopuszczone formaty, robimy z nich listę iterujemy i jak na listingu 2

Listing 2. Konwerter dat pierwsza poprawka

private static Date convertStringToDate(String s) {                           
	if (s == null || s.trim().isEmpty()) return null;                         
	ArrayList<String> patterns = Lists.newArrayList(YYYY_MM_DD_T_HH_MM_SS_SSS,
			YYYY_MM_DD_T_HH_MM_SS                                             
			, YYYY_MM_DD_T_HH_MM                                              
			, YYYY_MM_DD);                                                    
	for (String pattern : patterns) {                                         
		try {                                                                 
			return new SimpleDateFormat(pattern).parse(s);                    
		} catch (ParseException e) {                                          
		}                                                                     
	}                                                                         
	return new Date(Long.valueOf(s));                                         
}

Generalnie to rozwiązanie jest takie samo jak poprzednie. Różnica jest na poziomie “zamiast stu zmiennych zrób tablicę”.
My oczekujemy czegoś innego. Teraz będzie dużo kodu i magii javy 8 oraz trochę programowania prawie funkcyjnego 😉 Da się to rozwiązanie zaimplementować też z użyciem np. Guavy, ale to jak musicie mieć javę 7.

Użyjmy javy 8

Koncepcja tej refaktoryzacji opera się o stwierdzenie, że rzucenie wyjątku jest tak na prawdę rodzajem instrukcji powrotu. Nasza metoda ma w takim przypadku dwa możliwe typu na wyjściu. Jeden ten zadeklarowany i drugi dla wyjątku.

Interfejs opakowujący

Na początek zdefiniujmy sobie interfejs opakowujący ParseTask. Pozwoli on na ujednolicenie różnych mechanizmów parsowania.

Listing 3. Interfejs ParseTask

interface ParseTask<R>{
	R parse() throws ParseException;
}

Implementacja będzie realizować zadanie parsowania. Może zwrócić coś R albo wyrzucić wyjątek ParseException.

Rezultat parsowania

Potrzebujemy też klasę, która będzie reprezentować rezultat parsowania. Nie chcę używać Optional ponieważ jest zbyt ogólny, a jeżeli chcemy gdzieś zachować informacje o błędzie to musimy mieć klasę specjalizowaną.

Listing 4. Klasa ParseResult

abstract class ParseResult<R> {

	static class Valid<R> extends ParseResult<R>{
		final R r;

		Valid(R r) {
			this.r = r;
		}

		@Override
		public boolean isValid() {
			return true;
		}

		@Override
		public R result() {
			return r;
		}
	}

	static class Failed<R> extends ParseResult<R>{
		final ParseException e;

		Failed(ParseException e) {
			this.e = e;
		}

		@Override
		public boolean isValid() {
			return false;
		}

		@Override
		public R result() {
			throw new NoSuchElementException("No value present");
		}
	}

	public abstract boolean isValid();
	public abstract R result();
}
Maszynka do uruchamiania

Czyli klasa Parser, której zadaniem jest uruchamianie poszczególnych zadań parsowania i tworzenie odpowiednich wyników

Listing 5. Klasa Parsersamp>

class Parser<R> implements Function<ParseTask<R>, ParseResult<R>> {

	@Override
	public ParseResult<R> apply(ParseTask<R> parseTask) {
		try {
			return new ParseResult.Valid(parseTask.parse());
		} catch (ParseException e) {
			return new ParseResult.Failed(e);
		}
	}
}

Uruchamiamy

Finalnie nasza metoda konwertująca może wyglądać w następujący sposób:

Listing 5. Klasa Parser

private static Date convertStringToDate(String s) {
		if (s == null || s.trim().isEmpty()) return null;
		ArrayList<String> patterns = Lists.newArrayList(YYYY_MM_DD_T_HH_MM_SS_SSS,
				YYYY_MM_DD_T_HH_MM_SS
				, YYYY_MM_DD_T_HH_MM
				, YYYY_MM_DD);

		return patterns.stream()
				.map(p -> (ParseTask<Date>) () -> new SimpleDateFormat(p).parse(s))
				.map(new Parser<>())
				.filter(ParseResult::isValid)
				.map(ParseResult::result)
				.findFirst().orElseGet(() -> new Date(Long.valueOf(s)));

	}

Najpierw tworzymy sobie listę zdań parsowania. Następnie odpalamy parser (feralna nazwa), sprawdzamy czy wynik jest poprawny i pierwszy poprawny wynik zwracamy. Jeżeli nie będzie żadnego poprawnego wyniku to dokonujemy domyślnej konwersji.

Podsumowanie

Czy kod wynikowy jest lepszy? Jeżeli ta operacja konwersji i parsowania była by tylko w jednym miejscu w całym systemie to można takie rozwiązanie potraktować jako ciekawostkę. Jeżeli mamy jednak wiele różnych praserów i konwersji to można wprowadzić prostą klasę narzędziową, która ułatwi nam życie:

Listing 6. Klasa ParserMatcher

class ParserMatcher<R>{

	public R findParserAndParse(Collection<ParseTask<R>> pt, Supplier<R> defVal){
		return pt.stream()
				.map(new Parser<>())
				.filter(ParseResult::isValid)
				.map(ParseResult::result)
				.findFirst().orElseGet(defVal);
	}
}

i pozwoli zredukować kod do kilku prostych regułek.

Cały “myk” w tym rozwiązaniu opiera się na dwóch rzeczach. Pierwsza to redukcja efektu ubocznego jakim jest wyjątek do poziomu opcjonalnego wyjścia. Efekt nie opuszcza miejsca, w którym się pojawił. Zostaje opakowany i zwrócony jako rezultat. Druga to rozgraniczenie sposobu przetwarzania, czyli parsowania od danych, czyli w tym przypadku parserów. W pewnym sensie tworzymy tu bardzo prymitywną gramatykę i efektywnie język obróbki informacji.

Podatki kochane… kiedy ja z was zwrot dostanę…

April 12th, 2015

…no to nie ma kuja we wsi. Zgodnie z obietnicą wpis o podatkach i moich pomysłach na ten fragment Polski. W poprzednim poście z tej serii pisałem, że rzeczywista kwota wolna od podatku w Polsce wynosi około 9tyś PLN. W pierwszym wpisie pokusiłem się o wyliczenie ile płacę składek i podatków.

Dziś kilka słów o tym ile podatków płacę efektywnie za 2014 rok. Na początek ważna informacja dostałem zwrot. I to taki naprawdę solidny (powyżej średniej krajowej brutto). Rozliczam się sam (mamy rozdzielność majątkową) i jedyną ulga jaką mam to ulga na dziecko. W 2014 byłem zatrudniony przez 6 miesięcy na UoP więc efektywnie płaciłem dość “normalny” ZUS. Z DG płacę “duży” ZUS.

Normalny ZUS oznacza, że wysokość składki jest proporcjonalna do wynagrodzenia.
Duży ZUS z DG oznacza, że nie jestem już na okresie ulgowym.

I na koniec ostatnia stała z DG odprowadzam liniowy PIT 19%.

Kwot z oczywistych powodów kwoty dochodu nie mogę podać dlatego będziemy bawić się na procentach. Powiem tylko, że jest to znacząco powyżej 100tys rocznie.

Podatek obliczony

Czyli to co trafia do pozycji 115 w PIT37 i pozycji 39 w PIT 36L. W sumie (z obu pitów podzielone przez dochód z obu pitów) daje zawrotne 11% podatku jakie oddałem państwu. Dodajmy do tego jeszcze składki na ubezpieczenie społeczne (bo na zdrowotne odliczasz od podatku) gdyż zdaniem niektórych, słusznie zresztą, są to podatki (celowe, ale podatki). W takim wypadku podatek efektywnie wynosi… 19,5%. LOL… No normalnie zabójcza stawka.

Podatek zapłacony

Samo obliczenie podatku należnego to pikuś. Są koszty, są ulgi. Jeżeli weźmiemy pod uwagę te czynniki i od dochodu odejmiemy koszty, a następnie porównamy to z kwotami odprowadzonych zaliczek to otrzymamy… 13,3%… o kurwa… w tym przypadku nie musimy już robić zabaw z ZUSem, bo ta kwota pomniejsza dochód i tym samym podstawę opodatkowania. Dla ciekawskich jeżeli doliczymy zapłacone składki to otrzymamy 26% podatku.

Efektywna stopa opodatkowania

Czyli to ile w rzeczywistości zapłaciłem podatku. Kwota zaliczek pomniejszona o kwotę zwrotu w stosunku do podstawy opodatkowania… 7,1%, ze składkami ZUS to 19,8%. W stosunku do całości dochodu… 4,8%, a dodając składki ZUS to 12,8%. I niech mi tu kurwa ktoś powie, że w Polsce mamy wysokie podatki…

Jak naprawić ten system

System jest skonstruowany by dobrze robić osobom zamożnym. Osoba biedna nie wrzuci sobie w koszty księgowej, paliwa czy elektroniki. Nie wrzuci sobie w koszty rachunków za komórkę, prąd, internet czy części rachunków za mieszkanie. Tu nie ma rat leasingowych, kredytów obrotowych czy opłat bankowych.
Inaczej mówiąc sama zabawa z kwota wolną nic nie da. Co więcej osoby biedne nie zyskają zbyt dużo (kilkadziesiąt PLN miesięcznie), a ze względu na cięcia wydatków socjalnych z powodu spadku dochodów państwa stracą część z otrzymywanej pomocy. I będzie to strata znacznie większa niż to co oszczędzą na podatkach.

Skala podatkowa

Progresywna z wieloma progami. Kwota wolna bez większych zmian. Jej coroczna waloryzacja jest wystarczająca. Następnie progi podatkowe 18 (do około 60tys), 30 (100-120tys), 40(300-350tys), 55(500tys), 80(powyżej 1 mln). Ulgi i odliczenia ograniczone do minimum. Pamiętajcie, że skala progresywna oznacza, że dany podatek płacimy od zarobków powyżej danej kwoty.

Warunkowe opodatkowanie DG podatkiem liniowym

Tu będzie cały “myk”. Obecnie podatek liniowy jest opcją dla “przedsiębiorcy”. Jego próg opłacalności jest gdzieś w okolicach 96tys rocznego dochodu (z założeniem dużego ZUSu). Inaczej mówiąc jeżeli nie zarabiasz co najmniej 96 tys to nie opłaca się płacić liniowego.
Moim zdaniem prawo do płacenia liniowego powinni mieć tylko ci co zatrudniają na UoP, czyli prowadzą rzeczywiste przedsiębiorstwa. Istota indywidualnej DG polega na tym, że na rynku istnieje pewna grupa usług, których świadczenie jest mocno ograniczone czasowo w stosunku do innych podmiotów. Upraszczając są pewne zawody i prace gdzie zatrudnienie na stałe nie ma sensu. Przykładowo projektant wnętrz nie będzie pracować na stałe w firmie robiącej srajtaśmę. Ma do wykonania pewne zadanie. Może je rozliczyć na umowie o dzieło (umowa rezultatu) albo jako przedsiębiorca (umowa rezultatu z ograniczeniami czasowo przestrzennymi). Nie powinno się to różnic od normalnej pracy w zakresie podatków czy składek. Po prostu masz specyficzną robotę, ale nadal jest to normalna praca.

Zasada 20/80

20% mojego podatku zabiera budżet centralny. 80% samorząd. Do tego zmiana mechanizmu “janosikowego” tak by uwzględniał dochody z bieżącego okresu rozliczeniowego, a nie lat poprzednich. Pozwoli to uniknąć sytuacji gdzie kryzys wali dwa razy w silne ośrodki. Raz obniżając bieżące dochody i dwa ciągnąc kasę z poprzednich lat. Technicznie oznaczało by to, że beneficjenci tego systemu dostawali by pieniądze za kwiecień w maju. Nie tak jak obecnie kasę za 2013 w 2015.

Naliczać wszystko od tzw. “Supergross”

Obecnie w systemie funkcjonują pojęcia “część pracownika” i “część pracodawcy”. Przeniesienie tego drugiego elementu bezpośrednio do umów oraz (ustawowo) aneksowanie umów tak by kwota na umowie była kwotą wszystkich kosztów jakie ponosi pracodawca pozwoli na uproszenie systemu.

Zapraszam do dyskusji…

JSR-354 oraz Moneta na DevCrowd 2015

April 10th, 2015

Dawno nie pisałem. Będzie tekst o podatkach, bo dostałem PIT i to takiego jakiego podsumowanie podesłał mi Paweł Szulc

Generalnie #wódka #dziwki #lasery.

Ale nie o tym dziś, choć nadal o pieniądzach.

Już za tydzień będziecie mogli posłuchać dwóch moich prezentacji na DevCrowd w Szczecinie. Jedna będzie machana rękoma o architekturze i infrastrukturze (to ta nudniejsza i poobiednia). Druga będzie poświęcona temu jak w Javie bezpiecznie liczyć pieniądze. Wczoraj siadłem do składania slajdów do tej prezentacji i jak zajrzałem do skrzynki to zobaczyłem dwa fajne maile.

Wczoraj oficjalnie pojawiła się wersja 1.0 specyfikacji JSR-354, czyli Java Money. Można ją pobrać tutaj. Całość jest skompilowana pod Javę 8, ale można sobie zrobić forka i jak ktoś potrzebuje mieć to w javie 1.5… (nekrofil).
Wraz ze specyfikacją pojawiła się też implementacja referencyjna, czyli Moneta (do pobrania tutaj).

Zatem mogę już oficjalnie powiedzieć, że na DevCrowd będzie świeże mięcho 😀

Naprawiamy Kontomierz

March 16th, 2015

Lubię Kontomierz. Serio. Pozwala na ogarnięcie finansów i ma przypominajkę dzięki czemu nie trzeba koniecznie pamiętać o wszystkich wydatkach. Taki manager finansowy tylko trochę bardziej estetyczny. Ma jednak kilka wad związanych ze statykami. Na przykład nie potrafi odpowiedzieć na pytanie jakiej wielkości transakcje w danej kategorii stanowią większość. Niby duperela, ale istotna, bo pozwala na wyszukiwanie “wycieków” gotówki w ramach np. zakupów spożywczych.

Kontomierz pozwala na eksport danych do pliku csv. Kodowanego w CP1250 (żenua), ale to rozwiązujemy za pomocą:

Listing 1. Zmiana kodowania pliku

$ iconf -f cp1250 -t utf8 in.csv > out.csv

Mając już taki plik możemy go sobie obrobić w javie… jest jednak mały problem, bo maksymalna wielkość eksportu to 2000 transakcji (bardzo mało tak naprawdę). Zatem trzeba łączyć pliki z danego roku (co jest trochę upierdliwe). W nagrodę dostajemy jednak możliwość zrobienia dokładniejszych statystyk dla naszych finansów.

Poniżej przykładowy program liczący kilka statystyk dla zakupów z kategorii “spożywcze”.

Listing 2. Statystyka zakupów spożywczych

public class App {

	private static Set<Range> ranges = new HashSet<>();

	static {
		ranges.add(Range.closedOpen(0., 10.));
		ranges.add(Range.closedOpen(10., 20.));
		ranges.add(Range.closedOpen(20., 50.));
		ranges.add(Range.closedOpen(50., 100.));
		ranges.add(Range.closedOpen(100., 200.));
		ranges.add(Range.closedOpen(200., 500.));
		ranges.add(Range.closedOpen(500., 1000000.));
	}

	public static void main(String[] args) throws IOException, ParseException {
		CSVReader csvReader = new CSVReader();
		TransactionFile read = csvReader.read(new File("all_2014.csv"));

		Predicate<Record> ekonto = r -> r.accountName.equalsIgnoreCase("ekonto");
		Predicate<Record> portfel = r -> r.accountName.equalsIgnoreCase("portfel");
		read.transactions.stream()
				.filter(ekonto.or(portfel).and(r-> r.category.equalsIgnoreCase("spożywcze")))
				.collect(Collectors.groupingBy(r -> r.category))
				.entrySet()
				.forEach(e -> stat(e.getKey(), e.getValue()));
	}

	private static void stat(String key, List<Record> value) {
		System.out.println("Statystyka dla " + key);
		System.out.println("Liczba transakcji " + value.size());
		OptionalDouble average = value.stream()
				.mapToDouble(r -> Math.abs(r.amount.getNumber().doubleValueExact())).average();
		System.out.println("Średnia wartość transakcji " + average.orElse(0.));
		Map<Range, List<Record>> byRange = value.stream().collect(Collectors.groupingBy(r -> inRange(r)));
		ranges.stream()
				.sorted((l, r) -> l.lowerEndpoint().compareTo(r.lowerEndpoint()))
				.forEach(rng -> {
					Optional<List<Record>> records = Optional.ofNullable(byRange.get(rng));
					System.out.println("W zakresie " + rng + ": jest " + records.orElse(Collections.emptyList()).size() + " transakcji");
				});

	}

	private static Range inRange(Record r) {
		Optional<Range> range = ranges.stream()
				.filter(rng -> rng.contains(Math.abs(r.amount.getNumber().doubleValueExact()))).findFirst();
		return range.orElse(Range.closedOpen(-1., 0.));
	}
}

Nie jest to wzór piękna, ale napisanie tego zajęło mi wraz z napisaniem parsera do plików około godzinki.

Całość może pisać do pliku by można było to wrzucić do gnuplota aby mieć ładny rysunek.

Co jeszcze można np. odpowiednio dobierając filtry można sprawdzić czy wydajemy więcej na piwo czy żona na kosmetyki. Można też śledzić zmiany wydatków dla poszczególnych kategorii na przestrzeni np. lat czy miesięcy. Generalnie ogranicza nas wyobraźnia. Kod wrzucę na githuba jak go ogarnę tak by był bardziej przyjazny w użyciu.

Jest to przykład narzędzia, które robimy gdy chcemy szybko załatać braki w funkcjonalności jakiegoś innego narzędzia. Przy czym to inne narzędzie, tu Kontomierz, musi pozwalać na eksport danych w jakiś rozsądny sposób.


Translate »