Запросите github api и отфильтруйте последние 1000 коммитов, которые включают и исключают определенные слова из трех ведущих репозиториев в данной организации.

Этот код должен запрашивать github api и фильтровать из последних 1000 коммитов, которые включают или исключают определенные слова из трех ведущих репозиториев в данной организации.

Например, я мог бы запросить конечную точку /search с такими параметрами, как org:facebook а также include:fixed exclude:try. Затем он должен вернуть из 3 самых популярных репозиториев facebook и из 1000 самых последних коммитов сообщения фиксации, которые включают и исключают fixed а также try, соответственно.

from flask import Flask, request
import requests
import json

app = Flask(__name__)

NUM_REPOS = 3
MAX_PER_PAGE = 100
NUM_PAGES = 10
HEADERS = {"Accept": "application/vnd.github.cloak-preview"}


def get_matches():
    if request.method == 'POST':
        try:
            org = request.get_json()["org"]
            include = request.get_json()["include"]
            word_to_ignore = request.get_json()["ignore"]

        except:
            return {"error": "your request is missing the 'org', 'include' or 'ignore' parameters"}

        repo_data = requests.get( f"https://api.github.com/search/repositories?q=org:{org}&sort=stars&order=desc&per_page={NUM_REPOS}").content
        repo_data_dict = json.loads(repo_data.decode('utf-8'))

        if not "items" in repo_data_dict:
            if "errors" in repo_data_dict:
                return {"invalid_organization": f"{org} does not exist or doesn't have any repositories"}
            else:
                return {"reached_api_limit": True}
        else:
            repo_list = repo_data_dict["items"]

        matches = []

        for repo in repo_list:
            repo_info = {}
            repo_name = repo["name"]

            repo_info["repo"] = repo_name

            page = 1
            items_exist = True
            total_count = 0

            while (items_exist and page <= NUM_PAGES):
                commits = requests.get( f"https://api.github.com/search/commits?q={include} repo:{org}/{repo_name}&per_page={MAX_PER_PAGE}&page={page}", headers=HEADERS).content
                commits = json.loads(commits.decode('utf-8'))

                if "message" in commits:
                    matches.append({"reached_api_limit": True})
                    return {"matches": matches}

                if "items" in commits and len(commits["items"]) > 0 and (page == 1 or commits["total_count"] > page-1 * MAX_PER_PAGE):

                    for commit in commits["items"]:

                        if word_to_ignore in commit["commit"]["message"]:
                            continue

                        repo_info["message"] = commit["commit"]["message"]

                        matches.append(repo_info)

                        if commits["total_count"] < MAX_PER_PAGE:
                            items_exist = False

                else:
                    items_exist = False

                page += 1

        return {"matches": matches}


@ app.route('/search', methods=['POST'])
def example():
    return get_matches()


if __name__ == '__main__':
    app.run()

1 ответ
1

if request.method == 'POST': является избыточным; удалите это.

Ваш

в вашем запросе отсутствуют параметры org, include или ignore

менее чем полезно. Почему бы не сказать пользователю, что именно ему не хватает?

Этот except блокировать:

    try:
        org = request.get_json()["org"]
        include = request.get_json()["include"]
        word_to_ignore = request.get_json()["ignore"]

    except:
        return {"error": "your request is missing the 'org', 'include' or 'ignore' parameters"}

сообщит пользователю, что в их запросе отсутствует параметр, даже если серверу не хватает памяти или вся полезная нагрузка представляет собой строку banana вместо правильного тела JSON; или кто-то прикрепляет серверный процесс к консоли и нажимает Ctrl + C, чтобы прервать его. Вы уверены, что все три из этих исключений означают, что пользователь отправил правильно сформированную полезную нагрузку JSON, в которой отсутствует один или несколько ключей? Никогда не обнажать except:.

Переместите параметры строки запроса в params словарь, отправленный на запросы.

Этот:

    if not "items" in repo_data_dict:
        if "errors" in repo_data_dict:
            return {"invalid_organization": f"{org} does not exist or doesn't have any repositories"}
        else:
            return {"reached_api_limit": True}

это дикая и безумная догадка. Что, если «ошибки» сообщают вам, что сервер не работает на техническое обслуживание, или вы отправили искаженные данные, или конечная точка устарела? Вам необходимо обратить внимание на содержание предоставленного вам сообщения об ошибке.

Не надо json.loads здесь. Просто позвони .json() на ответ.

Обязательно проверьте условия ошибки HTTP через response.raise_for_status().

Тебе нужно расстаться get_matches на несколько методов.

Вам нужно улучшить разделение проблем — get_matches возвращает словари, предназначенные для использования REST API, но этого не следует делать в этой функции — формирование словаря полезной нагрузки должно выполняться выше, в функции обработчика Flask. Чтобы указать на ошибки, вы можете генерировать собственные исключения, перехватывать их указанным выше методом и добавлять в словарь ответов. Чтобы указать, достигли ли вы ограничения API, вы можете добавить логическое значение в свой возвращаемый кортеж.

total_count не используется; удалите это.

Формат вашего ответа плоский, и это может быть неудобно и неэффективно для использования на стороне клиента. Вместо этого рассмотрите словарь репозиториев, каждое значение которого представляет собой список сообщений, а не добавляйте имя репо к каждому элементу ответа.

Эта проверка:

                    if commits["total_count"] < MAX_PER_PAGE:
                        items_exist = False

кажется, существует на неправильном уровне отступа, поскольку он применяется к commits в общем, а не текущая итерация commit.

Этот:

commits["total_count"] > page-1 * MAX_PER_PAGE

почти наверняка не делает то, что вы хотите, так как делает

page - (1*MAX_PER_PAGE)

и нет

(page - 1)*MAX_PER_PAGE

поэтому добавьте скобки по мере необходимости.

Некоторые из приведенных выше предложений превратились в пример:

from pprint import pprint
from typing import List, Tuple

import requests

NUM_REPOS = 3
MAX_PER_PAGE = 100
NUM_PAGES = 10
HEADERS = {"Accept": "application/vnd.github.cloak-preview"}


class InvalidOrganizationError(Exception):
    pass


class APILimitError(Exception):
    pass


# @app.route('/search', methods=('POST',))
def handle_search() -> dict:
    payload = request.get_json()

    try:
        org = payload['org']
    except KeyError:
        return {'error': 'Missing the "org" parameter'}

    try:
        include = payload['include']
    except KeyError:
        return {'error': 'Missing the "include" parameter'}

    try:
        word_to_ignore = payload['word_to_ignore']
    except KeyError:
        return {'error': 'Missing the "word_to_ignore" parameter'}

    matches, api_limited = get_matches(org, include, word_to_ignore)
    return {'matches': matches, 'reached_api_limit': api_limited}


def get_repo(org: str) -> dict:
    with requests.get(
        'https://api.github.com/search/repositories',
        params={
            'q': f'org:{org}',
            'sort': 'stars',
            'order': 'desc',
            'per_page': NUM_REPOS,
        },
        headers=HEADERS,
    ) as resp:
        resp.raise_for_status()
        repo_data_dict = resp.json()

    repo_list = repo_data_dict.get('items')
    if repo_list is not None:
        return repo_list

    if 'errors' in repo_data_dict:
        raise InvalidOrganizationError(
            f'{org} does not exist or does not have any repositories'
        )
    raise APILimitError()


def get_commits(org: str, include: str, repo_name: str, page: int) -> dict:
    with requests.get(
        'https://api.github.com/search/commits',
        params={
            'q': f'{include} repo:{org}/{repo_name}',
            'per_page': MAX_PER_PAGE,
            'page': page,
        },
        headers=HEADERS,
    ) as resp:
        resp.raise_for_status()
        return resp.json()


def get_matches(
    org: str, include: str, word_to_ignore: str,
) -> Tuple[
    List[object],  # All matches
    bool,          # Did we hit the API limit?
]:
    repo_list = get_repo(org)

    matches = []

    for repo in repo_list:
        messages = []
        repo_info = {'repo': repo['name'], 'messages': messages}
        matches.append(repo_info)

        page = 1
        items_exist = True

        while items_exist and page <= NUM_PAGES:
            commits = get_commits(org, include, repo['name'], page)

            if "message" in commits:
                return matches, True

            if "items" in commits and (
                page == 1 or commits["total_count"] > (page-1) * MAX_PER_PAGE
            ):
                for commit in commits["items"]:
                    message = commit["commit"]["message"]

                    if word_to_ignore in message:
                        continue

                    messages.append(message)

            else:
                break

            if commits["total_count"] < MAX_PER_PAGE:
                items_exist = False
                
            page += 1

    return matches, False


def test():
    matches, api_limited = get_matches('microsoft', 'repo', 'fetch')
    pprint(matches)


if __name__ == '__main__':
    test()

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *