Developer Blog

Tipps und Tricks für Entwickler und IT-Interessierte

Laravel | Benutzermodel erweitern

Laravel bietet mit Eloquent ORM ein leistungsfähiges Datenbank-Framework.

Mit Hilfe von Modellen werden die Datenbankobjekte (z. B. Benutzer) definiert. Diese Objekte können dabei einfach durch zusätzliche Felder erweitert werden.

Im nachfolgenden Beispiel wollen wir das vorhandene Objekt User einer initialen Laravel-Installation durch ein Feld username erweitern.

Neues Feld den Benutzermodel hinzufügen

Erstellen eines Datenbank-Migrationsskriptes, das unser gewünschtes Feld zur Tabelle users hinzufügt.

php artisan make:migration add_column_username_to_users_table --table users

Hinzufügen des Felder Username in der neu erstellten Datei. Dies liegt unter database/migrations.

    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('username')->nullable();
        });
    }

    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('username');
        });
    }

Hinzufügen des Feldes Username im Datenbankmodel User in der Datei app\Models\User.php

    protected $fillable = [
        'name',
        'email',
        'password',
        'username'
    ];

Datenbank aktualisieren.

Dieser Schritt entfernt alle Tabellen und erstellt sie neu. Dadurch gehen natürlich alle bereits vorhandenen Daten verloren!

❯ php artisan migrate:fresh --seed
Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (2,326.47ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (1,789.28ms)
Migrating: 2014_10_12_200000_add_two_factor_columns_to_users_table
Migrated:  2014_10_12_200000_add_two_factor_columns_to_users_table (1,010.74ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (2,399.99ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (2,313.60ms)
Migrating: 2021_06_19_085151_create_sessions_table
Migrated:  2021_06_19_085151_create_sessions_table (5,279.76ms)
Migrating: 2021_06_19_102316_add_username_to_users_table
Migrated:  2021_06_19_102316_add_username_to_users_table (432.59ms)
Database seeding completed successfully.

Erweitern der Frontend Views

Hinzufügen des Feldes Username in der Datei resources\views\profile\edit.blade.php

<x-slot name="form">
   ...

   <!-- Email -->
      ...


   <!-- column username -->
   <div class="form-group row">
      <label for="username" class="col-md-4 col-form-label text-md-right">{{ __('Benutzername') }}</label>

      <div class="col-md-6">
         <input id="username" 
               type="text" 
               class="form-control @error('username') is-invalid @enderror" name="email" 
               value="{{ old('email') ?? auth()->user()->email }}" 
               required autocomplete="email" autofocus>

          @error('username')
          <span class="invalid-feedback" role="alert">
             <strong>{{ $message }}</strong>
          </span>
          @enderror
       </div>
    </div>
 </x-slot>

Anpassen des Controllers

Wir passen die PHP Komponente UpdateUserProfileInformation an, so das die neuen Werte gespeichert werden

app\Actions\Fortify\UpdateUserProfileInformation.php
    public function update($user, array $input)
    {
        ...

        if ($input['email'] !== $user->email &&
            $user instanceof MustVerifyEmail) {
            $this->updateVerifiedUser($user, $input);
        } else {
            $user->forceFill([
                'name' => $input['name'],
                'email' => $input['email'],
                'username' => $input['username'],
            ])->save();
        }
    }

Laravel | Installation der Entwicklungsumgebung

Installation

Voraussetzung für die Verwendung von Laravel ist eine Web-Server, der eine PHP Umgebung unterstützt bzw bereitstellt.

Eine einfache Umgebung wird durch XAMPP bereitgestellt. Eine Implementierung kann man bei Apache Friends herunterladen.

XAMPP ist hier ein Akronym für X (Beliebiges Betriebssystem) Apache + MariaDB + PHP + Perl

Im Laufe dieses Post werden mehrerer Pakete und Programme installiert. Alle werden unter einem Startorder installiert. Diese benennen wir im nachfolgenden mit $LARAVEL_TUTORIAL_ROOT

mkdir C:\LARAVEL_TUTORIAL

Setzen der Umgebungsvariablen unter Windows Command Line cmd

set LARAVEL_TUTORIAL_ROOT="C:\LARAVEL_TUTORIAL"

Setzen der Umgebungsvariablen unter Poweshell

$ENV:LARAVEL_TUTORIAL_ROOT="C:\LARAVEL_TUTOPRIAL"

Installation XAMPP

Download des Installer: https://www.apachefriends.org/index.html

Um mehrerer Versionen parallel zu installieren, stehen ZIP-Dateien zur Verfügung. Die aktuelle Version ist

  • Zip Datei herunterladen: $LARAVEL_TUTORIAL_ROOT\xampp
  • Installation mit dem Composer

    Zielverzeichnis erstellen

    mkdir $LARAVEL_TUTORIAL_ROOT\composer
    cd $LARAVEL_TUTORIAL_ROOT\composer

    Diese Verzeichnis als Umgebungsvariable festlehen

    $ENV:COMPOSER_HOME = $ENV:LARAVEL_TUTORIAL_ROOT\composer
    set COMPOSER_HOME
    
    
    
    

    Composer herunterladen

    php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
    php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
    php composer-setup.php
    php -r "unlink('composer-setup.php');"

    Pfad erweitern um

    Fügen Sie die beiden nachfolgenden Pfad ihrer PATH Variablen hinzu:

    composer.bat erstellen
    
    
    
    

    Der Composer ist ein PHP Archiv mit der Dateierweiterung phar. Diese Dateien können nicht direkt aufgerufen werden, sondern stellen ein PHP Programm dar. Es muss als über PHP gestartet werden.

    Hierzu erstellen wir einfach ein Skript, das diese Aufgabe für uns übernimmt. Erstellen Sie dazu die Datei composer.bat im Verzeichnis php

    composer.ps1 erstellen

    php $env:composer_home\composer.phar $args

    composer Tipps und Tricks

    Um weniger Meldungen anzuzeigen kann die nachfolgenden Konfigurationseinstellung verwendet werden:

    composer global config bin-dir --absolute --quiet

    Installation von NodeJS

    https://nodejs.org/en/download/

    Installation des Laravel Installer

    Der Laravel Installer erleichterter das Einrichten und Erstellen neuer Laravel Anwendungen.

    Installiert wird er mit Hilfe des Composer:

    composer global require "laravel/installer"

    Installation einer Beispielanwendung

    laravel new blog

    Nächste Schritte

    Laravel | Installation einer Beispielanwendung

    Laravel | Tutorial: Eine Blog erstellen

    SAS | Migrate from SAS to Python

    Introduction

    Cookbook

    proc freq

    proc freq data=mydata;
        tables myvar / nocol nopercent nocum;
    run;
    mydata.myvar.value_counts().sort_index()

    sort by frequency

    proc freq order=freq data=mydata;
    	tables myvar / nocol nopercent nocum;
    run;
    mydata.myvar.value_counts()

    with missing

    proc freq order=freq data=mydata;
        tables myvar / nocol nopercent nocum missing;
    run;
    mydata.myvar.value_counts(dropna=False)

    proc means

    proc means data=mydata n mean std min max p25 median p75;
        var myvar;
    run;
    mydata.myvar.describe()

    more percentiles

    proc means data=mydata n mean std min max p1 p5 p10 p25 median p75 p90 p95 p99;
    	var myvar;
    run;
    mydata.myvar.describe(percentiles=[.01, .05, .1, .25, .5, .75, .9, .95, .99])

    data step

    concatenate datasets

    data concatenated;
        set mydata1 mydata2;
    run;
    concatenated = pandas.concat([mydata1, mydata2])

    proc contents

    proc contents data=mydata;
    run;
    mydata.info()

    save output

    proc contents noprint data=mydata out=contents;
    run;
    contents = mydata.info()  # check this is right

    Misc

    number of rows in a datastep

    * Try this for size: http://www2.sas.com/proceedings/sugi26/p095-26.pdf;
    len(mydata)

    Django | Cookbook

    Installation

    Install current Version (3.2.8)

    ❯ pip install django==3.2.8

    Install next Version (4.0)

    ❯ pip install --pre django

    Check installed version

    ❯ python -m django --version
    ❯ django-admin.exe version

    First steps

    The following steps are based on a summary of the Django Tutorial

    Create project

    django-admin startproject main
    cd working_with_django
    python manage.py migrate
    python manage.py runserver 8080
    python manage.py startapp app_base

    Create view

    Create view in app_base/views.py

    from django.http import HttpResponse
    
    def index(request):
        return HttpResponse("Hello, world. You're at the polls index.")

    Add view to app_base/urls.py

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('', views.index, name='index'),
    ]

    Add urls to project main/urls.py

    from django.contrib import admin
    from django.urls import include, path
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('app_base/', include('app_base.urls')),
    ]

    Create admin user

    $ python manage.py createsuperuser
    Username (leave blank to use 'user'): admin
    Email address: admin@localhost
    Password: 
    Password (again): 
    Superuser created successfully.

    Create data and database

    Create database model in app_base/models.py

    from django.db import models
    
    class Question(models.Model):
        question_text = models.CharField(max_length=200)
        pub_date = models.DateTimeField('date published')
    
    class Choice(models.Model):
        question = models.ForeignKey(Question, on_delete=models.CASCADE)
        choice_text = models.CharField(max_length=200)
        votes = models.IntegerField(default=0)

    Activating models in main/settings.py

    INSTALLED_APPS = [
        'app_base.apps.AppBaseConfig',
    
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    ]
    $ python manage.py makemigrations app_base
    $ python manage.py sqlmigrate app_base 0001

    Make app modifiable in the admin (app_base/admin.py)

    from django.contrib import admin
    from .models import Question
    
    admin.site.register(Question)

    Writing more views

    Create views in app_base/views.py

    def detail(request, question_id):
        return HttpResponse("You're looking at question
    
    def results(request, question_id):
        response = "You're looking at the results of question
        return HttpResponse(response
    
    def vote(request, question_id):
        return HttpResponse("You're voting on question
    

    Add new views into app_base/urls.py

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('', views.index, name='index'),
    
        path('<int:question_id>/', views.detail, name='detail'),
        path('<int:question_id>/results/', views.results, name='results'),
        path('<int:question_id>/vote/', views.vote, name='vote'),
    ]

    Add template in app_base/templates/polls/index.html

        <ul>
        
            <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
        
        </ul>
    
        <p>No polls are available.</p>
    
    
    
    
    

    Modify view in app_base/views.py

    from django.shortcuts import render
    ...
    def index(request):
        latest_question_list = Question.objects.order_by('-pub_date')[:5]
        context = {'latest_question_list': latest_question_list}
        return render(request, 'polls/index.html', context)

    Raising a 404 error in app_base/views.py

    from django.http import HttpResponse
    from django.shortcuts import render, get_object_or_404
    
    from .models import Question
    # ...
    def detail(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        return render(request, 'polls/detail.html', {'question': question})

    Create template app_base/templates/polls/detail.html

    <h1>{{ question.question_text }}</h1>
    <ul>
    
        <li>{{ choice.choice_text }}</li>
    
    </ul>

    Removing hardcoded URLs in app_base/templates/polls/index.html

    <li>
       <a href="
    </li>

    The way this works is by looking up the URL definition as specified in the app_base/urs.py

    ...
    # the 'name' value as called by the 
    path('<int:question_id>/', views.detail, name='detail'),
    ...

    Namespacing URL names in app_base/urls.py

    app_name = 'app_base'
    
    urlpatterns = [
    ...

    Then, modify link in app_base/templates/polls/index.html

    from url ‘detail’ to url ‘app_base:detail’

    <li>
        <a href="
    </li>

    Use generic views: Less code is better

    Create class in app_views/views.py

    class HomeView(generic.TemplateView):
        template_name = 'index.html'

    Create template app_views/templates/index.html

    <h1>App Views:</h1>
    Welcome

    Modify app_views/urls.py

    urlpatterns = [
        path('', views.HomeView.as_view(), name='home'),
    ]

    Add another app to main project

    Create app

    $ python manage.py startapp app_view
    

    Modify main/urls.py

    urlpatterns = [
        path('admin/',     admin.site.urls),
        path('app_base/',  include('app_base.urls')),
        path('app_views/', include('app_views.urls')),
    ]

    Add data model in app_views/models.py

    from django.db import models
    
    class DataItem(models.Model):
        text = models.CharField(max_length=200)
        data = models.IntegerField(default=0)
    
        def __str__(self):
            return self.text

    Register data in app_views/admin.py

    from django.contrib import admin
    from .models import DataItem
    
    admin.site.register(DataItem)

    Activate models

    $ python manage.py makemigrations app_views
    $ python manage.py sqlmigrate app_views 0001
    $ python manage.py migrate app_views

    Navigation / Redirection

    Set root page of Django project

    When accessing your Django project, the root page will normaly doesn’n show your app homepage.

    To change this, you hate to modiy the url handling.

    In the following sample, replace <appname> with the name of your app

    Define a redirection view in your app (/<appname>/urls.py)

    def redirect_to_home(request):
        return redirect('/<appname>')

    Define path in the global urls.py (/main/urls.py)

    from django.contrib import admin
    from django.urls import include, path
    from django.shortcuts import redirect
    
    from <appname> import views
    
    urlpatterns = [
        path('',            views.redirect_to_home, name='home'),
        path('<appname>/',  include('<appname>.urls')),
        path('admin/',      admin.site.urls)
    ]

    Highlight current page in navigation menu

    <div class="list-group">
        <a href="
                Basic Upload
        </a>
        <a href="
                Progress Bar Upload
        </a>
    </div>

    Using PostgresSQL Database

    Install PostgresSQL

    Create Superuser

    createuser.exe --interactive --pwprompt

    Logging

    Additional reading

    Tutorials

    Testing

    Blogs and Posts

    Resolving problems

    Wrong template is used

    The template system is using a search approach to find the specified template file, e.g. ‘home.html’.

    If you created more than one apps with the same filenames for templates, the first one will be used.

    Change the template folders and add the app name, e.g.

    template/
            app_base/
                    home.html

    Resolving error messages and erors

    ‘app_name’ is not a registered namespace

    One reason for this error is the usage of a namespace in a link.

    Back to <a href="
    
    
    
    

    If you want to use this way of links, you have to define the namespace/appname in your <app>/urls.py file

    app_name = 'app_views'
    urlpatterns = [
        path('', views.HomeView.as_view(), name='home'),
    ]

    dependencies reference nonexistent parent node

    • Recreate database and migration files
    • Remove all migration files under */migrations/00*.py
    • Remove all pycache folders under */__pycache__ and */*/__pycache__
    • Run migration again
    $ python manage.py makemigrations
    $ python manage migrate

    ValueError: Dependency on app with no migrations: customuser

    $ python manage.py makemigrations

    Project Structure

    Running tasks with Makefile

    PREFIX_PKG := app
    
    default:
    	grep -E ':\s+#' Makefile
    
    clearcache:	# Clear Cache
    	python3 manage.py clearcache
    
    run:		# Run Server
    	python3 manage.py runserver 8000
    
    deploy:		# Deploy
    	rm -rf dist $(PREFIX_PKG)*
    	rm -rf polls.dist
    	cd polls && python3 setup.py sdist
    	mkdir polls.dist && mv polls/dist/* polls/$(PREFIX_PKG)* polls.dist
    
    install_bootstrap:	# Install Bootstrap Library
    	cd .. && yarn add bootstrap
    	rm -rf  polls/static/bootstrap
    	mkdir   polls/static/bootstrap
    	cp -R ../node_modules/bootstrap/dist/* polls/static/bootstrap
    
    install_jquery:		# Install jQuery Library
    	cd .. && yarn add jquery
    	rm -rf polls/static/jquery
    	mkdir  polls/static/jquery
    	cp ../node_modules/jquery/dist/* polls/static/jquery
    
    install_bootstrap_from_source:	# Install Bootstrap from Source
    	mkdir -p install && \
    	wget https://github.com/twbs/bootstrap/releases/download/v4.1.3/bootstrap-4.1.3-dist.zip -O install/bootstrap-4.1.3-dist.zip && \
    	unzip install/bootstrap-4.1.3-dist.zip -d polls/static/bootstrap/4.1.3

    Scrimba: JavaScript Advent Calendar

    Solutions for the Scrimba JavaScriptmas Challenge

    Day 24: Test Your Agility!

    var pushed = false //Has the stop button been pushed - false is default
    var targetInt; //The target number to stop the wheel on
    var spinningElem = document.getElementById('spinning'); //The spinning number
    
    
    const maxLoop = 10;
    const waitTime = 250;
    
    //event listener
    document.getElementById("buttonPressed").addEventListener("click", buttonPressed);
    
    //When the stop button is pushed
    function buttonPressed(){
        pushed = true;
    }
    
    //set the target Int
    function setTargetInt(){
        var targetElem = document.getElementById('targetNum');
        targetInt=Math.floor(Math.random() * (maxLoop + 1))
        targetElem.innerHTML = targetInt;
    }
    
    //sleep const
    const sleep = (milliseconds) => {
      return new Promise(resolve => setTimeout(resolve, milliseconds))
    }
    
    
    var curr_spin=-1;
    
    //EDIT THIS FUNCTION
    const spin = async () => {
        var spinningElem = document.getElementById('spinning');
        for(let spin=0; spin < maxLoop; spin++) {
            spinningElem.innerHTML = spin;
            console.log('spin:', spin, targetInt, curr_spin)
    
            if (!pushed) {
                //
            } else if (spin == targetInt) {
                console.log('Button pushed and won')
                 var result = document.getElementById('result');
                result.innerText = "WON!";
                spin = maxLoop+1;
            } else {
                console.log('Buitton pushed but missed')
                spin = maxLoop+1;
            }
            
            await sleep(waitTime)
        }
    }
    
    //main
    setTargetInt();
    spin();

    Day 23: Social media input challenge

    <html>
        <head>
            <link rel="stylesheet" href="index.css">
        </head>
        <body>
            <div class="container">
                <textarea type="text" id="tweet" placeholder="Type in the box"></textarea>
                <div id="counterFooter">140/140</div>
                <button id="btn"><h2>Tweet</h2></button>
            </div>
            <script src="index.js"></script>
        </body>
    </html>
    body{
      background-color: #023F6A;
      font-family: sans-serif;
    }
    .container{
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;
    }
    textarea{
      width:50%;
      height: 30vh;
      background-color: rgb(21, 32, 43);
      color: #fff;
      border-radius:10px;
    }
    textarea::placeholder{
        color:#fff;
    }
    #counterFooter {
      margin-top: 0.2rem;
      font-size:0.8rem;
      color: #fff;
      margin-left:30%;
      font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    }
    button{
      width:50%;
      background-color: rgb(29, 161, 242);
      border-radius: 10px;
      padding: 0 10%;
    }
    .tweetRed {
        color: red;
    }
    button h2{
        color: #fff;
    }
    .buttonDisabled {
       opacity: .5;
       cursor: default;
    }
    const counter = document.getElementById('counterFooter');
    const button = document.getElementById('btn');
    function updateState() {
        const nrOfChar = 140 - tweet.value.length;
        
        counter.innerText = nrOfChar + '/140';
        
        (nrOfChar < 20) ? tweet.classList.add("tweetRed") : tweet.classList.remove("tweetRed");    
        (nrOfChar <  0) ? btn.classList.add("buttonDisabled") : btn.classList.remove("buttonDisabled");
    }
    function handleKeyUp(event) {
        updateState()
    }
    tweet.addEventListener('keyup', handleKeyUp);
    updateState()

    Day 22: Extract Matrix Column

    function extractMatrixColumn(matrix, column) {
        let extracted = []
        
        matrix.forEach( row => {
            extracted.push(row[column])
        })
        
        return extracted;
    }
    function extractMatrixColumn(matrix, column) {   
        return matrix.map( row => row[column])
    }

    Day 21: Sum of 2

    function sumOfTwo(nums1, nums2, value) {
        let result = false;
        
        nums1.forEach( num1 => {
            nums2.forEach( num2 => {            
                if ( (num1 + num2) === value) {
                    result = true;
                }
            })
        })
        return result;
    }

    Day 20: Domain Type

    function domainType(domains) {
        map = { "org": "organization",
                "com": "commercial", 
                "net": "network",
                "info":  "information"
        }
        
        types=[]    
        
        domains.forEach( (domain) => {
            last = domain.split('.').slice(-1)[0]
            
            types.push(map[last])
        })
        
        return types
    }

    Day 19: Alpahabet Subsequence

    function alphabetSubsequence(str) {
        chars = str.split("")
        
        result=true
        for(pos=1; pos<chars.length; pos++) {       
            result = result && chars[pos-1] < chars[pos] 
        }
        return result
    }

    Day 18: Array previous less

    function arrayPreviousLess(nums) {
        let result = [];
        let temp=[];
        
        for(indx=0; indx < nums.length; indx++) {
            curr = nums[indx];
            
            // Build array with previous values
            temp.push(curr)
            
            // Find previous values less than current value
            mins = temp.filter( (val) => val < curr)
            
            // Return value at most right
            max = (mins.length == 0) ? -1 : max = mins[mins.length-1];
            
            result.push(max);
        }
        
        return result;
    }

    Day 17: Different symbols naive

    function differentSymbolsNaive(str) {
        m={};
        
        n=0;
        str.split("").sort().forEach( (c) => {                
            if (! (c in m)) n++;
            m[c]=1;
        });
            
        return n
    }

    Day 16: Insert dashes

    function insertDashes(arr) {
        return arr.split(" ").map( part =>part.split("").join("-") ).join(" ");
    }

    Day 15: Javascript Carousel

    function getIndexOfCurrent(tags) {
    	let result = -1;
    	tags.forEach((node, indx) => {
    		if (node.classList.contains('curr')) {
    			result = indx;
    		}
    	});
    	return result;
    }
    function move(dir) {
    	var tags = Array.prototype.slice.call(
    		document.getElementsByClassName('card')
    	);
    	let curr = getIndexOfCurrent(tags);
    	if (dir === 'prev') {
    		next = curr === 0 ? 0 : curr - 1;
    	}
    	if (dir === 'next') {
    		next = curr === tags.length - 1 ? tags.length - 1 : curr + 1;
    	}
    	tags[curr].setAttribute('class', 'card last');
    	tags[next].setAttribute('class', 'card curr');
    }
    <html>
        <head>
            <link rel="stylesheet" href="index.css">
        </head>
        <body>
            
            <div class="container">
                <img src="previous.svg" class="previous" alt="previous image">
                <div class="gallery-wrapper">
                    <div class="gallery">
                        <img class="card curr" src="presents.jpg" alt="Christmas Cookies">
                        <img class="card" src="cookies.jpg" alt="Christmas Cookies">
                        <img class="card" src="santa.jpg" alt="Christmas Cookies">
                        <img class="card" src="candycane.jpg" alt="Christmas Cookies">
                        <img class="card" src="reindeer.jpg" alt="Christmas Cookies">
                    </div>
                </div>
                <img src="next.svg" class="next" alt="next image">
            
            </div>
            
            <script src="index.pack.js"></script>
        
        </body>
    </html>
    /*
    Thanks to these fine individuals from Unsplash:
    https://unsplash.com/photos/AmzKuEnr1VY
    https://unsplash.com/photos/eDnJQL21amc
    https://unsplash.com/photos/LXy2DOOxESQ
    https://unsplash.com/photos/KBKHXjhVQVM
    https://unsplash.com/photos/PxM8aeJbzvk
    */
    @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600&display=swap');
    html,
    body {
    	margin: 0;
    	padding: 0;
    	height: 100vh;
    	font-family: 'Playfair Display';
    	display: grid;
    	justify-content: center;
    }
    img {
    	width: 200px;
    }
    .previous,
    .next {
    	width: 35px;
    	justify-self: center;
    	align-self: center;
    	cursor: pointer;
    }
    .previous {
    	opacity: 0.3;
    }
    .container {
    	display: grid;
    	grid-template-columns: 20% 200px 20%;
    	place-content: center;
    }
    .card {
    	width: 100%;
    	display: none;
    }
    .curr {
    	display: block;
    }
    .curr {
    	animation-duration: 5s;
    	animation-name: slideIn;
    }
    .last {
    	animation-duration: 2s;
    	animation-name: slideOut;
    }
    @keyframes slideOut {
    	from { opacity: 1; }
    	to   { opacity: 0; }
    }
    @keyframes slideIn {
    	from { opacity: 0; }
    	to   { opacity: 1; }
    }

    Day 14: Maximal Adjacent Difference

        let result=0, max=0, val1=0, val2=0;
        
        for(pos=0; pos < nums.length; pos++) {
            
            [val1, val2 ] = [ nums[pos], nums[pos+1] ];
            max = (val1 > val2) ? (val1 - val2) : (val2 - val1);
            
            if (max > result) {
                result = max;
            }
        }
         
        return result;
    }

    Day 13: Extract Eacth Kth

    function extractEachKth(nums, kth) {
        return nums.filter( (value, index) => (index+1)
    }

    Day 12: Valid Time

    Number.prototype.between = function(min, max) {
      return this > min && this < max;
    };
    function validTime(str) {
        const parts = str.split(':')
       
        return parseInt(parts[0]).between(0,24) && parseInt(parts[1]).between(0,59);
    }

    Day 11: Avoid Obstacles

    function avoidObstacles(nums) {
        const sorted = nums.sort();
            
        for(num=2; num <= sorted[nums.length-1]; num++) {
            match = nums.filter((val) => (val
            if (match.length === 0) {
                return num;
            }
        }
    }

    Day 10: Adjacent Elements Product

    function adjacentElementsProduct(nums) {
        let result = 0;
        
        for (i1 = 0; i1 < nums.length-1; ++i1) {
            const product=nums[i1]*nums[i1+1];
                     
            if (product > result) {
                result = product;
            }
        }
        
        return result;
    }

    Day 09: Sum Odd Fibonacci Numbers

    function sumOddFibonacciNumbers(num) {
        let fibPrev=1;
        let fib=0;
        let sum=0;
        
        while (fibPrev < num) {       
            [ fibPrev, fib ] = [fib + fibPrev, fibPrev];
            
            if (fib
                sum += fib;
            }
        }
        
        return sum;
    }

    Day 08: Rolling Dice

    <html>
        <head>
            <link rel="stylesheet" href="style.css">
            <script src="index.pack.js"></script>
        </head>
        <body>
            <div class="dice">
                <div class="dot dot_ dot2 dot_ dot4 dot5 dot6"></div>
                <div class="dot dot_ dot_ dot_ dot_ dot_ dot_"></div>
                <div class="dot dot_ dot_ dot3 dot4 dot5 dot6"></div>
                
                <div class="dot dot_ dot_ dot_ dot_ dot_ dot6"></div>
                <div class="dot dot1 dot_ dot3 dot_ dot5 dot_"></div>
                <div class="dot                          dot6"></div>
                
                <div class="dot dot_ dot_ dot3 dot4 dot5 dot6"></div>
                <div class="dot"></div>
                <div class="dot dot_ dot2 dot_ dot4 dot5 dot6"></div>
            </div>
            
        </body>
    </html>
    function clear_dice() {
        document.querySelectorAll('.dot').forEach( (dot) => dot.style.visibility = "hidden")
    }
    function roll_dice() {
        const roll = 1 + Math.floor(Math.random() * 6);
        
        console.log(roll);
        clear_dice()
        document.querySelectorAll('.dot'+roll).forEach( (dot) => {
            dot.style.visibility = "visible"
        })
    }
    clear_dice()
    document.getElementsByClassName('dice')[0].addEventListener('click', roll_dice)
    body {
        background-color: #AEB8FE;
        display: flex;
    }
    .dice {
        width: 230px;
        height: 230px;
        border-radius: 10px;
        background-color: #EFE5DC;
        margin: 100px;
        
        display: grid;
        grid-template-columns: repeat(3, 40px);
        gap: 20px;
        grid-auto-rows: minmax(40px, 40px);
    }
    .dot {
        width: 40px;
        height: 40px;
        border-radius: 15px;
        background-color: black;
        margin: 35px;
        
        visibility: hidden
    }
    .dot1 { visibility: none; }
    .dot2 { visibility: none; }
    .dot3 { visibility: none; }
    .dot4 { visibility: none; }
    .dot5 { visibility: none; }
    .dot6 { visibility: none; }

    Day 07: Count Vowel Consonant

    function countVowelConsonant(str) {
      function reducer(s,c) {
          return s + ("aeiou".includes(c) ? 1 : 2)
      };
        
      return str.split("").reduce(reducer, 0)
    }

    Day 06: Sort By Length

    function sortByLength(strs) {    
        return strs.sort(function(arg1, arg2) { return arg1.length - arg2.length })
    }

    Day 05: Reverse String

    function reverseAString(str) {   
        return str.split("").reverse().join("")
    }

    Day 04: Century From Year

    function centuryFromYear(num) {
        return Math.floor((num + 99) / 100)
    }

    Day 03: Chunky Monkey

    function chunkyMonkey(values, size) {
        result = [];
        pos = 0;
        
        while (pos < values.length) {
            result.push(values.slice(pos, pos+size));
            pos += size;
        }
        
        return result;
    }

    Day 02: Deposit Profit

    function depositProfit(deposit, rate, threshold) {
        let profit = deposit;
        let years = 0
        while (profit < threshold) {
            profit += profit * 0.01 * rate;
            years++;
        }
        
        return years;
    }

    Day 01: Candies

    https://scrimba.com/learn/adventcalendar/note-at-0-00-coef0430d83cd2a72113bbf09

    function candies(children, candy) {
        return children * Math.floor(candy / children)//  write code here.
    }

    Keycloak | Getting Started

    Introduction

    Keycloak – Open Source Identity and Access Management for Modern Applications and Services

    Installation

    Java

    Using AdoptOpenJDK8

    brew info adoptopenjdk8

    Eclipse

    Keycloak

    WildFly

    See Also

    Troubleshooting

    Change Server Port

    By default, executing standalone.sh (for Linux) / standalone.bat (for Windows) makes the server available at following urls:

    https://localhost:8443/auth/
    http://localhost:8080/auth/
    

    If we need to run multiple instance of server at different ports, run the following command:

    For Linux:

    standalone.sh -Djboss.socket.binding.port-offset=100
    

    For Windows:

    standalone.bat -Djboss.socket.binding.port-offset=100
    

    The above commands will add the offset of 100 to the defaults ports available for Keycloak server.

    For example, previously the ports where 8443 and 8000. Now the new ports become 8443 + 100 = 8543 and 8080 + 100 = 8180.

    References:

    https://www.keycloak.org/docs/3.2/getting_started/topics/secure-jboss-app/before.html

    Location of Server Logfile

    \standalone\log

    Additional Readings

    Maven | Getting Started

    Introduction

    Installation

    Install on macOS

    $ brew install maven

    Maven Commands

    Maven Commands

    Let’s look into some popular and must know maven commands. We will use a sample Maven project to showcase the command output.

    1. mvn clean

    This command cleans the maven project by deleting the target directory. The command output relevant messages are shown below.

    $ mvn clean
    ...
    [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maven-example-jar ---
    [INFO] Deleting /Users/pankaj/Desktop/maven-examples/maven-example-jar/target
    ...
    2. mvn compiler:compile

    This command compiles the java source classes of the maven project.

    $ mvn compiler:compile
    ...
    [INFO] --- maven-compiler-plugin:3.8.1:compile (default-cli) @ maven-example-jar ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 1 source file to /Users/pankaj/Desktop/maven-examples/maven-example-jar/target/classes
    ...

    3. mvn compiler:testCompile

    This command compiles the test classes of the maven project.

    $ mvn compiler:testCompile
    ...
    [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-cli) @ maven-example-jar ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 1 source file to /Users/pankaj/Desktop/maven-examples/maven-example-jar/target/test-classes
    ...

    4. mvn package

    This command builds the maven project and packages them into a JAR, WAR, etc.

    $ mvn package
    ...
    [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ maven-example-jar ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 1 source file to /Users/pankaj/Desktop/maven-examples/maven-example-jar/target/classes
    ...
    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    Running com.journaldev.maven.classes.AppTest
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 sec
    
    Results :
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
    
    [INFO] 
    [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ maven-example-jar ---
    [INFO] Building jar: .../maven-examples/maven-example-jar/target/maven-example-jar-0.0.1-SNAPSHOT.jar
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    ...
    

    The output shows the location of the JAR file just before the “BUILD SUCCESS” message. Notice the package goal executes compile, testCompile, and test goals before packaging the build.

    5. mvn install

    This command builds the maven project and installs the project files (JAR, WAR, pom.xml, etc) to the local repository.

    $ mvn install
    ...
    [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-example-jar ---
    ...
    [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ maven-example-jar ---
    ...
    

    6. mvn deploy

    This command is used to deploy the artifact to the remote repository. The remote repository should be configured properly in the project pom.xml file distributionManagement tag. The server entries in the maven settings.xml file is used to provide authentication details.

    7. mvn validate

    This command validates the maven project that everything is correct and all the necessary information is available.

    8. mvn dependency:tree

    This command generates the dependency tree of the maven project.

    $ mvn dependency:tree
    ...
    [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ Mockito-Examples ---
    [INFO] com.journaldev.mockito:Mockito-Examples:jar:1.0-SNAPSHOT
    [INFO] +- org.junit.platform:junit-platform-runner:jar:1.2.0:test
    [INFO] |  +- org.apiguardian:apiguardian-api:jar:1.0.0:test
    [INFO] |  +- org.junit.platform:junit-platform-launcher:jar:1.2.0:test
    ...
    

    9. mvn dependency:analyze

    This command analyzes the maven project to identify the unused declared and used undeclared dependencies. It’s useful in reducing the build size by identifying the unused dependencies and then remove it from the pom.xml file.

    $ mvn dependency:analyze
    ...
    [INFO] --- maven-dependency-plugin:2.8:analyze (default-cli) @ Mockito-Examples ---
    [WARNING] Used undeclared dependencies found:
    [WARNING]    org.junit.jupiter:junit-jupiter-api:jar:5.2.0:test
    [WARNING]    org.mockito:mockito-core:jar:2.19.0:test
    [WARNING] Unused declared dependencies found:
    [WARNING]    org.junit.platform:junit-platform-runner:jar:1.2.0:test
    ...

    10. mvn archetype:generate

    Maven archetypes is a maven project templating toolkit. We can use this command to generate a skeleton maven project of different types, such as JAR, web application, maven site, etc.

    Recommended Reading: Creating a Java Project using Maven Archetypes

    11. mvn site:site

    This command generates a site for the project. You will notice a “site” directory in the target after executing this command. There will be multiple HTML files inside the site directory that provides information related to the project.

    12. mvn test

    This command is used to run the test cases of the project using the maven-surefire-plugin.

    $ mvn test
    ...
    [INFO] --- maven-surefire-plugin:2.22.0:test (default-test) @ Mockito-Examples ---
    [INFO] 
    [INFO] -------------------------------------------------------
    [INFO]  T E S T S
    [INFO] -------------------------------------------------------
    [INFO] Running TestSuite
    first-element
    second-element
    Employee setName Argument = Pankaj
    ...
    [INFO] Results:
    [INFO] 
    [INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
    ...

    13. mvn compile

    It’s used to compile the source Java classes of the project.

    $ mvn compile
    ...
    [INFO] --- maven-compiler-plugin:3.7.0:compile (default-compile) @ Mockito-Examples ---
    [INFO] Changes detected - recompiling the module!
    [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
    [INFO] Compiling 10 source files to /Users/pankaj/Desktop/maven-examples/Mockito-Examples/target/classes
    ...

    14. mvn verify

    This command build the project, runs all the test cases and run any checks on the results of the integration tests to ensure quality criteria are met.

    Maven Options

    Maven provides a lot of command-line options to alter the maven build process. Let’s look at some of the important maven options.

    15. mvn -help

    This command prints the maven usage and all the available options for us to use.

    16. mvn -f maven-example-jar/pom.xml package

    This command is used to build a project from a different location. We are providing the pom.xml file location to build the project. It’s useful when you have to run a maven build from a script.

    17. mvn -o package

    This command is used to run the maven build in the offline mode. It’s useful when we have all the required JARs download in the local repository and we don’t want Maven to look for any JARs in the remote repository.

    18. mvn -q package

    Runs the maven build in the quiet mode, only the test cases results and errors are displayed.

    $ mvn -q package         
    
    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    Running com.journaldev.maven.classes.AppTest
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 sec
    
    Results :
    
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

    19. mvn -X package

    Prints the maven version and runs the build in the debug mode. It’s opposite of the quiet mode and you will see a lot of debug messages in the console.

    mvn -X Debug Mode

    20. mvn -v

    Used to display the maven version information.

    $ mvn -v
    Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
    Maven home: /Users/pankaj/Downloads/apache-maven-3.6.3
    Java version: 13.0.1, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk-13.0.1.jdk/Contents/Home
    Default locale: en_IN, platform encoding: UTF-8
    OS name: "mac os x", version: "10.15.1", arch: "x86_64", family: "mac"

    21. mvn -V package

    This command prints the maven version and then continue with the build. It’s equivalent to the commands mvn -v;mvn package.

    22. mvn -DskipTests package

    The skipTests system property is used to skip the unit test cases from the build cycle. We can also use -Dmaven.test.skip=true to skip the test cases execution.

    23. mvn -T 4 package

    This command tells maven to run parallel builds using the specified thread count. It’s useful in multiple module projects where modules can be built in parallel. It can reduce the build time of the project.

    See also

    Trobleshooting

    Could not find or load main class org.apache.maven.wrapper.MavenWrapperMain

    You’re missing the .mvn folder in your git repository. You should have a folder called .mvn which contains the following files

    • wrapper/maven-wrapper.jarwrapper/maven-wrapper.properties
    • jvm.config.

    Try doing git add -f .mvn from the command line then commit and push.

    Here is how to create .mvn directory with needed jar inside.

    mvn -N io.takari:maven:wrapper

    We can also specify the version of Maven:

    mvn -N io.takari:maven:wrapper -Dmaven=3.5.2

    Java | Getting Started

    Introduction

    Installation

    Install on macOS

    $ brew tap adoptopenjdk/openjdk

    Java 8

    # Install Java 8
    $ brew cask install adoptopenjdk8
    
    # Install Java 11
    $ brew cask install adoptopenjdk11
    # Install Gradle
    $ brew install gradle
    
    # Install Maven
    $ brew install maven

    Managing different Version

    Install jenv and setup

    $ brew install jenv

    Add java home folder to jenv

    $ jenv add /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
    $ jenv add /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home

    Spring Boot | Getting Started

    Introduction

    Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed, simplicity, and productivity has made it the world’s most popular Java framework.

    Required Software

    Java

    Spring recommend AdoptOpenJDK version 8 or version 11.

    Oracle AdoptOpenJDK OpenJDK

    Eclipse

    Eclipse IDE for Enterprise Java Developers

    Sprint Tools | 4

    Spring Tools 4 for Eclipse

    Create a new App from CLI

    Create a starter app using spring.io from the commandline

    $ curl https://start.spring.io/starter.zip -d language=java -d dependencies=web,mustache,jpa,h2,devtools -d packageName=com.example.blog -d name=Blog -o blog.zip

    Working with Maven

    Build App./mvnw clean package
    Run App./mvnw spring-boot:run

    Tipps and Tricks

    Change App Port Number

    Add line to file src/main/resources/application.properties

    server.port=9010

    Learning Path

    Start with the following tutorials / guides:

    See Also