Etikett bokarenan

Lita på metoden

Du ska kunna lita på metoden, men var lite vaksam på utfallet. Så skulle jag kortfattat vilja beskriva vad konfidensintervall handlar om. Idag är det detta som ligger på bordet.

I måndags skrev jag om centrala gränsvärdessatsen. Den handlade om att fördelningen av medelvärden närmade sig en normalfördelning när man tog tillräckligt stort urval. Det handlar alltså inte specifikt om enskilda värden, utan medelvärdet. I en population med en höger-skev fördelning skulle medelvärdet kunna vara 10. Om vi tar tillräckligt stort urval ur populationen och kontrollerar medelvärdena i den skulle de fördela sig runt 10 på sådant sätt att det är att betrakta som en normalfördelning.

Sedan tidigare kommer vi ihåg sannolikhetsfördelningen runt medelvärdet i en normalkurva som arean under kurvan baserat på standardavvikelse. Det var den kända 68-95-90.7%-regeln som jag skrev om förra gången. Om jag vill säga något om medelvärdet i populationen baserat på mitt urval (det som även kallas statistisk inferens) så säger regeln att jag kan ta medelvärdet och subtrahera och addera standardavvikelse (vilket ger en felmarginal) för att få fram ett intervall. Intervallet ger då en viss felmarginal – som blir större ju större del av befolkningen jag vill täcka. En vanlig andel att vilja täcka är 95%. Detta motsvarar 2 standardavvikelser. Om vi bygger vidare på exemplet ovan så säger vi att vi har en standardavvikelse på 1,5. Vi tar detta värde och multiplicerar med 2 standardavvikelser; detta ger oss en felmarginal på +/-3. Då har vi ett lägre värde på 7 och ett högre värde på 13. Detta är ett intervall som täcker 95% av populationen.

Vi kan nu säga att vi med en konfidensnivå på 95% har ett medelvärde inom 7-13, baserat på vårat urval. Det innebär inte att utfallet med 95% sannolikhet är korrekt; det betyder att metoden i 95% av fallen ger ett medelvärde som faller inom intervallet.

Det om konfidensintervall. På fredag ska jag berätta om vad som händer när vi inte vet standardavvikelsen i populationen utan måste uppskatta den. Det blir ju kul!

Och så vill jag bara säga något om programmering. Igår kväll satt jag och programmerade lite på Bokarenan. Jag höll på att skriva funktioner som motsvarade URI-vägar (typ URL) men det var hela tiden fel funktion som fångade en URI. Jag angav /utforska/bok/ny och en funktion för att skapa en ny bok skulle fånga detta, men istället så var det en funktion för att presentera en enskild bok som fångade anropet. Den funktionen hade till uppgift att fånga /utforska/bok/{id}. Men jag förstod först inte att det var detta som var problemet för det som jag fick till felmeddelande var att applikationen inte kunde hitta något bokobjekt. Jag googlade efter problemet och hittade på stackoverflow att problemet kunde ha att göra med i vilken ordning som funktionerna låg i. Liksom när man har en switch-struktur som avslutar med en generisk defaultcase så ska man tänka på att ha de mer generiska URI:erna längre ned i strukturen. Så jag fixade problemet genom att helt enkelt flytta ena funktionen över den andra!

Vi ses på fredag!


Centrala gränsvärdessatsen

Äntligen en ny vecka! Jag har kommit ut på andra sidan av denna helg där jag mest suttit och slipat på Bokarenan. Jag kände att jag hade den tiden över efter att ha gjort en del plugg under den gångna veckan. Mycket tid har gått åt att slipa på CSS och skapa rimliga views (sidor) för de entiteter jag vill presentera. Jag har upplevt det som enklare att ha koll på vad jag skapar och var jag har det, nu när jag skapar mina views manuellt, istället för att låta Symfony skapa dem åt mig. De views som Symfony skapade var i klass med att sitta med phpMyAdmin, men mer begränsad. Så de färdigbakade sidorna motsvarade inte de syften jag hade med dem. Och jag börjar gilla vart åt det barkar.

För närvarande ser sidan ut som nedan. Jag har avvikit lite från hur jag hade skissat den i Adobe XD och gjort den generellt lite ljusare.

Men nog om programmeringen. Denna vecka fortsätter jag med statistik. Denna gång om centrala gränsvärdessatsen.

I förra veckan beskrev jag normalfördelning och hur bra det var att ha en sådan modell. Kortfattat så innebär det att man enkelt kan uppskatta fördelning av befolkning utifrån hur långt från medelvärdet man kollar baserat på standardavvikelse. Denna vecka säger jag att man kommer få en normalfördelning om man tar tillräckligt stora samplar (urval), i princip oavsett hur den verkliga populationen ser ut. Och det är inte helt negativt! Det man vill göra med att ta stora samplar är att komma så nära det verkliga medelvärdet som möjligt med så liten standardavvikelse som möjligt. Om man lyckas med det så har man fått ett urval som har ett medelvärde som ligger där det riktiga medelvärdet ligger, med liten felmarginal.

I sitt urval kommer man ha en standardavvikelse. Och om man vill säga något om 95% av populationen så fångas de inom 2 standardavvikelser från medelvärdet. Detta har jag skrivit om i förra veckan som 68-95-99.7% regeln. Så om standardavvikelsen är stor så kommer det intervall som beskriver 95% av populationen att vara stor. Förhållandet mellan populationens standardavvikelse och urvalets standardavvikelse är s = σ/(√n), där n är urvalsstorlek. Det innebär att man minskar standardavvikelsen med roten ur sampelstorlekens förändring. Vill man halvera standardavvikelsen så får man fyrdubbla urvalsstorleken.

Nästa gång ska jag skriva något om konfidensintervall. Jag misstänker att vi redan har börjat fila på det, men jag får återkomma när jag läst lite om saken.


Block 1 och lite annat

Denna vecka kommer jag avsluta första delen på den statistikkurs jag går. Jag kommer då att ha gått igenom grundläggande begrepp vad gäller undersökningsmetodik och deskription.

Ett av de grundläggande koncepten man måste ha koll på är samband. Bara för att där finns ett samband så behöver det inte betyda att en variabel orsakar ett visst utfall. Och finns ett orsakssamband så måste man ha koll på vad som orsakar ett utfall. Ett enkelt exempel på detta är att man skulle kunna se ett samband mellan glassförsäljning och soligt väder. Det är ju inte att glassförsäljningen går bra som orsakar soligt väder! Sedan kan man se att när glassförsäljning går bra är det också många som badar. Men det är inte glassförsäljning som gör att folk badar. Det råkar bara sammanfalla med att det är soligt väder. Så de korrelerar med varandra.

Jag kommer börja använda ett open-source program denna vecka för att göra statistiska analyser. Det heter Jamovi. Jag kommer använda det för att göra lite övningsuppgifter.

I helgen så har jag inte ägnat någon tid till statistik, så det blir mer av det i veckan. Nej, snarare så har jag ägnat tid till programmering, eller snarare web design. Jag har efter lite fram och tillbaka med css och visat sambon kommit fram till att jag först behöver en målbild innan jag sitter och prövar olika färger och css-funktioner. Så istället har jag suttit med Adobe XD och tagit fram ett koncept. Det tog ett tag men jag gillar färgerna och presentationen som jag fått fram.

Så när jag eventuellt får lite tid över efter bebisar och statistik så kommer jag köra lite CSS med lite grid och flex.


Sökfunktion och DQL

Denna veckan blir antagligen den sista på ett tag med uppdateringar om Bokarenan och programmering. Jag påbörjar en kurs om statistik och kommer använda Femte Arenan för att beskriva de lärdomar jag drar av det. Så jag passar på att beskriva det senaste jag gjort i programmeringsväg över helgen.

Jag lämnade förra veckan med en fundering på om jag skulle hinna få på plats en sökfunktion. När jag gjorde en sökfunktion till den nuvarande Bokarenan så svalde jag efter för mycket. Jag gjorde så att användaren kunde söka efter varje detalj i varje liten entitet. Det gjorde sökfunktionen lite svår att använda. Istället har jag gjort sökfunktionen till ett textfält som alltid finns tillhands i navigationsfältet och det man söker efter är BÖCKER. Jag har för närvarande gjort så att man kan söka efter författare eller boktitel, men sökresultatet är alltid BÖCKER. Inte författare eller något annat. Jag är rätt nöjd med resultatet. Det var också rätt så enkelt att implementera med hjälp av Doctrine och dess queryBuilder.

Så efter att ha fått på plats sökfunktionen så kände jag att jag var på rull. Därnäst så implementerade jag ett navigationsträd för böcker. I navigationsfältet har användaren en länk som heter Böcker. Klickar man på den så kommer man till sida för att välja böcker enligt genrer. Genrer är uppbyggda hierarkiskt, så att till exempel thriller är en underkategori till skönlitteratur. Klickar man på Skönlitteratur så får man upp en ny lista på genrer inom skönlitteratur samt de populäraste böckerna associerade med genren skönlitteratur.

Implementeringen av genrer i kombination med lista på populära böcker innebar att jag hade att hantera tre olika entiteter: Book, BookAnalysis och Genre. Tidigare har jag helt enkelt använt den repository som var lämplig. Jag försökte mig på att använda repository för BookAnalysis med förhoppning att kunna använda den Book-referens som den entiteten har. Book har i sin tur referens till Genre-entitet. Jag vred och vände men fick inte till det. Jag hade några alternativ kvar, som tur var. Nuvarande Bokarena använder sig av SQL-kod som jag skrivit. Nackdelen med att använda det är att jag inte får tillbaka entitets-objekt, utan det är något som jag fått initialisera ”manuellt”. Men jag behöver inte göra på samma sätt här. Jag kan använda DQL (Doctrine Query Language) istället. Det innebär att jag blir lika flexibel som om jag skrev SQL men jag får fördelen med att få tillbaka de rätta entiteterna. Med följande sats så blev det inte så komplicerat trots allt:
"SELECT a FROM App\Entity\BookAnalysis a JOIN a.book b WHERE b.genre = $id ORDER BY a.numReviews DESC, a.avgRate DESC"

Ha en sån trevlig vecka. Nästa gång skriver jag om data i stora mängder.


Favoriter på framsidan

Så till slut kan jag säga att jag har uppnått veckans mål; att få fram en lista med rekommenderade recensioner och en lista på de böcker som fått bäst betyg. Resan hit tog en liten omväg. Hur lång? – Inte särskilt lång!

Respektive bok har inte ett betyg, men däremot har de recensioner som boken är associerad med ett betyg. För att kunna ta fram exempelvis de fem bästa böckerna behöver jag hämta alla böckers respektive recensioner, sedan ordna böckerna i fallande ordning utefter de genomsnittliga betygen. Därefter skulle jag kunna presentera resultatet för användarna. Detta tyckte jag verkade som ganska mycket jobb att göra varje gång någon laddar framsidan. Den lösning som jag tagit fram är istället att ordna en ny entitet: BookAnalysis. Denna entitet innehåller såväl genomsnittliga betyg för respektive bok som antal användare som har boken som ”att läsa”. Denna entitet kan innehålla fler fält, men vi börjar med detta. Idén är att bara administratörer kan komma åt kontrollen för BookAnalysis och därigenom aktivera en analys av databasens recensioner och böcker. På framsidan kan jag därför presentera de 5 bästa böckerna genom att hämta toppresultatet från BookAnalysis. Nackdelen är att användarna inte får det absolut senaste resultatet, men fördelen är att servern inte behöver genomföra beräkningar som bara kommer växa och bli större och tynga ner processorerna. Kanske är det så att beräkningarna inte skulle bli så tunga, men eftersom jag har en loop inom en loop så vill jag undvika risken.

Nu har jag fått ganska mycket på plats, men jag saknar någon sök-funktion. Detta skulle jag vilja få plats härnäst. Men! Frågan är om jag kommer ha någon tid över till det den närmaste tiden. Nästa vecka börjar jag en kurs om statistik och det kommer också synas på denna sidan. Och innan vi lämnar denna vecka så vill jag gå in på lite nöje!

Jag har under veckan haft nöjet att pröva ett spel som min sambo valt ut till mig: Shadow of the Tomb Raider. Spelet påminner mig i delar om Indiana Jones-filmerna och Rovdjuret. Man spelar som Lara Croft, en forskare/äventyrare, som är på jakt för att hämnas sin far. Men jakten uppdagar förebud om världens undergång. Och detta är något som Lara känner stor skuld till eftersom det mycket väl kan ha varit hennes hätska agerande som orsakat det. Så lika mycket som det handlar om hämnd handlar spelet om att försöka med alla medel att stoppa undergången. Jag har haft så himla kul och varit så imponerad av ett spel på länge. Det hänför mig med fantastiska miljöer och påhittiga banor. Jag sitter på spänn när jag hoppar mot en klippkant för att försöka klättra högre. Jag känner mig så grym när jag gömmer mig indränkt i lera mot en klippvägg och inväntar Laras fiender. På resans gång har Lara gått från att vara ett bytesdjur till att bli djungelns jaguar. Det spel jag kommer bita i härnäst är Hellblade: Senua’s Sacrifice. Jag har hört lite grann om spelet och jag är rädd för att det inte kommer ge mig samma tillfredsställelse som Tomb Raider men däremot en resa som kommer vara med mig länge. Vi får se.

Trevlig helg, allihopa!


Lista av recensioner och lista av böcker

Nu har jag i applikationen (Bokarenan) fått till såväl en lista av rekommenderade recensioner såväl som ett urval av de senaste recenserade böckerna. Jag påstod i föregående inlägg att jag denna vecka skulle få till en lista med de böcker med bäst betyg. Det kan fortfarande bli så men jag fokuserade istället på att få till de senaste recenserade böckerna.

För att få till urvalet av de senaste recenserade böckerna så har jag fått gå till Repository-klassen för Review. Där har jag lagt till funktionen findByLatest(int $limit). Genom den funktionen så hämtar jag de senaste x-antal recensionerna. Utifrån den samlingen av recensioner så hämtar jag referenserna till de böcker som de refererar. Eftersom en bok kan recenseras flera gånger så använder jag mig av php-funktionen array_unique(). Därmed så tar jag bort överflödiga referenser och kan presentera en bok inte fler än en gång på framsidan.

Det jag vill göra härnäst är att skapa en personlig lista av böcker att läsa. Och från den listan kan man sedan välja att recensera den bok man vill.


review.review.review

Det som är veckans uppgift är att få till en presentation på välkomstsidan som innehåller två delar. Första delen är en lista av recensioner som har valts av administratörer som lämpliga. Andra delen är en lista av böcker med bäst betyg.

Jag har kommit en bit på vägen med en lista av rekommenderade recensioner. Det har inneburit att jag skapat en ny entitet (RecommendedReview), och gjort det möjligt för de med administratörsrättigheter att lägga till recensioner till listan. Jag har också gjort det möjligt att visa alla de recensioner som finns i rekommendationslistan. Nu återstår att få denna listan till framsidan. Men i utvecklingen av presentationen av listan så har jag råkat göra något hemskt! Genom en template engine (Twig) så låter jag loopa igenom en array och presentera innehållet i en tabell:

{% for review in reviews %}            
<tr>
<td>{{ review.review.id }}</td>
<td>{{ review.review.name }}</td>
<td>{{ review.review.review }}</td>
</tr>
{% endfor %}

Som ni kan se så har jag alltså följande sats: review.review.review. Det är inte hållbart! Det första review står för RecommendedReview och innebär att den relaterar ett recensionsobjekt till det datum när den lades till i listan för rekommenderade recensioner. Det andra review står för ett recensionsobjekt. Det tredje review är ett fält i recensionsobjektet som står för recensionstexten. Det är inte så snyggt eller pedagogiskt att låta en sak betyda tre olika saker. Lämpligen borde jag refaktorera det hela så att jag istället har: recommendedReview.review.text. Det bästa hade varit att göra det rätt från början. Det näst bästa är att göra det rätt nu.

Först går jag till controllern och ändrar variabelnamnet för arrayen med RecommendedReview-objekt från reviews till recommendedReviews. Sedan ändrar jag variabelnamnet för det aktuella RecommededReview-objektet i for-loopen från review till recommendedReview.

Sedan återstår att ändra fältnamnet i Review-entiteten från review till text. Jag gör det, och passar också på att ändra getter– och setter-metoderna i entiteten. Nu har jag gjort ett antal ändringar som kommer bryta vissa referenser i ett antal templates och, framförallt, hur databasen är uppbyggd. Jag kör därför Doctrine:s konsolverktyg för att analysera och åtgärda differensen mellan databasen och entiteterna. Och mycket riktigt så snappar Doctrine upp att jag ändrat namnet på fältet review till text.


Administratörsrättigheter

Den här veckan har varit full av diskussioner om rättigheter. Jag har upprättat ett auktoriseringssystem som kan särskilja användare utifrån vilka rättigheter de har. En vanlig användare ska inte kunna komma åt administratörsverktyg. En administratör ska inte kunna bevilja administratörsrättigheter. En super-administratör kan allt – nästan. Super-administratörens viktigaste uppgift är att bevilja eller ta bort administratörsrättigheter. De är i en hierarkisk ordning, vilket innebär att det inte finns något behov för en användare att ha flera olika rättigheter; det är en rättighet åt gången som gäller.

Något som blivit en utmaning för mig denna vecka är hur jag ska tillsätta rättigheter. Jag hade problem om jag direkt i databasen försökte tillsätta rättigheter, så jag försöker via applikationen istället. Men då behöver jag få till en sökfunktion för att ta fram rätt användare, och sedan en sida för att visa användaren och där i ändra rättigheter.

Jag har fått till sökfunktion och sidan för att visa användaren. Men jag har ännu inte fått till funktionen för att tillsätta rättigheter. Snart har jag fått min Sorteringshatt.

Så är det ju fredag igen! Jag tar på mig glittriga strumpor och sjunger Rebecka Blacks ”Friday”. Jag har spelat ut spelet Control, och jag gillade det! Jag gillade det jättemycket. Det hade en unik atmosfär och världsuppbyggnad. Jag läste varje papper som jag hittade i spelvärlden och för varje papper så blev jag mer trollbunden. Nu får vi se vad det blir härnäst. Jag lämnar tillbaka spelet på biblioteket och får se vad de har inne denna gång.


Användarfunktioner

Vad är nyttan med användare om inte användare har funktioner till hands som inte besökare har? Det är ganska riskabelt att låta vem som helst få skapa innehåll på hemsidan, och därför vill jag avskärma applikationen till att bara kunna användas av folk som går med på lite grundläggande värderingar. Tanken är att Bokarenan är för vettiga och vänliga människor. Dessa medlemmar kan skapa recensioner till böcker som finns i databasen, och om den aktuella boken inte finns så ska de kunna skapa den. Men för att få detta att funka som jag vill så kan jag ta det som jag gjort hittills och avgränsa funktioner till att bara vara tillgängliga för de som loggat in. I tidigare version av Bokarenan så har jag skapat en SESSION COOKIE när användare loggar in, och därmed kan jag i headern för varje sida på applikationen kontrollera att det finns en användare närvarande. I och med att jag använder Symfony så finns det lite andra verktyg jag kan ta hjälp av. Och vilka verktyg är det då som finns? Jag vet inte! Inte än.

Denna vecka kommer jag därför att fokusera på Symfony:s verktyg för att hantera auktorisering. Min förhoppning är att jag kan skapa ett flexibelt system för att hantera olika roller och presentera relevant material och verktyg för användarna. Dessutom hoppas jag på att koden fortsättningsvis kan vara överblickbar och lätt att modifiera vid behov.


Grunderna kommer på plats

Nu går det framåt, men är det snyggt? Med hjälp av Symfony så har jag fått formulär som är anpassade mot respektive entitet som jag sparar i min databas. Som jag tidigare beskrivit så vill jag att användaren ska kunna lägga till all relevant data när en recension ska läggas till. Det innebär att flera entiteter kan komma att läggas till när en recension läggs till, så som förläggare och författare. Min första lösning var att lägga till alla förproducerade formulär som Symfony skapat, men det innebar att ett antal fält skapades som förekom flertalet gånger (eftersom varje enskild entitet kunde relatera till en annan entitet). Jag tog därför och gjorde anpassade formulär för respektive entitet som avhandlade just den enskilda entiteten utan dess relation till andra entiteter. Resultatet blev något vackrare.

Så tillbaka till ett tidigare dilemma; hur ska jag implementera funktionaliteten att lägga till fält för fler författare? Ska jag använda min tidigare lösning som exkluderar JavaScript. Detta skulle innebära att sidan laddas snabbare och att jag undviker att använda sådant som jag inte är van vid. Nackdelen är att jag måste i så fall lägga till fält för upp till ett max antal författare för en bok, säg 10 stycken. Oavsett om användaren vill lägga till en författare eller 20. Det skulle kunna i udda fall kunna innebära att användare inte får lägga till så många författare som användaren vill.

Den andra lösningen, så som även beskrivs i en guide från Symfony, är att använda JavaScript och JQuery för att dynamiskt lägga till fler fält. Nackdelen är att källkoden inte blir vacker och sidan kan ta längre tid att ladda. Fördelen är att endast så pass många fält som användaren vill ha finns till hands. Det innebär också något tydligare indelning på ansvarsfördelning vad avser att JavaScript hanterar beteende och låter HTML stå för stommen.

Jag har valt den andra lösning, för att jag vill pröva något nytt och kanske lära mig något nytt. Resultatet nu är som sådant:

Det är tydligt att jag inte jobbat något med utseende, utan endast innehåll och funktion. När jag fått till de grundläggande funktionerna för applikationen så kommer jag ta hand om utseendet.