#!/usr/bin/env python3 import json import os def generate_dashboard(): json_path = "/opt/ai-os/products/ceo/projects/GLV/reports/seo_raw_data.json" if not os.path.exists(json_path): print(f"Error: {json_path} not found.") return with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) # Output file path out_dir = "/opt/ai-os/products/ceo/projects/GLV/reports" os.makedirs(out_dir, exist_ok=True) out_html_path = os.path.join(out_dir, "glv_seo_dashboard.html") # Serialize raw data for embedding in HTML as JS constant embedded_data_json = json.dumps(data, ensure_ascii=False) html_template = """ Green Leaf Vietnam · SEO & Visibility Dashboard

Green Leaf Vietnam · SEO & Visibility

Snapshot of GSC + GA4 via Local Analytics API · last 30 days vs prior 30 days

__CURRENT_FROM__ – __CURRENT_TO__ vs __PREVIOUS_FROM__ – __PREVIOUS_TO__ Domain: __SITE_URL__
⚠️

No key events configured in GA4

Hệ thống ghi nhận sự kiện chuyển đổi chính thức (Key Events) chưa được bật cờ trong GA4. Báo cáo đang tạm thời sử dụng tổng phiên tương tác (Engaged Sessions) làm chỉ số thay thế.

Fix: GA4 → Admin → Events → Bật cờ "Mark as key event" cho form_submit.

01 Headline numbers

30-day totals with delta vs prior 30 days and a daily sparkline. Position uses inverted color logic – lower rank value = better.

02 Visibility & traffic

Where visits come from and how channel mix is moving. Stacked area = GA4 sessions by channel daily; right chart = GSC clicks & impressions trend.

Sessions by channel · daily

GA4 · stacked area, last 30 days

Organic search trend

GSC · clicks (lime) & impressions (cyan)

Channel performance · current vs prior 30d

Volume, quality, and the conversion proxy

Branded vs non-branded search

If branded grows but non-branded is flat, you're winning back people who already know you — not new demand.

03 Quick wins

High-speed improvement opportunities generated via algorithmic patterns.

Striking Distance (Pos 4-10)

Từ khóa đang nằm ở trang 1 nhưng chưa vào top 3. Cần tối ưu nội dung để đẩy top.

    Page-2 Opportunity (Pos 11-20)

    Từ khóa ở trang 2 có lượng hiển thị tốt. Bổ sung liên kết nội bộ để đẩy sang trang 1.

      CTR Opportunity (CTR < 0.5%)

      Trang hiển thị nhiều nhưng tỷ lệ nhấp thấp. Cần viết lại Title và Meta Description.

        High Impression, Zero Clicks

        Từ khóa có trên 30 hiển thị nhưng chưa nhận được nhấp nào. Kiểm tra Intent từ khóa.

          04 Top performers

          Detailed breakdown of top driving search queries and landing pages.

          Danh sách Từ khóa Tìm kiếm Hàng đầu (GSC)

          Lọc theo số lượt nhấp và hiển thị.

          Hiệu suất Các Trang Đích Chính (GA4)

          Lọc theo đường dẫn URL.

          """ html_content = html_template.replace("__EMBEDDED_JSON__", embedded_data_json) html_content = html_content.replace("__CURRENT_FROM__", data['current_period']['from']) html_content = html_content.replace("__CURRENT_TO__", data['current_period']['to']) html_content = html_content.replace("__PREVIOUS_FROM__", data['previous_period']['from']) html_content = html_content.replace("__PREVIOUS_TO__", data['previous_period']['to']) html_content = html_content.replace("__SITE_URL__", data['site_url']) with open(out_html_path, "w", encoding="utf-8") as f: f.write(html_content) print(f"Dashboard HTML successfully generated at: {out_html_path}") if __name__ == "__main__": generate_dashboard()