Rabu, 17 Maret 2021

NestJS is Great, Why?

I have started with NextJS for my front-end stack and looking for another framework or library for the backend stack. Now, my eyes staring at NestJS. If you on javascript environment most of you will choose the plain NodeJS or Express to be your back-end stack. Express is symbol of freedom, express is a framework that you can choose any NodeJS libraries that you want to used. 

Maybe is a positive if you are a single fighter or small team. When your team getting bigger or you change/add team member the pattern and docs are must be clear to be fastest on adaptation. So, your new team member can focus on task list instead of deducting how is your backend works (optimize the productivity)

When I looking at NestJS, all the good things are in it. The code are readable, clean, and structurable. Almost the same when I try Angular 5 (if you are Java programmer is easy to read and code the Angular 5 app). If you need a great documentation it's easy to convert it into Swagger.

Let's try to install NestJS

First, make a directory folder for your project. Open your terminal and route into the folder. After that type the code below to init/create NestJS project.

npm i -g @nestjs/cli
nest new your-project-name

After finish, run the project with typing "npm run start" or "npm run start:dev" if you want to using hot reload feature. Now, access the project typing "localhost:3000" on your browser. If you want to change the port, you can open and edit the "main.ts" file.

For Swagger, you can type code below 

npm install --save @nestjs/swagger swagger-ui-express

Database

For database abstraction, you can use Prisma or TypeORM. If you want to know more about them, you can read it on this link What the difference Prisma and TypeORM. For this case, I choose to use the Prisma because it easy, simple, and readable syntax. But for you. you decided by yourself. 

  1. Installation: npm install prisma --save-dev
  2. After install successful, type "npx prisma" on terminal it will give you information about Prisma command.
  3. Then type "npx prisma init", it will create .env file to setup your database. 
  4. More information you can read it here "How to setup Prisma on NestJS". 





Sabtu, 19 September 2020

Using Nodemon to Make Fast Refresh on Dynamic Routes and Express API in NextJS Project

The time when I create dynamic routes using route.js file in my NextJS project is very confusing. When I created or edited file in Express API, there are no automatic changes. I should typing "npm run dev" in my terminal cause it takes so much time to wait for the changes. 

So, I start to search on Google is there I can do with this, to make it faster if it cannot be automatic instead. Yup, there is NPM library called Nodemon. You still have to type some code to refresh the changes, but it is faster than just "npm run dev" do. 

Let's start to try how it is. 
  1. Let's install the library by typing "npm i nodemon" on the terminal. 
  2. After the nodemon successful installed, open the "package.json" and change dev script into "nodemon -w server.js" like on below. 
  3. Then, open your terminal and type "npm run build". 
  4. Wait for it, after finish then types "npm run build".
  5. Makes some changes on your Express API files. 
  6. Open the terminal again, type "rs" and press enter. 
  7. Voila... it's much faster than "npm run dev" to refresh or restart the server. 
Note:
  • I read some articles and found another way to solve this problem. You can use ts-node-dev but I'll try it later. Source: https://stackoverflow.com/questions/37979489/how-to-watch-and-reload-ts-node-when-typescript-files-change
Thank you for reading my blog post, see you in other articles. You can contact me if you wanna some help or make a collaboration.


Sabtu, 05 September 2020

Menambahkan Sitemap di Aplikasi Next.js Agar Lebih Mudah Dikenali Google

Hallo apa kabar, semoga kalian selalu sehat dalam lindungan-Nya. Kali ini saya ingin berbagi tips mengenai bagaimana cara menambahkan sitemap pada aplikasi Next.js secara otomatis atau auto generate. Mungkin ada sebagain dari kalian yang bertanya, apa sih fungsi dari sitemap ini? Baikalah akan saya jelaskan apa itu sitemap dan apa fungsinya.

Sitemap merupakan salah satu protokol yang digunakan sebuah webmaster untuk menginformasikan kepada para search engine tentang apa aja sih isi dari website tersebut. Sitemap berbentuk file XML yang berisi kumpulan daftar url yang ada di website tersebut. Tapi kamu juga bisa memilih bagian mana yang perlu untuk diberitahukan ke search engine dan yang tidak perlu. Yang tidak perlu ini umumnya yang bersifat credentials seperti bagian admin dan login.

Lalu, apa bedanya jika sebuah website memiliki sitemap dan tidak memiliki sitemap? Bedanya adalah jika menggunakan sitemap pada saat robot crawler miliki Google berkunjung ke website kamu maka robot crawler tersebut akan diberikan kumpulan url atau halaman yang ada di website kamu yang kemudian disimpan di database Google. Dengan begitu search engine seperti Google, Bing, Yahoo, dan Yandex dapat dengan mudah memetakan halaman yang ada di website kamu. Yang nantinya dicocokan dengan kebutuhan pengguna pada saat melakukan pencarian. Jika, tidak ada sitemap crawler bisa kebingungan untuk memetakan halaman website dan juga tidak maksimal dalam pengindeksan halaman website yang berpengaruh terhadap SEO (Search Engine Optimization).

Gimana kamu tertarik untuk menambahkan Sitemap di aplikasi Next.js kamu?

  1. Yang pertama dilakukan adalah kita buka halaman dokumentasi npm library-nya  nextjs-sitemap-generator
  2. Jika sudah membacanya selanjutnya buka root directory aplikasi Next.js kamu melalui terminal.
  3. Kemudian install library nextjs-sitemap-generator dengan mengetikkan ini di terminal: npm i nextjs-sitemap-generator
  4. Jika sudah buat file dengan nama "your_nextjs_sitemap_generator.js" atau terserah kamu yang penting tidak bentrok dengan file lain. Tapi usahakan penamaannya jelas dan sesuai. 
  5. Ketikan teks berikut di dalam file. 
  6. const sitemap = require("nextjs-sitemap-generator");
    sitemap({
      baseUrl: "https://urlwebsitekamu.com",
      pagesDirectory: __dirname + "/pages",
      targetDirectory: "public/"
    });
  7.  Kamu juga bisa menggantinya sesuai dengan kemauan kamu, seperti menambahkan route url atau file mana saya yang perlu di-ignore atau hide. Untuk keterangan lebih lanjut bisa kamu baca di dokumentasinya.
  8. Setelah selesai, lanjut build project kamu dengan mengetikkan : npm run build
  9. Selanjutnya jalankan file "your_nextjs_sitemap_generator.js" dengan menteikkan ini di terminal : node your_nextjs_sitemap_generator.js
  10. Setelah selesai, coba cek didalam directory public apakah ada file sitemap.xml atau tidak. Jika ada, selamat kamu telah berhasil menambahkan file sitemap di dalam aplikasi Next.js milikmu.
Baiklah, mungkin itu saja tips yang dapat saya bagikan semoga bermanfaat. Jika ada salah dan kurang saya mohon maaf. Jika ada masukan terkait artikel ini jangan boleh untuk kontak saya atau berikan komentar di bawah. Sekian dan terima kasih.




  




Sabtu, 08 Agustus 2020

Berbagi Tips dan Cara Optimalisasi Aplikasi Web NextJS

Hallo apa kabar? Semoga kalian baik-baik saja dimana pun kalian berada. Kali ini saya akan berbagi tips bagainana cara mengoptimasi aplikasi web yang menggunakan NextJS untuk tools benchmarking-nya saya menggunakan Lighthouse. 

Untuk kamu yang belum tahu, Lighthouse merupakan tools yang wajib digunakan untuk kamu developer aplikasi web. Dengan tools ini kamu akan mengetahui apakah aplikasi website kamu sudah memenuhi beberapa kaidah seperti dari segi performa, aksesbilitas (berkaitan dengan user experience), best pratices, SEO, dan juga PWA. Untuk tools lain yang sering saya gunakan kamu bisa membaca artikel ini. Kamu juga bisa menggunakan WebDev Measure untuk mengukur performa website. Kemarin saya sempat mengukur performa website saya, hasilnya seperti ini.




Performance

  • Serve images in next-gen formats
Apakah kamu tahu kalau Google mempunyai format image yang baru, yakni WebP. Format ini memiliki kemampuan lossy dan loseless. Apa itu lossy dan loseloss? Jadi gambar akan mengalami kompresi sehingga ukurannya akan lebih kecil dibandingkan ukuran aslinya (lossy). Meski begitu gambar tidak otomatis berubah atau berbeda (secara visual) dibandingkan gambar asli atau gambar sudah dikompres tidak kehilangan kualitasnya.

Beberapa web browser modern seperti Google Chrome, Firefox, Opera dan Edge, sudah support format WebP. Untuk coverage support-nya bisa kamu lihat di situs ini Can I Use, di situ dijelaskan bahwa hingga saat ini coverage support-nya sudah 94%. Sudah cukup baik bukan?

Memang belum semuanya ter-cover, kalau kamu tidak mau menggunakan WebP kamu masih bisa menggunakan format lain seperti PNG atau JPEG. Situs yang biasa saya gunakan adalah TinyPNG dan juga TinyJPG.

  • Gunakan attribute "lazy" saat memuat gambar
Salah satu aspek yang mesti diperhatikan dalam pembuatan website adalah FCP atau First Content Painful. Menurut survei, 53% pengguna akan pergi apabila suatu website membutuhkan waktu lebih dari 3 detik untuk merender halaman website. Salah satu yang memperlambat proses render website adalah pada saat memuat image atau gambar. Untuk mengatasi masalah ini kamu bisa menambahkan atribute "lazy" pada saat memuat gambar, dengan menambahkan atribute ini seperti memberitahu pada browser "bagian ini nanti saja dirender, render dulu bagian lain yang lebih penting". Untuk contoh kodenya seperti berikut : <img src="./gambarku.png" loading="lazy">. Untuk lebih lengkapnya kamu bisa membaca artikel berikut ini dan juga ini.

Accessbility 

  • <html> element does not have a [lang] attribute
Dengan menambahkan atribut "lang" pada tag html akan sangat berguna dan membantu untuk meningkatkan SEO website kamu. Jadi Google akan tahu apakah website kamu menggunakan bahasa Inggris atau bahasa Indonesia. Sehinngga Google akan lebih merekomendasikan artikel atau website kamu apabila cocok dengan background pengguna seperti lokasi dan faktor lainnya. Selain itu, hal ini akan sangat membantu bagi para teman tuna netra yang biasa menggunakan bantuan tools Narator untuk membacakan isi teksnya. Jadi mesin narrator akan menyesuaikan gaya bicara sesuai atribute lang yang ditambahkan, jadi akan lebih enak didengar. 
  • Background and foreground colors do not have a sufficient contrast ratio
Ketika membuat tampilan website ada baiknya kamu juga mempertimbangkan penggunaan warna. Hal ini terkait dengan aksesbilitas dalam hal kemudahaan membaca teks. Apabila warna teks dengan background tidak memiliki kontras yang cukup maka hal ini dapat menyusahkan user. User akan menatap lebih lama ke layar yang dituju sampai menemukan maksud teks tersebut. Solusinya kamu bisa menggunakan berbagai macam tools untuk mengecek kontras, salah satunya adalah website ContrastRatio. Pastikan nilainya di atas 4.5, semakin besar semakin bagus.
  • Nanti dilanjut lagi... :)














Sabtu, 30 Mei 2020

Manajemen Color atau Warna di Xcode (Swift) Agar Rapi dan Dinamis

Oke, sekarang saya akan berbagi tips bagaimana mengatur atau memanajemen penggunaan warna agar lebih rapi dan dinamis. Untuk kamu yang sedang belajar membuat aplikasi untuk iOS menggunakan Xcode dan Swift tips ini akan sangat membantu. Hal dasar yang perlu dipelajari pertama kali adalah bagaimana cara mengimport warna dari luar. Secara default Xcode telah menyediakan warna dasar, jika kalian memakai SwiftUI cukup memanggilnya dengan kode
Color.red
atau jika kalian memakai UIKit cukup memanggilnya dengan
UIColor.red
Untuk mengimport warna dari luar,

  1. Pada tab list item file pilih asset color 
  2. Klik kanan new set color, lalu rename sesuai mau kamu 
  3. Klik kanan pada folder asset color, pilih reveal in finder 
  4. Cari file JSON berdasarkan nama warna tadi, terus buka pakai text editor lalu edit nilai RGB agar sesuai dengan warna yang kamu inginkan. 
  5. Tips dari saya adalah buka directory/folder warna menggunakan text editor Atom, jadi apabila kamu menambahkan warna kembali di Xcode untuk mengubah nilainya tinggal membuka Atom. 

Setelah selesai, kamu bisa memanggil warna yang kamu tambahkan tadi dengan kode sebagai berikut :

Untuk SwiftUI,
Color.init(“nama warna”)
Sedangkan untuk UIKit,
Color.init(named : ”nama warna”) 
 Jika kamu menggunakan cara di atas kekurangannya adalah

  1. Akan susah untuk tracing secara langsung siapa yang menggunakan warna tersebut (caller), kalau mau mencarinya harus menggunakan menu “search in workshop”. 
  2. Kamu akan kesulitan untuk memanggil nama warna. Jadi tiap mau menambahkan warna kamu harus membuka asset color untuk mengetahui nama warna yang ingin kamu gunakan. 
  3. Apabila ada perubahan refactor akan sulit dilakukan. 
Lalu, apakah ada cara lain yang lebih mudah? Tentu saja, caranya kamu bisa membuat file extension dari class Color atau UIColor. Contohnya seperti di bawah ini.

import Foundation
import SwiftUI

extension Color {

    struct FlatColor {
        
        struct Grey {
            static let Accent = Color.init("Color Accent Grey")
            static let Accent400 = Color.init("Color Accent Grey 400")
            static let Light = Color.init("Color Grey Light")
            static let Light1 = Color.init("Color Light Grey 1")
            static let Light2 = Color.init("Color Light Grey 2")
            static let IronGray = Color(netHex: 0x75706B)
        }
        
        struct Red {
            static let Maroon = Color.init("Color Red Maroon")
        }
        
        struct View {
            static let Shadow = Color.init("Color Accent Grey 400")
        }
    }

    struct GradientColor {
        struct View {
            static let Shadow = Color.init("Color Accent Grey 400")
        }
    }

    init(netHex:Int) {
        self.init(red:Double((netHex >> 16) & 0xff), green:Double((netHex >> 8) & 0xff), blue:Double(netHex & 0xff))
    }

}


Nah, jika dilihat dari kodingan tersebut terdapat kata extension. Extension kalau di Java sama seperti extend yakni kamu dapat mengextend class utama (penurunan sifat). Class utama yang sama pakai adalah class Color. Lalu terdapat dua struct utama, yakni FlatColor dan GradientColor. Hal tersebut saya pakai agar mempermudah untuk membedakan warna yang ingin saya panggil. Misalnya kalau saya mau memanggil warna dasar/satu macam warna, contohnya memanggil warna light grey cukup dengan. 
Color.FlatColor.Grey.Light
Selain itu terdapat struct View untuk mengelompokkan warna yang khusus dipakai untuk mengolah element view seperti shadow. Sedangkan struct utama GradientColor untuk mengelompokkan warna-warna gradient.

Terdapat juga fungsi untuk dengan parameter netHex yang bertujuan untuk memanggil warna berdasarkan parameter nilai hex yang diinputkan, contohnya :
Color(netHex: 0x757068)



Jumat, 29 Mei 2020

Kenapa Saya Lebih Memilih Menggunakan SwiftUI Ketimbang UIKit dan Storyboard



Storyboard and UIKit

Untuk programmer/developer iOS yang sudah mempunyai banyak pengalaman tentu tidak asing dengan Storyboard dan UIKit. Storyboard adalah fitur atau tool yang terdapat pada aplikasi Xcode. Fitur ini membantu developer untuk melihat layout atau tampilan aplikasi yang sedang dibikin tanpa harus melakukan build. Selain itu, untuk membuat layout kamu juga tinggal drag and drop.

Di atas sudah saya jelaskan kelebihannya, lalu apakah Storyboard memiliki kekurangan? Ketika saya mencoba membuat layout menggunakan Storyboard banyak kekurangan yang saya temukan antara lain pada saat membuka file Storyboard loadingnya lumayan lama. Kekurangan lainnya adalah penggunaan constraint agar posisi atau tata letak layout sesuai dengan yang kita inginkan cukup memakan waktu karena sangat berbeda dibandingkan constraint layout yang ada di Android Studio.

File Storyboard kalau dibuka berisi kodingan atau tulisan XML. Jika dibaca mungkin akan membingungkan, lebih mudah untuk membaca kodingan bahasa Swift. Sehingga apabila terjadi conflict di repository project kemungkinan besar akan memakan waktu. Daripada membuat dan me-resolve kodingan layout menggunakan Swift.

Untuk penjelasan mengenai kekeurangan dan keleihan Storybaord di atas sudah saya rangkum seperti di bawah ini.

Kekurangan :
  1. Loading lama 
  2. Harus terbiasa dengan constraint, kalau menurut saya ribet, butuh waktu untuk mengatur layout sesuai dengan yg diinginkan 
  3. Akan susah untuk meresolve apabila terjadi conflict saat menggunakan git
Kelebihan :
  1. Kamu bisa melihat preview layout yg kamu buat dan tahu screen selanjutnya ke mana 
Membuat Layout menggunakan Swift

Selain menggunakan Storyboard, kamu juga bisa membuat layout menggunakan kodingan Swift. Jadi, kamu tidak perlu lagi atau tidak sering-sering membuka file Storyboard. Bagi saya membuat layout menggunakan Swift lebih mudah dibaca dan dipahami daripada drag and drop menggunakan Storyboard. Namun, kekurangannya adalah kamu harus mem-build project untuk bisa melihat perubahan pada layout yang kamu buat.

Kelebihan :
  1. Loading cepat dibandingkan membuka Storyboard.
  2. Kodingan mudah dibaca dan dipahami.
  3. Reusable.
  4. Mudah me-resolve apabaila terjadi conflict.
Kekurangan : 
  1. Harus mem-build untuk melihat perubahan layout.
  2. Tidak ada ada fitur preview.
Membuat Layout Menggunakan SwiftUI




Swift adalah bahasa pemrograman hasil karya Apple untuk menggantikan bahasa Objective-C dan Cococoa Touch yang bisa dibilang sudah ketinggalan dan tidak relevan dengan perkembangan developer. Oleh karena itu Apple membuat Swift untuk mendukung ekosistem Apple ke arah lebih baik lebih modern. Tidak hanya itu, bahasa Swift juga bisa digunakan untuk bahasa server misalnya membuat API.

Jika kamu menggunakan Storyboard, di situ akan kamu temui di mana view dan controller dibuat secara terpisah. Sehingga banyak sekali kodingan yang tidak perlu. Karena dianggap obsolete, usang, dan tidak relevan. Apple menghadirkan teknologi atau fitur baru yang bernama SwiftUI. Pada SwiftUI view dan controller dibuat dalam satu file. Sehingga lebih simple, ringkas, dan lightweight atau mempunyai ukuran file yang lebih kecil dibandingkan menggunakan Storyboard atau UIKit.

Untuk menggunakan SwiftUI kamu harus menggunakan Xcode versi 11. Minimal kamu menggunakan OS Mojave meski tanpa ada fitur live preview. Jika kamu ingin menggunakan fitur live preview SwiftUI maka kamu harus meng-upgrade OS kamu ke Catalina.

Syarat lain untuk menjalankan SwiftUI di Xcode adalah minimal target aplikasi iOS 13. Sehingga apabila target aplikasi masih dibawah iOS13, misalnya iOS 12. Kamu masih bisa menjalankan SwifUI berbarengan dengan UIKit dengan memberikan exception yakni aplikasi iOS 13 akan menjalankan kodingan SefitUI sedangkan dibawah versi itu akan menjalankan kodingan di controller Storyboard yang menggunakn UIKit.

Saat ini adalah waktu yang tepat untuk berinvestasi menggunakan SwiftUI dan mulai migrasi atau meninggalkan Storyboard dan UIKit. Dukungan penuh Apple membuat masa depan SwiftUI semakin cerah. Untuk developer, syarat mengunggah  aplikasi ke App Store adalah minimal target aplikasi untuk iOS 13. Jadi, menurut saya ini adalah salah satu cara atau strategi Apple untuk menyuruh developer untuk mulai migrasi menggunakan SwiftUI.

Membuat layout menggunakan SwiftUI sangatlah mudah, mudah dipahami dan mudah dibaca. Apabila sebelumnya kalian sudah mengenal atau belajar Jetpack Compose di Android ataupun Flutter. Bersyukurlah, karena struktur kodenya sangat mirip (declarative UI) sehingga memudahkan untuk beradaptasi. Oke, sekian penjalsan dari saya.

Kelebihan :
  1. Kodingan mudah dibaca dan mudah dipahami sehingga mempersingkat waktu untuk membuat layout.
  2. Strukturnya sama dengan Jetpack Compose di android dan juga flutter sehingga memudahkan developer untuk beradaptasi.
  3. Karena view dan controller jadi satu jadinya lebih simple dan ringakas.
  4. Dukungan penuh dari Apple. 
  5. Storyboard sudah tidak relevan untuk kebutuhan programmer
Kekurangan :
  1. Karena baru jadi referensinya masih sedikit. Akan tetapi sudah mulai banyak course yang bermunculan baik di Udemy amupun di YouTube.

Selasa, 26 Mei 2020

Mencoba Fungsi Git di Xcode dengan Meng-clone Project dari Gitlab

Oke, kali ini saya ingin berbagi tips tentang bagaimana mengclone repository yang ada di Gitlab untuk dijalankan di Xcode.

Pertama adalah pastikan kamu sudah menginstal aplikasi Xcode. Jika belum kamu bisa mendownloadnya dari AppStore Apple.

Kalau sudah, silakan buka terminal lalu ketik : git config --list

Selanjutnya akan ditampilkan settingan Git global, di situ kamu dapat melihat user.name dan user.email.

Setelah itu, kita membutuhkan access account Gitlab dari Xcode. Caranya buka aplikasi Xcode melalui Spolight Search. Ketika aplikasi Xcode sudah terbuka, tekan “Command + ,”. Ketika halaman preference sudah tampil, pilih tab Account. Lalu, login ke account Gitlab caranya dengan mengklik icon “+” di pojok kiri bawah.

Pilih Gitlab.com dan tekan tombol confirm.

Login Gitlab Account
Login ke akun Gitlab

Seperti pada gambar di atas, kamu disuruh memasukkan alamat email dan juga token yang harus kamu generate dari Gitlab.com.


Berikut ini adalah cara untuk mendaptkan access token Gitlab :

1.     Kalau sudah login ke website Gitlab, klik foto profil kamu. Selanjutnya pilih Settings dan Access Tokens.
2.     Ketik nama aplikasi kamu dan pilih tanggal expired token. Jangan lupa pilih grant access yang kamu butuhkan. Untuk lebih lengkapnya kamu bisa baca dokumentasi berikut: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html
3.     Jangan lupa untuk menyimpan hasil generate token karena setelah halaman tersebut kamu refresh maka kamu tidak bisa melihat token tersebut lagi. Namun kamu jangan kawatir apabila ingin menghapus token yang sedang aktif, caranya pilih tombol Revoke pada menu Active Personal Access Token.


Jika sudah mendapatkan token selanjutnya, kembali ke halaman Preference. Masukkan alamat email dan juga access token yang sduah didapat. Lalu, klik tombol Sign In.

Setelah berhasil login, kembali ke buka aplikasi Xcode dan pilih menu “Clone an existing project”. Selanjutnya kamu diminta memasukkan alamat url project di Gitlab yang akan kamu clone dan kemudian tekan tombol Clone.

Jika alamat url valid, selanjutnya kamu akan disuruh memilih branch yang kamu inginkan. Selesai.


Pada saat saya mau menjalankan project terdapat error sebagai berikut :

Showing Recent Issues
/Users/aprilian/Dev iOS /rencanamu-ios/Pods/Target Support Files/Pods-Rencanamu.id/Pods-Rencanamu.id.debug.xcconfig: unable to open file (in target "Rencanamu.id" in project "Rencanamu.id")


Error tersebut dikarenakan saya belum menginstal Pod. So, kita install CocoaPods dengan mengetikkan code berikut di terminal: brew install cocoapods

Install Cocoapods
Install Cocoapods

Setelah Cocoapods berhasil diinstall, sekarang buka directory project kamu di terminal. Kalau sudah ketik ini :
pod deintegrate
pod install

Hasilnya seperti gambar berikut:

 

Oke error yang tadi sudah solve. Berikut ada error library yang belum ke install.

/Users/aprilian/Dev iOS /rencanamu-ios/Rencanamu.id/Screens/RegisterScreen/RegisterViewController.swift:10:8: No such module 'SVGKit'

Ternyata cukup build ulang, dan jadilah seperti gambar berikut ini.


Aplikasi berhasil dijalankan
Aplikasi berhasil dijalankan


Kamis, 14 Mei 2020

Pede Sesuai Porsinya

Pengen nulis lagi, kali ini tentang "pede sesuai porsinya".

Gagal itu normal, lu gak bisa kayak yang lain itu normal.
Berhenti ngebanding-bandingin diri lu sama orang lain, kalau orang lain begini begitu, pengen kantong ajaib, ya biarain aja, lha wong itu hak mereka.

Effort belum tentu sama dengan hasil, artinya kalau effort lu gede tapi hasilnya gak sesuai yang lu harepin ya itu normal.
Sering kali itu yang bikin kita kecewa.
Tapi bukan berati lu gak harus berusaha.
Iya awalnya gw kek gitu, kecewa, tapi pas gw merasa seperti itu, gak ada untungnya buat gw, kalau terus dipikirin malah makin blangsak aja.

Dulu gw pernah menulis, kalau ada sesautu yang tidak bisa kita kontrol dan sering kali itulah yang membuat kita kecewa, faktor eksternal.

Praise yourself, praise your work, lu gak harus butuh approval dari orang lain untuk menganggap diri lu berharga.
Kalau orang lain gak notice diri lu atau karya lu ya gak masalah, tapi ketika lu gak bangga dengan diri lu, karya lu, atau pencapain lu itu menjadi masalah.
Kenapa? karena lu punya hak untuk itu, gunain dong. lu patut bangga dengan diri lu dan juga karya lu.

Don't use their standard to be your standard.

Dari situ gw belajar buat gak expect ke apapun sih.
Gw gak berharap terhadap apapun, gw gak berharap gw sukses, gw gak berharap ini itu.
Tapi ada kalanya gw pengen sesuatu, misalnya gw pengen beli sepeda tapi gw gak berharap gw punya sepeda.

Gw pengen ke Kathmandu, tapi gw gak berharap pergi ke Kathmandu.
Ya ntar kalau duit gw udah cukup, ya tinggal pergi aja, kalau gak ada duit gimana ? ya gak gimana-gimana.
Gw lakuin apa yang gw bisa, gw lakuin apa yang gw bisa, gw lakuin apa yang gw mampu.

Ada satu jargon yang menurut gw sesuai dengan apa yang gw cari “start small”.

Ada satu hal lagi, ketika gw baca jurnal orang-orang mereka ngetawain kehidupan mereka sampe hal terkecil dan yang awalnya menurut gw weird, gak jelas, paansi.
Lama kelamaan gw enjoy dan agak merubah cara pandang gw, meski gw tidak percaya dengan yang namanya harapan.
Tapi dengan melihat harapan orang-orang tersebut membuat gw tersenyum dan lebih menikmati hidup.

Lalu kalau ada orang lain yang bilang kamu weirdo, kamu harus begini begitu, say it “i am what i am, and i am authentic”.
So, love yourself dan ingat kamu itu berarti.

Kalau cara pandang lu beda sama gw ya itu normal, cara pandang kita gak harus sama, gak harus sepaham. Karena kita adalah mahluk berpikir.

“I expect nothing, but I want something“, eh gak deng gw pengen banyak sih. hehe..

Kamis, 16 Januari 2020

Don’t know why God created fear when it becomes insecurities

Today, i am trying to make journal cause i want to make sure i have a medium to outta what’s on my mind. Fear? Do you know what fear is? Do you have a fear? yeah, me too. I am an introvert person, but when i see funny picture or meme i became so impulsive. But, my funny things maybe not your funny things.

Some say fear is good, but when it comes too much, it can tackle you down from your life path. This all about the insecurities problem that i faced. Insecurities are the biggest mistake that God has created. Still don’t know how to solve this issue.

Rabu, 08 Januari 2020

Rp 18.000,- Pas, Tidak Kurang Tidak Lebih

Tadi malam sepulang kerja menyempatkan waktu untuk membeli makan. Bukan kwetiaw, kali ini nasi goreng jadi pilihan karena bunyi perut kelaparan sudah mengalu-alu. Belilah nasi goreng campur mie tanpa tanya harganya dulu kepada bapak penjual. Setelah selesai makan masih duduk tenang, menunggu makanan turun ke usus.

Photo ini diambil sebelum makan karena ada tulisan "Dilarang parkir di depan pintu" persis di depan meja makan


Setelah itu, mau balik tanyalah harga nasi goreng ke penjualnya. Ternyata total habis 18ribu, mulailah merogoh uang yang ada di saku celana. Di sana cuma ada 1 lembar uang 10ribu dan 3 lembar uang 2ribu. Kemudian mulai membuka dompet, di situ cuma ada kertas bon dan kertas transaksi ATM. Mulai panik, di saat seperti itu yang teringat adalah Ibuk dimana beliau selalu bisa menemukan barang yang hilang di rumah. Di saat seperti itu, biasanya Ibuk bilang. "tenang.. ambil nafas.. kamu tadi mulai dari mana coba ditelusuri pelan pelan" dan ajaib benda itu ketemu.

Di saat saya panik, tenang menjadi solusi utama. Kemudian barulah saya ingat, biasanya di bagasi motor ada uang receh. Sebelum keluar tenda tempat makan untuk mengambil uang, entah ada angin apa saya nyeletuk dalam hati. "Bener nggak sih kalau di saat orang kesusahan Allah bakal bantu". Lalu saya tidak jadi keluar tenda balik ke tempat duduk. Membuka ransel bagian depan di situ ada 2 uang koin 500-an. Nah alhamdulillah, sekarang kurang seribu.

Gak tahu kenapa, tangan saya membuka dompet dan membuka-buka tiap sekat didalamnya barang kali ada yang terlewat. Benar saja, di antara tumpukan kertas bon tersebut ada 2 koin uang 500-an. Kalau ditotal pas Rp 18.000,- tidak kurang tidak lebih. Ternyata benar, kalau kita husnudzon kita yakin sama Allah maka Allah akan beri kemudahan untuk kita.

Minggu, 20 Oktober 2019

How to Know What the Most Video That I Like on Youtube [Text Extraction and Analysis] using Beautiful Soup, Selenium, and Youtube API in Python



Okay, this project was started on Saturday, 19 October 2019. At first, the reason why I want to build this project is I want to know what the topics that I like on Youtube and what is my favorite channel. 

Introduction 

1. Selenium

So, what is Selenium? "Selenium automates browsers. "

In modern web application, it's common that they use infinite scroll to load data instead of pagination. We know that is hard to scrapping the javascript web page than the PHP web page (server-side rendering). Most of the javascript web uses client-side rendering to render a web page.  

Youtube uses client-side rendering and infinite scroll, to avoid the limitation I use Selenium which commonly used for automation testing. With this library, you can control a web browser and interact with the website. I used it to load all Youtube data on the page using the Chrome browser and then scrapping it.

2. Beautiful Soup

Beautiful Soup is a Python library that use for data extraction of HTML and XML files. That is my favorite library to scrape and parse the HTML web page because it's easy to use and save a lot of time compare to manual parsing. 

3. Youtube API

Youtube is a video streaming services platform owned by Google where you can watch a lot of videos. Nowadays, Google opens API services for Youtube. So, it helps developers and everyone who wants to use Youtube as a 3rd party library services and to gather information.


The Goal 
The goal of this project is to give me information about: 
  • The topics that mostly I like based on the category.
  • My favorite channels.
  • What the words that commonly appear on the video that I like based on the title and tagging. 


1. Let's get started
First, install Anaconda and use Jyupter Notebook to run your python code. Here is the download link: https://www.anaconda.com/distribution/

After you finished install Anaconda, then install Selenium by running command below :
pip install selenium

Create your first project in Jupiter Notebook.  

After that, we need to import the web driver to control Chrome with our code. Here is the download link and chose the one that compatible with your browser: https://sites.google.com/a/chromium.org/chromedriver/downloads

more information about chrome web driver: https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver

Note: If you using macOS, at first you must enable permission at security settings. 

2. Getting Youtube API Key 
At this session, I need Youtube API to get information about video detail. The fields that I use are the title, description, category, channel id, channel name, and tags.

Before you can use Youtube API, you must register your application to get permission and access key.

3. Save the data to CSV File

Youtube has limitation quota access per day. So, I decided to save the data into local storage. Instead of save it into the MySQL database, I prefer to save it into CSV file because it easy to save and load data without querying first.

4. Stop words

I use stop words to filter and erase common words like the, a, that, and etc.

5. Present data into Chart and Wordcloud


The result 

My Favorite Categories




My Favorite Channels






Words by Title







Words by Tag




The Code using Juypter Notebook


from selenium import webdriver
from bs4 import BeautifulSoup
from selenium.webdriver.common.keys import Keys
import time
import json
import requests as reqs
from altair import Row, Column, Chart, Text, Scale, Color
import pandas as pd
import csv
from itertools import islice
import ast
import numpy as np
import json
import matplotlib.pyplot as plt
from stop_words import get_stop_words
import re

from os import path
from PIL import Image
import os

from wordcloud import WordCloud, STOPWORDS



class Video:
    def __init__(self, idx, title, description, date, channelID, channel, categoryID, tags):
        self.idx = idx
        self.title = title
        self.description = description
        self.date = date
        self.channelID = channelID
        self.channel = channel
        self.categoryID = categoryID
        self.tags = tags
    def asList(self):
        return [
            self.idx, 
            self.title,
            self.description,
            self.date,
            self.channelID,
            self.channel,
            self.categoryID,
            self.tags
        ]
    
    
def get_data(totalVideo, perPage, channelID, youtubeAPIKEY):

    driver = webdriver.Chrome('/Users/aprilian/Downloads/chromedriver')
    driver.get("https://www.youtube.com/channel/"+channelID+"/videos?view=15&flow=grid")

    maxIterate = int(totalVideo/perPage)

    elem = driver.find_element_by_tag_name('html')
    for i in range(0,maxIterate):
        elem.send_keys(Keys.END)
        time.sleep(5)
        
    html = driver.page_source
    soup = BeautifulSoup(html)
    
    videos = []
    videoList = []
    for tag in soup.find_all("a", {'id':['video-title']}, href=True):
        url = tag['href']
        videoID = remove_prefix(url, '/watch?v=')
        video = get_video_detail(youtubeAPIKEY, videoID)
        videos.append(video)
        videoList.append(video.asList())
        
    with open('My-Youtube-Data.csv', 'a') as csvFile:
        writer = csv.writer(csvFile)
        writer.writerows(videoList)
    csvFile.close()
    
    return videos
        
def get_video_detail(youtubeAPIKEY, videoID):
    response = reqs.get('https://www.googleapis.com/youtube/v3/videos?id='+videoID+'&key='+youtubeAPIKEY+'&part=snippet,contentDetails,statistics,status&hl=id')
    response_dict = json.loads(response.text)

    item = response_dict['items'][0]
    snippet = item['snippet']
    vid_id = item['id']
    vid_title = snippet['title']
    vid_date = snippet['publishedAt']
    vid_desc = snippet['description']
    vid_channelID = snippet['channelId']
    vid_channel = snippet['channelTitle']
    vid_categoryId = snippet['categoryId']
    vid_tags = snippet.get('tags')
    
    return Video(vid_id, vid_title, vid_desc, vid_date, vid_channelID, vid_channel, vid_categoryId, vid_tags)
    
    
def remove_prefix(text, prefix):
    if text.startswith(prefix): 
         text = text.replace(prefix, "", 1)
    return text

def result_by_title(videos, youtubeAPIKEY):
    
    text = ''
    counts = dict()
    for vid in videos:
        title = re.sub('\W+',' ', vid.title.lower())
        title = ' '.join( [w for w in title.split() if len(w)>1] )
        title = title.split()
        title = [word for word in title if word not in get_stop_words('en')]
        title = [word for word in title if word not in get_stop_words('id')]
        
        text += ' '.join(title)
        
        for t in title:
            counts[t] = counts.get(t, 0) + 1
            
    getWordCloud(text)
    
    counts = sorted(counts.items(), key=lambda x: x[1], reverse=True)

    words = dict()
    for c in counts:
        words[c[0]] = c[1]
        
    
    MAX_RESULTS = 18
    words = {k: words[k] for k in list(words.keys())[:MAX_RESULTS]} 
        
    #change dictionary into dataframe 
    print("======== Title Most Likely ========")
    plt.figure(figsize=(10,10)) 
    dfY = pd.Series(words, name='Title')
    
    #show data as chart
    ax = dfY.plot.barh(x='lab', y='val', rot=0)
    plt.show()
    

def result_by_category(videos, youtubeAPIKEY):
    counts = dict()
    for vid in videos:
        counts[vid.categoryID] = counts.get(vid.categoryID, 0) + 1
        
    counts = sorted(counts.items(), key=lambda x: x[1], reverse=True)
    
    categories = dict()
    for r in counts:
        response = reqs.get('https://www.googleapis.com/youtube/v3/videoCategories?id='+r[0]+'&key='+youtubeAPIKEY+'&part=snippet&hl=id')
        response_dict = json.loads(response.text)
        category = response_dict['items'][0]['snippet']['title']
        categories[category] = r[1]

    #change dictionary into dataframe 
    print("======== Category Most Likely ========")
    plt.figure(figsize=(10,10))
    dfX = pd.Series(categories, name='Category')
    
    #show data as chart
    ax = dfX.plot.barh(x='lab', y='val', rot=0) 
    plt.show()
        
    #return categories



def result_by_channel(videos):
    counts = dict()
    for vid in videos:
        counts[vid.channel] = counts.get(vid.channel, 0) + 1
        
    counts = sorted(counts.items(), key=lambda x: x[1], reverse=True)
    
    channels = dict()
    for c in counts:
        channels[c[0]] = c[1]
        
    
    MAX_RESULTS = 18
    channels = {k: channels[k] for k in list(channels.keys())[:MAX_RESULTS]} 
        
    #change dictionary into dataframe 
    print("======== Channel Most Likely ========")
    plt.figure(figsize=(10,10))
    dfY = pd.Series(channels, name='Channel')
    
    #show data as chart
    ax = dfY.plot.barh(x='lab', y='val', rot=0) 
    plt.show()

    
    #return channels


def result_by_tag(videos):
    text = ''
    counts = dict()
    for vid in videos:

        tag = vid.tags
        if len(tag) > 0:
            tag = ast.literal_eval(tag)
            for t in tag:
                #print(t)
                t = t.lower()
                counts[t] = counts.get(t, 0) + 1
                text += ''.join(t)
                
                #tList = t.split()
                #for t1 in tList: 
                    #print(t1)
                    #counts[t1] = counts.get(t1, 0) + 1
                    #text += ''.join(t1)
                
     
    getWordCloud(text)
                
    
    counts = sorted(counts.items(), key=lambda x: x[1], reverse=True)
    
        
    tags = dict()
    for t in counts:
        tags[t[0]] = t[1]
        
    
    MAX_RESULTS = 20
    tags = {k: tags[k] for k in list(tags.keys())[:MAX_RESULTS]} 
        
    #change dictionary into dataframe 
    print("======== Tags Most Likely ========")
    plt.figure(figsize=(10,10)) 
    dfY = pd.Series(tags, name='Tags')
    
    #show data as chart
    ax = dfY.plot.barh(x='lab', y='val', rot=2)
    plt.show()

    #return tags
    

def loadCSVData():
    with open('My-Youtube-Data.csv', 'r') as readFile:
        reader = csv.reader(readFile)
        data = list(reader)
    videos = []
    for v in data:
        videos.append(Video(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]))
    return videos


def getWordCloud(text):
    d = path.dirname(__file__) if "__file__" in locals() else os.getcwd()
    
    
    filtered_words = list(filter(lambda word: word not in getStopwords(), text))
    
    # read the mask image
    #alice_mask = np.array(Image.open(path.join(d, "owl.png")))

    stopwords = getStopwords()

    wc = WordCloud(background_color="white", max_words=2000,
                   stopwords=stopwords, contour_width=3, contour_color='steelblue')

    # generate word cloud
    wc.generate(text)

    # store to file
    wc.to_file(path.join(d, "result.png"))

    # show
    plt.figure(figsize=(20,20)) 
    plt.imshow(wc, interpolation='bilinear')
    plt.axis("off")
    plt.show()
    
    
def getStopwords():
    # Create stopword list:
    stopwords = set(STOPWORDS)
    stopwords.update(["official", "di"])
    return stopwords




youtubeAPIKEY = '[YOUR_YOUTUBE_API_KEY]'
channelID = '[YOUR_CHANNEL_ID]'
totalVideo = 1000
perPage = 30

print("starting and getting data...")

#getting data from Internet, but if you have your local data (CSV file command code on below)
videos = get_data(totalVideo, perPage, channelID, youtubeAPIKEY)
#load data from local file (CSV)
videos = loadCSVData()

print("processing data...")

result_by_title(videos, youtubeAPIKEY)

result_by_category(videos, youtubeAPIKEY)

result_by_channel(videos)

result_by_tag(videos)

print("finished...")


Reference :

Install Selenium webdriver
https://sites.google.com/a/chromium.org/chromedriver/getting-started
https://stackoverflow.com/questions/55304226/selenium-webdriver-driver-issue-mac

Trying Selenium infinite scroll
https://stackoverflow.com/questions/32391303/how-to-scroll-to-the-end-of-the-page-using-selenium-in-python/32629481
https://stackoverflow.com/questions/55400703/how-to-scroll-down-in-youtube-using-selenium


Save load CSV File
https://datatofish.com/export-dataframe-to-csv/
https://stackoverflow.com/questions/14037540/writing-a-python-list-of-lists-to-a-csv-file
https://www.programiz.com/python-programming/working-csv-files
https://www.dev2qa.com/python-read-write-csv-file-example/



Membuat Function di Python
https://www.w3schools.com/python/python_functions.asp

Membuat CLASS di Python
https://www.w3schools.com/python/python_classes.asp


Sorting Ditctionary
https://stackoverflow.com/questions/613183/how-do-i-sort-a-dictionary-by-value


Chart
https://altair-viz.github.io/user_guide/data.html

WordCloud
https://github.com/amueller/word_cloud
https://www.datacamp.com/community/tutorials/wordcloud-python


Remove Prefix
https://stackoverflow.com/questions/16891340/remove-a-prefix-from-a-string




Get ARray From JSON
https://stackoverflow.com/questions/2687225/json-keyerror-with-json-loads


Stop words
https://pypi.org/project/stop-words/
https://stackoverflow.com/questions/5486337/how-to-remove-stop-words-using-nltk-or-python


Remove Special Char 
https://stackoverflow.com/questions/43358857/how-to-remove-special-characters-except-space-from-a-file-in-python/43358965

Remove Single Letter
https://stackoverflow.com/questions/32705962/removing-any-single-letter-on-a-string-in-python