Blog

  • When it comes to compensation and benefits, other than base salary, which is the most important to you?

    When it comes to compensation and benefits, other than base salary, which is the most important to you?

    When it comes to compensation and benefits, other than base salary, which is the most important to you?

    Pic5

    This blog post is part of Udacity Data Scientists Nanodegree Program. Detailed analysis with all required code is posted in my github repository and Jupyter notebook.

    My github repository:

    My github repository

    Jupyter notebook code:

    My Jupyter notebook code

    Data source:

    Data source

    Each stage of the CRISP-DM process. These stages are:

    1. Business Understanding
    2. Data Understanding
    3. Prepare Data
    4. Data Modeling
    5. Evaluate the Resultssults

    Business Understanding

    In this Notebook, I will explore the 2023 Stack Overflow results to gather some information about the organization, main field of study, important benefits, and the increase in salary with the years of learning how to program.

    There are 4 questions I want to find answers to:

    • Question 1: What type of company or organization do you work for?
    • Question 2: What is your main field of study in college or university/for your undergraduate studies in 2023?
    • Question 3: Which is the most important benefits to you?
    • Question 4: The more you learn, will the more salary you get?

    Data Understanding

    In order to understanding data. I will do these steps:

    • Handle categorical data
    • Analyze, Model, and Visualize

    Question 1: What type of company or organization do you work for?

    pic1

    Evaluate the Results

    Through the chart, we can easily see:

    • The number of people working in Privately-held limited companies, not in startup mode, accounts for the largest number, about more than 16,000 people.
    • The number of people working in Publicly-traded corporations accounts for the second largest number, about 6,000 people.
    • The number of people working in State-owned companies is small, about less than 1,000 people. Thus, the difference between the number of people working between a Privately-held limited company and a State-owned company is quite large, about 15,000 people

    Question 2: What is your main field of study in college or university/for your undergraduate studies in 2023?

    pic2

    Evaluate the Results

    Through the chart, we can easily see:

    • The proportion of people graduating from field Computer science or software engineering is the largest, about 43%.
    • The rate of people graduating from field Computer engineering or electrical/electronics engineering ranks second, about 9%.
    • The rate of people graduating from field A health science is the least, about 1%. Thus, the proportion of people graduating from field Computer science or software engineering is much larger than field A health science, about 42%. Thereby, we see that field Computer science or software engineering is a field that is very attractive to learnersers.

    Question 3: Which is most important benefits to you?

    pic3

    Evaluate the Results

    Based on the chart, we see:

    • The benefit users are most interested in is Vacation/days off, accounting for about 15%.
    • Remote options is the second most important benefit, accounting for about 13.7%.
    • Child/elder care and Other are benefits that receive little attention, accounting for about 2% and 1% respectively. Lastly, when it comes to compensation and benefits, other than base salary, Vacation/days off is the following are most important to users

    Question 4: The more you learn, will the more salary you get?

    pic4

    Evaluate the Results

    Based on the map, we can see:

    • We can achieve the highest salary when we have 20 years or more of study experience.
    • From 19 to 20 years of study, we can reach the 2nd highest average salary.
    • The average salary is lowest when we have less than 1 year of study experience. From there we conclude that the more years of experience accumulated working on a project, the higher the average salary and vice versa.

    Conclusion

    In this article, we took a look at main field of study, important benefits, and the increase in salary with the years of learning how to program according to Stack Overflow 2023 survey data.

    We gathered the advice of the masses as to how to increase in salary with the years, which showed that the more years of experience accumulated working on a project, the higher the average salary and vice versa.

    When it comes to compensation and benefits, other than base salary, we then looked at the satisfaction of important benefits. This showed that Vacation/days off is the following are most important to users. We found that field Computer science or software engineering is a field that is very attractive to learnersers.

    The findings here are observational, not the result of a formal study

    To see more about this analysis, see the link to my Github available here

    View a detailed analysis report on Medium

    Blog_Medium

    Files Description in the repo

    The Stack Overflow Survey – 2023.ipynb – Notebook containing the data Analysis

    stack-overflow-developer-survey-2023.zip – Developers Survey Result Data and Result Schema

    Requirements

    pandas, matplotlib, jupyter-notebook (if running locally)

    License

    License

    Acknowledgements

    Data source:

    Data source

  • Giúp background của View co giãn tối ưu với 9-patch image trong Android

    Giúp background của View co giãn tối ưu với 9-patch image trong Android

    Đã bao giờ bạn phải xử lý một Dialog, Button hoặc 1 cái CardView design đặc biệt tràn đầy graphical art các kiểu con đà điểu chưa? Có thể bạn nhìn file design một lúc và export nguyên cái ảnh ra để rồi nhận ra nếu nhiều chữ hơn hoặc màn hình bé hơn to hơn -> ảnh vỡ, code của bạn tèo ?

    pain...
    không chỉ ở làng Mưa, mà trong file design cũng có pain…

    Và rồi bạn đành phải work around, tự cắt các thành phần của một design ra nhiều phần nhỏ và dùng ImageView ghép lại theo định nghĩa phần có thể giãn và phần không cần giãn. Well, code bạn vẫn chạy ổn, nhưng chưa phải tối ưu.

    Để tạo background cho những component mang tính graphical như vậy, chúng ta đã 9-patch image hỗ trợ.

    9-patch Image là gì cơ?

    9-patch image là drawable dạng ảnh png đặc biệt có thể co giãn theo một định nghĩa nằm trong file này. Loại drawable này thường được sử dụng để làm background cho các View, Android OS sẽ xử lý background dạng 9-patch co giãn theo định nghĩa của file thay vì giãn đều như drawable bình thường. File 9-patch sẽ có đuôi 9.png.
    Btw, 9-patch là 9 mảnh vá đó ? (trích của anh Nguyen Van Toan)

    Cấu trúc của ảnh 9-patch

    Như ảnh bạn có thể thấy 9-patch image được chia làm 2 thành phần:

    • Stretchable area
    • Fill area (Padding box)

    Stretchable area là phần mà ảnh có thể được giãn nếu cần thiết.
    Fill area là phần mà content của bạn được phép nằm trong đó (Vì thế nên còn gọi là Padding box đó hehe).

    Ngoài ra thì 9-patch image còn hỗ trợ Optical Bounds. Optical bounds trên 9-patch image sẽ hiển thị bằng đường kẻ màu đỏ.

    Như bạn thấy, khi định nghĩa chuẩn, View của bạn sẽ được co giãn một cách tối ưu.

    Nghe hay phết, vậy tạo 9-patch như thế nào z bro?

    Cách tạo 9-patch image

    Để tạo ra 9-patch image, Android Studio đã có sẵn tool support đầy đủ, bạn không cần tải thêm bất cứ thứ gì khác. Ngoài ra thì cũng có cách khác là sử dụng Simple nine-patch generator trong bộ tools online của Roman Nurik.

    Tạo bằng Android Studio

    Bước 1: Chúng ta click chuột phải vào ảnh PNG gốc, chọn Create 9-Patch file…

    Bước 2: bắt đầu tuỳ chỉnh các phẩn Stretchable area và Fill area cho hợp lý

    Bước 3: Sử dụng file 9-patch như một drawable bình thường (không cần ghi đuôi .9)

    Tạo bằng Simple nine-patch generator

    Để hỗ trợ các density khác nhau của các device Android khác nhau, chúng ta cần tạo đủ các resources như drawable-ldpi, drawable-mdpi, drawable-hdpi, drawable-xhdpi… nên nếu tạo bằng tay từng cái một có thể sẽ là 1 đống việc. Nên chúng ta dùng Tool!

    Bước 1: Mở trang web Simple nine-patch generator chọn Select image và chọn đúng loại desity của source ảnh bạn chọn.

    Bước 2: Chỉnh Stretchable area và Fill area cho hợp lý. Nếu bạn sử dụng Optical bounds thì nó sẽ là viền đỏ trên file 9-Patch.

    Bước 3: Tải ảnh về khi đã edit xong, chúng ta sẽ click vào icon download ở góc phải trên cùng để tải về.

    Bước 4: Sử dụng file 9-patch như một drawable bình thường (không cần ghi đuôi .9)

    Việc tạo bằng tool sẽ giúp chúng ta tinh chỉnh dễ dàng hơn, đỡ phải take time tạo từng 9-patch image khác nhau cho các density mà chưa chắc đã có độ nhất quán tuyệt đối. Nhưng bên cạnh đó lại có một nhược điểm là bạn chỉ có thể tạo một Stretchable area trong một drawable. Android studio thì lại giúp bạn tuỳ ý tạo các Stretchable area khác nhau.

    Summary

    Để styling trong Android thì muôn vàn cách, 9-patch chỉ là một trong số cách đó. Trên đây chỉ là một vài ví dụ cơ bản về việc sử dụng 9-patch. Mong rằng bài viết đã giúp bạn có thêm một kiến thức mới – phép thuật mới cho chặng đường của một phù thủy Android ??

  • Delegate trong Kotlin

    Delegate trong Kotlin

    Delegate là gì nhỉ? Tại sao lại là Delegate? Đúng như cái tên, Delegate là một design pattern mà bạn ủy quyền xử lý logic của Class hiện tại cho một Object/Class khác. Delegate thường được sử dụng để tách logic code theo việc của nó (separate concerns) hoặc common hóa một đoạn logic.
    Bài viết này sẽ giúp bạn tìm hiểu cơ bản về Delegate trong Kotlin và cách sử dụng khái niệm này. Trong Kotlin, có 2 cách để sử dụng Delegate
    – Interface/Class Delegation
    – Delegate Properties

    Delegate là cách bạn cho phép một object khác xử lý một logic cho object hiện tại

    Interface/Class Delegation

    Với cách thứ nhất, chúng ta sẽ sử dụng một interface làm abstract cho một object và truyền object đó vào phần khai báo implement thông qua keyword by. Bằng cách này, các abstract methods (method của interface) sẽ chạy code của delegating object!

    interface CameraOptimization {
        fun optimize()
    }
    
    object XiaomiDevicesOptimization : CameraOptimization {
        override fun optimize() {
            TODO("do something for Xiaomi devices")
        }
    
    }
    
    object DefaultDevicesOptimization : CameraOptimization {
        override fun optimize() {
            TODO("do something for others devices")
        }
    }
    
    class CameraManager(optimization: CameraOptimization) : CameraOptimization by optimization {
        fun cameraFocus() {
            //todo: focus camera
        }
    }

    Đây là cách setup cơ bản của Interface/Class Delegation. Như các bạn thấy thì implementation của method optimize() không trực tiếp xuất hiện ở trong class CameraManager mà sẽ được delegate đến object truyền vào bằng keyword by.

    class CameraActivity : BaseActivity() {
        private lateinit var _cameraManager: CameraManager
    
        private fun initView() {
            val vendor = android.os.Build.MANUFACTURER
            val config = when {
                vendor.equals("Xiaomi", ignoreCase = true) -> XiaomiDevicesOptimization
                else -> DefaultDevicesOptimization
            }
            _cameraManager = CameraManager(config)
            _cameraManager.optimize()
            _cameraManager.cameraFocus()
        }
    }


    Khi method optimize() được gọi, nó sẽ delegate đến Config của XiaomiDevicesOptimization hoặc DefaultDevicesOptimization tùy theo device đó là gì. Với cách tiếp cận này logic của CameraManager vẫn có khả năng tối ưu mà không cần phải quan tâm rằng nó sinh ra cho vendor cụ thể nào cả. Đồng thời cũng tăng khả năng mở rộng của Class này hơn. Nếu app của bạn quyết định support optimize thêm cả anh zai Samsung cũng oke luôn, code thêm 1 class và 1 dòng duy nhất.

    Delegate Properties

    Chắc bạn đã từng sử dụng rất nhiều lần lazy trong kotlin rồi đúng không? Delegate đó :v Delegate Properties là việc bạn implement operator getValue (có thể thêm cả setValue luôn nếu bạn muốn nó set được cả value) của một class. Hoặc một cách khác tường mình hơn là implement interface ReadWriteProperty/ReadOnlyProperty. Class đó sẽ trở thành Delegate. Vẫn là keyword by, chúng ta khai báo một biến với Delegate thông qua by.

    class IntNaturalSet : ReadWriteProperty<Any, Int> {
        private var _value: Int = 0
        override fun getValue(thisRef: Any, property: KProperty<*>): Int {
            return _value
        }
    
        override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
            _value = if(value < 0) 0 else value
        }
    }

    Trong ví dụ đơn giản này chúng ta đã ủy quyền getter setter của biến cho class IntNaturalSet can thiệp và xử lý logic. Bên cạnh việc ủy quyển xử lý logic getter (setter) thì Delegate cũng có thể access đến Class chứa biến được delegate thông qua param thisRef(Chính là generic T trong ReadWriteProperty/ReadOnlyProperty). Có thể là built-in delegate cho một Type hoặc ép kiểu thisRef trong logic của getter setter để có thể sử dụng param này.

    Ứng dụng của Delegate Properties rất rộng, chúng ta có thể custom cho logic in/out data như shared preferences, cache…
    Trong Androidx/Kotlin cơ bản cũng có vài delegate như lazy, viewModels, activityViewModels…

    Summary

    Delegate là một phương pháp khá hay trong lập trình giúp chúng ta tối ưu logic source code. Một source base có thể sẽ clean hơn nếu rút gọn các common logic hay boilerplate code. Một class có thể tăng tính mở rộng trong tương lai. Một project có thể sẽ triển khai nhanh hơn nhờ những common và khả năng scalable tốt. Lần tới nếu như bạn gặp phải một vấn đề có thể xử lý bằng Delegate, cứ thử xem sao nhé!
    Hi vọng bài viết giúp bạn có thêm chút kiến thức trong chặng đường của một Engineer :3!

  • 4 mức độ hiểu biết của developers

    Xin chào, lại là mình đây – baka3k – một coder thích nói lan man về những điều nhảm nhí

    Đã rất lâu rồi mình ko viết về những thứ ngoài công nghệ, lần này mình sẽ cố gắng giữ sự cân bằng để ko đưa quá nhiều toxic vào bài viết này, nên nếu bạn thấy mình trong đó, đừng giật mình, vì mình không cố ý đâu

    4 mức độ hiểu biết(vài chỗ thì người ta sẽ gọi là 4 mức độ ngu dốt) gồm 4 level sau:

    • Không Biết rằng mình Không Biết
    • Không Biết rằng mình Biết
    • Biết rằng mình Không Biết
    • Biết rằng mình Biết

    Không Biết rằng mình Không Biết

    Hay còn gọi là Ngu mà không biết là mình ngu

    Cái Ngu ở đây mình đang nói về cái ngu của Trí giả, ko phải cái ngu mang tính miệt thị đâu nhé

    Thời kỳ mông muội sơ khai, khi linh trí của tu hành giả chưa được khai mở, ý lộn… khi lập trình viên còn chưa biết mình không biết cái gì, cần học cái gì, cần đi theo đường nào.

    Đôi khi nó xuất hiện cả ở những level cao hơn một chút Middle hoặc Senior. Có những giai đoạn các bạn này làm việc & phát triển bản thân trong vô định

    Vô định ở đây là các bạn làm rất tốt công việc hiện tại của mình, nhưng loay hoay không biết mình nên phát triển tiếp thế nào vì mọi thứ đều đang rất tốt, các bài toán công ty đưa cho đều ở mức độ vừa phải, không nhiều thách thức. Ừ thì đại khái là training vài bạn fresher, đôi khi review code hộ ai đó, đôi khi fix bug hộ, làm vài cái seminar, hiểu cách triển khai vài cái design pattern…blabla. Mọi thứ các bạn đều làm được, các bạn cảm thấy mình đang đứng ở đỉnh của domain đang làm việc, cuối chuỗi thức ăn. Lúc này rất dễ dàng để bạn lạc lối. rất dễ dàng để tự nhận rank của mình là Expert, Guru. Nếu bạn tự thấy mình đang ở level này, thì nên ngồi xuống, bình tĩnh lại và tự review bản thân mình thật cẩn thận.

    Senior 5 năm Kinh nghiệm, Expert 7 năm kinh nghiệm, Guru 10 năm kinh nghiệm… chỉ làm loanh quanh mấy cái app client server, vài ba cái architecture MVVM, MVP, rồi cao hơn tý là Hexagonal hay Clean architecture – đó là một sự giễu cợt

    Tuy người ta không thể lấy số năm kinh nghiệm để đánh giá về Competency của một người, nhưng nó sẽ phản ánh đúng được phần nào đó, giống như lái xe ô tô cần khoảng 1 vạn km, lái máy bay cần khoảng 10.000 giờ bay mới có thể tạm gọi là nhuần nhuyễn vậy.

    Tất nhiên, còn liên quan đến lĩnh vực công việc bạn đang làm việc, ví dụ như 5 năm CRUD, 5 năm làm ứng dụng client server chả hạn… thì thôi, bỏ đi. Nếu bạn không muốn ngồi trong giếng, hãy nghiêm túc suy nghĩ lại con đường phát triển bản thân, tìm các cột mốc mới, tìm ra những điểm thiếu sót – nếu không thể, hãy tìm cho mình một Mentor có tâm và đủ Tầm

    Không Biết rằng mình Biết

    Ai cũng sẽ ở giai đoạn này, chúng ta đều sẽ gặp giai đoạn này trong quá trình phát triển bản thân

    Khi chúng ta học và khám phá, khi chúng ta dung nhập một lượng kiến thức khổng lồ vào, trước khi biến nó thành của bản thân mình, bạn sẽ thấy keyword này quen quen, mình thấy nó rồi, có thể nó là lĩnh vực này

    Đó là chúng ta, khi vào giai đoạn Không biết là mình Biết

    Biết rằng mình không biết

    Mọi thứ trở lên rõ ràng hơn, kiến thức được sắp xếp lại, được hoạch định đúng cách hơn Bạn đủ overview, đủ nhận thức để phân biệt giữa biết lờ mờ, biết keyword, nắm vững, hiểu biết chuyên sâu…etc

    Đừng khinh thường những cụm từ được bôi đen này vì nó giúp bạn không lạc lối

    Việc không hiểu rõ các cụm từ này – sẽ khiến bạn quay về thời kỳ mông muội Không biết là mình Không Biết

    Khi bạn biết là mình không biết cái gì, bạn mới có thể có kế hoạch để cải thiện, hoạch định lại bản thân một cách rõ ràng hơn

    Đây là thời điểm bạn sẵng sàng và đủ năng lượng để trở lên mạnh mẽ hơn, thời điểm này mang lại sức bật rất lớn cho hầu hết tất cả mọi người. Nếu bạn ở trong thời điểm này, đừng bỏ lỡ, tận dụng tốt, bạn có thể đi rất xa

    Biết rằng mình biết

    Không có gì để nói.

  • Tạo Telegram bot để phục vụ làm việc với GoogleSheet

    Tạo Telegram bot để phục vụ làm việc với GoogleSheet

    1. Bài toán

    • Việc truy xuất các dữ liệu trên một file Excel hay GoogleSheet là rất dễ dàng với những người có kĩ năng Office cơ bản.
    • Tuy nhiên với một người không có kiến thức đủ để làm việc với Excel, GoogleSheet hoặc muốn truy xuất thông tin từ file ngay lập tức mà lại đang không có các công cụ hỗ trợ làm việc, viết các câu lệnh trên Excel, GoogleSheet, thì việc truy xuất dữ liệu này sẽ trở nên khó khăn.
    • Khi đó việc tạo một trợ lý ảo, một con bot giúp người dùng có thể truy cập đến dữ liệu trên một file Excel, GoogleSheet là một việc rất hữu ích.
    • Ở đây tôi sẽ dùng bot trên Telegram vì đây là một ứng dụng nhắn tin khá phổ biến và cho phép developer phát triển nhiều thứ trên đó.
    • Về ngôn ngữ lập trình tôi sẽ sử dụng Python vì ngôn ngữ này cho phép developer làm việc dễ dàng với Excel, GoogleSheet.

    2. Tạo bot trên Telegram

    • Đầu tiên người dùng cần phải truy cập vào ứng dụng Telegram, vào thanh tìm kiếm và tìm kiếm từ khoá BotFather
    • Gõ lệnh /newbot để tạo bắt đầu tạo bot mới
    • Tiếp theo chúng ta sẽ đặt tên cho bot của mình.
    • Sau khi đặt tên BotFather sẽ yêu cầu chúng ta chọn username cho con bot, đây cũng chính là tên giúp chúng ta có thể tìm kiếm con bot ở trên Telegram như các tìm kiếm một người dùng.
    Sau khi hoàn thành xong bước trên BotFather sẽ gửi lại cho chúng ta thông tin về bot
    • Link để truy cập vào bot: t.me/csv_xlsx_bot
    • Access token: developer sẽ sử dụng token để làm việc với các API từ đó phát triển các tính năng khác cho con bot của mình.

    3. Sử dụng Python để truy xuất data từ GoogleSheet

    • Đầu tiên chúng ta cần tạo một project Python
    • Thêm các thư viện cần thiết: pip, pandas, numpy
    • Chạy các câu lệnh sau ở terminal để thêm các thư viện cần dùng
    • Lưu ý là link GoogleSheet cần được public access cho tất cả người dùng có thể truy cập
    # Install pip
    python3 get-pip.py
    # Install pandas
    pip install pandas
    # Install numpy
    pip install numpy

    Tạo file .env: file này được dùng để lưu token của Telegram API developer

    export BOT_TOKEN=6354775494:*************************************

    Tạo một file đặt tên là utils.py: file này sẽ chứa các thuật toán làm việc với file GoogleSheet

    • Chúng ta sẽ sử dụng thư viện pandas đọc file GoogleSheet
    • Các trường thông tin truy vấn được sử dụng ở đây là
    Các trường thông tin truy vấn được sử dụng:
    • download_link: đường dẫn file GoogleSheet cần truy vấn
    • sheet_name: tab trong file GoogleSheet cần truy vấn
    • usecols: các cột giá trị truy vấn
    • df = df[(df[‘City’]==’Los Angeles’)]: dùng để filter các bản ghi có trường ‘City’ là ‘Los Angeles’
    Kết quả trả về sẽ được parse sang string bằng việc sử dụng thư viện numpy
    import requests
    import pandas as pd
    import numpy as np
    
    download_link_prefix = "https://drive.google.com/uc?export=download&id="
    def read_data_from_file(file_path):
        sheet_name = 'FoodSales'
        query_cols = ['ID','Date','City']
        download_link = get_google_sheets_download_link(file_path)
        if (download_link == None): return None
        else: 
            df = pd.read_excel(download_link, sheet_name=sheet_name, usecols=query_cols)
            df = df[(df['City']=='Los Angeles')]
            mat = np.array(df)
            return np.array2string(mat)
    
    def get_google_sheets_download_link(file_path):
        file_split= file_path.split('/')
        if len(file_split) > 5:
            return download_link_prefix + file_split[5]
        else:
            return None

    Tạo file main.py: file này sẽ bao gồm các thao tác làm việc với bot Telegram

    • bot = telebot.TeleBot(BOT_TOKEN): tạo một instance của bot Telegram
    • @bot.message_handler(commands=[]): đoạn code sẽ tạo phương thức lắng nghe lệnh điều khiển từ người dùng
    • bot.reply_to(): gửi lại tin nhắn tới người dùng
    • bot.infinity_polling(): câu lệnh cho phép bot Telegram sẽ lắng nghe liên tục các thông tin, lệnh từ phía người dùng
    • bot.send_message(): gửi lại câu trả lời cho tin nhắn nhận được từ người dùng
    • bot.register_next_step_handler(): đăng ký function sẽ được thực thi ngay sau khi nhận được phản hồi từ phía người dùng
    • file_path = message.text: đọc tin nhắn, câu lệnh được gửi từ phía người dùng
    import os
    import telebot
    from dotenv import load_dotenv
    load_dotenv()
    from utils import read_data_from_file, write_data_to_file
    
    BOT_TOKEN = os.environ.get('BOT_TOKEN')
    
    bot = telebot.TeleBot(BOT_TOKEN)
    
    @bot.message_handler(commands=['start', 'hello'])
    def send_welcome(message):
        bot.reply_to(message, "Howdy, how are you doing?")
    
    @bot.message_handler(commands=['read_record'])
    def send_welcome(message):
        sent_msg = bot.send_message(message.chat.id, "So, what link would you like to read?")
        bot.register_next_step_handler(sent_msg, read_csv_xlsx_handler)
    
    def read_csv_xlsx_handler(message):
        file_path = message.text
        result = read_data_from_file(file_path)
        if (result == None): bot.send_message(message.chat.id, "Could not read")
        else: bot.send_message(message.chat.id, result)
    
    bot.infinity_polling()
    • Để chạy con bot của mình ta cần gõ lệnh sau trên terminal:
    python3 main.py

    4. Kết quả

    • File được dùng để đọc dữ liệu: Sample Link
    • Video Demo

    5. Tham khảo

    • Telegram Bot Developer API: https://core.telegram.org/bots/features
    • Pandas: https://pandas.pydata.org/docs/
    • IDE: Visual Studio Code
    • Language: Python
  • Apple Developer Account: Sự khác nhau giữa tài khoản developer thông thường và tài khoản doanh nghiệp

    Apple Developer Account: Sự khác nhau giữa tài khoản developer thông thường và tài khoản doanh nghiệp

    Bạn là một nhà phát triển ứng dụng của Apple, bạn muốn ứng dụng mình phát triển được bày ở trên cửa hàng ứng dụng (App Store) của Apple, hay bạn muốn phát triển một ứng dụng nội bộ chỉ dành riêng cho nhân viên trong công ty, tổ chức của bạn. Để thực hiện được việc này bạn cần phải có một tài khoản nhà phát triển của Apple cung cấp. Tuy nhiên mỗi một loại tài khoản lại có một mục đích khác nhau. Nếu bạn đang băn khoăn về việc chọn tài khoản nào phù hợp với trường hợp của mình thì ở bài viết này mình sẽ giải đáp thắc mắc đó.

    Có những loại tài khoản nào?

    Apple cung cấp hai loại tài khoản: Apple Developer ProgramApple Developer Enterprise Program. Vậy chúng ta nên chọn tài khoản nào cho ứng dụng của mình?

    Apple Developer Program

    Tài khoản này dành cho các nhà phát triển hoặc công ty muốn cho phép phân phối qua App Store cho bất kỳ ai. Các bản dựng được phát hành trong quá trình phát triển vẫn được cài đặt qua App Center.

    Ưu điểm

    • Giá rẻ nhất $99 một năm
    • Có thể đẩy ứng dụng lên cửa hàng ứng dụng của Apple cho tất cả mọi người tiếp cận và tải xuống
    • Bạn có thể thực hiện build/test trên 100 thiết bị khác nhau, điều này khá là tiện lợi cho việc phát triển ứng dụng của bạn
    • Ứng dụng của bạn có thể tìm kiếm được bằng các công cụ tìm kiếm khác nhau như Google, Bing …

    Nhược điểm

    • Trong quá trình phát triển các thiết bị được phép bị giới hạn ở 100 thiết bị, các thiết bị này cần phải đăng ký trước khi cài đặt các bản buil.
    • Do nó được công khai cho tất cả mọi người có thể tải xuống và sử dụng, nên nó sẽ không phù hợp với các ứng dụng nội bộ chỉ dành riêng cho nhân viên
    • Ứng dụng của bạn phải đảm bảo các yêu cầu khắt khe của Apple
    • In-app purchase bắt buộc phải qua Apple Payment center, Apple sẽ thu 30% phí.

    Chọn loại tài khoản này nếu bạn có ý định đưa ứng dụng của mình lên cửa hàng ứng dụng của Apple để tất cả mọi người có thể tiếp cận và tải xuống app của bạn.

    Bạn có thể xem thêm thông tin về chương trình tài khoản này ở đây

    Apple Developer Enterprise Program

    Đây là loại tài khoản cho phép các tổ chức lớn phát triển và triển khai các ứng dụng độc quyền, sử dụng nội bộ cho nhân viên của họ. Chương trình này dành cho các trường hợp sử dụng cụ thể yêu cầu phân phối riêng tư trực tiếp cho nhân viên sử dụng hệ thống nội bộ an toàn hoặc thông qua giải pháp Quản lý thiết bị di động.

    Ưu điểm

    • Không bị giới hạn số lượng thiết bị cài đặt
    • Các thiết bị không cần đăng ký trước để có thể cài đặt ứng dụng
    • Ứng dụng của bạn không cần phải tuân theo các quy tắc của Apple và không bị Apple review.
    • Vì ứng dụng không cần thiết lập trong cửa hàng ứng dụng Apple nên bạn không cần điền mô tả ứng dụng đầy đủ, tạo hình ảnh để quảng cáo ứng dụng hoặc tạo chính sách bảo mật công khai, v.v.
    • In-App purchases không cần thiết phải qua Apple Payment center vì vậy không phải chịu phí của Apple
    • Ứng dụng của bạn sẽ không bị các bên khác tìm kiếm được thông qua các công cụ tìm kiếm, vì vậy nó bảo mật hơn

    Nhược điểm

    • Người dùng sẽ phải cài lại ứng dụng hàng năm trước khi hồ sơ bị hết hạn, trừ phi bạn sử dụng giải pháp MDM(Mobile Devices Manager)
    • Việc cài đặt ứng dụng phức tạp hơn vì người dùng phải cài đặt, tin cậy nhà phát triển thay vì tự động như khi tải trên App Store
    • Giá cao nhất $299 một năm

    Bạn nên chọn loại tài khoản này nếu bạn cần phát triển các ứng dụng nội bộ, độc quyền dành riêng cho nhân viên trong công ty, tổ chức mà không muốn đẩy ứng dụng lên cửa hàng ứng dụng của Apple.

    Bạn có thể xem thêm thông tin về chương trình tài khoản này ở đây

  • Hàm ARRAY_AGG (JSON_ARRAYAGG), ARRAY_DISTINCT trong SQL và ví dụ cụ thể

    Hàm ARRAY_AGG (JSON_ARRAYAGG), ARRAY_DISTINCT trong SQL và ví dụ cụ thể

    Xin chào mọi người, hôm nay mình sẽ chia sẻ với các bạn về hàm ARRAY_AGG (JSON_ARRAYAGG), ARRAY_DISTINCT trong SQL và ví dụ cụ thể để truy vấn dữ liệu.

    Chúng ta có 1 bảng dữ liệu như sau:

    Table_cus

    Đề bài: Tổng hợp các giá trị bao gồm cả giá trị NULL trong cột Cus_rank thành một mảng (group by id). Loại bỏ các giá trị trùng lặp trong mảng đó.

    Để giải quyết được bài toán trên chúng ta sẽ phải sử dụng đến hàm ARRAY_AGG, vậy hàm ARRAY_AGG có chức năng gì?

    1. Chức năng hàm ARRAY_AGG

    Cú pháp:

    ARRAY_AGG(expression [ORDER BY [sort_expression {ASC | DESC}], [...])

    Chức năng: tổng hợp tập hợp các giá trị bao gồm NULL trong một cột thành một mảng

    Chi tiết:

    • ARRAY_AGG() nhận một biểu thức trả về giá trị thuộc bất kỳ loại nào hợp lệ cho một phần tử mảng.
    • Mệnh đề ORDER BY là mệnh đề tùy chọn. Nó chỉ định thứ tự các hàng được xử lý trong tập hợp, xác định thứ tự của các phần tử trong mảng kết quả.
    • Tương tự như các hàm tổng hợp khác như AVG(), COUNT(), MAX(), MIN() và SUM(), ARRAY_AGG() thường được sử dụng với mệnh đề GROUP BY.

    Chú ý:

    • Thứ tự của các phần tử trong mảng là ngẫu nhiên.
    • Kiểu dữ liệu của các phần tử trong mảng trả về giống với kiểu dữ liệu của các giá trị trong cột.

    2. Giải toán

    Chúng ta cùng nhau đi giải quyết từng yêu cầu của bài toán trên nhé!

    • Đối với yêu cầu ‘Tổng hợp các giá trị bao gồm cả giá trị NULL trong cột Cus_rank thành một mảng (group by id)’ chúng ta sử dụng hàm ARRAY_AGG() để thực hiện truy vấn như sau:

    Select id, ARRAY_AGG(cus_rank) as cus_rank_1 from Customer group by id;

    Tuy nhiên, trong MySQL Workbench lại không hỗ trợ hàm ARRAY_AGG => chúng ta sẽ thay thế bằng hàm JSON_ARRAYAGG như sau:

    Select id, JSON_ARRAYAGG(cus_rank) as cus_rank_1 from Customer group by id;

    Kết quả trả về như sau:

    array_2

    • Đối với case Loại bỏ các giá trị trùng lặp trong mảng, thường chúng ta sẽ sử dụng hàm array_distinct:

    Cú pháp: array_distinct(array) - Removes duplicate values from the array.

    Tuy nhiên, trong MySQL Workbench JSON_ARRAYAGG() không hỗ trợ DISTINCT. Bạn có thể SELECT DISTINCT trong truy vấn subquery và sau đó tổng hợp, như sau:

    SELECT id, JSON_ARRAYAGG(cus_rank) AS cus_rank_1 FROM (SELECT DISTINCT id, cus_rank from Customer) a GROUP BY id;

    Kết quả trả về như sau:

    array_3

    Trên đây là một vài chia sẻ của mình, nếu các bạn có cách làm nào hay, đừng ngần ngại comment nhé. Hẹn gặp lại mọi người trong bài viết tiếp theo.

    Tài liệu tham khảo:

  • Hàm LAG, LEAD trong SQL và ví dụ cụ thể

    Hàm LAG, LEAD trong SQL và ví dụ cụ thể

    Xin chào mọi người, hôm nay mình sẽ chia sẻ với các bạn về hàm Lag và Lead trong SQL và ví dụ cụ thể để truy vấn dữ liệu.

    Chúng ta có 1 bảng dữ liệu như sau:

    Table_cus

    Đề bài: Lấy ra giá trị cus_rank liền trước (Pre_cus_rank) của cùng 1 cus_id trong bảng Customer. Đối với case null thì gán Pre_cus_rank = cus_rank.

    Để giải quyết được bài toán trên chúng ta sẽ phải sử dụng đến hàm LAG, vậy chức năng của hàm LAG là gì?

    1. Chức năng hàm LAG

    Cú pháp:

    LAG (value_expr [, offset ]) [ IGNORE NULLS | RESPECT NULLS ] OVER ( [ PARTITION BY window_partition ] ORDER BY window_ordering )

    Chức năng: Hàm LAG trả về các giá trị cho một hàng tại một khoảng lệch nhất định phía trên (trước) hàng hiện tại trong phân vùng.

    Chi tiết:

    • value_expr: Cột mục tiêu hoặc biểu thức mà hàm hoạt động trên đó.
    • offset: Một tham số tùy chọn chỉ định số lượng hàng trước hàng hiện tại cần trả về giá trị. Phần bù có thể là một số nguyên không đổi hoặc một biểu thức đánh giá thành một số nguyên. Nếu bạn không chỉ định chênh lệch, Amazon Redshift sẽ sử dụng 1 làm giá trị mặc định. Độ lệch bằng 0 cho biết hàng hiện tại.
    • IGNORE NULLS: Thông số kỹ thuật tùy chọn chỉ ra rằng Amazon Redshift nên bỏ qua các giá trị null khi xác định hàng nào sẽ sử dụng. Giá trị null được bao gồm nếu IGNORE NULLS không được liệt kê.
    • RESPECT NULLS: Cho biết Amazon Redshift phải bao gồm các giá trị null khi xác định hàng nào sẽ sử dụng. RESPECT NULLS được hỗ trợ theo mặc định nếu bạn không chỉ định IGNORE NULLS.
    • OVER: Chỉ định phân vùng và thứ tự.
    • PARTITION BY window_partition: Đối số tùy chọn đặt phạm vi bản ghi cho mỗi nhóm trong mệnh đề OVER. 8 ORDER BY window_ordering: Sắp xếp các hàng trong mỗi phân vùn

    2. Giải toán

    Chúng ta cùng nhau đi giải quyết từng yêu cầu của bài toán trên nhé!

    • Đối với yêu cầu ‘Lấy ra giá trị cus_rank liền trước của cùng 1 cus_id trong bảng Customer’ chúng ta sử dụng hàm lag để thực hiện truy vấn như sau:

    Select *, lag(Cus_rank,1) over (partition by id order by update_dt) as Pre_cus_rank from Customer

    Kết quả trả về như sau: lag_1

    • Đối với case null thì gán Pre_cus_rank = cus_rank của bài toán, ta thực hiện truy vấn như sau:

    with a as (Select *, lag(Cus_rank,1) over (partition by id order by update_dt) as Pre_cus_rank from Customer) select *, case when Pre_cus_rank <> Cus_rank then Pre_cus_rank else Cus_rank end as Pre_cus_rank_1 from a;

    Kết quả trả về như sau: lag_2

    • Trên đây là trường hợp lấy ra giá trị liền trước của 1 bản ghi (dùng hàm LAG), ngược lại muốn lấy giá trị tiếp sau của bản ghi đó chúng ta sẽ sử dụng hàm LEAD

    Cú pháp:LEAD (value_expr [, offset ]) [ IGNORE NULLS | RESPECT NULLS ] OVER ( [ PARTITION BY window_partition ] ORDER BY window_ordering )

    Áp dụng:

    with a as (Select *, lead(Cus_rank) over (partition by id order by update_dt) as Update_cus_rank from Customer) select *, case when Update_cus_rank <> Cus_rank then Update_cus_rank else Cus_rank end as Update_cus_rank_1 from a;

    Kết quả: Lead_1

    Trên đây là một vài chia sẻ của mình, nếu các bạn có cách nào giải toán hay, đừng ngần ngại commnet nhé. Hẹn gặp lại mọi người trong bài viết tiếp theo.

    Tài liệu tham khảo:

  • Unit Test – How to unwrap optional value in XCTest

    Unit Test – How to unwrap optional value in XCTest

    Trước khi Xcode 11 ra mắt, để unwrap một optional value chúng ta vẫn thường phải dùng Guard/if let, điều này khá bất tiện trong khi viết Unit test. Khi viết test case chúng ta không nên đưa các câu lệnh điều kiện vào trong các func test vì nó sẽ tạo ra một logic mới trong Unit Test nó khiến test case của chúng ta rắc rối và phức tạp.

    Để các bạn dễ hình dung hơn, mình có tạo một Struct Person có thuộc tính address là optional (có thể nil) như dưới đây:

    struct Person {
        let name: String
        let address: String?
    }

    Thông thường chúng ta sẽ thực hiện unwrap như sau:

    func test_Address_caseNil() throws {
        let personModel: Person = Person(name: "John", address: nil)
        // unwrap optional value 
        guard let address = personModel.address else {
            XCTFail("Expected non-nil address")// throw fail
            return
        }
        
        XCTAssertEqual(address, "Hanoi")
    }

    Trong trường hợp này địa chỉ đang nil nên test case này sẽ bị lỗi và throw thông báo “Expected non-nil address”

    Có một cách thông dụng hơn là không unwrap mà sử dụng trực tiếp giá trị optional để verify test case như sau:

    func test_address_caseNil() throws {
        let personModel: Person = Person(name: "John", address: nil)
        
        XCTAssertEqual(personModel.address, "Hanoi")
    }

    Khi chạy test case này ta nhận được thông báo lỗi như sau:

    test_address_caseNil(): XCTAssertEqual failed: ("nil") is not equal to ("Optional("Hanoi")")

    Sử dụng cách này thì khá là tiện và nhanh, tuy nhiên nó có một nhược điểm là các thông báo lỗi thường không rõ ràng. Ngoài ra nó cũng sẽ không sử dụng được trong các trường hợp đặc biệt.

    Sử dụng XCTUnwrap

    XCTUnwrap() được giới thiệu trên Xcode 11, nó làm nhiệm vụ kiểm tra giá trị optional có nil hay không? nếu nil nó sẽ throw ra lỗi, không nil thì trả về giá trị. Từ đó ta có thể thoải mái sử dụng giá trị đó để thực hiện việc testing.

    func test_address_caseNil() throws {
        let personModel: Person = Person(name: "John", address: nil)
        // result
        let result = try XCTUnwrap(personModel.address)
        XCTAssertEqual(personModel.address, "Hanoi")
    }

    Sử dụng XCTUnwrap giúp source code của chúng ta gọn gàng sạch sẽ hơn rất nhiều so với các cách thông thường khác.

    Để sử dụng được XCTUnwrap bạn nhớ thêm throws cho func test để khi kiểm tra dữ liệu bị nil nó sẽ throw lỗi và đánh dấu test case này bị fail. Nếu bạn muốn thông báo rõ ràng hơn hay đơn giản là bạn muốn viết thông báo lỗi dễ hiểu bạn có thể thêm thuộc tính như sau

     let result = try XCTUnwrap(personModel.address, "Width is nil, please config data for test case")

    Thông báo lỗi ta nhận được sẽ như sau:

    test_address_caseNil(): XCTUnwrap failed: expected non-nil value of type "String" - Width is nil, please config data for test case

    Lúc này thông báo lỗi đã rõ ràng hơn, từ đó bạn có thể xử lí vấn đề một cách nhanh hơn.

    Hi vọng bài viết sẽ giúp cho các bạn có thêm lựa chọn để xử lí các tình huống khi viết Unit Test, từ đó sử dụng nó một cách hiệu quả và phù hợp với các tình huống.

  • Unit Test – Các cách chạy test trên Xcode

    Unit Test – Các cách chạy test trên Xcode

    Thông thường khi các bạn mới vào nghề khi viết xong các test case của mình các bạn thường sử dụng Command + U để chạy. Điều này không sai, tuy nhiên nếu bạn đang thực hiện trên một project lớn thì mình nghĩ bạn không nên dùng cách này vì nó có thể khiến bạn mất rất nhiều thời gian để có được kết quả test. Sau đây mình sẽ giới thiệu cho các bạn một số cách để bạn chạy test case một cách nhanh chóng và hiệu quả hơn.

    Chạy test case trên Test Navigator

    Các bạn có thể tuỳ chọn việc test cả một file hay test từng test case một trên Test Navigator

    1. Chạy test cho cả Target tests: Dùng khi bạn muốn chạy test và lấy báo cáo cho cả target này, khi này Xcode sẽ chạy nhiều test case cùng lúc nên có thể sẽ mất nhiều thời gian
    2. Chạy test cho chỉ một file tests: Dùng khi bạn viết xong toàn bộ test case của một file và muốn chạy test để kiểm tra báo cáo hay khi bạn muốn kiểm tra xem giữa các test case có bị conflict hay không?
    3. Chạy test một case cụ thể: Dùng khi bạn vừa viết xong test case và muốn kiểm tra xem func đã chạy đúng hay chưa, đây là trường hợp bạn nên dùng khi viết UT vì nó chạy nhanh nên tốn ít thời gian mà vẫn đảm bảo mục đích của bạn

    Bạn có thể chạy một hoặc nhiều test case mà bạn muốn bằng cách chọn các test case đó -> chuột phải -> Run x Methods

    Chạy test case trực tiếp trên file Tests

    Khi các bạn viết xong các test case của mình và bấm Command + S, trên func test case của bạn sẽ xuất hiện một button cho bạn chạy test luôn, điều này giúp bạn dễ dàng kiểm tra được test case của mình. Ngoài ra Xcode cũng cung cấp cho chúng ta một nút chạy test cho toàn bộ file ở trên đầu của file. Bạn có thể xem chi tiết ở hình dưới đây

    Chạy lại test case vừa test

    Để chạy lại test các test case bạn vừa mới test thì bạn dùng tổ hợp phím sau:

    ⌃ Control + ⌥ Option + ⌘ Command + G

    Đây là tổ hợp phím rất hữu dụng và được sử dụng thường xuyên vì nó giúp bạn chạy lại test case một cách nhanh chóng khi các bạn phải sửa lại các test case bị fail do viết sai input/output.

    Chạy test tất cả file trong Test Plan

    Khi bạn muốn xem báo cáo tất cả cho file Test Plan của bạn, bạn chỉ cần nhấn tổ hợp phím Command + U.

    Bật Code Coverage trên Sidebar

    Theo mặc định thì code coverage sẽ được bật khi bạn chạy test, tuy nhiên nếu bạn nhỡ tay tắt nó đi mà không biết bật nó ở đâu thì làm theo hướng dẫn sau: Adjust Editor Options -> Code Coverage

    Khi này Xcode sẽ cho bạn biết được dòng code đó của bạn được chạy qua bao nhiêu lần, nếu con số là 0 thì có vẻ bạn chưa có test case nào được viết để test đoạn code đó. Từ dữ liệu đó bạn có thể thực hiện viết thêm test case nếu cần.

    Hi vọng bài viết giúp ích cho các bạn. Xin cảm ơn!