Sunday, December 31, 2017

How to create PDF downloader in CakePHP3

1.  How to create uploader in CakePHP3
2.  How to create downloader in CakePHP3
3.  How to create PDF downloader in CakePHP3

At first, we need to install libreoffice for the PDF conversion and language packs if needed. (Without language packs, the PDF can not display the language properly.)
$ sudo yum install libreoffice libreoffice-langpack-fr

And edit the sudo command by "visudo" and add "apache ALL=(ALL) NOPASSWD: /usr/bin/libreoffice" in the file:
$ sudo visudo
(Add "apache ALL=(ALL) NOPASSWD: /usr/bin/libreoffice" in the file)

Restart apache:
$ sudo service httpd restart

Add the following function in index.ctp:
    function downloadPdf(){
        var files = getSelectedDocuments(true);
        files_not_excel = files.files_notexcel;
        files = files.checkeditems;
        console.log(files);
        if(files.length < 1 || files === undefined){
            alert('Please select at least one Excel document.');
        } else {
            if(files_not_excel.length > 0 && files_not_excel !== undefined){
                var r = confirm("The following files will not be converted to PDF because they are not excel files:\n"
                + files_not_excel.join(', '));
            } else {
                var r = true;
            }
            if(r === true){
                var csrfToken = $('[name=_csrfToken]').val();
                $.ajax({
                    type: "POST",
                    url: '<?= $this->Url->build(array("controller" => "Customers", "action" => "pdfDownload")); ?>',
                    data: {'files' : files},
                    beforeSend: function(xhr){
                        xhr.setRequestHeader('X-CSRF-Token', csrfToken);
                    },
                    success: function(data){
                        var url = '/cake/temp/zip/' + data;
                        console.log(url);
                        download(url);
                    }
                });
            }
        }
    }

And a button to download the PDF files:
<p><a href="#" onclick="downloadPdf()">Download As PDF</a></p>

The index.ctp as a whole:
<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Customer[]|\Cake\Collection\CollectionInterface $customers
 */
?>
<nav class="large-3 medium-4 columns" id="actions-sidebar">
    <ul class="side-nav">
        <li class="heading"><?= __('Actions') ?></li>
        <li><?= $this->Html->link(__('New Customer'), ['action' => 'add']) ?></li>
    </ul>
</nav>
<div class="customers index large-9 medium-8 columns content">
    <h3><?= __('Customers') ?></h3>
    <p><a href="#" onclick="downloadDocuments()">Download As Zip</a></p>
    <p><a href="#" onclick="downloadPdf()">Download As PDF</a></p>
    <table cellpadding="0" cellspacing="0">
        <thead>
            <tr>
                <th scope="col" class="text-center" style="vertical-align: middle;"><?= $this->Form->checkbox('check_all', ['id'=>'check_all']);?></th>
                <th scope="col"><?= $this->Paginator->sort('id') ?></th>
                <th scope="col"><?= $this->Paginator->sort('name') ?></th>
                <th scope="col"><?= $this->Paginator->sort('file_location', 'File') ?></th>
                <th scope="col"><?= $this->Paginator->sort('created') ?></th>
                <th scope="col"><?= $this->Paginator->sort('modified') ?></th>
                <th scope="col" class="actions"><?= __('Actions') ?></th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($customers as $customer): ?>
            <tr>
                <?php $temp = explode('.', $customer->file_location); $extension = strtolower(end($temp)); ?>
                <?php $file = ['file_location' => $customer->file_location, 'file_name'=>$customer->name, 'extension'=>$extension] ?>
                <td class="text-center"><?= $this->Form->checkbox('checkbox',['name'=>'check_box', 'value'=>h(json_encode($file))]) ?></td>
                <td><?= $this->Number->format($customer->id) ?></td>
                <td><?= h($customer->name) ?></td>
                <td><img src="/cake/uploads/customers/<?= $customer->file_location ?>" width="100" /></td>
                <td><?= h($customer->created) ?></td>
                <td><?= h($customer->modified) ?></td>
                <td class="actions">
                    <?= $this->Html->link(__('View'), ['action' => 'view', $customer->id]) ?>
                    <?= $this->Html->link(__('Edit'), ['action' => 'edit', $customer->id]) ?>
                    <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $customer->id], ['confirm' => __('Are you sure you want to delete # {0}?', $customer->id)]) ?>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
    <div class="paginator">
        <ul class="pagination">
            <?= $this->Paginator->first('<< ' . __('first')) ?>
            <?= $this->Paginator->prev('< ' . __('previous')) ?>
            <?= $this->Paginator->numbers() ?>
            <?= $this->Paginator->next(__('next') . ' >') ?>
            <?= $this->Paginator->last(__('last') . ' >>') ?>
        </ul>
        <p><?= $this->Paginator->counter(['format' => __('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')]) ?></p>
    </div>
</div>
<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
<script>
    $(document).ready(function(){
        $('#check_all').on('click', function() {
            $('input:checkbox[name=check_box]').prop('checked', this.checked);
        });
    });
    function getSelectedDocuments(pdfonly){
        if (pdfonly === undefined) {
            pdfonly = false;
        }
        var checkeditems = [];
        var files_notexcel = [];
        $("input[type=checkbox][name=check_box]").each(function(){
            if (this.checked) {
                var value = JSON.parse($(this).val());
                if(pdfonly === true){
                    var extension = value.extension.toLowerCase();
                    if($.inArray(extension, ['xlsx','xlsm','xls']) !== -1){
                        checkeditems.push(value.file_location);
                    } else {
                        var name = value.file_location.split("/");
                        files_notexcel.push(name[name.length - 1]);
                    }
                } else {
                    checkeditems.push(value.file_location);
                }
            }
        });
        var files = {"checkeditems" : checkeditems, "files_notexcel" : files_notexcel};
        return files;
    }
    function download(url){
        var link=document.createElement('a');
        document.body.appendChild(link);
        link.href=url;
        link.download = 'download';
        link.click();
        link.parentNode.removeChild(link);
    }
    function downloadDocuments(){
        var checkeditems = getSelectedDocuments();
        checkeditems = checkeditems.checkeditems;
        if(checkeditems.length < 1 || checkeditems === undefined){
            alert('Please select at least one document.');
        } else {
            var csrfToken = $('[name=_csrfToken]').val();
            $.ajax({
                type: "POST",
                url: '<?= $this->Url->build(array("controller" => "Customers", "action" => "zipDownload")); ?>',
                data: {'files' : checkeditems},
                beforeSend: function(xhr){
                    xhr.setRequestHeader('X-CSRF-Token', csrfToken);
                },
                success: function(data){
                    var url = '/cake/temp/zip/'+data;
                    console.log(url);
                    download(url);
                }
            });
        }
    }
    function downloadPdf(){
        var files = getSelectedDocuments(true);
        files_not_excel = files.files_notexcel;
        files = files.checkeditems;
        console.log(files);
        if(files.length < 1 || files === undefined){
            alert('Please select at least one Excel document.');
        } else {
            if(files_not_excel.length > 0 && files_not_excel !== undefined){
                var r = confirm("The following files will not be converted to PDF because they are not excel files:\n"
                + files_not_excel.join(', '));
            } else {
                var r = true;
            }
            if(r === true){
                var csrfToken = $('[name=_csrfToken]').val();
                $.ajax({
                    type: "POST",
                    url: '<?= $this->Url->build(array("controller" => "Customers", "action" => "pdfDownload")); ?>',
                    data: {'files' : files},
                    beforeSend: function(xhr){
                        xhr.setRequestHeader('X-CSRF-Token', csrfToken);
                    },
                    success: function(data){
                        var url = '/cake/temp/zip/' + data;
                        console.log(url);
                        download(url);
                    }
                });
            }
        }
    }
</script>

Change customers controller and add the following functions:
    function createZip($uploadToDir = null, $copyFromDir = null, $files = null, $containsYearmonth = false){
        if(!file_exists($uploadToDir)){
            if(!mkdir($uploadToDir, 0755, true)){
                throw new BadRequestException('Can not mkdir.');
            }
            chmod($uploadToDir, 0755);
        }
        try {
            $zip = new ZipArchive();
            $zip_file_name = sha1(time() . rand()).".zip";
            $zip_name = $uploadToDir.$zip_file_name; // Zip name
            $zip->open($zip_name,  ZipArchive::CREATE);
            foreach ($files as $file) {
                $path = $copyFromDir.$file;
                if(file_exists($path)){
                    $zip->addFromString(basename($path), file_get_contents($path));
                }
                else{
                    echo "file does not exist";
                }
            }
            $zip->close();
            return $zip_file_name;
        } catch (BadRequestException $e) {
            throw $e;
        }
    }

    public function pdfDownload(){
        if ($this->request->is('ajax')) {
            $files = $this->request->data['files'];
            $layout = 'ajax';
            $this->loadModel('Documents');
            $this->autoRender = false;
            try {
                $filesPdf = [];
                $uploadToDirPdf = WWW_ROOT.'temp/zip/';
                $copyFromDirPdf = WWW_ROOT.'uploads/customers/';
                $uploadToDirZip = WWW_ROOT.'temp/zip/';
                $copyFromDirZip = WWW_ROOT;
                foreach($files as $file){
                    $extension = $this->getExtension($file);
                    if(in_array($extension,array_map('strtolower',['xlsx','xlsm','xls']))){
                        $pathPdf = $this->excelToPdf($uploadToDirPdf, $copyFromDirPdf, $file);
                        array_push($filesPdf, $pathPdf);
                    }
                }
                $pathZip = $this->createZip($uploadToDirZip, $copyFromDirZip, $filesPdf);
            }catch(BadRequestException $b){
                //$this->Flash->error(__($b->getMessage()));
            }
            echo $pathZip;
        }
    }

    private function excelToPdf($uploadToDir = null, $copyFromDir = null, $file = null){
        $filePath = explode("/", $file);
        $today = new Date();
        $yearmonth = $today->i18nFormat('yyyyMM');
        $dir = $uploadToDir.$filePath[0];
        if(!file_exists($dir)){
            if(!mkdir($dir, 0755, true)){
                throw new BadRequestException('Couldn\'t make directory.');
            }
            chmod($dir, 0755);
        }
        $command = 'sudo /usr/bin/libreoffice --headless --invisible --norestore --nologo --nofirststartwizard --convert-to pdf --outdir "'
        .$uploadToDir.$filePath[0].'" "'
        .$copyFromDir.$file.'"';
        $result = shell_exec($command);
        if(is_null($result)){
            throw new BadRequestException('pdf couldn\'t be created.');
        }
        $filenameArray = explode(".", $file);
        $filename = "";
        for($i = 0; $i < count($filenameArray)-1; $i++){
            $filename .= $filenameArray[$i];
        }
        return 'temp/zip/'.$filename.'.pdf';
    }

Now you can download excel files as PDF from index.ctp:



How to create downloader in CakePHP3

1.  How to create uploader in CakePHP3
2.  How to create downloader in CakePHP3
3.  How to create PDF downloader in CakePHP3

Add these functions in the "customers" controller:
(Please note that you need to add these lines also at the top of the controller:
use Cake\I18n\Date;
use ZipArchive;
use Cake\Network\Exception\BadRequestException;
)
    private function getExtension($filename){
        $temp = explode('.', $filename);
        $extension = strtolower(end($temp));
        return $extension;
    }

    public function zipDownload(){
        if ($this->request->is('ajax')) {
            $layout = 'ajax';
            $this->autoRender = false;
            $dir = WWW_ROOT . 'temp/zip/';
            $copyFromDir = WWW_ROOT.'uploads/customers/';
            $files = $this->request->data['files'];
            try {
                echo $this->createZip($dir, $copyFromDir, $files);
            } catch (BadRequestException $e) {
                throw $e;
            }
        }
    }

    function createZip($uploadToDir = null, $copyFromDir = null, $files = null, $containsYearmonth = false){
        if(!file_exists($uploadToDir)){
            if(!mkdir($uploadToDir, 0755, true)){
                throw new BadRequestException('Can not mkdir.');
            }
            chmod($uploadToDir, 0755);
        }
        try {
            $zip = new ZipArchive();
            $zip_file_name = sha1(time() . rand()).".zip";
            $zip_name = $uploadToDir.$zip_file_name; // Zip name
            $zip->open($zip_name,  ZipArchive::CREATE);
            foreach ($files as $file) {
                $path = $copyFromDir.$file;
                if(file_exists($path)){
                    $zip->addFromString(basename($path), file_get_contents($path));
                }
                else{
                    echo "file does not exist";
                }
            }
            $zip->close();
            return $zip_file_name;
        } catch (BadRequestException $e) {
            throw $e;
        }
    }

And change "index.ctp" in the "Template" folder like this:
<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Customer[]|\Cake\Collection\CollectionInterface $customers
 */
?>
<nav class="large-3 medium-4 columns" id="actions-sidebar">
    <ul class="side-nav">
        <li class="heading"><?= __('Actions') ?></li>
        <li><?= $this->Html->link(__('New Customer'), ['action' => 'add']) ?></li>
    </ul>
</nav>
<div class="customers index large-9 medium-8 columns content">
    <h3><?= __('Customers') ?></h3>
    <div><a href="#" onclick="downloadDocuments()">Download As Zip</a></div>
    <table cellpadding="0" cellspacing="0">
        <thead>
            <tr>
                <th scope="col" class="text-center" style="vertical-align: middle;"><?= $this->Form->checkbox('check_all', ['id'=>'check_all']);?></th>
                <th scope="col"><?= $this->Paginator->sort('id') ?></th>
                <th scope="col"><?= $this->Paginator->sort('name') ?></th>
                <th scope="col"><?= $this->Paginator->sort('file_location', 'File') ?></th>
                <th scope="col"><?= $this->Paginator->sort('created') ?></th>
                <th scope="col"><?= $this->Paginator->sort('modified') ?></th>
                <th scope="col" class="actions"><?= __('Actions') ?></th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($customers as $customer): ?>
            <tr>
                <?php $temp = explode('.', $customer->file_location); $extension = strtolower(end($temp)); ?>
                <?php $file = ['file_location' => $customer->file_location, 'file_name'=>$customer->name, 'extension'=>$extension] ?>
                <td class="text-center"><?= $this->Form->checkbox('checkbox',['name'=>'check_box', 'value'=>h(json_encode($file))]) ?></td>
                <td><?= $this->Number->format($customer->id) ?></td>
                <td><?= h($customer->name) ?></td>
                <td><img src="/cake/uploads/customers/<?= $customer->file_location ?>" width="100" /></td>
                <td><?= h($customer->created) ?></td>
                <td><?= h($customer->modified) ?></td>
                <td class="actions">
                    <?= $this->Html->link(__('View'), ['action' => 'view', $customer->id]) ?>
                    <?= $this->Html->link(__('Edit'), ['action' => 'edit', $customer->id]) ?>
                    <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $customer->id], ['confirm' => __('Are you sure you want to delete # {0}?', $customer->id)]) ?>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
    <div class="paginator">
        <ul class="pagination">
            <?= $this->Paginator->first('<< ' . __('first')) ?>
            <?= $this->Paginator->prev('< ' . __('previous')) ?>
            <?= $this->Paginator->numbers() ?>
            <?= $this->Paginator->next(__('next') . ' >') ?>
            <?= $this->Paginator->last(__('last') . ' >>') ?>
        </ul>
        <p><?= $this->Paginator->counter(['format' => __('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')]) ?></p>
    </div>
</div>
<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
<script>
    $(document).ready(function(){
        $('#check_all').on('click', function() {
            $('input:checkbox[name=check_box]').prop('checked', this.checked);
        });
    });
    function getSelectedDocuments(pdfonly){
        if (pdfonly === undefined) {
            pdfonly = false;
        }
        var checkeditems = [];
        var files_notexcel = [];
        $("input[type=checkbox][name=check_box]").each(function(){
            if (this.checked) {
                var value = JSON.parse($(this).val());
                if(pdfonly === true){
                    var extension = value.extension.toLowerCase();
                    if($.inArray(extension, ['xlsx','xlsm','xls']) !== -1){
                        checkeditems.push(value.file_location);
                    } else {
                        var name = value.file_location.split("/");
                        files_notexcel.push(name[name.length - 1]);
                    }
                } else {
                    checkeditems.push(value.file_location);
                }
            }
        });
        var files = {"checkeditems" : checkeditems, "files_notexcel" : files_notexcel};
        return files;
    }
    function download(url){
        var link=document.createElement('a');
        document.body.appendChild(link);
        link.href=url;
        link.download = 'download';
        link.click();
        link.parentNode.removeChild(link);
    }
    function downloadDocuments(){
        var checkeditems = getSelectedDocuments();
        checkeditems = checkeditems.checkeditems;
        if(checkeditems.length < 1 || checkeditems === undefined){
            alert('Please select at least one document.');
        } else {
            var csrfToken = $('[name=_csrfToken]').val();
            $.ajax({
                type: "POST",
                url: '<?= $this->Url->build(array("controller" => "Customers", "action" => "zipDownload")); ?>',
                data: {'files' : checkeditems},
                beforeSend: function(xhr){
                    xhr.setRequestHeader('X-CSRF-Token', csrfToken);
                },
                success: function(data){
                    var url = '/cake/temp/zip/'+data;
                    console.log(url);
                    download(url);
                }
            });
        }
    }
</script>

And you can download the files in index.ctp:



Function to make "get" parameters of URL

A function to make "get" parameters of URL.
    function getquery_build(query,name,value){
        query = query + (query.slice(0, 1) == '?' ? '&'+name+'='+value : '?'+name+'='+value);
        return encodeURI(query);
    }

Usage:
query = "";
query = getquery_build(query, 'index1', 'value1');
query = getquery_build(query, 'index2', 'value2');
query = getquery_build(query, 'index3', 'value3');
query = getquery_build(query, 'index4', 'value4');
console.log(query);

The output:
?index1=value1&index2=value2&index2=value2&index2=value2

"For each" method of Javascript

"For each" method of Javascript.


        var some_array = ['aaa', 'bbb', 'ccc'];
        some_array.forEach(function(value, index) {
             console.log("index: " + index);
             console.log("value: " + value);
        });




How to create an uploader with CakePHP3

1.  How to create uploader in CakePHP3
2.  How to create downloader in CakePHP3
3.  How to create PDF downloader in CakePHP3

At first, we will create a table in the database by a migration file below:
<?php
use Migrations\AbstractMigration;

class CreateCustomers extends AbstractMigration
{
    /**
     * Change Method.
     *
     * More information on this method is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-change-method
     * @return void
     */
    public function change()
    {
        $table = $this->table('customers');
        $table->addColumn('name', 'string', [
            'default' => null,
            'limit' => 255,
            'null' => false,
        ]);
        $table->addColumn('description', 'text', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('file_location', 'string', [
            'default' => null,
            'null' => true,
            'limit' => 255,
        ]);
        $table->addColumn('created', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('modified', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->create();
    }
}

Do the following commands for the migrations file:
$ bin/cake migrations migrate
$ bin/cake bake all customers

Now we will create an uploader with html5.
<input type = "file" name="file"/>

If we use $this->Form->input() in "index.ctp" of CakePHP3:
$this->Form->input('file', ['type'=>'file']);

And we will get an uploader like this:


As a whole, the template page (index.ctp) will be like this:
The code:
<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Customer $customer
 */
?>
<nav class="large-3 medium-4 columns" id="actions-sidebar">
    <ul class="side-nav">
        <li class="heading"><?= __('Actions') ?></li>
        <li><?= $this->Html->link(__('List Customers'), ['action' => 'index']) ?></li>
    </ul>
</nav>
<div class="customers form large-9 medium-8 columns content">
    <?= $this->Form->create($customer, ['enctype' => 'multipart/form-data']) ?>
    <fieldset>
        <legend><?= __('Add Customer') ?></legend>
        <?php
            echo $this->Form->control('name');
            echo $this->Form->control('description');
            echo $this->Form->control('file_location', ['type'=>'file']);
        ?>
    </fieldset>
    <?= $this->Form->button(__('Submit')) ?>
    <?= $this->Form->end() ?>
</div>


Now we have a "controller" of  "customers" table because we have used the commad "bake". Open the controller file and edit it to add a "upload" function like this (Please note that you need to add "use Cake\Network\Exception\BadRequestException;" and "use Cake\I18n\Date;" at the top of the file):
    private function upload($file, $directory) {
        $today = new Date();
        $yearmonth = $today->i18nFormat('yyyyMM');
        $directory = $directory . "/" . $yearmonth;
        if (!file_exists($directory)) {
            if (!mkdir($directory, 0755, true)) {
                throw new BadRequestException('Impossible de créer le répertoire.');
            }
            chmod($directory, 0755);
        }
        switch ($file['error']) {
            case 0:
                break;
            case UPLOAD_ERR_OK:
                break;
            case UPLOAD_ERR_NO_FILE:
                throw new BadRequestException('File not found.');
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                throw new BadRequestException('File is too big.');
            default:
                throw new BadRequestException('Unknown error');
        }
        $temp = explode('.', $file["name"]);
        $extension = strtolower(end($temp));
        $randString = sha1(time() . rand());
        $uploadFile = $randString . "." . $extension;
        if (!rename($file["tmp_name"], $directory . "/" . $uploadFile)) {
            throw new BadRequestException('Impossible to move the file.');
        }
        return $yearmonth.'/'.$uploadFile;
    }

And change "add" function too:
    public function add()
    {
        $customer = $this->Customers->newEntity();
        if ($this->request->is('post')) {
            $data = $this->request->getData();
            $data['file_location'] = $this->upload($data['file_location'], WWW_ROOT.'uploads/customers');
            $customer = $this->Customers->patchEntity($customer, $data);
            if ($this->Customers->save($customer)) {
                $this->Flash->success(__('The customer has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The customer could not be saved. Please, try again.'));
        }
        $this->set(compact('customer'));
        $this->set('_serialize', ['customer']);
    }

Now you can upload a file from the "add" page.

The uploaded file is in this directory: \cake\webroot\uploads\customers\201712

We can use the uploaded file like this (This example is "index.ctp"):

<?php foreach ($customers as $customer): ?>
            <tr>
                <td><?= $this->Number->format($customer->id) ?></td>
                <td><?= h($customer->name) ?></td>
                <td><img src="/cake/uploads/customers/<?= $customer->file_location ?>" width="100" /></td>
                <td><?= h($customer->created) ?></td>
                <td><?= h($customer->modified) ?></td>
                <td class="actions">
                    <?= $this->Html->link(__('View'), ['action' => 'view', $customer->id]) ?>
                    <?= $this->Html->link(__('Edit'), ['action' => 'edit', $customer->id]) ?>
                    <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $customer->id], ['confirm' => __('Are you sure you want to delete # {0}?', $customer->id)]) ?>
                </td>
            </tr>
            <?php endforeach; ?>
(Please note that "/cake" out of "/cake/uploads/customers/" might not be needed depending on the setting of your webroot.)

This is the complete code of "index.ctp":
<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Customer[]|\Cake\Collection\CollectionInterface $customers
 */
?>
<nav class="large-3 medium-4 columns" id="actions-sidebar">
    <ul class="side-nav">
        <li class="heading"><?= __('Actions') ?></li>
        <li><?= $this->Html->link(__('New Customer'), ['action' => 'add']) ?></li>
    </ul>
</nav>
<div class="customers index large-9 medium-8 columns content">
    <h3><?= __('Customers') ?></h3>
    <table cellpadding="0" cellspacing="0">
        <thead>
            <tr>
                <th scope="col"><?= $this->Paginator->sort('id') ?></th>
                <th scope="col"><?= $this->Paginator->sort('name') ?></th>
                <th scope="col"><?= $this->Paginator->sort('file_location', 'File') ?></th>
                <th scope="col"><?= $this->Paginator->sort('created') ?></th>
                <th scope="col"><?= $this->Paginator->sort('modified') ?></th>
                <th scope="col" class="actions"><?= __('Actions') ?></th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($customers as $customer): ?>
            <tr>
                <td><?= $this->Number->format($customer->id) ?></td>
                <td><?= h($customer->name) ?></td>
                <td><img src="/cake/uploads/customers/<?= $customer->file_location ?>" width="100" /></td>
                <td><?= h($customer->created) ?></td>
                <td><?= h($customer->modified) ?></td>
                <td class="actions">
                    <?= $this->Html->link(__('View'), ['action' => 'view', $customer->id]) ?>
                    <?= $this->Html->link(__('Edit'), ['action' => 'edit', $customer->id]) ?>
                    <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $customer->id], ['confirm' => __('Are you sure you want to delete # {0}?', $customer->id)]) ?>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
    <div class="paginator">
        <ul class="pagination">
            <?= $this->Paginator->first('<< ' . __('first')) ?>
            <?= $this->Paginator->prev('< ' . __('previous')) ?>
            <?= $this->Paginator->numbers() ?>
            <?= $this->Paginator->next(__('next') . ' >') ?>
            <?= $this->Paginator->last(__('last') . ' >>') ?>
        </ul>
        <p><?= $this->Paginator->counter(['format' => __('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')]) ?></p>
    </div>
</div>



Saturday, December 23, 2017

How to install Keras-RL and try reinforcement learning with cart pole

At first, install keras:
Install Keras on Linux mint

Then install necessary packages:
$ python3.5 -m pip install keras-rl
$ python3.5 -m pip install gym
$ python3.5 -m pip install gym[all]

You might need libav-tools too:
$ sudo apt-get install libav-tools

Create a text file and copy and paste the following code inside:
import numpy as np
import gym

from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam

from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory


ENV_NAME = 'CartPole-v0'


# Get the environment and extract the number of actions.
env = gym.make(ENV_NAME)
np.random.seed(123)
env.seed(123)
nb_actions = env.action_space.n

# Next, we build a very simple model.
model = Sequential()
model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(nb_actions))
model.add(Activation('linear'))
print(model.summary())

# Finally, we configure and compile our agent. You can use every built-in Keras optimizer and
# even the metrics!
memory = SequentialMemory(limit=50000, window_length=1)
policy = BoltzmannQPolicy()
dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory, nb_steps_warmup=10,
               target_model_update=1e-2, policy=policy)
dqn.compile(Adam(lr=1e-3), metrics=['mae'])

# Okay, now it's time to learn something! We visualize the training here for show, but this
# slows down training quite a lot. You can always safely abort the training prematurely using
# Ctrl + C.
dqn.fit(env, nb_steps=50000, visualize=True, verbose=2)

# After training is done, we save the final weights.
dqn.save_weights('dqn_{}_weights.h5f'.format(ENV_NAME), overwrite=True)

# Finally, evaluate our algorithm for 5 episodes.
dqn.test(env, nb_episodes=5, visualize=True)

And save it as "test.py".

Run the script:
$ sudo python3 test.py


Reinforcement learning of cart pole will start:

Other examples:
https://github.com/matthiasplappert/keras-rl/tree/master/examples


Refereces

matthiasplappert/keras-rl examples (cartpole)
https://github.com/matthiasplappert/keras-rl/tree/master/examples

Tuesday, December 12, 2017

How the size of layers is decided with dense method of Keras

How the size of layers with dense method of Keras is decided? It is decided by trial and error[1]. Such parameters are called hyperparameters and these must be tuned on a data set (validation set). To tune the parameter, there are two ways[1]:
Grid search: For each parameter, decide a range and steps into that range, like 8 to 64 neurons, in powers of two (8, 16, 32, 64), and try each combination of the parameters. This is obviously requires an exponential number of models to be trained and tested and takes a lot of time. 
Random search: Do the same but just define a range for each parameter and try a random set of parameters, drawn from an uniform distribution over each range. You can try as many parameters sets you want, for as how long you can. This is just a informed random guess.
For example, look at this code[2]:
from __future__ import print_function

import numpy as np
import keras
from keras.datasets import reuters
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.preprocessing.text import Tokenizer

max_words = 1000
batch_size = 32
epochs = 5

print('Loading data...')
(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=max_words,
                                                         test_split=0.2)
print(len(x_train), 'train sequences')
print(len(x_test), 'test sequences')

num_classes = np.max(y_train) + 1
print(num_classes, 'classes')

print('Vectorizing sequence data...')
tokenizer = Tokenizer(num_words=max_words)
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
x_test = tokenizer.sequences_to_matrix(x_test, mode='binary')
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)

print('Convert class vector to binary class matrix '
      '(for use with categorical_crossentropy)')
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('y_train shape:', y_train.shape)
print('y_test shape:', y_test.shape)

print('Building model...')
model = Sequential()
model.add(Dense(512, input_shape=(max_words,)))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_split=0.1)
score = model.evaluate(x_test, y_test,
                       batch_size=batch_size, verbose=1)
print('Test score:', score[0])
print('Test accuracy:', score[1])

The code above is using 512 dense for the first layer and (np.max(y_train) + 1) dense for the second layer. The result of learning of the code is:
Test score: 0.883731356924
Test accuracy: 0.796081923419

We can change the dense just for a try and see what happens. 
from __future__ import print_function

import numpy as np
import keras
from keras.datasets import reuters
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.preprocessing.text import Tokenizer

max_words = 1000
batch_size = 32
epochs = 5

print('Loading data...')
(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=max_words,
                                                         test_split=0.2)
print(len(x_train), 'train sequences')
print(len(x_test), 'test sequences')

num_classes = np.max(y_train) + 1
print(num_classes, 'classes')

print('Vectorizing sequence data...')
tokenizer = Tokenizer(num_words=max_words)
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
x_test = tokenizer.sequences_to_matrix(x_test, mode='binary')
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)

print('Convert class vector to binary class matrix '
      '(for use with categorical_crossentropy)')
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('y_train shape:', y_train.shape)
print('y_test shape:', y_test.shape)

print('Building model...')
model = Sequential()
model.add(Dense(1024, input_shape=(max_words,)))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_split=0.1)
score = model.evaluate(x_test, y_test,
                       batch_size=batch_size, verbose=1)
print('Test score:', score[0])
print('Test accuracy:', score[1])
The result is:
Test score: 0.941498034784
Test accuracy: 0.785841495993
The result became a little worse.

What happens if I change it even more?
from __future__ import print_function

import numpy as np
import keras
from keras.datasets import reuters
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.preprocessing.text import Tokenizer

max_words = 1000
batch_size = 32
epochs = 5

print('Loading data...')
(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=max_words,
                                                         test_split=0.2)
print(len(x_train), 'train sequences')
print(len(x_test), 'test sequences')

num_classes = np.max(y_train) + 1
print(num_classes, 'classes')

print('Vectorizing sequence data...')
tokenizer = Tokenizer(num_words=max_words)
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
x_test = tokenizer.sequences_to_matrix(x_test, mode='binary')
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)

print('Convert class vector to binary class matrix '
      '(for use with categorical_crossentropy)')
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('y_train shape:', y_train.shape)
print('y_test shape:', y_test.shape)

print('Building model...')
model = Sequential()
model.add(Dense(2048, input_shape=(max_words,)))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_split=0.1)
score = model.evaluate(x_test, y_test,
                       batch_size=batch_size, verbose=1)
print('Test score:', score[0])
print('Test accuracy:', score[1])
The result is:
Test score: 0.998095460163
Test accuracy: 0.779162956367
It got more loss. The result became slightly worse. Maybe the default number 512 is the best.

Like this, the amount of dense method is decided by trial and error.


[1] https://stackoverflow.com/questions/36950394/how-to-decide-the-size-of-layers-in-keras-dense-method
[2] This code is cited from: https://github.com/fchollet/keras/blob/master/examples/reuters_mlp.py

Saturday, December 9, 2017

Install Tor browser for Linux mint

For Linux Mint 16, you can install Tor Bundle by simply by running:

    sudo add-apt-repository ppa:webupd8team/tor-browser
    sudo apt-get update
    sudo apt-get install tor-browser

Then you will find tor browser here:



How to create a PC by combining parts. (Linux OS). Step1: collect parts

At first, you need the following to build a PC at least:
1. Motherboard
2. CPU
3. RAM (Memory)
4. GPU
5. Power supply
6. Storage(HDD, Flash SSD)
7. Tower (PC case)
8. Keyboard
9. PC monitor
10. Mouse
11. OS(We will use Linux because it is free OS. Windows and Mac are not free)
Choose what you will buy (Brand, Power, Price etc..).
But even if you collect all these parts, if there is no compatibility between the parts, they will not work when they are put together. You MUST check if they are compatible with each other. Maybe use this website for that:
https://pcpartpicker.com/

Maybe choose the most important part for you at first (CPU?GPU?Motherboard?) and choose recommendations from the website.

Friday, December 1, 2017

The Snake Chapter

The monk who subdues his arisen anger
as, with herbs, snake-venom once it has spread,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who has cut off passion
without leaving a trace,
as he would, plunging into a lake, a lotus,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who has cut off craving
without leaving a trace,
drying up the swift-flowing flood, sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who has uprooted conceit
without leaving a trace,
as a great flood, a very weak bridge made of reeds,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk seeing
in states of becoming
no essence,
as he would,
when examining fig trees,
no flowers, sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk with no inner anger,
who has thus gone beyond
becoming & not-,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk whose discursive thoughts are dispersed,
well-dealt with inside
without leaving a trace,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who hasn’t slipped past or held back,
transcending all
this objectification, sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who hasn’t slipped past or held back,
knowing with regard to the world
that “All this is unreal,”
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who hasn’t slipped past or held back,
without greed, as “All this is unreal,”
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who hasn’t slipped past or held back,
without aversion, as “All this is unreal,”
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who hasn’t slipped past or turned back,
without delusion, as “All this is unreal,”
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk in whom
there are no obsessions —the roots of unskillfulness totally destroyed—
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk in whom
there’s nothing born of disturbance that would lead him back to this shore,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk in whom
there’s nothing born of the underbrush
that would act as a cause
for binding him to becoming,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

The monk who’s abandoned five hindrances,
who, untroubled, de-arrowed,
has crossed over doubt,
sloughs off the near shore & far—
as a snake, its decrepit old skin.

Citation from www.dhammatalks.org
https://www.dhammatalks.org/Archive/Writings/SuttaNipata160803.pdf

Wednesday, November 29, 2017

cakephp3, use contain() and filter the result by associated table's field

There is contain() method in cakephp. This method is useful and is often used. But if you are using hasMany or belongsToMany in the table files, and if you try to filter the result in accordance with the associated field in where, you get failed.

$this->find() // Suppose this fetches records from Articles
    ->contain(['Comments'])  // Suppose Articles has many Comments
    ->where(['Comments.type' => 1]); //Error!


Articles Table
idname
1test article

Comments Table
idnamearticle_id
1comment1
2comment1
3comment1

But if the relationship is hasOne or belongsTo, you do not get any error. You can filter the result by associated table's field.

Articles Table
idnamecomment_id
1test article1

Comments Table
idname
1comment
2comment
3comment

This is because, if the tables are hasMany or belongsToMany, separate SQL statements are executed as follows:

# Comments doesn't exist in the first SQL statement,
# so you can not specify fields of Comments to filter Articles.
SELECT * FROM articles;
SELECT * FROM comments WHERE article_id IN (1, 2, 3, 4, 5); 

If you want to filter the Articles records in accordance with Comments records, use a closure to give conditions to the other SQL statement:

$query = $articles->find()->contain([
    'Comments' => function (\Cake\ORM\Query $q) {
       return $q->where(['Comments.type' => 1]);
    }
]);

So that you will get records of Articles where Comments.type is 1.
(You can also use matching() instead of contain().)

document of cakephp about this


Sunday, November 26, 2017

Use find('list')

Contents

CakePHP
1. Setting up CakePHP3 with CentOS

2. Bake and Migration


4. Add a calendar page

5. Use "contain"

6. Use find('list')

find('list')


In cakephp3, find('list') is also very useful. It is used like this:
// In a controller or table method.
$products = $this->Products; //Or use table registry.
$query = $products->find('list');
$data = $query->toArray();

// Data now looks like:
// id of the record => "name" field of the table.
$data = [
    1 => 'product name 1',
    2 => 'product name 2',
];

The array data can be used as options of a HTML drop down select box:
<?php echo $this->Form->input('products', ['type'=>'select', 'options'=>$data]); ?>

If you want other field than 'name' for the value, you can specify what field should be the value:
// In a controller or table method.
$query = $articles->find('list', [
    'keyField' => 'id',
    'valueField' => 'description'
]);
$data = $query->toArray();

// Data now looks like
$data = [
    '1' => 'desctiption 1',
    '2' => 'desctiption 2',
];

Relevant cakephp cookbook:
https://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html#finding-key-value-pairs

Sunday, November 19, 2017

How to start Unity programming with C#

In this post, suppose we are using Windows OS (8.1).

1. Install Unity


Download Unity from here: https://store.unity.com/
Personal version is free, so choose the personal version. When you get the installer, start installing Unity.

2. Open Unity And Add A Cube


You will see the Unity's window:


Right click on the "Untiled*" pane. Select cube from the menu.

Cube is added in the scene.


Now click "Main Camera" from the left pane. Then click "Inspector" from the right side.

Change the position from the Inspector. I changed it to "x:0, y:1, z:-3". It will change the default position of the Main Camera.

Now we will check if the position was really changed. Click the play button from the upper menu.


You can see the Main Camera's position is right in front of the cube, which is same as the position that we wrote in the inspector.

Now exit from the play mode by pressing the play button.


3. Apply Gravity To The Object


Click the cube and click "add component".

Click "Physics".

Then click "Rigid body".

Rigid body component is added to the cube.

Change the position (to make the cube float in the air).
Position: x0, y3, z0.

Run the code by pressing the play button.

The cube will fall by the gravity (and fall forever because there is no ground).

Now add plane under the cube and set the position of the plane x:0, y:0, z:0.

Run the code by pressing the play button. The cube will fall on the plane now.

To apply gravity and make objects collide with each other:

Falling objects need the following:
    1.Collider
    2. Rigidbody
Ground that receives the falling objects needs the following:
    1. Collider that fits the form of the ground.

You can find the collider in the inspector of the cube and the plane. Box and Plane have the collider by default, so we didn't add collider manually this time.
"Box collider" is the collider



4. Add Prefab


Drag and drop the cube object to Assets. You get a prefab of cube on the Assets view.

Or you can create prefab from the Assets view too. Right click on the Assets and click "Create" > "Prefab".

We get a Prefab on the Assets view. Drag and drop the game object on the Prefab.

Anyway, after creating the prefab of the cube, create an empty game object.
Right click on the left pane > "Create Empty"

Then you get an empty game object.

 And click the empty game object and from its inspector, click "add component".

Scroll down the menu and click "New Script" and name it "CubeControl". Then click "Create and Add".

Maybe automatically MonoDevelop or VisualStudio or both of them will be opened. If not opened, just double-click the script from the Assets view:


As you see on the code, Start() function is used for initialization. Update() is called per frame.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CubeControl : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }

Change the script like this:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CubeControl: MonoBehaviour { public GameObject prefab; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.Z)) { GameObject.Instantiate(prefab); } } }

Then see again the Unity view and click the empty game cube. From the inspector of the empty game object, you can see that a "prefab" item is created.


Drag and drop the prefab of the cube to the "prefab" item of the CubeControl of the empty game object.


You will see that the cube prefab is attached to the CubeControl as its prefab.


Now look at the script again... According to the script, as long as you press "Z", the prefab is instantiated.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CubeControl: MonoBehaviour { public GameObject prefab; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.Z)) { GameObject.Instantiate(prefab); } } }

Now check if it really works.

As long as you press "z" key on the keyboard... the cube prefab is instantiated..!


If you change "GetKey" to "GetKeyDown", the cube prefab is instantiated "every time" you press z on the keyboard.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CubeControl: MonoBehaviour { public GameObject prefab; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKeyDown(KeyCode.Z))
{ GameObject.Instantiate(prefab); } } }

5. Move the main camera


Now create a script for the main camera. Click on "Add Component" of the main camera and add "New Script". The name is "CameraControl".




Open the script and change the code like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraControl : MonoBehaviour {
// Use this for initialization
void Start () {
     
    }

// Update is called once per frame
void Update () {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.Translate(Vector3.forward * Time.deltaTime * 2);
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
this.transform.Rotate(Vector3.up, Time.deltaTime*50);
        }
        if (Input.GetKey(KeyCode.LeftArrow))
        {
this.transform.Rotate(Vector3.up, -Time.deltaTime*50);
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {
            this.transform.Translate( -1 * Vector3.forward * Time.deltaTime * 2);
        }
    }
}

And run the project:

Your main camera moves (by pressing up arrow, down arrow, right arrow, left arrow)!