How I created a Blog with Github PyGithub & Flask
Tags: eodstockapi, githubblog, stockmarketapi
Published: 2023-04-14 19:39:49

## How I created a Blog with Github PyGithub & Flask

**Before we start my blog is still a work in progress and is viewable here [EOD Stock API Developers Blogs](https://eod-stock-api.site/blog/Developers/index.html)**

### Introduction
Sometimes when you are creating a website or web application you are simply looking for an easy way to intergrate a Blog into your website, however creating a fully fledged solution may require a lot such as 

- Backend Database such us MySQL
- CMS System
- Authorization & Authentication System

However this is overwelming especially if you just want to post simple updates from time to time.

This is the problem i was trying to solve when i finally decided to just use Github as my Content Management System , Backend Database & Authorization, the result was a simple blog i could utilize for my site.

### Here is how i created a Blog with Github as a CMS.

Let's start by breaking down the steps involved in creating a blog with Github, PyGithub, and Python Flask:

Set up a Github account: If you don't already have a Github account, create one by visiting https://github.com/ and following the prompts.

Create a new repository: Once you're logged into Github, create a new repository by clicking the "New" button on the upper-left corner of the dashboard. Give your repository a name and description, and make sure to select "Public" if you want your blog to be accessible to everyone.

Clone the repository: Once your repository is created, you'll need to clone it to your local machine so you can work on it locally. To do this, copy the repository URL from the Github dashboard and open a terminal window on your local machine. Type the following command and replace "repository-url" with the actual URL of your repository:

```sh
git clone repository-url
```


Create a virtual environment: It's always a good practice to work in a virtual environment when developing Python applications. To create a virtual environment, open a terminal window and type the following commands:

```sh
python -m venv venv
source venv/bin/activate
```

Install PyGithub and Flask: PyGithub is a Python library that makes it easy to interact with Github's API, while Flask is a lightweight web framework that you can use to build your blog. To install these packages, type the following command in your terminal window:

```sh
pip install PyGithub Flask
```
Create a Flask app: Create a new Python file named "app.py" in the root directory of your project, and add the following code to it:

```python

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello, world!'

if __name__ == '__main__':
    app.run()
```
Test your app: To test your app, open a terminal window and navigate to the root directory of your project. Type the following command to run your Flask app:

```sh
python app.py

```
You should see a message indicating that your app is running on http://localhost:5000. Open a web browser and navigate to that URL to see the "Hello, world!" message.

Add a blog post: Now that you have a basic Flask app up and running, it's time to add some blog functionality. 

Create a new Python file named "blog.py" in the root directory of your project, and add the following code to it:

```python
from flask import Blueprint, render_template

blog_bp = Blueprint('blog', __name__)

@blog_bp.route('/')
def index():
    return render_template('blog/index.html')

@blog_bp.route('/blog/<slug>')
def post(slug):
    return render_template(f'blog/{slug}.html')

```


Create HTML templates: Create two new folders in the root directory of your project named "templates" and "static". Inside the "templates" folder, create two new HTML files named "base.html" and "index.html". Inside the "blog" folder, create a new HTML file named "first-post.html" and add some content to it. The content could be something like "My first blog post!".

In "base.html", add the following code:

```jinja2

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    {% block content %}{% endblock %}
</body>
</html>

```

Now that we are done with the boilerplate, lets get to the real thing, lets first create a class called GithubBlog like this

```python
class GithubBlog:
    """
    A class representing a blog hosted on GitHub.
    """

    def __init__(self, github_token, blog_repo, ignore_files=None, github_url=None, blog_url=None):
        """
            Initialize a GithubBlog object.

            Parameters:
            - github_token (str): A personal access token for accessing the GitHub API.
            - blog_repo (str): The name of the GitHub repository containing the blog.
            - ignore_files (list of str, optional): A list of filenames to ignore when fetching blog files.
            - github_url (str, optional): The base URL for the raw GitHub content.
            - blog_url (str, optional): The base URL for the published blog.
        """
        self.token = github_token
        self.github = Github(self.token)
        self.repo = self.github.get_repo(blog_repo)
        self.ignore_files = ignore_files or ['readme.md', '.gitignore', 'license']
        self.github_url = github_url or 'https://raw.githubusercontent.com/MJ-API-Development/eod-api-blog/main/'
        self.blog_url = blog_url or 'https://eod-stock-api.site/blog/'
        self.last_commit_time = None
        self.blog_files = {}
        self._logger = init_logger('github_blog')
```

The GithubBlog class is a Python class that represents a blog hosted on GitHub. It is designed to interact with the GitHub API using the PyGithub library, and provides a convenient way to fetch and process blog files.

The class has an __init__ method that takes several parameters, including a personal access token for accessing the GitHub API, the name of the GitHub repository containing the blog, a list of filenames to ignore when fetching blog files, and base URLs for the raw GitHub content and the published blog.

The GithubBlog class also has several attributes, including the token (the personal access token), github (a PyGithub Github object), repo (a PyGithub Repository object representing the blog repository), ignore_files (a list of filenames to ignore when fetching blog files), github_url (the base URL for the raw GitHub content), blog_url (the base URL for the published blog), last_commit_time (the time of the most recent commit to the blog repository), blog_files (a dictionary of blog files, where the keys are the filenames and the values are the file contents), and _logger (a Python logger object for logging messages).

The GithubBlog class provides several methods for fetching and processing blog files, including get_last_commit_time (to get the time of the most recent commit to the blog repository), fetch_blog_files (to fetch and process blog files from the GitHub repository), get_blog_file (to get the contents of a specific blog file), and get_blog_posts (to get a list of all blog posts, sorted by date). These methods are designed to be used by the Flask app to display blog content to the user.

we will discuss this methods in the following chapter.

```python
    def check_for_updates(self):
        """
        Check if there has been a commit since the last time this method was called.
        """
        latest_commit_time = self.repo.updated_at
        if self.last_commit_time is None or latest_commit_time > self.last_commit_time:
            self.last_commit_time = latest_commit_time
            # Do something if there has been an update
            self.update_blog()
            sitemap_url = f"{self.blog_url}sitemap.xml"
            submit_sitemap_to_google_search_console(sitemap_url)
        else:
            # Do something if there has not been an update
            pass
```

the above mention method is used to check if your github repo has new updates if this is the case the method calls self.update_blog() after which it creates a new sitemap.xml file then submit the sitemap.xml file to Google Search.

```python
def submit_sitemap_to_google_search_console(sitemap_url):
    """
    Submit the sitemap to Google Search Console
    """
    api_endpoint = f"https://www.google.com/ping?sitemap={sitemap_url}"
    # Define the request headers
    headers = {
        'Content-Type': 'application/xml',
        'User-Agent': 'Mozilla/5.0 (Windows; U; Windows)',
    }
    params = {'key': config_instance().SEARCH_CONSOLE_API_KEY}
    response = requests.get(api_endpoint, headers=headers, params=params)

    return response
```
the above function is a helper function to GithubBlog Class which is used to submit Sitemap to Google Search for indexing.


```python

    @cached
    def fetch_all_blog_files(self):
        """
        Fetch the blog files from the GitHub repository.
        """
        contents = self.repo.get_contents('')
        while contents:
            file_content = contents.pop(0)
            if file_content.type == 'dir':
                contents.extend(self.repo.get_contents(file_content.path))
            else:
                if file_content.name.lower() not in self.ignore_files:
                    self.blog_files[file_content.name] = file_content
```

The above method uses self.repo and get the contents of the repo then while there is still contents in the Repo it will add the filenames to self.blog_files unless the file is already added.

```python

    @cached
    def blog_directories(self, directory=""):
        """
        Recursively searches for blog files in a given directory and its subdirectories,
        and returns a dictionary of file names and URLs.
        """
        blog_files = {}

        # Get the contents of the given directory using the GitHub API
        contents = self.repo.get_contents(directory)

        for content_file in contents:
            if content_file.type == "dir" and not content_file.name.lower().startswith(".idea"):
                # If the content file is a directory, recursively call this method to get its files
                subdir_files = self.blog_directories(content_file.path)
                # Add the subdirectory files to the parent directory's dictionary
                blog_files[content_file.name] = subdir_files
            elif content_file.type == "file":
                # If the content file is a file, check if it's a blog file and add it to the dictionary
                file_name = content_file.name
                file_url = content_file.download_url
                if self._is_blog_file(file_name) and file_url not in blog_files:
                    blog_files[file_url] = file_name

        return blog_files
```
The blog_directories method is a Python method within the GithubBlog class that recursively searches for blog files in a given directory and its subdirectories, and returns a dictionary of file names and URLs. The @cached decorator indicates that the results of this method will be cached for performance optimization.

The method takes a directory parameter, which specifies the starting directory to search for blog files. The method then uses the PyGithub library to get the contents of the given directory using the GitHub API.

The method then iterates through the contents of the directory and checks whether each content file is a directory or a file. If the content file is a directory, the method recursively calls itself to get the files in the subdirectory, and adds them to the parent directory's dictionary. If the content file is a file, the method checks whether it is a blog file (using the _is_blog_file method) and adds it to the dictionary if it is.

The method then returns the blog_files dictionary, which contains the file names and URLs of all the blog files found in the given directory and its subdirectories.

Overall, the blog_directories method is an important part of the GithubBlog class, as it allows the Flask app to easily fetch and process all the blog files in the repository. By recursively searching through all directories, the method ensures that all blog files are included, even if they are in nested subdirectories.

Because this article has turned up to be too long, we will finish up the article as part two of this article.

This project source can be found at Github at the following URL
[Github Blog with PyGithub](https://github.com/MJ-API-Development/stock-api-client) 

The Live Blog is part of my [EOD-Stock-API Developers Blog](https://eod-stock-api.site/blog/Developers/index.html)

[EOD Stock API](https://eod-stock-api.site)
**is a Stock Market API created to help developers to obtain stock market data, financial news & social media financial trends in one easy to use API.**