Skip to content

Conversation

@Khainc
Copy link

@Khainc Khainc commented Aug 9, 2025

PR Type

Enhancement


Description

  • Add automated network speed report generation script

  • Implement email functionality with chart and Excel attachments

  • Parse speedtest log data with regex pattern matching

  • Generate visualization and statistical analysis of network performance


Diagram Walkthrough

flowchart LR
  A["Log File"] --> B["Parse Data"]
  B --> C["Generate Chart"]
  B --> D["Create Excel"]
  C --> E["Email Report"]
  D --> E
  E --> F["Send Notification"]
Loading

File Walkthrough

Relevant files
Enhancement
Khai_send_report.py
Network speed reporting automation script                               

Khai_send_report.py

  • Implements log file parsing with regex for speedtest results
  • Creates matplotlib charts and Excel reports from network data
  • Configures SMTP email sending with file attachments
  • Adds statistical analysis and automated report generation
+100/-0 

@qodo-merge-for-open-source
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 Security concerns

Sensitive information exposure:
Hardcoded Gmail sender, receiver, and app password are committed in code. This exposes credentials and risks account compromise. Move these to environment variables (e.g., EMAIL_SENDER, EMAIL_PASSWORD, EMAIL_RECEIVER) and load via os.environ; ensure credentials are rotated and removed from history. Additionally, validate and sanitize attachments' paths to avoid unintended file disclosures if paths become user-controlled in the future.

⚡ Recommended focus areas for review

Hardcoded Secrets

The SMTP app password and email addresses are hardcoded in the script. These should be moved to environment variables or a secrets manager and excluded from source control.

EMAIL_SENDER = "[email protected]"
EMAIL_PASSWORD = "zoelpdyuokwrknnp"  # App password, không dùng password Gmail trực tiếp
EMAIL_RECEIVER = "[email protected]"
REPORT_PERIOD_DAYS = 1  # 1 = hàng ngày, 7 = hàng tuần
Error Handling

File I/O, SMTP login/send, and plotting lack try/except handling; failures (missing log file, bad attachments, SMTP errors) will crash the script without actionable logs.

with open(LOG_FILE, "r") as f:
    for line in f:
        match = pattern.match(line.strip())
        if match:
            records.append(match.groupdict())

df = pd.DataFrame(records)
df["time"] = pd.to_datetime(df["time"])
df["dl"] = df["dl"].astype(float)
df["ul"] = df["ul"].astype(float)
df["ping"] = df["ping"].astype(float)

# Lọc theo khoảng thời gian
start_date = datetime.now() - timedelta(days=REPORT_PERIOD_DAYS)
df = df[df["time"] >= start_date]

if df.empty:
    print("Không có dữ liệu trong khoảng thời gian.")
    exit()

# Vẽ biểu đồ
plt.figure(figsize=(10, 6))
plt.plot(df["time"], df["dl"], label="Download (Mbps)", marker="o")
plt.plot(df["time"], df["ul"], label="Upload (Mbps)", marker="o")
plt.xlabel("Thời gian")
plt.ylabel("Tốc độ (Mbps)")
plt.title("Báo cáo tốc độ mạng")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.savefig("report_chart.png")

# Lưu Excel
excel_path = "report_data.xlsx"
df.to_excel(excel_path, index=False)

# Nội dung email
subject = f"Báo cáo tốc độ mạng {REPORT_PERIOD_DAYS} ngày gần nhất"
body = f"""
Xin chào,

Đây là báo cáo tốc độ mạng {REPORT_PERIOD_DAYS} ngày gần nhất.

📊 Thống kê:
- Số lần đo: {len(df)}
- Download trung bình: {df['dl'].mean():.2f} Mbps
- Upload trung bình: {df['ul'].mean():.2f} Mbps
- Ping trung bình: {df['ping'].mean():.2f} ms

File đính kèm:
- report_chart.png (biểu đồ)
- report_data.xlsx (dữ liệu chi tiết)

Trân trọng,
Hệ thống LibreSpeed
"""

# Gửi email
msg = MIMEMultipart()
msg["From"] = EMAIL_SENDER
msg["To"] = EMAIL_RECEIVER
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain"))

# Đính kèm file
for file_path in ["report_chart.png", excel_path]:
    with open(file_path, "rb") as f:
        part = MIMEApplication(f.read(), Name=file_path)
        part["Content-Disposition"] = f'attachment; filename="{file_path}"'
        msg.attach(part)

# Gửi qua Gmail SMTP
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
    server.login(EMAIL_SENDER, EMAIL_PASSWORD)
    server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, msg.as_string())
Regex Robustness

The strict regex may fail on minor format variations; consider using re.search and validating parsed fields, with logging for skipped lines.

pattern = re.compile(
    r"(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \| IP: (?P<ip>[\d\.]+) \| Download: (?P<dl>[\d\.]+) Mbps \| Upload: (?P<ul>[\d\.]+) Mbps \| Ping: (?P<ping>[\d\.]+) ms"
)

records = []
with open(LOG_FILE, "r") as f:
    for line in f:
        match = pattern.match(line.strip())
        if match:
            records.append(match.groupdict())

@qodo-merge-for-open-source
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Remove hardcoded email credentials

Do not hardcode credentials in source code; this is a critical security risk and
can leak secrets. Read the sender, receiver, and app password from environment
variables (with sane fallbacks or fail fast) and document required env vars.
This also prevents accidental credential exposure in version control.

Khai_send_report.py [12-14]

-EMAIL_SENDER = "[email protected]"
-EMAIL_PASSWORD = "zoelpdyuokwrknnp"  # App password, không dùng password Gmail trực tiếp
-EMAIL_RECEIVER = "[email protected]"
+import os
 
+EMAIL_SENDER = os.getenv("EMAIL_SENDER")
+EMAIL_PASSWORD = os.getenv("EMAIL_PASSWORD")  # Use Gmail App Password
+EMAIL_RECEIVER = os.getenv("EMAIL_RECEIVER", EMAIL_SENDER)
+
+if not EMAIL_SENDER or not EMAIL_PASSWORD:
+    raise RuntimeError("Missing EMAIL_SENDER or EMAIL_PASSWORD environment variables.")
+
  • Apply / Chat
Suggestion importance[1-10]: 10

__

Why: The suggestion addresses a critical security vulnerability by removing hardcoded credentials from the source code, which is a best practice to prevent secret leaks.

High
Possible issue
Add robust log read error handling

If the log file is missing or unreadable, or regex yields no matches, the script
will crash or create empty DataFrames with confusing errors. Add error handling
for file I/O and the empty-records case, and use a non-zero exit code via
sys.exit(1) to signal failure in automation.

Khai_send_report.py [24-42]

-with open(LOG_FILE, "r") as f:
-    for line in f:
-        match = pattern.match(line.strip())
-        if match:
-            records.append(match.groupdict())
+import sys
+try:
+    with open(LOG_FILE, "r") as f:
+        for line in f:
+            match = pattern.match(line.strip())
+            if match:
+                records.append(match.groupdict())
+except FileNotFoundError:
+    print(f"Log file not found: {LOG_FILE}")
+    sys.exit(1)
+except OSError as e:
+    print(f"Error reading log file '{LOG_FILE}': {e}")
+    sys.exit(1)
+
+if not records:
+    print("No valid log records found.")
+    sys.exit(1)
 
 df = pd.DataFrame(records)
 df["time"] = pd.to_datetime(df["time"])
 df["dl"] = df["dl"].astype(float)
 df["ul"] = df["ul"].astype(float)
 df["ping"] = df["ping"].astype(float)
 
-# Lọc theo khoảng thời gian
 start_date = datetime.now() - timedelta(days=REPORT_PERIOD_DAYS)
 df = df[df["time"] >= start_date]
 
 if df.empty:
     print("Không có dữ liệu trong khoảng thời gian.")
-    exit()
+    sys.exit(1)
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion improves the script's robustness by adding essential error handling for file I/O operations, preventing crashes from a missing log file and providing clearer failure diagnostics.

Medium
Handle attachment and SMTP failures

If attachments are missing or SMTP fails, the script will crash without clear
diagnostics. Add try/except around attachment reading and SMTP sending, and fail
with explicit messages to aid automation and retries.

Khai_send_report.py [88-98]

-# Đính kèm file
+import sys
+# Đính kèm file với xử lý lỗi
 for file_path in ["report_chart.png", excel_path]:
-    with open(file_path, "rb") as f:
-        part = MIMEApplication(f.read(), Name=file_path)
-        part["Content-Disposition"] = f'attachment; filename="{file_path}"'
-        msg.attach(part)
+    try:
+        with open(file_path, "rb") as f:
+            part = MIMEApplication(f.read(), Name=file_path)
+            part["Content-Disposition"] = f'attachment; filename="{file_path}"'
+            msg.attach(part)
+    except OSError as e:
+        print(f"Attachment error for '{file_path}': {e}")
+        sys.exit(1)
 
-# Gửi qua Gmail SMTP
-with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
-    server.login(EMAIL_SENDER, EMAIL_PASSWORD)
-    server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, msg.as_string())
+# Gửi qua Gmail SMTP với xử lý lỗi
+try:
+    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
+        server.login(EMAIL_SENDER, EMAIL_PASSWORD)
+        server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, msg.as_string())
+except smtplib.SMTPException as e:
+    print(f"SMTP error: {e}")
+    sys.exit(1)
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies and fixes potential runtime errors by adding try...except blocks for file attachment and SMTP operations, making the script more reliable against I/O or network issues.

Medium
  • More

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant