{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "ac069784-fbb6-48c8-a547-11b96cade97b", "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true, "source_hidden": true }, "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/tmp/ipykernel_218/1243805045.py:22: UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.\n", " df = pd.read_sql(sql, conn)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " product_name total_images\n", "0 s2_l2a 2074896\n", "1 sentinel_2_c1_l2a 1137446\n", "2 landsat7_c2l2_sr 169065\n", "3 landsat7_c2l2_st 168410\n", "4 landsat8_c2l2_sr 122846\n", "5 landsat8_c2l2_st 120778\n", "6 landsat5_c2l2_sr 118583\n", "7 landsat5_c2l2_st 118406\n", "8 landsat8_c2l1 111513\n", "9 nasa_aqua_l2_oc 37232\n", "10 landsat9_c2l2_sr 30214\n", "11 landsat9_c2l2_st 29709\n", "12 landsat9_c2l1 18875\n", "13 nasa_aqua_l2_sst 12070\n", "14 ga_ls_mangrove_cover_cyear_3 5580\n", "15 sentinel1_grd_gamma0_10m_unsmooth 5488\n", "16 sentinel1_grd_gamma0_20m 4260\n", "17 copernicus_dem_30 1334\n", "18 global_mangrove_soc_2020_30m 784\n", "19 global_mangrove_soc_2000_30m 781\n", "20 global_mangrove_canopy_height_2015_12m 761\n", "21 sentinel1_grd_gamma0_10m 291\n", "22 lpdaac_nasadem 264\n", "23 cci_biomass_annual_v51 248\n", "24 esa_worldcover_2020 184\n", "25 esa_worldcover_2021 184\n", "26 global_tidal_marsh_soc_30m 41\n", "27 global_tidal_marsh_distribution_2020_10m 35\n", "28 copernicus_dem_fiji 26\n", "29 lpdaac_mod11a1v061_lste 2\n", "30 global_mangrove_soc_2000_100m 1\n" ] } ], "source": [ "import psycopg2\n", "import os\n", "import pandas as pd\n", "\n", "# Kết nối (như cũ)\n", "conn = psycopg2.connect(\n", " host=os.environ.get('DB_HOSTNAME'),\n", " user=os.environ.get('DB_USERNAME'),\n", " password=os.environ.get('DB_PASSWORD'),\n", " dbname=os.environ.get('DB_DATABASE')\n", ")\n", "\n", "# Đếm số lượng ảnh theo từng loại sản phẩm\n", "sql = \"\"\"\n", "SELECT t.name as product_name, count(*) as total_images\n", "FROM agdc.dataset d\n", "JOIN agdc.dataset_type t ON d.dataset_type_ref = t.id\n", "GROUP BY t.name\n", "ORDER BY total_images DESC;\n", "\"\"\"\n", "\n", "df = pd.read_sql(sql, conn)\n", "print(df)\n", "conn.close()" ] }, { "cell_type": "code", "execution_count": 1, "id": "a0ae4051-3bfd-49fd-83fe-4c31c74338e2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Đang kết nối trực tiếp tới PostgreSQL...\n", "Kết nối thành công!\n", "\n", "--> Đang xuất bảng: agdc.metadata_type\n", " Đã lưu xong: metadata_type.csv\n", "--> Đang xuất bảng: agdc.dataset_type\n", " Đã lưu xong: dataset_type.csv\n", "--> Đang xuất bảng: agdc.dataset_location\n", " Đã lưu xong: dataset_location.csv\n", "--> Đang xuất bảng: agdc.dataset\n", " Đã lưu xong: dataset.csv\n", "\n", "HOÀN TẤT! Bạn hãy tải 4 file CSV về máy.\n" ] } ], "source": [ "import psycopg2\n", "import os\n", "\n", "# 1. Lấy thông tin kết nối\n", "db_host = os.environ.get('DB_HOSTNAME')\n", "db_user = os.environ.get('DB_USERNAME')\n", "db_pass = os.environ.get('DB_PASSWORD')\n", "db_name = os.environ.get('DB_DATABASE')\n", "\n", "# 2. Danh sách các bảng cần lấy\n", "tables = [\n", " 'agdc.metadata_type',\n", " 'agdc.dataset_type',\n", " 'agdc.dataset_location',\n", " 'agdc.dataset'\n", "]\n", "\n", "print(\"Đang kết nối trực tiếp tới PostgreSQL...\")\n", "\n", "try:\n", " # Kết nối trực tiếp (Bỏ qua Pandas/SQLAlchemy)\n", " conn = psycopg2.connect(\n", " host=db_host,\n", " user=db_user,\n", " password=db_pass,\n", " dbname=db_name\n", " )\n", " cur = conn.cursor()\n", " print(\"Kết nối thành công!\\n\")\n", "\n", " for table_name in tables:\n", " print(f\"--> Đang xuất bảng: {table_name}\")\n", " \n", " # Tên file CSV đầu ra\n", " file_name = table_name.split('.')[1] + \".csv\"\n", " \n", " # Sử dụng lệnh COPY chuyên dụng của Postgres (Cực nhanh và chuẩn)\n", " # SQL: COPY (SELECT * FROM table) TO STDOUT WITH CSV HEADER\n", " sql = f\"COPY (SELECT * FROM {table_name}) TO STDOUT WITH CSV HEADER\"\n", " \n", " with open(file_name, 'w') as f:\n", " cur.copy_expert(sql, f)\n", " \n", " print(f\" Đã lưu xong: {file_name}\")\n", "\n", " cur.close()\n", " conn.close()\n", " print(\"\\nHOÀN TẤT! Bạn hãy tải 4 file CSV về máy.\")\n", "\n", "except Exception as e:\n", " print(f\"\\nCÓ LỖI XẢY RA: {e}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "d139879e-c350-4986-a2e6-0d59099eb770", "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true, "source_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Đang kết nối...\n", "Kết nối thành công! Đang ước lượng nhanh...\n", "\n", "--- BẢNG DỰ TÍNH DUNG LƯỢNG (ƯỚC LƯỢNG) ---\n", "NĂM | SỐ ẢNH (ƯỚC TÍNH) | SIZE (CSV) \n", "------------------------------------------------------------\n", "2020 | ~ 21,448 | ~ 83.78 MB\n", "2021 | ~ 21,448 | ~ 83.78 MB\n", "2022 | ~ 21,448 | ~ 83.78 MB\n", "2023 | ~ 21,448 | ~ 83.78 MB\n", "2024 | ~ 21,448 | ~ 83.78 MB\n", "------------------------------------------------------------\n", "TỔNG CỘNG : ~ 107,240 ảnh (Khoảng 418.91 MB)\n", "------------------------------------------------------------\n", "(Lưu ý: Số liệu này là ước tính của Database, độ chính xác khoảng 80-90%)\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "\n", "Bạn có muốn bắt đầu tải không? (y/n): y\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", ">>> Đang tải bảng định nghĩa...\n", "\n", ">>> Đang tải dữ liệu chính...\n", "--> Năm 2020...\n" ] } ], "source": [ "import psycopg2\n", "import os\n", "import re # Thư viện xử lý chuỗi để lấy số liệu\n", "\n", "# --- CẤU HÌNH ---\n", "YEARS = [2020, 2021, 2022, 2023, 2024]\n", "AVG_ROW_SIZE_KB = 4 \n", "\n", "db_host = os.environ.get('DB_HOSTNAME')\n", "db_user = os.environ.get('DB_USERNAME')\n", "db_pass = os.environ.get('DB_PASSWORD')\n", "db_name = os.environ.get('DB_DATABASE')\n", "\n", "print(\"Đang kết nối...\")\n", "\n", "try:\n", " conn = psycopg2.connect(\n", " host=db_host, user=db_user, password=db_pass, dbname=db_name\n", " )\n", " cur = conn.cursor()\n", " print(\"Kết nối thành công! Đang ước lượng nhanh...\\n\")\n", "\n", " # ==========================================\n", " # BƯỚC 1: ƯỚC LƯỢNG SIÊU TỐC (INSTANT ESTIMATE)\n", " # ==========================================\n", " print(\"--- BẢNG DỰ TÍNH DUNG LƯỢNG (ƯỚC LƯỢNG) ---\")\n", " print(f\"{'NĂM':<10} | {'SỐ ẢNH (ƯỚC TÍNH)':<20} | {'SIZE (CSV)':<20}\")\n", " print(\"-\" * 60)\n", "\n", " total_est_rows = 0\n", "\n", " for year in YEARS:\n", " # Mẹo: Dùng EXPLAIN để lấy số liệu ước tính từ Query Planner\n", " # Nó sẽ trả về chuỗi kiểu: \"Seq Scan on dataset ... (rows=12345 ...)\"\n", " sql_estimate = f\"\"\"\n", " EXPLAIN SELECT 1 FROM agdc.dataset\n", " WHERE (metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01'\n", " AND (metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n", " \"\"\"\n", " cur.execute(sql_estimate)\n", " explain_result = cur.fetchone()[0] # Lấy dòng đầu tiên của kết quả EXPLAIN\n", " \n", " # Dùng Regex để bắt lấy con số sau chữ \"rows=\"\n", " match = re.search(r\"rows=(\\d+)\", explain_result)\n", " if match:\n", " count = int(match.group(1))\n", " else:\n", " count = 0 # Không bắt được số\n", " \n", " total_est_rows += count\n", " est_size_mb = (count * AVG_ROW_SIZE_KB) / 1024\n", " \n", " print(f\"{year:<10} | ~ {count:<18,} | ~ {est_size_mb:.2f} MB\")\n", "\n", " print(\"-\" * 60)\n", " print(f\"TỔNG CỘNG : ~ {total_est_rows:,} ảnh (Khoảng {(total_est_rows * AVG_ROW_SIZE_KB)/1024:.2f} MB)\")\n", " print(\"-\" * 60)\n", " print(\"(Lưu ý: Số liệu này là ước tính của Database, độ chính xác khoảng 80-90%)\")\n", "\n", " # ==========================================\n", " # QUYẾT ĐỊNH TẢI\n", " # ==========================================\n", " check = input(\"\\nBạn có muốn bắt đầu tải không? (y/n): \")\n", " if check.lower() != 'y':\n", " print(\"Đã hủy.\")\n", " exit()\n", "\n", " # ==========================================\n", " # BƯỚC 2 & 3: TẢI DỮ LIỆU (Giữ nguyên logic cũ)\n", " # ==========================================\n", " # ... (Phần code tải small_tables và tải dữ liệu chính giữ nguyên như cũ) ...\n", " # Để code gọn, mình viết tiếp phần tải ở dưới đây:\n", " \n", " # 2. Tải bảng nhỏ\n", " print(\"\\n>>> Đang tải bảng định nghĩa...\")\n", " for tb in ['agdc.metadata_type', 'agdc.dataset_type']:\n", " f_name = tb.split('.')[1] + \".csv\"\n", " with open(f_name, 'w') as f:\n", " cur.copy_expert(f\"COPY (SELECT * FROM {tb}) TO STDOUT WITH CSV HEADER\", f)\n", " \n", " # 3. Tải dữ liệu chính\n", " print(\"\\n>>> Đang tải dữ liệu chính...\")\n", " for year in YEARS:\n", " print(f\"--> Năm {year}...\")\n", " \n", " # Dataset\n", " f_ds = f\"dataset_{year}.csv\"\n", " sql_ds = f\"\"\"\n", " COPY (SELECT * FROM agdc.dataset \n", " WHERE (metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01' \n", " AND (metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n", " ) TO STDOUT WITH CSV HEADER\"\"\"\n", " with open(f_ds, 'w') as f: cur.copy_expert(sql_ds, f)\n", " \n", " # Location\n", " f_loc = f\"dataset_location_{year}.csv\"\n", " sql_loc = f\"\"\"\n", " COPY (SELECT l.* FROM agdc.dataset_location l JOIN agdc.dataset d ON l.dataset_ref = d.id\n", " WHERE (d.metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01' \n", " AND (d.metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n", " ) TO STDOUT WITH CSV HEADER\"\"\"\n", " with open(f_loc, 'w') as f: cur.copy_expert(sql_loc, f)\n", " \n", " print(\"\\nHOÀN TẤT TOÀN BỘ!\")\n", " cur.close()\n", " conn.close()\n", "\n", "except Exception as e:\n", " print(f\"\\nCÓ LỖI: {e}\")" ] }, { "cell_type": "code", "execution_count": 2, "id": "8a5bb199-9363-47cc-9640-bf7e0af33d19", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Đang quét kho dữ liệu (Điều này có thể mất 1-2 phút)...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/tmp/ipykernel_499/84613258.py:40: UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.\n", " df = pd.read_sql(sql, conn)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "============================================================\n", "PRODUCT NAME | SỐ LƯỢNG | DUNG LƯỢNG (TB)\n", "------------------------------------------------------------\n", "s2_l2a | 2,074,896 | 1621.01 TB\n", "sentinel_2_c1_l2a | 1,137,446 | 555.39 TB\n", "landsat7_c2l2_sr | 169,065 | 82.55 TB\n", "landsat7_c2l2_st | 168,410 | 82.23 TB\n", "landsat8_c2l2_sr | 122,846 | 59.98 TB\n", "landsat8_c2l2_st | 120,778 | 58.97 TB\n", "landsat5_c2l2_sr | 118,583 | 57.90 TB\n", "landsat5_c2l2_st | 118,406 | 57.82 TB\n", "landsat8_c2l1 | 111,513 | 54.45 TB\n", "nasa_aqua_l2_oc | 37,232 | 18.18 TB\n", "landsat9_c2l2_sr | 30,214 | 14.75 TB\n", "landsat9_c2l2_st | 29,709 | 14.51 TB\n", "landsat9_c2l1 | 18,875 | 9.22 TB\n", "nasa_aqua_l2_sst | 12,070 | 5.89 TB\n", "ga_ls_mangrove_cover_cyear_3 | 5,580 | 2.72 TB\n", "sentinel1_grd_gamma0_10m_unsmooth | 5,488 | 2.68 TB\n", "sentinel1_grd_gamma0_20m | 4,260 | 2.08 TB\n", "copernicus_dem_30 | 1,334 | 0.65 TB\n", "global_mangrove_soc_2020_30m | 784 | 0.38 TB\n", "global_mangrove_soc_2000_30m | 781 | 0.38 TB\n", "global_mangrove_canopy_height_2015_12m | 761 | 0.37 TB\n", "sentinel1_grd_gamma0_10m | 291 | 0.14 TB\n", "lpdaac_nasadem | 264 | 0.13 TB\n", "cci_biomass_annual_v51 | 248 | 0.12 TB\n", "esa_worldcover_2020 | 184 | 0.09 TB\n", "esa_worldcover_2021 | 184 | 0.09 TB\n", "global_tidal_marsh_soc_30m | 41 | 0.02 TB\n", "global_tidal_marsh_distribution_2020_10m | 35 | 0.02 TB\n", "copernicus_dem_fiji | 26 | 0.01 TB\n", "lpdaac_mod11a1v061_lste | 2 | 0.00 TB\n", "global_mangrove_soc_2000_100m | 1 | 0.00 TB\n", "------------------------------------------------------------\n", "TỔNG CỘNG TOÀN SERVER : ~ 2702.76 TB (Terabytes)\n", "Tương đương : ~ 2767622 GB\n", "============================================================\n" ] } ], "source": [ "import psycopg2\n", "import os\n", "import pandas as pd\n", "\n", "# CẤU HÌNH: Ước lượng dung lượng trung bình cho mỗi cảnh ảnh (Đơn vị: GB)\n", "# Đây là con số kinh nghiệm thực tế:\n", "AVG_SIZE_GB = {\n", " 's2_l2a': 0.8, # Sentinel-2 L2A (Khoảng 800MB/cảnh)\n", " 'ga_s2_gm': 0.1, # Geomedian (Ảnh tổng hợp, nhẹ hơn)\n", " 'ls5_sr': 0.5, # Landsat 5 (Nhẹ hơn S2)\n", " 'ls7_sr': 0.6, # Landsat 7\n", " 'ls8_sr': 1.0, # Landsat 8 (Nặng hơn)\n", " 'ls9_sr': 1.0, # Landsat 9\n", " 'wofs_albers': 0.05, # Water Observation (Chỉ là mask nước, rất nhẹ)\n", " 'default': 0.5 # Mặc định nếu không biết loại nào\n", "}\n", "\n", "# Kết nối Database\n", "db_host = os.environ.get('DB_HOSTNAME')\n", "db_user = os.environ.get('DB_USERNAME')\n", "db_pass = os.environ.get('DB_PASSWORD')\n", "db_name = os.environ.get('DB_DATABASE')\n", "\n", "print(\"Đang quét kho dữ liệu (Điều này có thể mất 1-2 phút)...\")\n", "\n", "try:\n", " conn = psycopg2.connect(\n", " host=db_host, user=db_user, password=db_pass, dbname=db_name\n", " )\n", " \n", " # Query: Đếm số lượng ảnh theo từng loại Product\n", " sql = \"\"\"\n", " SELECT t.name as product_name, count(*) as total_images\n", " FROM agdc.dataset d\n", " JOIN agdc.dataset_type t ON d.dataset_type_ref = t.id\n", " GROUP BY t.name\n", " ORDER BY total_images DESC;\n", " \"\"\"\n", " \n", " df = pd.read_sql(sql, conn)\n", " \n", " # Tính toán dung lượng\n", " def estimate_size(row):\n", " # Tìm size trong từ điển, nếu không có thì lấy default\n", " size_per_scene = AVG_SIZE_GB.get(row['product_name'], AVG_SIZE_GB['default'])\n", " return row['total_images'] * size_per_scene\n", "\n", " df['total_size_gb'] = df.apply(estimate_size, axis=1)\n", " df['total_size_tb'] = df['total_size_gb'] / 1024\n", " \n", " # Hiển thị báo cáo\n", " print(\"\\n\" + \"=\"*60)\n", " print(f\"{'PRODUCT NAME':<25} | {'SỐ LƯỢNG':<10} | {'DUNG LƯỢNG (TB)':<15}\")\n", " print(\"-\" * 60)\n", " \n", " for index, row in df.iterrows():\n", " print(f\"{row['product_name']:<25} | {row['total_images']:<10,} | {row['total_size_tb']:.2f} TB\")\n", " \n", " print(\"-\" * 60)\n", " total_tb = df['total_size_tb'].sum()\n", " print(f\"TỔNG CỘNG TOÀN SERVER : ~ {total_tb:.2f} TB (Terabytes)\")\n", " print(f\"Tương đương : ~ {total_tb * 1024:.0f} GB\")\n", " print(\"=\"*60)\n", "\n", " conn.close()\n", "\n", "except Exception as e:\n", " print(f\"Lỗi: {e}\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }