Dátumok csoportosítása

dátumintervallumok logó

dátumintervallumok logóEbben a Java projektben dátumok csoportosítását oldjuk meg, többféleképpen is. Mikor van erre szükség? Jelentés, kimutatás, riport, lista készítése során.

Példaként tekintsünk egy blogot. A blogban rendszeresen jelenik meg új tartalom (bejegyzés, poszt). Azért, hogy a blog hosszabb távon, sok bejegyzéssel is könnyen kereshető, átlátható, böngészhető legyen/maradjon a felhasználók, látogatók számára, célszerű:

  • taxonómia kialakítása. Ez kategóriákat és címkéket jelent. Ebből címkefelhő vagy szófelhő is készíthető, ahogyan erről blogoltunk már: Címkefelhő generálása.
  • marketing analitika használata. Ezek általában toplisták valamilyen könnyen hozzáférhető adat alapján. Például: látogatottság, népszerűség, eltöltött idő, hozzászólások száma, megosztások száma, egérmutató mozgása alapján hőtérkép. Ezek általában toplisták, amelyek eleje listázódik csökkenő sorrendben.
  • dátum szerint is csoportosítani a blog bejegyzéseit. Érdemes megjeleníteni a legújabbtól a régebbi felé haladó (retrospektív) listát, hierarchikus fa struktúrát, lenyíló panelt. Mindez kombinálható toplistával. A csoportosítás elvégezhető igény szerint tetszőlegesen, például évente, negyedévente, havonta.

Lássuk, hogyan lehet megvalósítani a dátumok csoportosítását Java programozás nyelven!

Milyen adatokra van szükség?

Egy megadott zárt dátumtartományban véletlenszerűen előállítunk néhány dátumot. Nem számít, hogy különböznek-e. A dátumokat tároló listát érdemes csökkenő sorrendben tárolni. Minden dátum múltbeli, így ez a sorrend a jelenhez legközelebbitől halad a legtávolabbi felé. Például a Java program ezekkel a dátumokkal dolgozik (lapozható):

Milyen eredményeket kaphatunk?

Az évenkénti csoportosítás így jelenik meg:

A havonkénti csoportosítás így jelenik meg (lapozható):

Természetesen blog esetén gyűjtőoldalra mutató hivatkozást kell tenni a megjelenő elemekre. Azok az évek és hónapok nem jelennek meg, ahol nincs dátum (blog bejegyzés).

Hogyan kapjuk meg az eredményeket?

Természetesen Java nyelven programozva készítünk megoldást, sőt többféle megoldást. Ezek szépen összevethetők és mindenki kiválaszthatja azt, amit szívesen használna. A dátumobjektumok tárolása generikus listában történik, aminek típusa LocalDate. A dátumok formátuma: DateTimeFormatter.ofPattern("yyyy.MM.dd.").

1. megoldás

Ez a hagyományosnak tekinthető megoldás. Végigjárja a dátumobjektumokat tartalmazó dateList dátumlista adatszerkezetet. Két egymásba ágyazott ciklussal csoportváltást valósít meg. Feltételezi – nem ellenőrzi -, hogy az adatok sorrendje megegyezik az eredmény kiírásának megfelelő sorrenddel. Amíg két egymást követő dátum GROUP_BY_FORMAT formátuma azonos, addig ugyanabba a csoportba tartoznak. A csoportváltáskor az eredmény TYPE_FORMAT formátumú. Közben a beépített megszámolás programozási tétel is működik.

A groupByDate1() függvény képes az évente és havonta csoportosítás megvalósítására. Mindez a paraméterezésén múlik. Évente csoportosít, ha így hívja meg a vezérlés:

Évenkénti csoportosítás során például a 2024.02.26. és a 2024.01.30. (dátumként, nem szövegként értelmezve) azért tartozik egy csoportba, mert a dátumobjektumoktól elkért év "2024" szövegként mindkettő esetében megegyezik.

A groupByDate1() függvény havonta csoportosít, ha így hívja meg a vezérlés:

Havonkénti csoportosítás során például a  2023.06.14. és a 2023.06.08. (szintén dátumként értelmezve) azért tartozik egy csoportba, mert mindkettő illeszkedik a "202306" szövegmintára.

2. megoldás

Ez a Stream API-t és funkcionális programozást használó, újabb megoldás. Ciklus helyett beépített műveletek vannak. A groupByDate2() függvény a dátumok évenkénti csoportosítását képes megoldani:

A groupByDate3() függvény a dátumok havonkénti csoportosítására készült. A YearMonth osztály beépített ( java.time csomag). A DateCount saját POJO. Konstruktora 4 paramétert kap: YearMonth key, Long value, DateTimeFormatter format és String groupText, valamint van két hasznos metódusa. Az egyik az örökölt és felüldefiniált toString() a formázott kiíráshoz, a másik pedig a Comparable interfésztől implementált compareTo() a sorrend kialakításához szükséges összehasonlításhoz.

A funkcionális programozáshoz kötődő lambda műveletekről többször is blogoltunk már, így azokat most nem részletezem. Helyette ajánlom a szakmai blog lambda kifejezés címkéjét.

Továbbfejlesztés

Érdemes átgondolni az 1. és 2. megoldás markáns különbözőségeit, illetve egymást kiegészítő gondolatmenetét. Zárjuk két ötlettel a továbbfejlesztésre vonatkozóan:

  • A 2. megoldás két függvénye megoldható egyetlen függvénnyel, amely hasonlóan paraméterezhető, mint az 1. megoldás függvénye. Ezáltal univerzális(abb)nak tekinthető megoldás is készülhetne. Aki kellően motivált és végiggyakorolja a fentieket, biztosan meg tudja oldani. Várjuk hozzászólásban, vagy az ILIAS-ban a megoldást!
  • A csoportosítás egyben hierarchiát jelent, amiből fa szerkezet építhető. A fa vizuális komponensen is megjeleníthető, ahogyan blogoltunk is róla: Fát építünk.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam, a Java EE szoftverfejlesztő tanfolyam és a Java adatbázis-kezelő tanfolyam szakmai moduljának több alkalmához és az orientáló moduljának 1-4. óra: Programozási tételek alkalmához is kötődik. A Stream API-val és a lambda kifejezésekkel sokszor foglalkozunk.

Szívgörbe ábrázolása

Szívgörbét ábrázolunk Java programmal. A Valentin-nap inspirálta ezt a feladatot. Számos matematikai görbe ismert, amelyek szívformához (kardioid) hasonlítanak. Szükséges egy megfelelő paraméteres görbe. A függvény szív formájú ábrája/grafikonja és egyenletrendszere alapján is nagy a választék.

Ábrázoljuk ezt a paraméteres szívgörbét Java swing GUI felületen!

A szívgörbe ábrázolásához felhasználom az StdDraw osztályt, amely ennek a tankönyvnek a példatárából származik: Robert Sedgewick, Kevin Wayne: Computer Science: An Interdisciplinary Approach, 1st edition, Princeton University, Addison-Wesley Professional, 2016, ISBN 978-0134076423. Az osztály metódusaival könnyen beállítható a nézőpont, a vízszintes/függőleges skála, a rajzoláshoz használt toll mérete/színe és a grafikai primitívek közül csak a pont ábrázolása szükséges.

Négy megoldást mutatok. Mindegyik azonos szívgörbét rajzol a fenti egyenletrendszer alapján. Mindegyik metódus átveszi az N paramétert, amely az összetartozó x és y koordinátapárok számát jelenti. Az N db pont meghatározása/kiszámolása szükséges a szívgörbe ábrázolásához. A szívgörbe ábrázolása önálló ablakban – grafikus felhasználói felületen – jelenik meg. A feladat matematikai jellegéből adódik, hogy tipikus a t nevű ciklusváltozó használata. A metódusokat a vezérlés az 512 paraméterrel hívja meg.

1. megoldás

A heartCurveDraw1() metódus a kiszámolt x és y koordinátákat két párhuzamos, double típusú tömb adatszerkezetben tárolja. A két tömbbe összesen 2*N db double típusú szám kerül. Azonos index jelöli az összetartozó koordinátapárokat. Az egymást követő két ciklus közül az első előállítja az adatszerkezetet és a második megjeleníti a pontokat.

2. megoldás

A heartCurveDraw2() metódus a párhuzamos tömbök helyett adatszerkezetként egyetlen tömböt használ. A java.awt.geom csomag Point2D osztályú objektumai kerülnek a tömbbe. Mivel a Point2D absztrakt osztály, így a Double() osztálymetódusával (factory method) példányosítható úgy, hogy a szükséges koordinátapárokat megfelelően tudja tárolni. A tömbbe N db objektum kerül.

3. megoldás

A heartCurveDraw3() metódus nem használ tömb adatszerkezetet. Tehát nem emlékszik az összes pont koordinátájára. Ehelyett a ciklus röptében, egyesével létrehozza a pontobjektumokat és azonnal ki is rajzolja azokat (átmeneti az emlékezet).

4. megoldás

A heartCurveDraw4() metódus Stream API-t és lambda kifejezéseket használ. Az első N természetes számból készül egy sorozat, amihez röptében hozzákötődik a t-edik Point2D típusú objektum. Ezzel létrejön egy folyam adatszerkezet. Tehát van egy pillanat, amíg a program emlékszik az összes folyambeli pontobjektumra. Végül a folyam feldolgozása, bejárása során egyesével megszólítva a folyam objektumait, a pontok kirajzolódnak a vászonra.

A vezérlés

Az eredmény

A szívgörbe önálló – swing, grafikus felhasználói felület, GUI – ablakban így jelenik meg:

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat – a matematikai háttértől eltekintve – a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 21-24. óra: Objektumorientált programozás 2. rész, valamint a 29-36. Grafikus felhasználói felület alkalmaihoz kötődik.

A 2D szívforma egyenletrendszerét erről a weboldalról választottam: Heart Curve – from Wolfram MathWorld. Egy merész továbbfejlesztési ötlet: a haladóknak megtalálható a 3D szívforma ábrázolása is: Heart Surface – from Wolfram MathWorld.

Sándor is blogolt már a Valentin-nap témában: Rómeó és Júlia. Ebből kiderül, hogy vajon ki szereti jobban a másikat: Rómeó vagy Júlia.

Doktoranduszok programoznak – újratöltve

it-tanfolyam.hu doktoranduszok programoznak

it-tanfolyam.hu doktoranduszok programoznakSaját doktorandusz csoporttársaimmal én is többször beszélgettem már arról – ahogyan Sándor is tette 2018-ban –, hogyan tudnák/tudják használni a programozás eszköztárát, módszereit, lehetőségeit saját kutatási munkájukban, beépítve a kutatási folyamat egyes lépéseibe, illetve disszertációjuk elkészítésébe.

A 7 fős csoportban mindenkinek más az alapvégzettsége, így szoftverfejlesztéshez, programozáshoz közös szókincs és terminológia haladó szinten természetesen nincs, viszont közös bennünk, hogy mindannyian alkotunk különféle modelleket és elemzünk adatokat. A csoport teljesen inhomogén, több szempontból is: ki melyik évfolyamot végzi, hol tart a kutatómunkájában, vannak-e ipari kapcsolatai, nappali vagy levelező képzésben végzi tanulmányait és persze ki mikor ér rá.

Különféle modelleket alkotunk

  • a mérnökök, fizikusok, geográfusok, biológusok többféle kísérletet végeznek el, szimulációkat terveznek és futtatnak, mérőeszközöket és műszereket használnak,
  • az informatikusok különböző matematikai eszközöket alkalmazva objektumorientált – vagy másféle – modellezést végeznek, szoftvereket terveznek, javítanak, újraírnak.

Adatokat is elemzünk, ki-ki előképzettségének megfelelően

  • kérdőívező szoftverekből exportálva valamit,
  • Excel munkalapokon, függvényekkel, adatbázis-kezelő funkciókkal, kimutatásokkal (Pivot táblák),
  • különböző fájlformátumokkal (CSV, XML, JSON, egyedi) dolgozunk és konvertálunk A-ból B-be,
  • távoli adatbázisokhoz, felhőbeli adattárházakhoz csatlakozunk, lekérdezünk és kapunk valamilyen – többnyire szabványos – adathalmazt,
  • matematikai, statisztikai szoftvereket használunk, például: MATLAB, Derive, Maple, SPSS.

Az öt évvel ezelőtti tematikát újragondoltuk. Kérdőívben felmértük a csoporttársak koncepcionális és konkrét igényeit. Más doktori iskolák hallgatói közül is toboroztunk. Ehhez kötődően köszönjük a DOSZ segítségét. Ezek alapján összeállítottunk egy olyan 3 részből álló tematikát, ami mindannyiunk számára hasznos. A 72 óra három 24 órás modulból áll: Java programozás, MATLAB programrendszer, mesterséges intelligencia.

Java programozás modul

  • 1-6. óra: Objektumorientált modellezés, MVC rétegek, algoritmus- és eseményvezérelt programozás
  • 7-12. óra: Fájlkezelés és szövegfeldolgozás (XLS, CSV, XML, JSON formátumú adatok írása, olvasása, feldolgozása), helyi és távoli adatforrásból
  • 13-18. óra: Adatbázis-kezelés JDBC alapon (SQL parancsok, CRUD műveletek, hierarchikus lekérdezések), helyi és távoli adatforrásból, natív módon és készen kapott API-kkal
  • 19-24. óra: Komplex adatfeldolgozási feladatok megoldása programozási tételek használatával, egyszerű statisztikai funkciók implementálásával

MATLAB programrendszer modul

  • 1-6. óra: Bevezetés az MATLAB nyelvbe (R2012 vs. R2022), utasításkészlet, vektorok, mátrixok, szkriptek, függvények, grafika
  • 7-12. óra: Szimulációk tervezése és készítése, numerikus módszerek áttekintése, algoritmizálása, tesztelés, analitikus megoldás, egyenletek megoldása
  • 13-18. óra: Adatok importálása helyi és távoli adatforrásból is, fájlkezelés: szövegfájlok, Excel-fájlok, import, feldolgozás, export, statisztikai alapok
  • 19-24. óra: Statisztikai próbák (illeszkedés- és függetlenség vizsgálata), hisztogramok készítése, differenciálegyenletek megoldása

Mesterséges intelligencia modul

  • 1-6. óra: Klasszikus és újabb megközelítések, alap AI funkcionalitás, megerősítéses és gépi tanulás lehetőségei és korlátai, OpenAI GPT nyelvi modell
  • 7-12. óra: Általános csevegés lehetőségei, korlátai, hasznos tanácsok; csevegés fájlok (szöveg, multimédia) tartalmáról; generatív AI funkciói; kép, ábra, grafikon, térkép, hang, animáció, videó generálása és ezek tömeges feldolgozása; programozási tételek alkalmazása multimédia analitikával együtt
  • 13-18. óra: Statisztikai adatok elemzése AI eszközökkel, automatikus tételbizonyítás AI eszközökkel, gráfelméleti kérdések kontra AI, hatékonysághoz kötődő kérdések AI eszközök esetén
  • 19-24. óra: Objektum- és aspektusorientált tervezés AI eszközökkel, kutatómunkát támogató AI eszközök

Mivel mindenki doktorandusz a csoportban, így a különböző MSc-s alapvégzettsége ellenére mindannyiunknak vannak strukturális programozáshoz kötődő alapismeretei, valamint adatok elemzéséhez szükséges elméleti matematikai/statisztikai alapjai.

A csoport órái szeptembertől decemberig, szombatonként zajlottak. Sándor tartotta a 24 órás Java programozás modult. Ez nagyban lefedi a Java SE szoftverfejlesztő tanfolyamunk tematikáját és kapcsolódik a Java EE szoftverfejlesztő tanfolyamunk és a Java adatbázis-kezelő tanfolyamunk tematikájához is. Én tartottam a 24 órás MATLAB programrendszer modult. Ketten közösen tartottuk a 24 órás Mesterséges intelligencia modult. Igazán tartalmas őszi időszakot jelentett számunkra ez a 12 szombat. Mindenki elvitte, amit beletett.

A koncepciót once-in-a-lifetime jelleggel dolgoztuk ki 🙂 (újratöltve) azzal a fő szándékkal, hogy hatékonyabban működjünk együtt a jövőben. A visszajelzések alapján bátran állíthatom, hogy ez gördülékenyen fog menni. Egyben köszönöm mindenkinek az aktív, konstruktív részvételt.

Kölcsönös ajándékozás véletlenszerűen

A kölcsönös ajándékozás időről-időre több közösségben is felmerül. Munkahelyi környezetben és iskolai csoportokban is (például: Télapó, karácsony). Hagyományos megközelítésben így hangzik a szabály: „húzzunk neveket a kalapból”. Másképpen: mindenki 1 ajándékot ad, mindenki 1 ajándékot kap és a sorsolás véletlenszerűen történik.

Készítsünk Java programot, ami megoldja a kölcsönös ajándékozást véletlenszerűen!

A neveket tároljuk el szövegfájlban ( nevsor10.txt). Soronként egy nevet. Ha különböznek, akkor elegendő a keresztnév. A soroknak/neveknek különbözniük kell. Ha szükséges, akkor hozzáírjuk a vezetéknevet, a vezetéknév első betűjét vagy sorszámot. Ezt a program beolvassa és megjegyzi egy szöveg típusú generikus nevsorLista nevű indexelhető adatszerkezetben. A nevek eredeti sorrendje nem befolyásolja a kiválasztást, mert a neveket a program összekeveri (helyben, véletlenszerűen, a shuffle() metódussal). Adott elemszámú lista indexelhető nullától elemszám-1-ig ( size()-1-ig).

A szövegfájl olvasása, tartalmának betöltése során – az ékezetes karakterek miatt – előfordulhatnak karakterkódolási problémák. Ekkor használható a readAllLines() függvény túlterhelt változata esetén a Charset típusú második paraméter, például így: Charset.forName("ISO-8859-2"). A fájlkezeléshez kötelezően kivételkezelés is szükséges (ezt most nem részletezem).

1. megoldás

Az ajándékot adó-kapó párosokat a listában egymás mellett lévő i-edik (bal) és i+1-edik (jobb) nevek adják. Az adó az elsőtől az utolsó előttiig, a kapó a másodiktól az utolsóig léptethető. Kimarad az a pár, amikor az utolsó ad és az első kap. A lista indexei szerint az adók esetében a nulladik elemétől az utolsó előtti eleméig és a kapók esetében a lista első elemétől az utolsó eleméig jelenti a kiválasztást. Mindez könnyen megoldható for számláló ciklussal. A kimaradó pár ajándékot adó tagja a lista size()-1-edik eleme és kapó tagja a lista nulladik eleme. Ez a ciklus után egyszerű kiírással megoldható.

2. megoldás

A program átmenetileg megváltoztatja a listát: az utolsó elem után bővül az első elemmel ( nevsorLista.add(nevsorLista.get(0))). Ennek köszönhetően az ajándékot adó-kapó párosokat a listában egymás mellett lévő lévő i-edik (bal) és i+1-edik nevek adják. Most nem lesz kimaradó pár, mert a korábbi utolsó elem most az utolsó előtti elem és az utolsó elem most az első. Másképpen: mindenki ad és mindenki kap.

A megoldás Stream API-t használ. Először előállít egy olyan IntStream típusú folyamot, amiben az ajándékot adó és kapó párosok adó (bal) tagjainak sorszámát/indexét tartalmazza. Ezután ezt végigjárva összefűzi a szövegeket ( mapToObj()) úgy, hogy a páros kapó (jobb) tagja az adó tag rákövetkezője. Végül a program kiírja a összefűzött szövegeket ( forEach()) a konzolra. Ha a neveket tartalmazó listát használnánk később még valamire (azaz kellene az eredeti összekevert állapota), akkor érdemes aktiválni a megjegyzésbe tett utolsó utasítást.

Eredmény

A program konzolos/szöveges eredménye mindkét esetben azonos. Persze a nevek sorrendje különbözhet, hiszen az összekeverés minden futtatás esetén másképpen alakul(hat), mert véletlenszerű. Például:

Érdemes tesztelni és átgondolni, hogy mi történne, ha üres a fájl, üres a generikus lista, 1 név van, 2 név van, illetve nem szabadna ilyet, de mi történne azonos nevek esetén. Vajon különbözik/különbözne a fenti két megoldás eredménye? Miért?

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 17-28. óra Objektumorientált programozás és 37-44. óra: Fájlkezelés alkalmaihoz kötődik.

Táblázatos hőtérkép készítése

Ebben a projektben táblázatos hőtérképet készítünk Java és JS nyelveken. Java programot készítünk az adatok véletlenszerű előállításához és a sablon alapján történő HTML fájl generálásához. JavaScript program fogja a grafikont megjeleníteni a weboldalon. Tervezünk, kódolunk, tesztelünk. Lássunk hozzá!

Mi az a hőtérkép?

A hőtérkép (heatmap) olyan grafikon, amely könnyen áttekinthetővé tesz nagy mennyiségű adatot úgy, hogy kategorizál/csoportosít és az előfordulások tartományai alapján különböző színeket rendel azokhoz. A szín hozzárendelése egy intervallumból történik. Például a világosabb a ritkább, a sötétedő szín az egyre gyakoribb értékeket jelenti. A tipikus hőtérkép kétdimenziós és az előforduló adatok mennyiségét, azok arányait, eloszlását, szóródását (nem szórását), gyakoriságát jeleníti meg. A hőtérkép gyors vizuális összefoglalást, áttekintést biztosít. A projekt során sorokból és oszlopokból álló táblázatos hőtérképet készítünk.

A táblázatos hőtérkép nem összekeverendő a következő két megközelítéssel:

  • Ha az adatok lokációhoz kötődnek és térképen jelennek meg, akkor azt tematikus térképnek nevezzük. Erről már blogoltam korábban, lásd: Céline Dion – Courage World Tour, amikor az énekesnő USA-beli államokban előforduló koncertjeinek számát jelenítettem meg. Ehhez hasonlók az amerikai elnökválasztás során használt tematikus térképek, amelyek a(z egyes) jelöltek szavazatainak arányát ábrázolják meg szintén államonként.
  • Ha az adatok webergonómiához, bannervaksághoz, inkább fókuszban lévő tartalmakhoz, felhasználói élmény (UX) tervezéshez kötődnek, akkor a weboldal grafikus elemeihez (azok pozíciója) alapján rendelünk hozzá színeket attól függően, hogy mennyi ideig nézi azt a felhasználó.

Mi a feladat (koncepcionálisan)?

Adott egy konditerem, amely hétköznapokon egy megadott időintervallumban használható. Ehhez kulcsot kell felvenni és leadni. Az első belépő nyitja és az utolsó távozó zárja az ajtót. A teremhasználat offline nyilvántartott, így nehézkes bármiféle kimutatás, statisztika. A „menet közbeni” jövés-menést nem tudjuk követni. Természetesen adott számos szabály (felelősség, biztonság, balesetvédelem, létszámkorlát), amit most nem részletezek.

Elhelyezünk az ajtó mellett, belülről egy belépéshez és egy távozáshoz tartozó QR kódot. Készítünk egy egyszerű mobil alkalmazást és megkérjük a konditermek használóit, hogy belépéskor és távozáskor „csekkoljanak”. Anonim gyűjtjük a belépések és távozások időpontját (valahol egy szerveren, bármilyen fájlban, adatbázisban).

A konditermek használatára vonatkozó összesített adatokat könnyen átlátható módon szeretnénk weboldalon megjeleníteni: heti bontásban, a nyitva tartás időszakát órás blokkokra bontva az igénybevételtől függően jelenjenek meg az adatok táblázatos hőtérképen.

Ez az állapot átmeneti. Segítheti a konditermekbe tervezett – egyéni használattól független – események ütemezését. Ezeket akkor lenne célszerű időzíteni, amikor nem, alig, vagy kevésbé használt, foglalt az adott konditerem. A továbbfejlesztés következő állapotában könnyen lecserélhető a QR kód RFID alapú proximity kártyára, proxy kulcstartóra: először csak a jelenlét nyilvántartásához, később akár az ajtó nyitásához is.

Mit valósítunk meg mindebből Java nyelven?

Egyetlen konditeremre fókuszálunk. A hétköznaponként nyitva tartás legyen 16 órától 21 óráig. Aki edzésre jön, véletlenszerűen 20 és 40 perc közötti időszakot tölt a konditeremben. 16 órától lehet belépni. Az utolsó belépés 20:40-kor lehet (érdemes). 21 órakor mindenki elhagyja a konditermet. Nem fordul elő, hogy valaki nem jelzi a belépését vagy a távozását. Mivel anonim a nyilvántartás, így elegendő a dátum/időhöz, időbélyeghez egyetlen állapotot tárolni: be vagy ki. Valaki belépett nyitáskor vagy valamikor utána, majd távozott 20-40 perccel később, de legfeljebb záráskor.

A szükséges adatokat véletlenszerűen állítjuk elő. Egy hétre vonatkozó adatokat generálunk. Ezek a fenti paramétereknek megfelelő, összetartozó belépéshez és távozáshoz köthető időpontok. Kiegészítve a napok ciklusban való léptetésével hétfőtől péntekig. Az adatokat feldolgozva, összegyűjtve, csoportosítva, kategorizálva olyan (kimeneti) formátumra alakítjuk, amely kompatibilis a táblázatos hőtérkép adatmodelljével (bemenetével). Mindez Java nyelven valósul meg.

Mi történik JavaScript nyelven?

A webes megjelenítéshez szükséges egy HTML fájl, amelyben beágyazva található meg egy téglalap alakú területként megjelenő táblázatos hőtérkép. Ez sokféleképpen testre szabható: adható hozzá felirat, beállítható a sorokhoz és oszlopokhoz tartozó szöveg és a táblázat celláiban megjelenő értékek, adott a lebegő, az egér kurzor helyzetétől függő – cellánként különböző – jelmagyarázat, és persze mindennek van formátuma (betűtípus, méret, szín, igazítás, kitöltés). A fix, adatoktól nem függő beállításokat tartalmazó weboldalt sablonként elkészítjük. Mindez JavaScript nyelven történik. Ezután Java program a weboldal sablonját kiegészíti (cseréli, behelyettesíti, feltölti) a szükséges adatokkal.

Hogyan alakul az időintervallumok átfedése?

A konditerem használatának alakulását követjük, amihez táblázatos hőtérképet készítünk. Ehhez a nyitva tartás időintervallumát órás blokkokra bontjuk. Blokkonként összesítjük a jelenlétet, azaz megszámoljuk, hogy éppen akkor hányan veszik igénybe a konditermet, hányan vannak jelen/benn.

A konkrét paraméterektől függően az alábbi képen látható 3 eset egyike fordulhat elő. A és B jelöli az órás blokk elejét és végén, tehát ez 60 perces intervallum. Tarthat például: 16:00:00-16:59:59-ig. X és Y jelöli a jelenlét intervallumát, azaz a belépés és távozás időpontjait. Ez 20 és 40 perc között alakul. Haladjunk balról jobbra az ábrán.

Az első esetben ugyanarra az órára esik a belépés és a távozás. Ez az eset egyértelmű. A másik két esetben átfedés van több órás blokk között, mert különböző órára esik a belépés és a távozás. El kell dönteni, hogy ekkor hogyan összesítjük a jelenlétet. Válasszunk az alábbi két módszer közül:

  • Az első módszer szerint mindkét órához – ahol átfedés van – összesítjük a jelenlétet 1-1 főként, hiszen ha nem is végig, de jelen volt mindkét órás blokkban. Például: egy 16:50:00-17:20:00 jelenlétet a 16 és 17 órás blokkban is figyelembe veszünk.
  • A második módszer szerint időarányosan tesszük mindezt, azaz súlyozunk aszerint, hogy milyen hosszú jelenlét esik az egymást követő órákban. Például: egy 16:50:00-17:20:00 jelenlétet a 16 órás blokk esetében egyharmad, a 17 órás blokk esetében kétharmad a jelenlét adott órára eső aránya, súlya.

Az első módszert valósítjuk meg. Ez a döntés jelentősen befolyásolja, hogyan kell értelmezni később az elkészült táblázatos hőtérképet.

Objektumorientált tervezés

A koncepcionális terv alapján modellezünk. A szükséges adatok tárolására és alapfunkcióira fókuszálunk. Az osztály tárolja az összetartozó adatokat és megvalósít rajtuk értelmezhető műveleteket. Az időintervallum/időtartam kezelését a Duration ősosztály oldja meg. Tárolja a start és stop – naptól független időpontokat tároló – adatok és a rájuk vonatkozó FORMAT – megjelenítéshez kötődő – konstanst. Biztosítja a szükséges műveleteket: konstruktor, getterek, megvalósítja az időintervallumok átfedését az isOverlapped() függvénnyel, valamint ad szöveges reprezentációt a toString() függvénnyel. Az ősosztályból öröklődik az utódosztály. A DurationMap osztály a naptól független időpontokat kibővíti a hozzájuk tartozó day nappal, valamint képes tárolni az összesített, megszámolt jelenlétet a count változóban. Részt vesz a megszámolás folyamatában azzal, hogy lépésenként meghívható az  incrementCount() eljárása.

A java.time csomagbeli LocalTime osztály képes a dátumtól független, napon belüli időpont tárolására és biztosít néhány alapvető funkciót a kezelésükre. Az adattárolás a napon belül eltelt időn alapul és nekünk (bőven) elegendő a másodperc alapú megjelenítés. A DateTimeFormatter alkalmas ezen időpontok formátumának tárolására, például óó:pp:mm alakban.

A Duration osztályból annyi objektum készül, ahány jelenlét adódik véletlenszerűen. Akár több száz is lehet. A DurationMap  osztályból generált objektumok száma jóval kevesebb. Heti 5 napra, napi 5 órás blokkra 25 db készül belőle.

A vezérléshez kötődő osztály tervezését nem részletezem.

Íme a Java forráskód

A Java forráskód minden megtervezett funkciót megvalósít, támogatva a koncepciót. Most nem részletezem a működését.

A véletlenszerűen előállított adatok

A lista görgethető:

A weboldal sablonja

HTML és JavaScript nyelvű forráskód vegyesen. A Java program a fájl 31. sorában lévő ##HEATMAP_DATA## szöveget cseréli le a táblázatos hőtérkép megjelenítéséhez szükséges véletlenszerűen előállított adatokra.

További részletekért, beállításra vonatkozó, testre szabási lehetőségekért érdemes tanulmányozni az AnyChart dokumentáció Heat Map Chart fejezetét.

Az eredmény

Az előállított weboldalt böngészőben megjelenítve ezt kaphatjuk eredményként (vagy a véletlenszerűen generált adatoktól függően hasonlót):

A táblázatos hőtérkép hasznos eszköz. Elemezve könnyen döntéseket hozhatunk a koncepcionális tervezés során vázoltak alapján.

Továbbfejlesztési lehetőségek

  • Lehetne több konditerem is. Ekkor rögtön felmerül az összehasonlítás lehetősége, egyben igénye is.
  • Lehetne hétköznaponként eltérő a konditerem nyitva tartása.
  • Az időpontok kezelési precíz. Egy másodpercen múlik, hogy nem fedik át egymást. Az időpontok megjelenítése lehetne óó:pp alapú is.
  • Az időintervallumok jelenleg állandóak, mindig 1 órásak. Könnyen megoldható lenne, hogy dinamikusak legyenek: például a népszerűbb időszakok felbonthatók lennének két 30 perces blokkra. A népszerűség értelmezhető minden nap (héten) másképp. Egyszerű képlettel: átlag felett, medián felett.
  • Általánosíthatnánk a létesítménygazdálkodáshoz kötődő erőforrást nem (feltétlenül) helyhez kötött, mozgatható, kölcsönözhető eszközökre is: hangszer, projektor, stúdió felszerelés.
  • Másképpen valósulna meg az adatgyűjtés, ha egyetlen QR kód állna rendelkezésre és back-end helyett a „mobil alkalmazás emlékezne” a belépésre és távozásra. Ez jelentheti legalább az aznapi adatokat, de tárolható historikusan is.
  • Hibát is kellene, lehetne kezelni. Például a kiléptetés lehetne automatikus a nyitva tartás végén. A jelenlét igazából igaz-hamis állapot: ha eddig hamis volt és történt valami, akkor igaz lesz és fordítva. Ha van mögötte állapotmegőrző emlékezet (mivel programozunk, így nyilvánvalóan azonnal objektumra gondolunk, vagy annak valamilyen fájlba vagy adatbázisba történő leképezésére).
  • A nyilvántartás könnyen megvalósítható személy hozzárendelésével, azaz lehetne nem anonim is a jelenlét.
  • Egymást átfedő időpontok esetén (ha nincsenek a hosszukra vonatkozó korlátozó feltételek) általánosítva 6 eset fordulhat elő. Például ha a jelenlét lehetne 60 percnél hosszabb, de 120 percnél rövidebb is, akkor nem lenne elegendő a fenti 3 esetet kezelni a jelenlét összesítése során.
  • Valósítsuk meg az időintervallumok átfedésénél bemutatott második – időarányos – módszert!
  • A napi jelenlét 20 fővel valósul meg a programban. Lehetne ez a paraméter is véletlenszerű, például 15-30 fő között, vagy esetleg népszerűbb a péntek.
  • Jelenleg a táblázatos hőtérkép statikus. Csak a (befejezett egész heti) múltbeli adatokat tudja megjeleníteni. Az aktuális, jelenlegi állapothoz szinkronizáció, ütemezés kell. Óránként (a blokkok végén automatizáltan), de akár 5 percenként is aktualizálható a hőtérkép.
  • A táblázatos hőtérkép megjelenítése önálló weboldal helyett beágyazható widget felületén is történhet.
  • Többféleképpen is készítettünk már grafikonokat, íme néhány a szakmai blogunkból: Kockadobás kliens-szerver alkalmazás, Sankey-diagram készítése, JFreeChart grafikon készítése.

A bejegyzéshez tartozó forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A projektfeladat – attól függően, milyen szinten valósítjuk meg – kapcsolódhat több tanfolyamunk tematikájához. A fenti forráskód a Java SE szoftverfejlesztő tanfolyam 17-28. óra: Objektumorientált programozás és a 37-44. óra Fájlkezelés alkalmaihoz kötődik. Ha többrétegű, elosztott alkalmazásként valósítjuk meg, akkor a Java EE szoftverfejlesztő tanfolyam a 9-16. óra: XML és JSON feldolgozás, dinamikusan generált weboldalba beépítve a 33-40. óra: Java Server Pages alkalmaihoz kapcsolódik. Ha fájlok helyett egyszerű adatbázist használnánk, akkor a Java SE szoftverfejlesztő tanfolyam 45-52. óra: Adatbázis-kezelés JDBC alapon, ha objektumrelációs leképezéssel oldanánk meg, akkor a Java EE szoftverfejlesztő tanfolyam 25-32. óra: Adatbázis-kezelés JPA alapon alkalmakhoz kötődhet.