Author: ken

  • Automate Your Email Marketing with Python

    Email marketing remains a cornerstone of digital strategy for businesses and individuals alike. However, manually sending personalized emails to hundreds or thousands of subscribers can be a tedious, time-consuming, and error-prone task. What if you could automate this entire process, personalize messages at scale, and free up valuable time? With Python, you can! This post will guide you through the basics of building your own email automation script, leveraging Python’s powerful libraries to streamline your marketing efforts.

    Why Python for Email Automation?

    Python offers several compelling reasons for automating your email campaigns:

    • Simplicity and Readability: Python’s clean, intuitive syntax makes it relatively easy to write, understand, and debug scripts, even for those new to programming.
    • Rich Ecosystem: Python boasts a vast collection of built-in and third-party libraries. Core modules like smtplib and email provide robust functionality specifically designed for email handling.
    • Integration Capabilities: Python can effortlessly integrate with databases, CSV files, web APIs, and other services, allowing for dynamic content generation and sophisticated recipient management.
    • Cost-Effective: As an open-source language, Python and most of its libraries are free to use, offering a powerful automation solution without additional licensing costs.

    Essential Python Libraries

    For our email automation task, we’ll primarily utilize two core Python libraries:

    • smtplib: This library defines an SMTP client session object that can be used to send mail to any Internet machine with an SMTP or ESMTP listener daemon. It handles the communication protocol with email servers.
    • email.mime.multipart and email.mime.text: These modules are part of Python’s comprehensive email package. They are crucial for creating and manipulating email messages, enabling us to construct rich, multi-part emails (e.g., combining plain text with HTML content) and manage headers effectively.

    Setting Up Your Gmail for Automation (Important!)

    If you plan to use Gmail’s SMTP server to send emails, you must configure your Google Account correctly. Due to enhanced security, simply using your regular password might not work, especially if you have 2-Factor Authentication (2FA) enabled.

    The recommended and most secure approach is to generate an App Password:

    • Go to your Google Account > Security > App Passwords. You may need to verify your identity.
    • Select “Mail” for the app and “Other (Custom name)” for the device. Give it a name like “Python Email Script” and generate the password.
    • Use this generated 16-character password (without spaces) in your script instead of your regular Gmail password.

    Note: Always keep your email credentials secure and avoid hardcoding them directly in shared scripts. For production environments, consider using environment variables or secure configuration files.

    Building Your Email Sender: A Code Example

    Let’s walk through a basic Python script that sends a personalized email to multiple recipients using Gmail’s SMTP server.

    import smtplib
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    
    sender_email = "your_email@gmail.com"
    sender_password = "your_app_password" 
    smtp_server = "smtp.gmail.com"
    smtp_port = 587  # Port for TLS/STARTTLS
    
    recipients = [
        {"name": "Alice", "email": "alice@example.com"},
        {"name": "Bob", "email": "bob@example.com"},
        {"name": "Charlie", "email": "charlie@example.com"}
    ]
    
    subject_template = "Exciting News, {name}! Your Python Update is Here!"
    
    html_content_template = """\
    <html>
      <body>
        <p>Hi {name},</p>
        <p>We're thrilled to share our latest update, sent directly to you via a Python script!</p>
        <p>This demonstrates the power of automation in email marketing. You can customize content, personalize greetings, and reach your audience efficiently.</p>
        <p>Don't miss out on future updates. Visit our <a href="http://www.example.com" style="color: #007bff; text-decoration: none;">website</a>!</p>
        <p>Best regards,<br>The Python Automation Team</p>
      </body>
    </html>
    """
    
    def send_personalized_email(recipient_name, recipient_email, subject, html_content):
        """
        Sends a single personalized email to a recipient.
        """
        try:
            # Create the base MIME message container
            msg = MIMEMultipart("alternative")
            msg["From"] = sender_email
            msg["To"] = recipient_email
            msg["Subject"] = subject
    
            # Attach the HTML content to the message
            # The 'html' subtype tells email clients to render this as HTML
            msg.attach(MIMEText(html_content, "html"))
    
            # Connect to the SMTP server and send the email
            with smtplib.SMTP(smtp_server, smtp_port) as server:
                server.starttls()  # Upgrade the connection to a secure TLS connection
                server.login(sender_email, sender_password) # Log in to your email account
                server.send_message(msg) # Send the prepared message
    
            print(f"Successfully sent email to {recipient_name} ({recipient_email})")
        except Exception as e:
            print(f"Failed to send email to {recipient_name} ({recipient_email}): {e}")
    
    if __name__ == "__main__":
        print("Starting email automation...")
        for recipient in recipients:
            name = recipient["name"]
            email = recipient["email"]
    
            # Personalize the subject and HTML content for the current recipient
            personalized_subject = subject_template.format(name=name)
            personalized_html_content = html_content_template.format(name=name)
    
            # Call the function to send the email
            send_personalized_email(name, email, personalized_subject, personalized_html_content)
        print("Email automation process completed.")
    

    Explanation of the Code:

    • Imports: We import smtplib for the SMTP client and MIMEMultipart, MIMEText from email.mime for creating structured email messages.
    • Configuration: sender_email, sender_password, smtp_server, and smtp_port are set up. Remember to use your specific Gmail details and App Password.
    • recipients List: This simple list of dictionaries simulates your subscriber database. In a real application, you might read this data from a CSV file, a database, or fetch it from a CRM system.
    • Content Templates: subject_template and html_content_template are f-string-like templates that include {name} placeholders. These allow for dynamic personalization for each recipient.
    • send_personalized_email Function:
      • It creates a MIMEMultipart("alternative") object, which is ideal for emails that offer both plain text and HTML versions. For simplicity, we only attach HTML here, but you could add a plain text part as well.
      • msg["From"], msg["To"], and msg["Subject"] headers are set.
      • msg.attach(MIMEText(html_content, "html")) adds the HTML content to the message.
      • A secure connection to the SMTP server is established using smtplib.SMTP(smtp_server, smtp_port). server.starttls() upgrades this connection to a secure TLS encrypted one.
      • server.login() authenticates with your email account.
      • server.send_message(msg) sends the fully prepared email.
      • Basic error handling is included to catch potential issues during sending.
    • Main Execution Block (if __name__ == "__main__":): This loop iterates through your recipients list, personalizes the subject and content for each individual, and then calls send_personalized_email to dispatch the message.

    Advanced Considerations & Next Steps

    This basic script is a fantastic starting point. You can significantly enhance its capabilities by:

    • Loading Recipients from CSV/Database: For larger lists, read recipient data from a .csv file using Python’s csv module or pandas, or connect to a database using libraries like psycopg2 (PostgreSQL) or mysql-connector-python.
    • Scheduling Emails: Integrate with system-level task schedulers (e.g., cron on Linux/macOS, Task Scheduler on Windows) or use Python libraries like APScheduler to schedule email dispatches at specific times or intervals.
    • Robust Error Handling and Logging: Implement more sophisticated try-except blocks, add retry mechanisms for transient errors, and log successful/failed email attempts to a file or a dedicated logging service for better monitoring.
    • Unsubscribe Links: Include compliant unsubscribe mechanisms, often requiring a hosted page or integration with an email service provider’s API.
    • Tracking and Analytics: For more advanced tracking (opens, clicks), you might need to embed unique pixel images or links and process their requests, or integrate with a dedicated email marketing service API.
    • Template Engines: For complex email layouts, consider using template engines like Jinja2 or Mako to separate your email design from your Python code, making templates easier to manage and update.
    • Rate Limits: Be mindful of SMTP server rate limits (e.g., Gmail has limits on the number of emails you can send per day). Implement delays (time.sleep()) between sending emails to avoid hitting these limits.

    Conclusion

    Automating your email marketing with Python empowers you to run efficient, personalized campaigns without the manual overhead. By understanding the core concepts of connecting to SMTP servers and crafting dynamic messages, you can build powerful tools that save time and enhance your communication strategy. Start experimenting with these scripts, adapt them to your specific needs, and unlock the full potential of Python for your marketing efforts!


    Category: Automation

    Tags: Automation, Gmail, Coding Skills

  • A Guide to Data Cleaning with Pandas

    Data is the new oil, but just like crude oil, it often needs refining before it can be truly valuable. In the world of data science and analytics, this refining process is known as data cleaning. Raw datasets are frequently messy, containing missing values, inconsistencies, duplicates, and outliers that can skew your analysis and lead to incorrect conclusions.

    Pandas, Python’s powerful data manipulation library, is an indispensable tool for tackling these data cleaning challenges. Its intuitive DataFrames and rich set of functions make the process efficient and manageable.

    Why Data Cleaning is Crucial

    Before diving into the “how,” let’s briefly recap the “why.” Clean data ensures:

    • Accuracy: Analyses are based on correct and complete information.
    • Reliability: Models built on clean data perform better and generalize well.
    • Efficiency: Less time is spent troubleshooting data-related issues down the line.
    • Trustworthiness: Stakeholders can trust the insights derived from the data.

    Common Data Cleaning Tasks with Pandas

    Let’s explore some of the most common data cleaning operations using Pandas.

    1. Handling Missing Values

    Missing data is a ubiquitous problem. Pandas offers several methods to identify and address it.

    • Identify Missing Values:
      You can easily count missing values per column.

      “`python
      import pandas as pd
      import numpy as np

      Create a sample DataFrame

      data = {‘A’: [1, 2, np.nan, 4, 5],
      ‘B’: [np.nan, 20, 30, np.nan, 50],
      ‘C’: [‘apple’, ‘banana’, ‘orange’, ‘grape’, np.nan]}
      df = pd.DataFrame(data)

      print(“Original DataFrame:”)
      print(df)

      print(“\nMissing values per column:”)
      print(df.isnull().sum())
      “`

    • Dropping Missing Values:
      If missing data is sparse and dropping rows won’t significantly reduce your dataset size, dropna() is a quick solution.

      “`python

      Drop rows with any missing values

      df_dropped_rows = df.dropna()
      print(“\nDataFrame after dropping rows with any missing values:”)
      print(df_dropped_rows)

      Drop columns with any missing values

      df_dropped_cols = df.dropna(axis=1)
      print(“\nDataFrame after dropping columns with any missing values:”)
      print(df_dropped_cols)
      “`

    • Filling Missing Values:
      Often, dropping isn’t ideal. fillna() allows you to replace NaN values with a specific value, the mean/median/mode, or using forward/backward fill.

      “`python

      Fill missing values in column ‘A’ with its mean

      df_filled_mean = df.copy() # Work on a copy
      df_filled_mean[‘A’] = df_filled_mean[‘A’].fillna(df_filled_mean[‘A’].mean())

      Fill missing values in column ‘B’ with a specific value (e.g., 0)

      df_filled_value = df.copy()
      df_filled_value[‘B’] = df_filled_value[‘B’].fillna(0)

      Fill missing string values with ‘unknown’

      df_filled_string = df.copy()
      df_filled_string[‘C’] = df_filled_string[‘C’].fillna(‘unknown’)

      print(“\nDataFrame after filling ‘A’ with mean:”)
      print(df_filled_mean)
      print(“\nDataFrame after filling ‘B’ with 0:”)
      print(df_filled_value)
      print(“\nDataFrame after filling ‘C’ with ‘unknown’:”)
      print(df_filled_string)
      “`

    2. Removing Duplicate Records

    Duplicate rows can lead to over-representation of certain data points, skewing analysis.

    • Identify Duplicates:

      “`python

      Create a DataFrame with duplicates

      data_dup = {‘ID’: [1, 2, 2, 3, 4, 4],
      ‘Name’: [‘Alice’, ‘Bob’, ‘Bob’, ‘Charlie’, ‘David’, ‘David’]}
      df_dup = pd.DataFrame(data_dup)

      print(“\nDataFrame with duplicates:”)
      print(df_dup)

      print(“\nNumber of duplicate rows:”)
      print(df_dup.duplicated().sum())

      print(“\nDuplicate rows (showing all identical rows):”)
      print(df_dup[df_dup.duplicated(keep=False)])
      “`

    • Drop Duplicates:

      “`python

      Drop all duplicate rows, keeping the first occurrence

      df_no_dup = df_dup.drop_duplicates()
      print(“\nDataFrame after dropping duplicates (keeping first):”)
      print(df_no_dup)

      Drop duplicates based on a subset of columns

      df_no_dup_subset = df_dup.drop_duplicates(subset=[‘Name’])
      print(“\nDataFrame after dropping duplicates based on ‘Name’ (keeping first):”)
      print(df_no_dup_subset)
      “`

    3. Correcting Inconsistent Data Formats

    Inconsistent casing, extra whitespace, or incorrect data types are common issues.

    • Standardizing Text Data (Case and Whitespace):

      “`python
      df_text = pd.DataFrame({‘Product’: [‘ Apple ‘, ‘ Banana’, ‘orange ‘, ‘apple’]})

      print(“\nOriginal text data:”)
      print(df_text)

      Convert to lowercase and strip whitespace

      df_text[‘Product_Clean’] = df_text[‘Product’].str.lower().str.strip()
      print(“\nCleaned text data:”)
      print(df_text)
      “`

    • Correcting Data Types:
      Often, numbers are loaded as strings or dates as generic objects.

      “`python
      df_types = pd.DataFrame({‘Value’: [’10’, ’20’, ’30’, ‘not_a_number’],
      ‘Date’: [‘2023-01-01’, ‘2023-01-02’, ‘2023/01/03’, ‘invalid-date’]})

      print(“\nOriginal data types:”)
      print(df_types.dtypes)

      Convert ‘Value’ to numeric, coercing errors to NaN

      df_types[‘Value_Numeric’] = pd.to_numeric(df_types[‘Value’], errors=’coerce’)

      Convert ‘Date’ to datetime, coercing errors to NaT (Not a Time)

      df_types[‘Date_Datetime’] = pd.to_datetime(df_types[‘Date’], errors=’coerce’)

      print(“\nData after type conversion:”)
      print(df_types)
      print(“\nNew data types:”)
      print(df_types.dtypes)
      “`

    4. Dealing with Outliers

    Outliers are data points significantly different from others. While not always “errors,” they can disproportionately influence models. Identifying and handling them is context-dependent (e.g., using IQR, Z-scores, or domain knowledge) and often involves capping, transforming, or removing them.

    A Simple Data Cleaning Workflow Example

    Let’s put some of these techniques together.

    dirty_data = {
        'TransactionID': [101, 102, 103, 104, 105, 106, 107],
        'CustomerName': [' Alice ', 'Bob', 'Alice', 'Charlie', 'DAVID', 'Bob', np.nan],
        'Amount': [100.5, 200.0, np.nan, 150.75, 5000.0, 200.0, 75.0],
        'OrderDate': ['2023-01-01', '2023/01/02', '2023-01-01', '2023-01-03', 'invalid-date', '2023-01-02', '2023-01-04'],
        'Status': ['Completed', 'completed ', 'Pending', 'Completed', 'CANCELED', 'Completed', 'Pending']
    }
    df_dirty = pd.DataFrame(dirty_data)
    
    print("--- Original Dirty DataFrame ---")
    print(df_dirty)
    print("\nOriginal dtypes:")
    print(df_dirty.dtypes)
    print("\nMissing values:")
    print(df_dirty.isnull().sum())
    print("\nDuplicate rows:")
    print(df_dirty.duplicated().sum())
    
    print("\n--- Applying Cleaning Steps ---")
    
    df_dirty['CustomerName'] = df_dirty['CustomerName'].str.strip().str.title()
    
    df_dirty['CustomerName'].fillna('Unknown', inplace=True)
    
    df_dirty['OrderDate'] = pd.to_datetime(df_dirty['OrderDate'], errors='coerce')
    
    df_dirty['OrderDate'].fillna(method='ffill', inplace=True)
    
    df_dirty['Status'] = df_dirty['Status'].str.strip().str.title()
    
    df_dirty.drop_duplicates(subset=['CustomerName', 'OrderDate'], inplace=True)
    
    df_dirty = df_dirty[df_dirty['Amount'] < 5000.0]
    
    df_dirty.reset_index(drop=True, inplace=True)
    
    print("\n--- Cleaned DataFrame ---")
    print(df_dirty)
    print("\nCleaned dtypes:")
    print(df_dirty.dtypes)
    print("\nMissing values after cleaning:")
    print(df_dirty.isnull().sum())
    print("\nDuplicate rows after cleaning:")
    print(df_dirty.duplicated().sum())
    

    Best Practices for Data Cleaning

    • Always Work on a Copy: Preserve your original dataset.
    • Document Your Steps: Keep a record of all cleaning transformations.
    • Validate After Cleaning: Check your data’s integrity and distributions post-cleaning.
    • Iterate and Refine: Data cleaning is often an iterative process.
    • Understand Your Data: Domain knowledge is invaluable for effective cleaning.

    Conclusion

    Data cleaning is a critical, albeit often time-consuming, phase in any data project. Pandas provides a robust and flexible toolkit to tackle common data imperfections, transforming raw, messy data into a reliable foundation for meaningful analysis and accurate machine learning models. Mastering these techniques will significantly enhance the quality and trustworthiness of your data-driven insights.

  • Automate Excel Reporting with Python

    Introduction to Python-Powered Excel Automation

    Are you tired of spending countless hours manually updating Excel spreadsheets, copying and pasting data, and generating reports? For many businesses, Excel remains a critical tool for data management and reporting. However, the repetitive nature of these tasks is not only time-consuming but also highly susceptible to human error. This is where Python, a versatile and powerful programming language, steps in to revolutionize your Excel workflows.

    Automating Excel reporting with Python can transform tedious manual processes into efficient, accurate, and scalable solutions. By leveraging Python’s rich ecosystem of libraries, you can eliminate mundane tasks, free up valuable time, and ensure the consistency and reliability of your reports.

    Why Python for Excel Automation?

    Python offers compelling advantages for automating your Excel tasks:

    • Efficiency: Automate repetitive data entry, formatting, and report generation, saving significant time.
    • Accuracy: Reduce the risk of human error inherent in manual processes, ensuring data integrity.
    • Scalability: Easily handle large datasets and complex reporting requirements that would be cumbersome in Excel alone.
    • Flexibility: Integrate Excel automation with other data sources (databases, APIs, web scraping) and different analytical tools.
    • Versatility: Not just for Excel, Python can be used for a wide range of data analysis, visualization, and machine learning tasks.

    Essential Python Libraries for Excel

    To effectively automate Excel tasks, Python provides several robust libraries. The two most commonly used are:

    • Pandas: A powerful data manipulation and analysis library. It’s excellent for reading data from Excel, performing complex data transformations, and writing data back to Excel (or other formats).
    • Openpyxl: Specifically designed for reading and writing .xlsx files. While Pandas handles basic data transfer, openpyxl gives you granular control over cell styles, formulas, charts, and more advanced Excel features.

    Setting Up Your Python Environment

    Before you begin, you’ll need to have Python installed. We also need to install the necessary libraries using pip:

    pip install pandas openpyxl
    

    A Practical Example: Automating a Sales Summary Report

    Let’s walk through a simple yet powerful example: reading sales data from an Excel file, processing it to summarize total sales per product, and then exporting this summary to a new Excel report.

    Imagine you have a sales_data.xlsx file with columns like ‘Product’, ‘Region’, and ‘SalesAmount’.

    1. Create Dummy Sales Data (Optional)

    First, let’s simulate the sales_data.xlsx file manually or by running this short Python script:

    import pandas as pd
    
    data = {
        'Date': pd.to_datetime(['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03']),
        'Product': ['Laptop', 'Mouse', 'Keyboard', 'Laptop', 'Mouse'],
        'Region': ['North', 'South', 'North', 'South', 'North'],
        'SalesAmount': [1200, 25, 75, 1100, 30]
    }
    df_dummy = pd.DataFrame(data)
    df_dummy.to_excel("sales_data.xlsx", index=False)
    print("Created sales_data.xlsx")
    

    2. Automate the Sales Summary Report

    Now, let’s write the script to automate the reporting:

    import pandas as pd
    from openpyxl import load_workbook
    from openpyxl.styles import Font, Alignment, Border, Side
    
    def generate_sales_report(input_file="sales_data.xlsx", output_file="sales_summary_report.xlsx"):
        """
        Reads sales data, summarizes total sales by product, and
        generates a formatted Excel report.
        """
        try:
            # 1. Read the input Excel file using pandas
            df = pd.read_excel(input_file)
            print(f"Successfully read data from {input_file}")
    
            # 2. Process the data: Calculate total sales per product
            sales_summary = df.groupby('Product')['SalesAmount'].sum().reset_index()
            sales_summary.rename(columns={'SalesAmount': 'TotalSales'}, inplace=True)
            print("Calculated sales summary:")
            print(sales_summary)
    
            # 3. Write the summary to a new Excel file using pandas
            # This creates the basic Excel file with data
            sales_summary.to_excel(output_file, index=False, sheet_name="Sales Summary")
            print(f"Basic report written to {output_file}")
    
            # 4. Enhance the report using openpyxl for formatting
            wb = load_workbook(output_file)
            ws = wb["Sales Summary"]
    
            # Apply bold font to header row
            header_font = Font(bold=True)
            for cell in ws[1]: # First row is header
                cell.font = header_font
                cell.alignment = Alignment(horizontal='center')
    
            # Add borders to all cells
            thin_border = Border(left=Side(style='thin'),
                                 right=Side(style='thin'),
                                 top=Side(style='thin'),
                                 bottom=Side(style='thin'))
            for row in ws.iter_rows():
                for cell in row:
                    cell.border = thin_border
    
            # Auto-adjust column widths
            for col in ws.columns:
                max_length = 0
                column = col[0].column_letter # Get the column name
                for cell in col:
                    try: # Necessary to avoid error on empty cells
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = (max_length + 2)
                ws.column_dimensions[column].width = adjusted_width
    
            wb.save(output_file)
            print(f"Formatted report saved to {output_file}")
    
        except FileNotFoundError:
            print(f"Error: The file '{input_file}' was not found.")
        except Exception as e:
            print(f"An error occurred: {e}")
    
    # Run the automation
    if __name__ == "__main__":
        generate_sales_report()
    

    This script demonstrates reading data with Pandas, performing aggregation, writing the initial output to Excel using Pandas, and then using openpyxl to apply custom formatting like bold headers, borders, and auto-adjusted column widths.

    Beyond Simple Reports: Advanced Capabilities

    Python’s power extends far beyond generating basic tables. You can:

    • Create Dynamic Charts: Generate various chart types (bar, line, pie) directly within your Excel reports.
    • Apply Conditional Formatting: Highlight key data points based on specific criteria (e.g., sales above target).
    • Email Reports Automatically: Integrate with email libraries to send generated reports to stakeholders.
    • Schedule Tasks: Use tools like cron (Linux/macOS) or Windows Task Scheduler to run your Python scripts at specified intervals (daily, weekly, monthly).
    • Integrate with Databases/APIs: Pull data directly from external sources, process it, and generate reports without manual data extraction.

    Conclusion

    Automating Excel reporting with Python is a game-changer for anyone dealing with repetitive data tasks. By investing a little time in learning Python and its powerful data libraries, you can significantly boost your productivity, enhance reporting accuracy, and elevate your data handling capabilities. Say goodbye to manual drudgery and embrace the efficiency of Python automation!

  • Nginx + PHP-FPM + MariaDB 環境でつまづきやすいポイント

    WordPress を動かすために Nginx + PHP-FPM + MariaDB を構築する場合、いくつかのハマりポイントがあります。今回はその中でも特によくあるものを3つ紹介します。


    1. localhost127.0.0.1 の違い

    WordPress の wp-config.php
    php
    define( 'DB_HOST', 'localhost' );

    と書いた場合、MariaDB へは ソケット接続 が使われます。一方で
    php
    define( 'DB_HOST', '127.0.0.1' );

    とすれば TCP接続 になります。
    MariaDB の設定と合わないと「Error establishing a database connection」が出るので注意が必要です。


    2. PHP-FPM のログ出力先

    php-fpm.conferror_log が存在しないディレクトリを指していると、起動自体に失敗します。
    特に /usr/local/var/log/ 配下は、権限やファイルシステムの状態でエラーが出やすいので、/var/log/php-fpm.log など OS 標準の場所に寄せるのが安全です。


    3. SELinux やファイルパーミッション

    一見正しく設定していても、502 Bad Gateway や DB 接続エラーが出る場合は、SELinux やディレクトリの権限を疑いましょう。
    テスト時は getenforce で Disabled/Permissive を確認、本番では適切なコンテキストを付与するのがベストです。


    まとめ

    環境構築でハマったときは
    1. DB 接続方法(ソケットかTCPか)
    2. PHP-FPM の設定ファイルとログパス
    3. SELinuxや権限まわり

    この3つを順に確認すると、原因切り分けがスムーズになります。