avatar

Đăng vào

Database với Google sheets

Tác giả

Currency Crawler

Mục lục

TL;DR

Yêu cầu

Vẫn là yêu cầu lấy dữ liệu tỷ giá hối đoái của 3 ngân hàng Việt Nam như đã trình bày ở bài viết trước. Dù không được trả tiền nhưng khi nghĩ đến việc mẹ của cháu tôi lọ mọ download file csv từ API gateway tôi vẫn thấy hơi cắn rứt. Giúp người thì giúp cho chót, thêm nữa tháng sau cháu mới đi mẫu giáo nên tôi đành làm phần giao diện friendly với người dùng hơn.

💡 Và đây là diagram của hệ thống mới

Currency crawler diagram v2

Google sheets là giải pháp rất tốt vì nó đáp ứng được 2 yêu cầu:

  • Thân thiện với người dùng (chị tôi)
  • Có thể làm database 🤥

Triển khai

Google sheets

  • Tạo google sheets mới. Lưu lại sheet id của bảng tính.

tạo google sheet

tạo project mới trên gcp

Kích hoạt Google sheet API

  • Crawler cần sử dụng api spreadsheet append nên không thể sử dụng kiểu xác thực với API key, lựa chọn đơn giản nhất là sử dụng service account.

  • Tạo service account và tạo key cho service account

tạo service account và key cho service account

  • Save key dưới tên file credentials.json ở trong thư mục lambda function

lưu lại dưới tên credential.json

  • Chia sẻ quyền sửa trang tính với email của service account

chia sẻ quyền sửa trang tính với email của service account

Sam template & Lambda function

  • Sửa lại file template, xoá bỏ những resource không cần dùng tới như
    • Get CSV function
    • HTTP API
    • DynamoDB
    • Thêm các biến môi trường cho hàm Crawler
template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  currency-crawler

  Sample SAM Template for currency-crawler

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 300
    Environment:
      Variables:
        SPREADSHEET_ID: '1nzKN3NSTh_AVQlSOm5Zv4CQZlejkPpHw3FkOm6T1zio'
        RANGE_NAME: 'A1:H1'
        VALUE_INPUT_OPTION: 'USER_ENTERED'


Resources:
  CurrencyCrawlFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: currency/
      Handler: app.crawl
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        InvocationLevel:
          Type: Schedule
          Properties:
            Schedule: cron(0 11 ? * MON-FRI *) # All scheduled events use UTC+0 time zone

Outputs:
  CurrencyCrawlFunction:
    Description: "Currency Crawler Lambda Function ARN"
    Value: !GetAtt CurrencyCrawlFunction.Arn
  CurrencyCrawlFunctionIamRole:
    Description: "Implicit IAM Role created for Currency Crawler function"
    Value: !GetAtt CurrencyCrawlFunctionRole.Arn
  • Sửa lại hàm crawler của các banks, chuyển từ kiểu decimal -> float cho các tỷ giá (DynamoDB chỉ nhận kiểu Decimal còn google sheet thì không)
app.py
from datetime import date
from mbb import get_record as get_mbb_record
from scb import get_record as get_scb_record
from bidv import get_record as get_bidv_record
import json
from config import logger
from utils import append_to_db, format_record

def crawl(event, context):
    try:
        requested_date_str = date.today().strftime("%d/%m/%Y")
        mbb_record = get_mbb_record(requested_date_str)
        append_to_db(format_record(mbb_record))

        bidv_record = get_bidv_record(requested_date_str)
        append_to_db(format_record(bidv_record))

        scb_record = get_scb_record(requested_date_str)
        append_to_db(format_record(scb_record))

        logger.info("Inserted to Google sheet")
    except Exception as e:
        logger.error(e)
        raise e

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "Done! Currencies data imported",
        }),
    }
banks.py
record[currency] = Decimal(ck) -> record[currency] = float(ck)
  • Xoá các hàm tương tác với DynamoDB, thay vào đó là các hàm append dữ liệu vào google sheet
requirements.txt
requests
bs4
google-api-python-client
google-auth-httplib2
google-auth-oauthlib
utils.py
import os
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = './credentials.json'
SPREADSHEET_ID = os.environ.get('SPREADSHEET_ID')
RANGE_NAME = os.environ.get('RANGE_NAME')
VALUE_INPUT_OPTION = os.environ.get('VALUE_INPUT_OPTION')

def append_to_db(values):
    creds, _ = google.auth.default()
    try:
        service = build('sheets', 'v4', credentials=creds)
        # ["2022/03/17", "MBB", "23020", "25876", "198.31", "16962", "2996", "17169"]
        body = {
            'values': [values]
        }
        result = service.spreadsheets().values().append(
            spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME,
            valueInputOption=VALUE_INPUT_OPTION, body=body).execute()
        print(f"{(result.get('updates').get('updatedCells'))} cells appended.")
        return result

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

# date bank USD	EUR	JPY	AUD	HKD	SGD
def format_record(record):
    arr = []
    arr.append(record['date'])
    arr.append(record['bank'])
    arr.append(record['USD'])
    arr.append(record['EUR'])
    arr.append(record['JPY'])
    arr.append(record['AUD'])
    arr.append(record['HKD'])
    arr.append(record['SGD'])
    return arr

Test & Validate & Build & Deploy

shell
# test function
sam local invoke "CurrencyCrawlFunction" -e events/event.json

# validate SAM template
sam validate --profile vntechies --region ap-southeast-1

# build package
sam build

# deploy
sam deploy --guided --profile vntechies

Demo ở local bằng sam local invoke

Bằng việc loại bỏ DynamoDB, API gateway khỏi hệ thống, những việc cần hoàn thiện cũng được lược bỏ

Thứ mà tôi vẫn sẽ không làm vì tôi không được trả tiền cho việc này 😂
  1. Xử lý lỗi cho các HTTP requests tới các site của banks đề phòng trường hợp bank nào đó bật maintainance mode, thay đổi giao diện, handle dead letter queue
  2. Đánh index cho bảng Currency, sử dụng query thay vì scan để giảm chi phí (nếu phát sinh), sử dụng DynamoDB update expressions xử lý việc ghi đè dữ liệu nếu có
  3. Sử dụng Authentication cho các APIs
  4. Headless browser trên Lambda
  5. Unit tests và integration tests, tạo các môi trường phát triển khác (dev, staging,...)
  6. Handle file size limit từ Lambda (6MB). Cũng là lời nhắn nhủ với chị gái, đừng download dữ liệu > 12 tháng. 🙄

Vậy là cháu tôi có thêm thời gian chơi với mẹ, giải pháp cồng kềnh đã được thu gọn bớt, thân thiện hơn với người dùng cuối 🤟

Hãy để lại đánh giá, comment nếu bạn cảm thấy tutorial này hữu ích (hoặc vui) nhé! 😬

Reference

VNTechies Dev Blog

Kho tài nguyên mã nguồn mở với sứ mệnh đào tạo kiến thức, định hướng nghề nghiệp cho cộng đồng Cloud ☁️ DevOps 🚀

Facebook page

Tham gia group VNTechies - Cloud ☁️ / DevOps 🚀 nếu bạn muốn giao lưu với cộng đồng và cập nhật các thông tin mới nhất về Cloud và DevOps.

Discord banner

Anh chị em hãy follow/ủng hộ VNTechies để cập nhật những thông tin mới nhất về Cloud và DevOps nhé!