美化你LeetCode倉庫的README

BBruceyuan發表於2018-02-01

LeetCode幾乎成了現在每個人找工作前都要刷的"新手村"的怪,每一個找工作的人面試之前都要都會去刷一刷。閒話不多說,先看看大佬們都是怎麼評價LeetCode

雖然面試官也不會看你github上leetcode的repo,但是如果你有這麼一個東西還是極好的,很多大佬都會去刷這個東西,放在github上面,當然新手也會這麼做。我們就會發現,大佬們的README超級好看,方便自己日後查詢,但是新手的命名就比較雜亂無章,一點也不規範,自己找起來非常費勁。我自己就是這麼一個情況,所以我決定重構一下這個目錄結構,生成一個好看一點的。

我們先看一下生成的效果吧!更詳細具體效果請檢視這裡

美化你LeetCode倉庫的README

直接上操作部分吧!

前期準備

在github上建立一個倉庫,比如叫leetcode或者像我的一樣叫algorithms_and_oj。然後從github中git clone下來。

然後,安裝專案前置需要的庫:

pip install requests
複製程式碼

開始分析

首先需要配置我們的路徑 (以下路徑均按照我專案來配置,需要按照自己要求修改)

class Config:
    """
    some config, such as your github page
    這裡需要配置你自己的專案地址
    1. 本地倉庫的的路徑
    2. github中的倉庫leetcode解法的路徑
    """
    local_path = '/home/yuan/PycharmProjects/algorithms_and_oj'
    # solution of leetcode
    github_leetcode_url = 'https://github.com/hey-bruce/algorithms_and_oj/blob/master/leetcode-algorithms/'
    # solution of pat, 暫時還沒用上
    github_pat_url = 'https://github.com/hey-bruce/algorithms_and_oj/blob/master/pat-algorithms/'
    leetcode_url = 'https://leetcode.com/problems/'
複製程式碼

我們需要哪些資訊,每個問題的ID,title, url,難度,已經使用什麼語言解決等,所以我們很自然的構造一個Question類

class Question:
    """
    this class used to store the inform of every question
    """

    def __init__(self, id_,
                 name, url,
                 lock, difficulty):
        self.id_ = id_
        self.title = name
        # the problem description url 問題描述頁
        self.url = url
        self.lock = lock  # boolean,鎖住了表示需要購買
        self.difficulty = difficulty
        # the solution url
        self.python = ''
        self.java = ''
        self.javascript = ''
        self.c_plus_plus = ''

    def __repr__(self):
        """
        沒啥用,我為了除錯方便寫的
        :return:
        """
        return str(self.id_) + ' ' + str(self.title) + ' ' + str(self.url)
複製程式碼

接下來,我們就要從LeetCode上獲取問題,在在google一查,就找到了https://leetcode.com/api/problems/algorithms/返回的是json資料。稍微進行了一下分析(這個自己看一下資料,很容易發現的,雖然leetcode沒有api描述頁),就可以知道stat中的frontend_question_ididquestion__title_slug對應的url的地址,question__title對應的是問題的名字,paid_only表示是否需要購買。difficulty表示難度.。然後我們可以出獲取LeetCode資訊的程式碼。

def get_leetcode_problems(self):
    """
    used to get leetcode inform
    :return:
    """
    # we should look the response data carefully to find law
    # return byte. content type is byte
    content = requests.get('https://leetcode.com/api/problems/algorithms/').content
    # get all problems
    self.questions = json.loads(content)['stat_status_pairs']
    # print(self.questions)
    difficultys = ['Easy', 'Medium', 'Hard']
    for i in range(len(self.questions) - 1, -1, -1):
        question = self.questions[i]
        name = question['stat']['question__title']
        url = question['stat']['question__title_slug']
        id_ = str(question['stat']['frontend_question_id'])
        if int(id_) < 10:
            id_ = '00' + id_
        elif int(id_) < 100:
            id_ = '0' + id_
        lock = question['paid_only']
        if lock:
            self.locked += 1
        difficulty = difficultys[question['difficulty']['level'] - 1]
        url = Config.leetcode_url + url + '/description/'
        q = Question(id_, name, url, lock, difficulty)
        # 這裡後面我們會放到類裡面,所以不用擔心
        # 之所以用一個table和table_item就是因為,我們後期已經用什麼語言解決題目的時候要進行索引
        self.table.append(q.id_)
        self.table_item[q.id_] = q
    return self.table, self.table_item
複製程式碼

我們需要一個東西記錄我們完成情況的類CompleteInform:

class CompleteInform:
    """
    this is statistic inform,
    用每種語言完成了多少題
    """

    def __init__(self):
        self.solved = {
            'python': 0,
            'c++': 0,
            'java': 0,
            'javascript': 0
        }
        self.total = 0

    def __repr__(self):
        return str(self.solved)
複製程式碼

然後我們根據題目資訊來建立題目對應的資料夾,

def __create_folder(self, oj_name):
    """
    oj_name後面會傳入'leetcode',這裡這麼做就是後期,我想擴充套件生成別的oj的table
    """
    oj_algorithms = Config.local_path + '/' + oj_name + '-algorithms'
    if os.path.exists(oj_algorithms):
        print(oj_name, ' algorithms is already exits')
    else:
        print('creating {} algorithms....'.format(oj_name))
        os.mkdir(oj_algorithms)
    for item in self.table_item.values():
        question_folder_name = oj_algorithms + '/' + item.id_ + '. ' + item.title
        if not os.path.exists(question_folder_name):
            print(question_folder_name + 'is not exits, create it now....')
            os.mkdir(question_folder_name)

# 這裡都會傳入一個‘leetcode',設定oj名字就是為了方便擴充套件
def update_table(self, oj):
    # the complete inform should be update
    complete_info = CompleteInform()
    self.get_leetcode_problems()
    # the total problem nums
    complete_info.total = len(self.table)
    self.__create_folder(oj)
    oj_algorithms = Config.local_path + '/' + oj + '-algorithms'
    # 檢視os.walk看具體返回的是什麼東西
    for _, folders, _ in os.walk(oj_algorithms):
        for folder in folders:
            for _, _, files in os.walk(os.path.join(oj_algorithms, folder)):
                if len(files) != 0:
                    complete_info.complete_num += 1
                for item in files:
                    if item.endswith('.py'):
                        # 這個部分可以寫成函式,不過我好像設計有點問題,不太好重構,請讀者自己思考
                        complete_info.solved['python'] += 1
                        folder_url = folder.replace(' ', "%20")
                        folder_url = os.path.join(folder_url, item)
                        folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                        self.table_item[folder[:3]].python = '[python]({})'.format(folder_url)
                    elif item.endswith('.java'):
                        complete_info.solved['java'] += 1
                        folder_url = folder.replace(' ', "%20")
                        folder_url = os.path.join(folder_url, item)
                        folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                        self.table_item[folder[:3]].java = '[Java]({})'.format(folder_url)
                    elif item.endswith('.cpp'):
                        complete_info.solved['c++'] += 1
                        folder_url = folder.replace(' ', "%20")
                        folder_url = os.path.join(folder_url, item)
                        folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                        self.table_item[folder[:3]].c_plus_plus = '[C++]({})'.format(folder_url)
                    elif item.endswith('.js'):
                        complete_info.solved['javascript'] += 1
                        folder_url = folder.replace(' ', "%20")
                        folder_url = os.path.join(folder_url, item)
                        folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                        self.table_item[folder[:3]].javascript = '[JavaScript]({})'.format(folder_url)
    # 這裡使用到的Readme這個類就是寫檔案,相對不是特別重要,沒什麼好講的
    readme = Readme(complete_info.total, complete_info.complete_num, complete_info.solved)
    readme.create_leetcode_readme([self.table, self.table_item])
    print('-------the complete inform-------')
    print(complete_info.solved)
複製程式碼

上面需用用到的Readme類,用來生成README.md,只是進行了檔案的讀寫,相對比較簡單。聰明的你肯定一看就知道了。(只需要瞭解一點markdown中表格的生成規則)

class Readme:
    """
    generate folder and markdown file
    update README.md when you finish one problem by some language
    """

    def __init__(self, total, solved, others):
        """
        :param total: total problems nums
        :param solved: solved problem nums
        :param others: 暫時還沒用,我想做擴充套件
        """
        self.total = total
        self.solved = solved
        self.others = others
        self.time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        self.msg = '# Keep thinking, keep alive\n' \
                   'Until {}, I have solved **{}** / **{}** problems. ' \
                   '\n\nCompletion statistic: ' \
                   '\n1. JavaScript: {javascript} ' \
                   '\n2. Python: {python}' \
                   '\n3. C++: {c++}' \
                   '\n4. Java: {java}' \
                   '\n\nNote: :lock: means you need to buy a book from LeetCode\n'.format(
                    self.time, self.solved, self.total, **self.others)

    def create_leetcode_readme(self, table_instance):
        """
        create REAdME.md
        :return:
        """
        file_path = Config.local_path + '/README.md'
        # write some basic inform about leetcode
        with open(file_path, 'w') as f:
            f.write(self.msg)
            f.write('\n----------------\n')

        with open(file_path, 'a') as f:
            f.write('## LeetCode Solution Table\n')
            f.write('| ID | Title | Difficulty | JavaScript | Python | C++ | Java |\n')
            f.write('|:---:' * 7 + '|\n')
            table, table_item = table_instance
            for index in table:
                item = table_item[index]
                if item.lock:
                    _lock = ':lock:'
                else:
                    _lock = ''
                data = {
                    'id': item.id_,
                    'title': '[{}]({}) {}'.format(item.title, item.url, _lock),
                    'difficulty': item.difficulty,
                    'js': item.javascript if item.javascript else 'To Do',
                    'python': item.python if item.python else 'To Do',
                    'c++': item.c_plus_plus if item.c_plus_plus else 'To Do',
                    'java': item.java if item.java else 'To Do'
                }
                line = '|{id}|{title}|{difficulty}|{js}|{python}|{c++}|{java}|\n'.format(**data)
                f.write(line)
            print('README.md was created.....')
複製程式碼

完整程式碼請看:(這個可以跑,歡迎使用)

                -------原始碼更好的閱讀體驗,請看這裡,程式碼使用方式

#!/usr/bin/env python
# Created by Bruce yuan on 18-1-22.
import requests
import os
import json
import time

class Config:
    """
    some config, such as your github page
    這裡需要配置你自己的專案地址
    1.&emsp;本地倉庫的的路徑
    2.&emsp;github中的倉庫leetcode解法的路徑
    """
    local_path = '/home/yuan/PycharmProjects/algorithms_and_oj'
    # solution of leetcode
    github_leetcode_url = 'https://github.com/hey-bruce/algorithms_and_oj/blob/master/leetcode-algorithms/'
    # solution of pat,&emsp;暫時還沒寫
    github_pat_url = 'https://github.com/hey-bruce/algorithms_and_oj/blob/master/pat-algorithms/'
    leetcode_url = 'https://leetcode.com/problems/'

class Question:
    """
    this class used to store the inform of every question
    """

    def __init__(self, id_,
                 name, url,
                 lock, difficulty):
        self.id_ = id_
        self.title = name
        # the problem description url&emsp;問題描述頁
        self.url = url
        self.lock = lock  # boolean,鎖住了表示需要購買
        self.difficulty = difficulty
        # the solution url
        self.python = ''
        self.java = ''
        self.javascript = ''
        self.c_plus_plus = ''

    def __repr__(self):
        """
        沒啥用,我為了除錯方便寫的
        :return:
        """
        return str(self.id_) + ' ' + str(self.title) + ' ' + str(self.url)

class TableInform:
    def __init__(self):
        # raw questions inform
        self.questions = []
        # this is table index
        self.table = []
        # this is the element of question
        self.table_item = {}
        self.locked = 0

    def get_leetcode_problems(self):
        """
        used to get leetcode inform
        :return:
        """
        # we should look the response data carefully to find law
        # return byte. content type is byte
        content = requests.get('https://leetcode.com/api/problems/algorithms/').content
        # get all problems
        self.questions = json.loads(content)['stat_status_pairs']
        # print(self.questions)
        difficultys = ['Easy', 'Medium', 'Hard']
        for i in range(len(self.questions) - 1, -1, -1):
            question = self.questions[i]
            name = question['stat']['question__title']
            url = question['stat']['question__title_slug']
            id_ = str(question['stat']['frontend_question_id'])
            if int(id_) < 10:
                id_ = '00' + id_
            elif int(id_) < 100:
                id_ = '0' + id_
            lock = question['paid_only']
            if lock:
                self.locked += 1
            difficulty = difficultys[question['difficulty']['level'] - 1]
            url = Config.leetcode_url + url + '/description/'
            q = Question(id_, name, url, lock, difficulty)
            self.table.append(q.id_)
            self.table_item[q.id_] = q
        return self.table, self.table_item

    # create problems folders
    def __create_folder(self, oj_name):
        oj_algorithms = Config.local_path + '/' + oj_name + '-algorithms'
        if os.path.exists(oj_algorithms):
            print(oj_name, ' algorithms is already exits')
        else:
            print('creating {} algorithms....'.format(oj_name))
            os.mkdir(oj_algorithms)
        for item in self.table_item.values():
            question_folder_name = oj_algorithms + '/' + item.id_ + '. ' + item.title
            if not os.path.exists(question_folder_name):
                print(question_folder_name + 'is not exits, create it now....')
                os.mkdir(question_folder_name)

    def update_table(self, oj):
        # the complete inform should be update
        complete_info = CompleteInform()
        self.get_leetcode_problems()
        # the total problem nums
        complete_info.total = len(self.table)
        complete_info.lock = self.locked
        self.__create_folder(oj)
        oj_algorithms = Config.local_path + '/' + oj + '-algorithms'
        # 檢視os.walk看具體返回的是什麼東西
        for _, folders, _ in os.walk(oj_algorithms):
            for folder in folders:
                for _, _, files in os.walk(os.path.join(oj_algorithms, folder)):
                    # print(files)
                    if len(files) != 0:
                        complete_info.complete_num += 1
                    for item in files:
                        if item.endswith('.py'):
                          
  complete_info.solved['python'] += 1
                            folder_url = folder.replace(' ', "%20")
                            folder_url = os.path.join(folder_url, item)
                            folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                            self.table_item[folder[:3]].python = '[Python]({})'.format(folder_url)
                        elif item.endswith('.java'):
                            complete_info.solved['java'] += 1
                            folder_url = folder.replace(' ', "%20")
                            folder_url = os.path.join(folder_url, item)
                            folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                            self.table_item[folder[:3]].java = '[Java]({})'.format(folder_url)
                        elif item.endswith('.cpp'):
                            complete_info.solved['c++'] += 1
                            folder_url = folder.replace(' ', "%20")
                            folder_url = os.path.join(folder_url, item)
                            folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                            self.table_item[folder[:3]].c_plus_plus = '[C++]({})'.format(folder_url)
                        elif item.endswith('.js'):
                            complete_info.solved['javascript'] += 1
                            folder_url = folder.replace(' ', "%20")
                            folder_url = os.path.join(folder_url, item)
                            folder_url = os.path.join(Config.github_leetcode_url, folder_url)
                            self.table_item[folder[:3]].javascript = '[JavaScript]({})'.format(folder_url)
        readme = Readme(complete_info.total, 
                        complete_info.complete_num, 
                        complete_info.lock, 
                        complete_info.solved)
        readme.create_leetcode_readme([self.table, self.table_item])
        print('-------the complete inform-------')
        print(complete_info.solved)

class CompleteInform:
    """
    this is statistic inform
    """

    def __init__(self):
        self.solved = {
            'python': 0,
            'c++': 0,
            'java': 0,
            'javascript': 0
        }
        self.complete_num = 0
        self.lock = 0
        self.total = 0

    def __repr__(self):
        return str(self.solved)

class Readme:
    """
    generate folder and markdown file
    update README.md when you finish one problem by some language
    """

    def __init__(self, total, solved, locked, others):
        """
        :param total: total problems nums
        :param solved: solved problem nums
        :param others: 暫時還沒用,我想做擴充套件
        """
        self.total = total
        self.solved = solved
        self.others = others
        self.locked = locked
        self.time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        self.msg = '# Keep thinking, keep alive\n' \
                   'Until {}, I have solved **{}** / **{}** problems ' \
                   'while **{}** are still locked.' \
                   '\n\nCompletion statistic: ' \
                   '\n1. JavaScript: {javascript} ' \
                   '\n2. Python: {python}' \
                   '\n3. C++: {c++}' \
                   '\n4. Java: {java}' \
                   '\n\nNote: :lock: means you need to buy a book from LeetCode\n'.format(
                    self.time, self.solved, self.total, self.locked, **self.others)

    def create_leetcode_readme(self, table_instance):
        """
        create REAdME.md
        :return:
        """
        file_path = Config.local_path + '/README.md'
        # write some basic inform about leetcode
        with open(file_path, 'w') as f:
            f.write(self.msg)
            f.write('\n----------------\n')

        with open(file_path, 'a') as f:
            f.write('## LeetCode Solution Table\n')
            f.write('| ID | Title | Difficulty | JavaScript | Python | C++ | Java |\n')
            f.write('|:---:' * 7 + '|\n')
            table, table_item = table_instance
            for index in table:
                item = table_item[index]
                if item.lock:
                    _lock = ':lock:'
                else:
                    _lock = ''
                data = {
                    'id': item.id_,
                    'title': '[{}]({}) {}'.format(item.title, item.url, _lock),
                    'difficulty': item.difficulty,
                    'js': item.javascript if item.javascript else 'To Do',
                    'python': item.python if item.python else 'To Do',
                    'c++': item.c_plus_plus if item.c_plus_plus else 'To Do',
                    'java': item.java if item.java else 'To Do'
                }
                line = '|{id}|{title}|{difficulty}|{js}|{python}|{c++}|{java}|\n'.format(**data)
                f.write(line)
            print('README.md was created.....')

def main():
    table = TableInform()
    table.update_table('leetcode')

if __name__ == '__main__':
    main()
複製程式碼

歡迎使用,歡迎star,Happy Coding!

作者:BBruceyuan(袁朝發)

Github:https://github.com/hey-bruce

知乎專欄:打點醬油

簡書地址:打點醬油

微信公眾號:打點醬油

相關文章