<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Dong Nguyen on Medium]]></title>
        <description><![CDATA[Stories by Dong Nguyen on Medium]]></description>
        <link>https://medium.com/@ndaidong?source=rss-528ebe75b3c2------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*a2VT9sI9_JKOrO4fpIKQqw.png</url>
            <title>Stories by Dong Nguyen on Medium</title>
            <link>https://medium.com/@ndaidong?source=rss-528ebe75b3c2------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 28 May 2026 12:17:14 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ndaidong/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Setup Ubuntu 18.04 from MinimalCD]]></title>
            <link>https://medium.com/@ndaidong/setup-ubuntu-18-04-from-minimalcd-10f7f2845e11?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/10f7f2845e11</guid>
            <category><![CDATA[xubuntu]]></category>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[xfce]]></category>
            <category><![CDATA[notes]]></category>
            <category><![CDATA[ubuntu]]></category>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Fri, 11 May 2018 23:45:58 GMT</pubDate>
            <atom:updated>2018-05-12T00:25:52.945Z</atom:updated>
            <content:encoded><![CDATA[<p>This short note tells about the way how I setup dev machine from Ubuntu 18.04 Minimal CD to get a lightweight system, without the annoying bloatware.</p><h3>1. Download Ubuntu 18.04 MinimalCD</h3><p>Go to <a href="https://help.ubuntu.com/community/Installation/MinimalCD">the download page</a>, scroll to “64-bit PC…” section and click on “<a href="http://archive.ubuntu.com/ubuntu/dists/bionic/main/installer-amd64/current/images/netboot/mini.iso">Ubuntu 18.04 “Bionic Beaver”</a> 64MB” to get it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VVQr9mPXHMPGJRxmtrSrow.png" /><figcaption>Download Ubuntu 18.04 minimal CD</figcaption></figure><h3>2. Create USB boot</h3><p>Get <a href="https://etcher.io">Etcher</a> or alternative tools.</p><p>Choose Ubuntu 18.04 mini.iso, and specify USB flash disk to create bootable device.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/806/1*oaDylkK_THSHEalx9LTr2A.png" /><figcaption>Etcher</figcaption></figure><h3>3. Boot the computer from USB</h3><p>Ensure that your computer can boot from USB. Legacy boot is recommended. However, advanced users should consider UEFI boot mode.</p><h3>4. Follow the installation process</h3><p>Open <a href="https://photos.google.com/share/AF1QipO110QkMfqo3PRke_wv9myqeVQexfHMgQnwqw4pflGj84wl6EoEc6qSNsaCt59vpw/photo/AF1QipOst5QxWR26a6Xo0dlF3F2IYYCCCZ_bLInkkVIL?key=S3NoSWVwcXZVNFFINlFGZDlHRDBOTWUwMEl5M2Jn">the slideshow here</a> to see how to install step by step. While viewing, you can press spacebar or click on “Info” icon to see the explanation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*oLjrlnza3zw_foMY1KIABQ.png" /><figcaption>Install Ubuntu 18.04 from Minimal CD</figcaption></figure><h3>5. Install the stuff</h3><p>At the first time logging into the OS, you may need to install several must-have tools:</p><pre>sudo apt update<br>sudo apt install — no-install-recommends -y \<br> software-properties-common ca-certificates build-essential \<br> make curl wget htop git nginx inetutils-tools \<br> geany vim vim-gnome \<br> thunar-archive-plugin file-roller \<br> viewnior gthumb mugshot \<br> gnome-disk-utility \<br> software-center</pre><p>It’s possible to install them and other utils via Software Center too.</p><p>That’s it. We’ve got a very clean system for development. I often pick Xubuntu Minimal because it’s one of the most lightweight desktop environments and it’s so flexible, easy to customize the appearance to fit my own aesthetic point of view.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GdpqfAvAEvyyOwA8xgaOhg.png" /></figure><p>Sometimes I install DE at the first login instead of during Ubuntu installation. In this case, I like to choose <a href="https://xfce.org/">xfce4</a>, which is even more lightweight than Xubuntu Minimal:</p><pre>sudo apt install xfce4</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J33YsnrjZbrRyMUbJOOxxg.png" /></figure><p>The rest is your business.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=10f7f2845e11" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setup a lightweight environment for deep learning]]></title>
            <link>https://medium.com/@ndaidong/setup-a-simple-environment-for-deep-learning-dc05c81c4914?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/dc05c81c4914</guid>
            <category><![CDATA[cuda]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[tensorflow]]></category>
            <category><![CDATA[nvidia]]></category>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Sun, 08 Apr 2018 17:28:29 GMT</pubDate>
            <atom:updated>2018-04-09T01:30:57.003Z</atom:updated>
            <content:encoded><![CDATA[<p>In this article, I would like to share about my experiences while setting up the environment for our deep learning project. It may be quite complicate at first, but not really. At the end, we will have a lightweight system based on Ubuntu 17.10, with CUDA 9.0, cuDNN 7.0.5, Python 3, TensorFlow-GPU and Jupyter Notebook already to start training.</p><h3>1, Hardware</h3><p>For a small project, we just need a set as below:</p><ul><li>Intel(R) Core(TM) i5–7600 CPU @ 3.50GHz</li><li>240 GB hard drive (SSD)</li><li>8 GB RAM (DDR4)</li><li>nVidia GP106 [GeForce GTX 1060 6GB]</li></ul><p>Of course, it also requires a case, power supply, keyboard, mouse and monitor. Total cost about $1500.</p><h3>2, OS &amp; platform</h3><p>In this machine we install:</p><ul><li><a href="https://help.ubuntu.com/community/Installation/MinimalCD">Ubuntu 17.10 “Artful Aardvark” 58MB</a></li><li>Xubuntu minimal</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/795/1*TYqusVWtiB_5j3HnZcBF_g.png" /></figure><p>Note that the 240GB of SSD drive is being separated to 3 parts:</p><ul><li>4 GB for swap</li><li>80 GB mounted as /storage to store persistent data</li><li>The rest mounted as / to install Ubuntu</li></ul><p>After the system is ready, we login as root and run the following commands to install several useful tools:</p><pre>sudo apt update<br>sudo apt install — no-install-recommends -y \<br> software-properties-common build-essential \<br> make curl wget \<br> ccze inetutils-tools \<br> python-minimal git nginx htop vim</pre><p>While other libs are quite familiar, ccze may be strange. It is used to color the logs output with journalctl.</p><p>Lastly, we chmod storage to share to all users:</p><pre>sudo chmod 0777 /storage</pre><h3>3. Python and Pip</h3><p>We love to work with Python 3 only, but some system libs may require Python 2. That’s why we have installed python-minimal, then we simply forget it.</p><p>The following script will install Python v3.6.4 from source:</p><pre>export PYTHON_VERSION=3.6.4<br>export PYTHON_DOWNLOAD_URL=<a href="https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz">https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz</a></pre><pre>sudo apt install --no-install-recommends -y libssl-dev libreadline-dev libbz2-dev libsqlite3-dev</pre><pre>wget &quot;$PYTHON_DOWNLOAD_URL&quot; -O python.tar.tgz<br>tar -zxvf python.tar.tgz<br>cd Python-$PYTHON_VERSION<br>./configure --enable-optimizations --enable-loadable-sqlite-extensions<br>make<br>sudo make install</pre><pre>pip3 install --upgrade pip</pre><p>The libs libbz2-dev, libsqlite3-dev, etc are required to get later tools such as Jupyter or TensorBoard stability work. Ignoring them would cause the unpleasant errors.</p><p>When we build Python 3 from source, pip3 is also installed too. It’s good to put these lines into ~/.bash_aliases or ~/.bash_profile:</p><pre>alias python=python3<br>alias pip=pip3</pre><p>To remember, the general rule is:</p><ul><li>~/.bash_profile is being activated just one time when you login (GUI or SSH)</li><li>~/.bash_aliases is being activated every time when you open the terminal (window or tab)</li></ul><p>However this behavior can be changed by modifying ~/.bashrc, ~/.profile, or /etc/bash.bashrc, etc.</p><h3>4. NVIDIA driver</h3><p>There is two available versions for NVIDIA graphic card’s driver: Nouveau driver and Nvidia driver. The first one is open source, by community. The last one is close source, by NVIDIA.</p><p>Normally, Nvidia driver is default. For Ubuntu 17.1 0, it’s nvidia-384. We can check it with:</p><pre>cat /proc/driver/nvidia/version</pre><p>If it’s not there for some reason, just install it.</p><p>From GUI, you can choose it via Drivers Management tool. It will be downloaded and installed automatically.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JrKer_82RJybSbiBFHLo8A.jpeg" /></figure><p>You can install Nvidia driver via terminal too. For this case, many experts suggest to add Nouveau to blacklist first:</p><pre>sudo nano /etc/modprobe.d/blacklist.conf</pre><p>Then paste the following lines into then save it:</p><pre>blacklist vga16fb<br>blacklist nouveau<br>blacklist rivafb<br>blacklist nvidiafb<br>blacklist rivatv</pre><p>And install:</p><pre>sudo add-apt-repository ppa:graphics-drivers/ppa<br>sudo apt update<br>sudo apt install nvidia-384 nvidia-384-dev</pre><p>Recheck it using the above cat command or nvidia-smi for more detail.</p><h3>5. CUDA v9.0</h3><p>TensorFlow team just <a href="https://github.com/tensorflow/tensorflow/releases/tag/v1.7.0">released v1.7</a> that has been <a href="https://github.com/tensorflow/tensorflow/issues/15656">built with CUDA 9.0</a>, so unless you have plan to build TensorFlow from source, you should not install CUDA v9.1 to avoid the unexpected issues.</p><p>IMHO, it’s always best practice to install pip modules into the virtual environments, and use TensorFlow from PyPI. This will provide a flexible solution. For the same reason, I didn’t recommend to use Anaconda.</p><p>CUDA v9.0 requires GCC 6, while default GCC version in Ubuntu 17.10 is GCC 7.2. So we have to install GCC 6 and create symlinks as below:</p><pre>sudo apt install gcc-6 g++-6<br>sudo ln -s /usr/bin/gcc-6 /usr/local/cuda/bin/gcc<br>sudo ln -s /usr/bin/g++-6 /usr/local/cuda/bin/g++</pre><p>Now gcc command is running as gcc-6, check it with:</p><pre>gcc -v</pre><p>Then, we stop x-server, download CUDA 9 and install it:</p><pre>sudo service lightdm stop<br>wget <a href="https://developer.nvidia.com/compute/cuda/9.0/Prod/local_installers/cuda_9.0.176_384.81_linux-run">https://developer.nvidia.com/compute/cuda/9.0/Prod/local_installers/cuda_9.0.176_384.81_linux-run</a><br>mv cuda_9.0.176_384.81_linux-run cuda_9.0.176_384.81_linux.run<br>chmod +x cuda_9.0.176_384.81_linux.run<br>sudo ./cuda_9.0.176_384.81_linux.run --override --dkms -s</pre><p>While compiling, it will ask several questions, answer as below:</p><pre>You are attempting to install on an unsupported configuration. Do you wish to continue?<br><strong>y</strong><br>Install NVIDIA Accelerated Graphics Driver for Linux-x86_64 384.81?<br><strong>n</strong><br>Install the CUDA 9.0 Toolkit?<br><strong>y</strong><br>Enter Toolkit Location<br>[default location]<br>Do you want to install a symbolic link at /usr/local/cuda?<br><strong>y</strong><br>Install the CUDA 9.0 Samples?<br><strong>y</strong><br>Enter CUDA Samples Location<br>[default location]</pre><p>If nothing special happens, the process will end succefully.</p><p>As <a href="http://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#post-installation-actions">NVIDIA’s docs</a>, we may need to add these paths into ~/.bash_aliases:</p><pre>$ export PATH=/usr/local/cuda-9.0/bin${PATH:+:${PATH}}</pre><pre>$ export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}</pre><p>Lastly, reboot the system.</p><h3>6. cuDNN v7.0.5 for CUDA 9.0</h3><p>CUDA 9.0 only plays with its appropriate cuDNN version, you can <a href="https://developer.nvidia.com/cudnn">download it here</a> after joining <a href="https://developer.nvidia.com/developer-program">NVIDIA Developer Program</a>.</p><p>Choose the correct item from list as below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OHxg3vx5Xyui3GMoxiBepg.png" /></figure><p>Download it then run these commands:</p><pre>tar -xzvf cudnn-9.0-linux-x64-v7.tgz<br>sudo cp cuda/include/cudnn.h /usr/local/cuda/include<br>sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64<br>sudo chmod a+r /usr/local/cuda/include/cudnn.h<br>/usr/local/cuda/lib64/libcudnn*</pre><p>Basically it is done.</p><p>Now we talk a little about the workspace.</p><h3>7. Setup project environment</h3><p>Depending on the project, the process and the team, you can choose the corresponding way to organize the workspace.</p><p>In our project, after finishing the above steps, we give each project member an account to access to the system as regular user.</p><p>Note that we have to ensure the paths at the step 5 are available to all users. Simply clone the ~/.bash_aliases.</p><p>Persistent data such as datasets, checkpoints, weights, etc can be stored at /storage.</p><p>Project member will login using ssh and setup virtual environment by himself, for example:</p><pre>python3 -m venv computer-vision<br>source computer-vision/bin/activate<br>(computer-vision) pip install tensorflow-gpu jupyter<br>(computer-vision) jupyter notebook --port 7777</pre><p>With regular user permission, they can do everything related to preprocessing and training, but could not install pip package globally or change system softwares. That will keep the system more stable.</p><p>While using GPU, nvidia-smi is powerful command. We can check the real-time stats with:</p><pre>watch -d -n 1.0 nvidia-smi</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hQvNa538DxsIjMZvtOvU5g.png" /></figure><h3>Conclution</h3><p>That’s all. Now we had a good enough environment to get started our deep learning tasks.</p><p>The whole script is available here:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/69ae55cf8955c26c1e8dd2a903c05508/href">https://medium.com/media/69ae55cf8955c26c1e8dd2a903c05508/href</a></iframe><p>In addition, there are some free places to play such as <a href="https://colab.research.google.com/">Google Colab</a> and <a href="https://www.floydhub.com">FloydHub</a>. Let’s take a look on them while you considering to invest.</p><p>Enjoy studying.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dc05c81c4914" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[“Piano Chords” app and the principles behind it]]></title>
            <link>https://medium.com/@ndaidong/piano-chords-and-the-principles-behind-it-2d2f8e57e1ff?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/2d2f8e57e1ff</guid>
            <category><![CDATA[application]]></category>
            <category><![CDATA[piano]]></category>
            <category><![CDATA[music]]></category>
            <category><![CDATA[utilities]]></category>
            <category><![CDATA[web-apps]]></category>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Tue, 01 Aug 2017 17:22:28 GMT</pubDate>
            <atom:updated>2025-04-19T11:51:30.516Z</atom:updated>
            <content:encoded><![CDATA[<p>One of the challenges for the people who just started to learn piano or keyboard is how to remember:</p><ul><li>the notes in a chord</li><li>the chords in a scale</li></ul><p>Me too. So I’ve created a <a href="https://ndaidong.github.io/piano-chords/">minimalist web app</a> that helps me determine the chords based on any given natural scale. With it, once I known which scale the song should be played with, I can choose it on the app’s interface and then the app would display the basic chords that belong to the given scale.</p><p>It looks like below:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FILuvPq7qkN8%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DILuvPq7qkN8&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FILuvPq7qkN8%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/bd20e70d1c24340407939347c27b8da3/href">https://medium.com/media/bd20e70d1c24340407939347c27b8da3/href</a></iframe><p>The algorithm I’used to program and calculate the notes and chords was founded on two principles:</p><ol><li>How to build the major/minor chords.</li><li>How to use “<a href="https://www.youtube.com/watch?v=d1aJ6HixSe0">circle of fifths</a>” and “circle of fourths” to find the chords those are belong to the specific scale.</li></ol><p>Here are some notes I would like to write down for later references after finishing the first version.</p><p><strong><em>Intervals and steps</em></strong></p><h4>1, On the piano, including black and white keys, each adjacent key has a distance of a half step between them.</h4><p>As you can see in the picture below, between D and D# is half step, between D# and E is half step, between E and F is half step … So between D and E is a whole step, between D and F is one and a half step, and so on.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/753/1*GJk2XtvYrzGawN7izcoVOw.png" /><figcaption>The distance in pitch between two adjacent keys is a half step</figcaption></figure><h4>2, If the distance between two notes is 2 steps (4 keys), we have a major third. If that is one and a half step (3 keys), we have a minor third.</h4><p><strong><em>Chords</em></strong></p><h4>3, A chord is a combination of at least 3 notes, in which the first note is called root note. The chords are named with the name of root note.</h4><h4>4, Major chord = 1 major third + 1 minor third</h4><p>So, to build a major chord, just start out with root note, count up 2 steps (4 keys), and we have the second note. From second note, count up 1.5 step (3 keys), and we have the third note.</p><p>For instance, with the chord C major, we have C as the root note. From any C key on the piano, count up 4 keys, we get E. From E, count up 3 keys, we get G. Thus, the keys that should be pressed are C — E — G.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/789/1*DJ8O8kNi_VuSx0SKLg_-EQ.png" /><figcaption>The keys in C major chord</figcaption></figure><h4>5, Minor chord = 1 minor third + 1 major third</h4><p>So, to build a minor chord, just start out with root note, count up 1.5 step (3 keys), and we have the second note. From second note, count up 2 steps (4 keys), and we have the third note.</p><p>For instance, with the chord C minor, we also have C as root. From any C key on the piano, count up 3 keys, we get E♭ (or D#). From E♭, count up 4 keys, we get G. Thus, the keys that should be pressed are C — E♭ — G.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/755/1*Pa8kjCsdGQ_Nh53i8AgZdw.png" /><figcaption>The keys in C minor chord</figcaption></figure><p><strong><em>Scales</em></strong></p><h4>6, A scale is the collection of notes — in an octave — ordered by fundamental frequency or pitch.</h4><p>The chain of notes in a scale starts from its root note — the first note — which the scale was named after. This note called the “tonic” of the scale.</p><p>From the tonic, count up until the last note, we will have a set of nodes which the distance between them follows a particular pattern.</p><p>Suppose H (half) represents a half-step and W (whole) represents a whole step, then:</p><ul><li><strong><em>The major scale is a sequence of notes that follows the W-W-H-W-W-W-H pattern.</em></strong></li></ul><p>For example C major:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/689/1*vRB5LXzVVm0dypwYY0KxTw.png" /><figcaption>C major scale</figcaption></figure><ul><li><strong><em>The minor scale is a sequence of notes that follows the </em>W-H-W-W-H-W-W<em> pattern.</em></strong></li></ul><p>For example A minor:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/503/1*Ska8WXja7jlK-tc0jQmXOA.png" /><figcaption>A minor scale</figcaption></figure><p><em>Note that here we just talk about natural scales — in real world, there are many other kinds of scale.</em></p><h4>7, About “circle of fifths” and “circle of fourths”</h4><p>The “circle of fifths” is a model which describe the relationship among the 12 tones of the chromatic scale. Here is the diagram:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/810/1*1YvmDqkWtcIx5y55DccbMw.png" /><figcaption>Circle of fifths/fourth</figcaption></figure><p>With the same picture above, in clockwise sense, we have the circle of fifths, and in the counterclockwise sense, we have the circle of fourths.</p><p><em>Major scale</em></p><p>To find the chords in the major scale, just start from the position of the scale’s tonic, two chords on either side of it are two major chords. Clockwise, the 2nd, 3rd and 4th chords are the minor chords.</p><p>For example, to identify the chords of C major, just take C as the first position. The two chords next to C — F and G — are major chords. Clockwise, from tonic, 3 next chords include D, E and A will be the minor chords. Thus, we’ve got at least six chords in C, F, G, Dm, Em and Am to play in C major scale.</p><p><em>Minor scale</em></p><p>To find the chords in the minor scale, we also start from the position of the scale’s tonic, two chords on either side of it are two minor chords. In the counterclockwise, the 2nd, 3rd and 4th chords are the major chords.</p><p>For example, to identify the chords of A minor, just take A as the first position. The two chords next to A — D and E — are minor chords. Counterclockwise, the next 3 chords includes G, C and F will be the major chords. Thus, we can play C major scale with Dm, Am, Em, C, F and G.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1R-GDVtWXoi0Kuo3XxxIGA.png" /></figure><p>That’s it. We just need some simple principles, and we can easily build the basic chords. And then, by referring to the “circle of fifths”, we can find all natural chords from any scale. There is a profound relationship between music and math. This is a great starting point for more in-depth studies later.</p><p>I hope that <a href="https://techpush.github.io/piano-chords/">the app</a> and/or the theory behind it would be useful for you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2d2f8e57e1ff" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Zato — a powerful Python-based ESB solution for your SOA]]></title>
            <link>https://medium.com/@ndaidong/zato-a-powerful-python-based-esb-solution-for-your-soa-5aef67114570?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/5aef67114570</guid>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[soa]]></category>
            <category><![CDATA[esb]]></category>
            <category><![CDATA[a-to-z]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Sat, 17 Jun 2017 19:35:16 GMT</pubDate>
            <atom:updated>2017-10-09T01:45:03.627Z</atom:updated>
            <content:encoded><![CDATA[<h4>If you are looking for an open source <a href="https://en.wikipedia.org/wiki/Enterprise_service_bus">ESB</a> platform, and you prefer Python environment than others, <a href="https://zato.io/">Zato</a> is exactly a brilliant candidate.</h4><p>I would not bore you with a long list of features that Zato provides. You can find them somewhere at <a href="https://zato.io/">Zato homepage</a>. In this article, I just want to introduce a demo, a very simple example, to show how we can quickly get started with Zato. But at first, a little overview regarding Zato architecture may be necessary for understanding next section.</p><h3>Zato architecture</h3><p>Zato was designed with high performance and scalability in mind. Once we start a Zato ESB instance, we get — not a single server — but a cluster that includes several servers, HAProxy, Operational Database (Postgres is recommended), Key/Value store database (Redis as default) and scheduler as you can see in figure 1 below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*5Np0n8TsC3GWB2DpICg2rw.png" /><figcaption>Figure 1 — Zato cluster architecture</figcaption></figure><p>This design gives Zato ability to process a near-unlimited number of connections. Even with a basic setup, Zato can handle hundreds of thousands of concurrent requests.</p><p>Is that cool?</p><p>Now we would start our ESB. So quickly, just few minutes.</p><h3>Demo scenario</h3><p>In this demo, we setup a Zato cluster as ESB core, then we run 2 Node.js processes to simulate external services. One for JSON Document service and other for XML Document service.</p><p>Then we try to send a XML document to JSON Document service — through ESB, of course — to see if JSON Document service receives this new item as JSON document.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eq6aPBCJ6feqQ858cAg7aQ.png" /><figcaption>Figure 2 — Demo scenario</figcaption></figure><h3>Let’s start</h3><p>We’ve prepared a <a href="https://github.com/greenglobal/zato-demo">GitHub repository</a> for you. Please ensure that you have <a href="https://git-scm.com/">git</a>, <a href="https://www.docker.com/get-docker">docker</a>, <a href="https://docs.docker.com/compose/install">docker-compose</a> already installed. We also use the ports 8183, 8185, 8186, and 11223.</p><p>When everything is ready, please open your favorite command line tool, then:</p><pre>git clone https://github.com/greenglobal/zato-demo.git<br>cd zato-demo<br>docker-compose up</pre><p>The process looks like in this clip:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FeHqNfmcSWqc%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DeHqNfmcSWqc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FeHqNfmcSWqc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/567fa17608ec9edf7bc38f1fbb39a3fa/href">https://medium.com/media/567fa17608ec9edf7bc38f1fbb39a3fa/href</a></iframe><p>At the first time, it may take few minutes to download the images from Docker Hub. Be patience. The next times you will not need to wait.</p><p>If nothing wrong, we should get:</p><ul><li>Zato dashboard located at <a href="http://localhost:8183/">http://localhost:8183</a></li><li>JSON Document service: <a href="http://localhost:8185/documents">http://localhost:8185/documents</a></li><li>XML Document service: <a href="http://localhost:8186/documents">http://localhost:8186/documents</a></li></ul><h4>Admin dashboard</h4><p>Zato provides web-based GUI, CLI and API that allow to use it, control it by any convenient way for you. In this article, we just take action on the ESB via its web-based GUI — that we called “Zato dashboard”.</p><p>Open your favorite web browser, then access <a href="http://localhost:8183/">http://localhost:8183</a> and login with username “admin” and password:</p><pre>2cdd3856-ced5-43b8-b3c1-7e8a5cdb0c37</pre><p>This password is being generated automatically every time we build <a href="https://hub.docker.com/r/ndaidong/zato/tags/">the image</a> and could be extracted from docker container by running the following command:</p><pre>docker exec cluster1 cat /opt/zato/web_admin_password</pre><h4>External services</h4><p>As we’ve mentioned above, there are JSON Document service and XML Document service run at 2 ports 8185 and 8186. They are 2 separate Node.js processes.</p><p>The main mission of an ESB is mix the different services together, allow them to communicate with each other without any modification from them self. These services may be different in protocol, data format and data structure. In our example, JSON Document service uses JSON format and opens a RESTFul API (It was built with <a href="https://github.com/typicode/json-server">json-server</a>). While XML Document service uses XML format (It is just a static XML file now, we have plan to make a SOAP service instead).</p><p>Basically, XML Document service and JSON Document service could not talk together because they don’t speak the same language. But, Zato ESB stands there, as middle-man, will help them. These 2 services have to connect to Zato ESB, pass the data to ESB, then ESB will do its regular tasks to translate, mediate, transform and then, forward to the receiver what it expects.</p><h4>Declare outgoing connections</h4><p>In order to get it works for us, let’s back to Admin dashboard. From the menu, go to Connections -&gt; Outgoing -&gt; Plain HTTP, then create 2 objects JSON_Docs and XML_Docs as in the following clip:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FvXFPkUKtssc%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DvXFPkUKtssc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FvXFPkUKtssc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7383fb5508b34dea3d0143938360b1c0/href">https://medium.com/media/7383fb5508b34dea3d0143938360b1c0/href</a></iframe><p>At this step, we are pointing the links between ESB service with its external connection — destination — so that ESB can start pushing requests towards it once the equivalent service involves.</p><p>Note that, we’re using docker container name in the URL paths “http://docsv:8185/documents” and “http://docsv:8186/documents” because that is the best way to connect 2 or multi docker containers in the same network together. “docsv” is docker containers’ name as we defined in docker-compose.yml file. In real world, we might use domain or public IP address instead.</p><h4>Register Document service</h4><p>The term “service” here may make us confused a little bit. Let’s simple look it as an agent that is responsible for transforming the documents.</p><p>In our example, there is a small Python script located at <a href="https://github.com/greenglobal/zato-demo/blob/master/service2/document_service.py">/service2/document_service.py</a> in which we transform XML data to JSON data and post to JSON Document service.</p><p>Back to Admin dashboard, go to Services -&gt; List services, click “Upload a service package” button and browse to the above script’s location. Watch the following clip for detail:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fim0IZxjL4jk%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dim0IZxjL4jk&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fim0IZxjL4jk%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/695c9ca8af5a784e4133774b59b1ea15/href">https://medium.com/media/695c9ca8af5a784e4133774b59b1ea15/href</a></iframe><p>Here Zato provides a great feature: hot-deployment. After we upload a service, it will be synced to all servers/clusters and become available to use immediately.</p><h4>Expose Document service over HTTP</h4><p>Now we need to expose our Document service so that it can be involved from outside. Let’s call it “Send XML Document”.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FsPaU_0CIAAk%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DsPaU_0CIAAk&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FsPaU_0CIAAk%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/42f97e6982ac46a72f2e63c2f6d5b7cf/href">https://medium.com/media/42f97e6982ac46a72f2e63c2f6d5b7cf/href</a></iframe><p>Note that, we’ve declared URL Path as <em>/documents</em>, that means the applications/services from outsite can call to ESB’s Document service via absolute path in the format of <a href="https://qn-esb.org/documents."><em>protocol://your-esb-domain.com/documents</em>.</a> In our example, the full path is <a href="http://localhost:11223/documents"><em>http://localhost:11223/documents</em></a>, with 11223 is cluster’s port.</p><p>When a HTTP request was sent to Zato cluster, HAProxy will capture and transfer it to the appropriate server. Zato ESB installed within selected server, in its tour, will call the equivalent service based on declared URL Path. This service does its task on data and then decides to save into somewhere or deliver to the specific destinations.</p><p>In our demo, we just send a XML document to ESB’s Document service. If it works as well, it should execute what we written in <a href="https://github.com/greenglobal/zato-demo/blob/master/service2/document_service.py">document_service.py</a>: convert XML data format to JSON data format and then post to JSON Document service. Now, let’s try.</p><h4>Check how our ESB works</h4><p>Use your favorite tool to send a post request to ESB’s Document service at <a href="http://localhost:11223/documents"><em>http://localhost:11223/documents</em></a> with the content below:</p><pre>&lt;request&gt;<br>  &lt;id&gt;11&lt;/id&gt;<br>  &lt;subject&gt;Hello Zato&lt;/subject&gt;<br>  &lt;body&gt;I&#39;m a tester and I expect this document will be inserted into list of JSON documents&lt;/body&gt;<br>  &lt;by&gt;Kenneth Stokes&lt;/by&gt;<br>  &lt;time&gt;612154834&lt;/time&gt;<br>&lt;/request&gt;</pre><p>Check the result by reloading <a href="http://localhost:8185/documents">http://localhost:8185/documents</a> to see if new item has been added to the end of the list.</p><p>Watch the following clip for more detail:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FVQInVfChsRU%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DVQInVfChsRU&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FVQInVfChsRU%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/299b795d9ebef67b78f1c7eff054ec46/href">https://medium.com/media/299b795d9ebef67b78f1c7eff054ec46/href</a></iframe><p>That’s it. The program works for me as well. Hope that it works for you too. If it doesn’t, feel free to <a href="https://github.com/greenglobal/zato-demo/issues">create issue</a> or leave comment here.</p><p>Now is the time to look in other capabilities: enable security, switch to another protocol, process in queue, apply pub/sub model, etc. I’m not a specialist in ESB and <a href="https://en.wikipedia.org/wiki/Service-oriented_architecture">SOA</a>, this is the first time I really work for an ESB, but with Zato, I had a pretty good start.</p><p>Let’s give Zato a try. Some days it may become your close friend.</p><p><em>(This article is written from a developer’s point of view. I have no benefit relationship with Zato team and Zato brand.)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5aef67114570" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Top 5 reasons why I like to play 0 A.D.]]></title>
            <link>https://medium.com/@ndaidong/top-5-reasons-why-i-like-0-a-d-better-than-aoe-aab5b0073bf7?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/aab5b0073bf7</guid>
            <category><![CDATA[aoe]]></category>
            <category><![CDATA[games]]></category>
            <category><![CDATA[0ad]]></category>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Sat, 09 Jan 2016 09:36:57 GMT</pubDate>
            <atom:updated>2017-06-17T19:54:34.436Z</atom:updated>
            <content:encoded><![CDATA[<p>While <a href="http://play0ad.com/game-info/features/">0 A.D. has many interesting features</a>, here are 5 things I found most impressive:</p><ol><li>The soldiers can gather resources and construct buildings as farmers.</li><li>Unlimited population, I can make the big battles with thousands of soldiers.</li><li>It’s open source.</li><li>It’s cross-platform so I can play it on Linux.</li><li>It’s 3D with very smooth experience.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RJb0WR-vOHoe_6KlGzkFiA.png" /></figure><p>If you have never played 0 A.D., this is the introduction on <a href="http://play0ad.com/">Play0ad</a>:</p><blockquote><strong>0 A.D.</strong> (pronounced <em>“zero ey-dee”</em>) is a free real-time strategy (RTS) game of ancient warfare. Lead a civilization set in the imaginary year of 0 A.D., develop a thriving city, raise a mighty army and contend with rivals for hegemony of the world. History is yours for the taking!</blockquote><p>I often play the month-long battles, each day about 15 or 20 minutes:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FlexE3NR0n34%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DlexE3NR0n34&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FlexE3NR0n34%2Fhqdefault.jpg&amp;key=d04bfffea46d4aeda930ec88cc64b87c&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/861fd6cf6b2982ed6be20fe02fca7ce8/href">https://medium.com/media/861fd6cf6b2982ed6be20fe02fca7ce8/href</a></iframe><p>Would you like to try it? Please follow the instructions here:</p><p><a href="http://play0ad.com/download/">http://play0ad.com/download/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aab5b0073bf7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setup Rocket Chat within 10 minutes]]></title>
            <link>https://medium.com/@ndaidong/setup-rocket-chat-within-10-minutes-2b00f3366c6?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/2b00f3366c6</guid>
            <category><![CDATA[rocket-chat]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[meteor]]></category>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Tue, 29 Dec 2015 12:33:49 GMT</pubDate>
            <atom:updated>2015-12-29T15:28:12.344Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9IX5MWrnaCBzzeS3h5N2oA.png" /><figcaption>Rocket Chat</figcaption></figure><p>Are you using Slack? Do you want to get your own Slack for your team or your company? Rocket Chat may be what you need.</p><p>I’ve just heard about this package today. It’s an online chat tool based on <a href="https://www.meteor.com/">Meteor</a>, open source, full features, multi devices, and quite easy to setup.</p><p>In order to install <a href="https://rocket.chat/">Rocket Chat</a>, you can use Ubuntu server. Your server must have nginx, node.js and MongoDB.</p><p>If everything is already, we install <a href="https://github.com/tmux/tmux">tmux</a> and <a href="https://www.meteor.com/">meteor</a> first:</p><pre>sudo apt-get install tmux<br>sudo npm install -g meteor</pre><p><strong>tmux</strong> would be used for keeing Meteor app alive even when you close terminal.</p><p>Now, clone Rocket Chat repository into <em>rocket-chat</em> directory:</p><pre>git clone <a href="https://github.com/RocketChat/Rocket.Chat.git">https://github.com/RocketChat/Rocket.Chat.git</a> rocket-chat<br>cd rocket-chat<br>meteor</pre><p>The last command would download the needed npm and Meteor packages then start server. Wait until the process done, press “Ctrl+C” to stop it. We still need to configure host and database connection.</p><p>Here I try to set public domain for Rocket Chat application at <a href="http://g8.techpush.net/">http://g8.techpush.net/</a> and use local MongoDB:</p><pre>export ROOT_URL=<a href="http://g8.techpush.net/">http://g8.techpush.net/</a><br>export MONGO_URL=mongodb://127.0.0.1:27017/rocketchat</pre><p>Now, open <strong>tmux</strong> session and run “meteor”:</p><pre>tmux<br>meteor</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/653/1*EsuUHHvHxACbUN65hXfDbQ.png" /><figcaption>Start running Meteor app within tmux session</figcaption></figure><p>If nothing wrong, you have got it runs at the port 3000 — as default. Press “Ctrl+B” and then “D” to leave <strong>tmux</strong> session.</p><p>To access app from the internet, you can use the following <strong>nginx</strong> config:</p><pre>server {<br>    listen 80;<br>    server_name g8.techpush.net;<br>    charset utf-8;</pre><pre>    location / {<br>        proxy_read_timeout 300;<br>        proxy_pass <a href="http://127.0.0.1:3000">http://127.0.0.1:3000</a>;<br>        proxy_http_version 1.1;<br>        proxy_set_header Upgrade $http_upgrade;<br>        proxy_set_header Connection ‘upgrade’;<br>        proxy_set_header Host $host;<br>        proxy_cache_bypass $http_upgrade;<br>    }<br>}</pre><p>Save config file and restart <strong>nginx</strong>:</p><pre>sudo service nginx restart</pre><p>That’s it. You can start using Rocket Chat now:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gqISGcDMXDqIxnQirzFGeg.png" /><figcaption>Rocket Chat — default interface</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1019/1*WxHM17mrtnIu8dxYYPx9gg.png" /><figcaption>Rocket Chat — Administration</figcaption></figure><p>Rocket Chat can do more than that. Please take a look on <a href="https://github.com/RocketChat/Rocket.Chat#features">its feature list</a> and <a href="https://demo.rocket.chat/">give it a try</a>.</p><p><strong>Note</strong>: If your server did not have <strong>nginx</strong>, <strong>node.js</strong> or <strong>MongoDB</strong> yet, it may take a little time more to install them:</p><pre># install nginx<br>sudo add-apt-repository ppa:nginx/stable<br>sudo apt-get update<br>sudo apt-get install nginx</pre><pre># install node.js v5.3.0<br>wget <a href="https://nodejs.org/dist/v5.3.0/node-v5.3.0.tar.gz">https://nodejs.org/dist/v5.3.0/node-v5.3.0.tar.gz</a><br>tar -zxvf <a href="http://node-v5.3.0.tar.gz/">node-v5.3.0.tar.gz</a><br>cd node-v5.3.0<br>./configure<br>make<br>sudo make install</pre><pre># install MongoDB v3.2.0<br>sudo apt-key adv --keyserver <a href="http://hkp//keyserver.ubuntu.com:80">hkp://keyserver.ubuntu.com:80</a> --recv EA312927<br>echo &quot;deb <a href="http://repo.mongodb.org/apt/ubuntu">http://repo.mongodb.org/apt/ubuntu</a> trusty/mongodb-org/3.2 multiverse&quot; | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list</pre><pre>sudo apt-get update<br>sudo apt-get install -y mongodb-org=3.2.0 mongodb-org-server=3.2.0 mongodb-org-shell=3.2.0 mongodb-org-mongos=3.2.0 mongodb-org-tools=3.2.0</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2b00f3366c6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Capture webpage screenshot with PhantomJS]]></title>
            <link>https://medium.com/@ndaidong/capture-webpage-screenshot-with-phantomjs-465648c8b4a0?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/465648c8b4a0</guid>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Thu, 12 Mar 2015 06:41:25 GMT</pubDate>
            <atom:updated>2015-03-12T06:41:25.508Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*QLY10XCc90xn1Sm0geGrOw.png" /></figure><p>In the time of Node.js and <a href="http://phantomjs.org/">PhantomJS</a>, taking the webpage screenshot has became easier than ever. The first thing we need to do is install PhantomJS:</p><pre>git clone git://github.com/ariya/phantomjs.git<br>cd phantomjs<br>git checkout 1.9<br>./build.sh</pre><p>The above command lines get the source code from GitHub and build PhantomJS 1.9 from this source. If you want to use another version, please check release notes here:</p><p><a href="http://phantomjs.org/download.html">http://phantomjs.org/download.html</a></p><p>Every released version of PhantomJS has a “release name” after the name of a flower. PhantomJS 1.9 is “Sakura”. PhantomJS 1.8 is “Blue Winter Rose”… Sakura marks the end of the 1.x series as what <a href="https://plus.google.com/u/0/+AriyaHidayat/about">Ariya Hidayat</a> has explained on the forum of team.</p><p>Capture screen just is one of PhantomJS’ features. The main target of this library is simulate web browser’s behaviors. With a given URL, PhantomJS will try to handle it as a web browser: load resources, parse DOM, compile CSS/Javascript, render layout… and then, generate the web page interface as the result we would see on any web browser. Thus, PhantomJS is usually used in testing, monitoring, etc.</p><p>In order to capture a webpage’s screen, PhantomJS loads its HTML, CSS, Javascript and fonts, handle them as what a web browser does. Finally, PhantomJS uses HTML5 Canvas API to take a shot.</p><p>Compiling PhantomJS may take a few time. While waiting, we install a Node.js module named <a href="https://www.npmjs.com/package/url-to-screenshot">url-to-screenshot</a>. This module provides a simple and very useful APIs that allows developers easy to deal with PhantomJS.</p><pre>npm install url-to-screenshot</pre><p>OK. We will come back here later.</p><p>To check if PhantomJS has been installed successfully or not, type the following command:</p><pre>phantomjs -v</pre><p>If the system says that <em>phantomjs</em> command is not available, you might run the following commands to download PhantomJS 1.9.7 and make some needed symlinks:</p><pre>cd /usr/local/share<br>sudo wget <a href="https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.7-linux-x86_64.tar.bz2">https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.7-linux-x86_64.tar.bz2</a><br>sudo tar xjf phantomjs-1.9.7-linux-x86_64.tar.bz2<br>sudo ln -s /usr/local/share/phantomjs-1.9.7-linux-x86_64/bin/phantomjs /usr/local/share/phantomjs<br>sudo ln -s /usr/local/share/phantomjs-1.9.7-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs<br>sudo ln -s /usr/local/share/phantomjs-1.9.7-linux-x86_64/bin/phantomjs /usr/bin/phantomjs</pre><p>Check it again. If everything is OK, <em>url-to-screenshot</em> module is also ready to use. Here is a simple example to show how we can apply PhantomJS and <em>url-to-screenshot</em>:</p><pre>var Screenshot = require(‘url-to-screenshot’);</pre><pre>function take(url, callback){<br>   Screenshot(url)<br>      .width(1366)<br>      .height(768)<br>      .capture(function(err, imageData){<br>           if(!err &amp;&amp; !!imageData){<br>               return callback(imageData);<br>           }<br>           return callback(false);<br>      });<br>}</pre><p>The function “take” requires 2 parameters:</p><p>- url of web page to take screenshot for<br>- callback function to handle returned data</p><p>This callback function has only one parameter. If PhantomJS could not generate the webpage UI, a value <strong><em>false</em></strong> will be passed to callback. Conversely, the image data of screenshot will be returned and you can do anything with it, for example write to local file:</p><pre>var fs = require(‘fs’);<br>take(‘<a href="http://techpush.net/&#39;">http://techpush.net/&#39;</a>, function(imgData){<br>   if(!imgData){<br>      console.log(‘Could not take screenshot for this url’);<br>      return false;<br>   }<br>   var file = __dirname+’/screenshots/test.png’;<br>   fs.writeFileSync(file, imgData);<br>});</pre><p>For more info about <em>url-to-screenshot</em>, please visit its repository here:</p><p><a href="https://github.com/juliangruber/url-to-screenshot">https://github.com/juliangruber/url-to-screenshot</a></p><p>However, you can choose <a href="https://github.com/search?l=JavaScript&amp;q=screenshot&amp;type=Repositories">another modules</a>. Some of them even supports more options such as browser type, screen size, image quality, etc.</p><p>The screenshots made by PhantomJS are very because it supports Unicode perfectly. Based on my experience, the accuracy can reach 95%. The problem often arises only when the web page using the rare fonts.</p><p>PhantomJS is used on <a href="http://fomo.link/">FOMO</a> while creating thumbnails for the articles. When we detect an article that does not have image for creating thumbnail, we will use the screenshot of that article’s URL, as the following figure:</p><p>FOMO Screenshot : <a href="http://i.imgur.com/NBHa9l7.png">http://i.imgur.com/NBHa9l7.png</a></p><p>Here are some screenshots to show the ability of PhantomJS, created for <a href="http://fomo.link/">FOMO</a> system.</p><p>URL : <a href="http://goo.gl/NvxTRd">http://goo.gl/NvxTRd</a><br>Screenshot : <a href="http://i.imgur.com/1iFe4z4.png">http://i.imgur.com/1iFe4z4.png</a></p><p>URL : <a href="http://goo.gl/LC2wml">http://goo.gl/LC2wml</a><br>Screenshot : <a href="http://i.imgur.com/4PlYrgJ.png">http://i.imgur.com/4PlYrgJ.png</a></p><p>URL : <a href="http://goo.gl/zF6O4j">http://goo.gl/zF6O4j</a><br>Screenshot : <a href="http://i.imgur.com/XcrrYku.png">http://i.imgur.com/XcrrYku.png</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=465648c8b4a0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Keep Node.js processes alive with PM2]]></title>
            <link>https://medium.com/@ndaidong/keep-node-js-processes-alive-with-pm2-baac19a2380d?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/baac19a2380d</guid>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Tue, 10 Mar 2015 05:24:53 GMT</pubDate>
            <atom:updated>2015-03-10T05:27:57.660Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/710/1*s6oVeEYRQpsW7qJ9eAKPvg.png" /></figure><p>There are more than one way to do that, such as <em>systemd, forever, nodemon, node-supervisor</em> and PM2. Forever was the best choice in the past but now it has become outdated. So I recommend to use <a href="http://promotion.pm2.io">PM2</a> here.</p><p>PM2 should be globally installed as below:</p><pre>sudo npm install -g pm2</pre><p>Since now, we have the command <em>pm2</em> that can be called from anywhere.</p><p>This blog is Ghost platform that I run on an EC2 instance. When the instance reboots by some reason, I’d like to Ghost can be restarted automatically. Here’s what I do:</p><pre>cd /var/www/ghost<br>pm2 start index.js —name “ghost”<br>pm2 dump<br>sudo pm2 startup ubuntu</pre><p>At the second line, <em>—name “ghost”</em> means that we name process “ghost” to distint it from other processes and can refer to it in other commands.</p><p>PM2 can generate and configure a startup script to keep itself and the processes it manages alive at every server restart. At last 2 lines, PM2 dump the current process into startup script. The command <em>dump</em> will save process information into a JSON file located at /home/USER/.pm2/dump.pm2 — it will be loaded while server start.</p><p>You can replace “ubuntu” there by other OS name as you are using, such as <em>amazon, centos,</em> etc.</p><p>After running the last command, it may return:</p><pre>[PM2] Generating system init script in /etc/init.d/pm2-init.sh<br>[PM2] Making script booting at startup…<br>[PM2] -ubuntu- Using the command su -c “chmod +x /etc/init.d/pm2-init.sh &amp;&amp; update-rc.d pm2-init.sh defaults”<br> Adding system startup for /etc/init.d/pm2-init.sh …<br> /etc/rc0.d/K20pm2-init.sh -&gt; ../init.d/pm2-init.sh<br> /etc/rc1.d/K20pm2-init.sh -&gt; ../init.d/pm2-init.sh<br> /etc/rc6.d/K20pm2-init.sh -&gt; ../init.d/pm2-init.sh<br> /etc/rc2.d/S20pm2-init.sh -&gt; ../init.d/pm2-init.sh<br> /etc/rc3.d/S20pm2-init.sh -&gt; ../init.d/pm2-init.sh<br> /etc/rc4.d/S20pm2-init.sh -&gt; ../init.d/pm2-init.sh<br> /etc/rc5.d/S20pm2-init.sh -&gt; ../init.d/pm2-init.sh</pre><pre>[PM2] Done.</pre><p>Sometimes, PM2 may return a guideline looks like this:</p><pre>[PM2] You have to run this command as root<br>[PM2] Execute the following command :<br>[PM2] sudo env PATH=$PATH:/usr/local/bin pm2 startup ubuntu -u ubuntu</pre><p>Just copy &amp; paste it to run:</p><pre>sudo env PATH=$PATH:/usr/local/bin pm2 startup ubuntu -u ubuntu</pre><p>That’s all.</p><p>PM2 also provides a set of useful methods to help developers manage and monitor the Node.js processes such as <em>monit, logs, status,</em> etc. Please refer the homepage and try them by yourself today:</p><p><a href="http://promotion.pm2.io/">http://promotion.pm2.io/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=baac19a2380d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Make a new history for old git repository]]></title>
            <link>https://medium.com/@ndaidong/make-a-new-history-for-old-git-repository-4486879b4cee?source=rss-528ebe75b3c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/4486879b4cee</guid>
            <dc:creator><![CDATA[Dong Nguyen]]></dc:creator>
            <pubDate>Sun, 08 Mar 2015 01:12:07 GMT</pubDate>
            <atom:updated>2015-03-10T05:28:51.532Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*LBTdc-JuFhif95aodoQbrA.png" /></figure><p>When you use <em>git clone</em> to get a repository, it always downloads all histories those were stored within <strong>.git</strong> folder. That means if you’ve been uploading a file 100MB in the past, and then you’ve removed it yesterday, and then now you’d like to clone it again, you still have to download that file, 100MB, because it’s a part of repo’s history.</p><p>So, sometimes you might want to clean up them all. Here is the way to do.</p><pre>git checkout —orphan temp<br>git add *<br>git commit -a<br>git branch -D master<br>git branch -m master<br>git push -f origin master</pre><p>What does that means? Theorically, we just create a new <strong><em>orphan</em></strong> branch named “temp”, add the whole data into it, remove the master branch and then set the temp branch as master. Finally, we push them to server with a <strong><em>force</em></strong> flag. That’s all.</p><p>Now we are staying on the master branch, a new master branch. We can make this branch track a remote “master” branch by using one of 2 following commands:</p><pre>git branch —set-upstream-to=origin/master</pre><p>or:</p><pre>git branch -u origin/master</pre><p>In the world of git, an <strong><em>orphan</em></strong> branch is a completely new branch where the first commit made on will have not any reference, so that it will be the root of a new history totally.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4486879b4cee" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>