<commit_before>"""
User account handlers, which are in a different file because
they have unique dependencies. These handlers make requests to the
GitHub API.
"""

import hashlib
import os
from base64 import b64decode, b64encode

import tornado
import tornado.gen
import tornado.web
import torngithub
from tornado.concurrent import run_on_executor
from tornado.httputil import url_concat
from torngithub import json_decode, json_encode

from base_handler import *
from search_helpers import *
from user_account_helpers import *

# BEGIN: read environment variables

if "github_client_id" not in os.environ:
    print("Set your github_client_id environment variable, and register your app at https://github.com/settings/developers")
    os.environ["github_client_id"] = "gh_client_id"
if "github_client_secret" not in os.environ:
    print("Set your github_client_secret environment variable, and register your app at https://github.com/settings/developers")
    os.environ["github_client_secret"] = "github_client_secret"

settings = {
    "github_client_id": os.environ["github_client_id"],
    "github_client_secret": os.environ["github_client_secret"]
}

# BEGIN: GitHub repo handlers


class GithubLoginHandler(tornado.web.RequestHandler, torngithub.GithubMixin):
    """ Handle GitHub OAuth. """

    route = "oauth"

    @tornado.gen.coroutine
    def get(self):
        # Heroku does not accurately give self.request.protocol
        if self.request.host[0:9] == "localhost":
            protocol = "http"
        else:
            protocol = "https"

        redirect_uri = url_concat(protocol
                                  + "://" + self.request.host
                                  + "/oauth",
                                  {"redirect_uri":
                                   self.get_argument('redirect_uri', '/')})

        # if we have a code, we have been authorized so we can log in
        if self.get_argument("code", False):
            user = yield self.get_authenticated_user(
                redirect_uri=redirect_uri,
                client_id=settings["github_client_id"],
                client_secret=settings["github_client_secret"],
                code=self.get_argument("code")
            )

            # if the user is valid
            if user:
                self.set_secure_cookie("user", json_encode(user))
                # idempotent operation to make sure GitHub user is in our
                # database
                register_github_user(json_encode(user))
                # generate a Brainspell API key
                hasher = hashlib.sha1()
                hasher.update(str(user["id"]).encode('utf-8'))
                api_key = hasher.hexdigest()
                self.set_secure_cookie("api_key", api_key)
            else:
                self.clear_cookie("user")
            self.redirect(self.get_argument("redirect_uri", "/"))
            return

        # otherwise we need to request an authorization code
        yield self.authorize_redirect(
            redirect_uri=redirect_uri,
            client_id=settings["github_client_id"],
            extra_params={"scope": "repo"})


class GithubLogoutHandler(BaseHandler):
    """ Clear login cookies. """

    route = "logout"

    def get(self):
        self.clear_cookie("user")
        self.clear_cookie("api_key")
        self.redirect(self.get_argument("redirect_uri", "/"))


def get_last_page_num(link):
    """ Extract the number of pages from the GitHub result. """

    if not link:
        return 0
    linkmap = {}
    for s in link.split(","):
        s = s.strip()
        linkmap[s[-5:-1]] = s.split(";")[0].rstrip()[1:-1]
    matches = re.search(r"[?&]page=(\d+)", linkmap["last"])
    return int(matches.group(1))


@tornado.gen.coroutine
def get_user_repos(http_client, access_token):
    """ Get a user's repos. """

    data = []

    # get the results in groups of 100
    first_page = yield torngithub.github_request(
        http_client, '/user/repos?page=1&per_page=100',
        access_token=access_token)
    data.extend(first_page.body)

    # get the number of pages (repos // 100 + 1)
    max_pages = get_last_page_num(first_page.headers.get('Link', ''))

    repos_list = yield [torngithub.github_request(
        http_client, '/user/repos?per_page=100&page=' + str(i),
        access_token=access_token) for i in range(2, max_pages + 1)]

    for repo in repos_list:
        data.extend(res.body)

    raise tornado.gen.Return(data)


class CollectionsEndpointHandler(BaseHandler, torngithub.GithubMixin):
    """ Return a list of the user's collections, with contributor information. """

    parameters = {
        "pmid": {
            "type": str,
            "default": 0
        },
        "github_access_token": {
            "type": str
        },
        "force_github_refresh": {
            "type": int,
            "default": 0,
            "description": "Set this to 1 to force Brainspell to sync its local database with GitHub. There should be a button for this on the web interface."
        }
    }

    endpoint_type = Endpoint.PUSH_API
    asynchronous = True

    @tornado.gen.coroutine<commit_msg>Fix typo for 100+ repos<commit_after>"""
User account handlers, which are in a different file because
they have unique dependencies. These handlers make requests to the
GitHub API.
"""

import hashlib
import os
from base64 import b64decode, b64encode

import tornado
import tornado.gen
import tornado.web
import torngithub
from tornado.concurrent import run_on_executor
from tornado.httputil import url_concat
from torngithub import json_decode, json_encode

from base_handler import *
from search_helpers import *
from user_account_helpers import *

# BEGIN: read environment variables

if "github_client_id" not in os.environ:
    print("Set your github_client_id environment variable, and register your app at https://github.com/settings/developers")
    os.environ["github_client_id"] = "gh_client_id"
if "github_client_secret" not in os.environ:
    print("Set your github_client_secret environment variable, and register your app at https://github.com/settings/developers")
    os.environ["github_client_secret"] = "github_client_secret"

settings = {
    "github_client_id": os.environ["github_client_id"],
    "github_client_secret": os.environ["github_client_secret"]
}

# BEGIN: GitHub repo handlers


class GithubLoginHandler(tornado.web.RequestHandler, torngithub.GithubMixin):
    """ Handle GitHub OAuth. """

    route = "oauth"

    @tornado.gen.coroutine
    def get(self):
        # Heroku does not accurately give self.request.protocol
        if self.request.host[0:9] == "localhost":
            protocol = "http"
        else:
            protocol = "https"

        redirect_uri = url_concat(protocol
                                  + "://" + self.request.host
                                  + "/oauth",
                                  {"redirect_uri":
                                   self.get_argument('redirect_uri', '/')})

        # if we have a code, we have been authorized so we can log in
        if self.get_argument("code", False):
            user = yield self.get_authenticated_user(
                redirect_uri=redirect_uri,
                client_id=settings["github_client_id"],
                client_secret=settings["github_client_secret"],
                code=self.get_argument("code")
            )

            # if the user is valid
            if user:
                self.set_secure_cookie("user", json_encode(user))
                # idempotent operation to make sure GitHub user is in our
                # database
                register_github_user(json_encode(user))
                # generate a Brainspell API key
                hasher = hashlib.sha1()
                hasher.update(str(user["id"]).encode('utf-8'))
                api_key = hasher.hexdigest()
                self.set_secure_cookie("api_key", api_key)
            else:
                self.clear_cookie("user")
            self.redirect(self.get_argument("redirect_uri", "/"))
            return

        # otherwise we need to request an authorization code
        yield self.authorize_redirect(
            redirect_uri=redirect_uri,
            client_id=settings["github_client_id"],
            extra_params={"scope": "repo"})


class GithubLogoutHandler(BaseHandler):
    """ Clear login cookies. """

    route = "logout"

    def get(self):
        self.clear_cookie("user")
        self.clear_cookie("api_key")
        self.redirect(self.get_argument("redirect_uri", "/"))


def get_last_page_num(link):
    """ Extract the number of pages from the GitHub result. """

    if not link:
        return 0
    linkmap = {}
    for s in link.split(","):
        s = s.strip()
        linkmap[s[-5:-1]] = s.split(";")[0].rstrip()[1:-1]
    matches = re.search(r"[?&]page=(\d+)", linkmap["last"])
    return int(matches.group(1))


@tornado.gen.coroutine
def get_user_repos(http_client, access_token):
    """ Get a user's repos. """

    data = []

    # get the results in groups of 100
    first_page = yield torngithub.github_request(
        http_client, '/user/repos?page=1&per_page=100',
        access_token=access_token)
    data.extend(first_page.body)

    # get the number of pages (repos // 100 + 1)
    max_pages = get_last_page_num(first_page.headers.get('Link', ''))

    repos_list = yield [torngithub.github_request(
        http_client, '/user/repos?per_page=100&page=' + str(i),
        access_token=access_token) for i in range(2, max_pages + 1)]

    for repo in repos_list:
        data.extend(repo.body)

    raise tornado.gen.Return(data)


class CollectionsEndpointHandler(BaseHandler, torngithub.GithubMixin):
    """ Return a list of the user's collections, with contributor information. """

    parameters = {
        "pmid": {
            "type": str,
            "default": 0
        },
        "github_access_token": {
            "type": str
        },
        "force_github_refresh": {
            "type": int,
            "default": 0,
            "description": "Set this to 1 to force Brainspell to sync its local database with GitHub. There should be a button for this on the web interface."
        }
    }

    endpoint_type = Endpoint.PUSH_API
    asynchronous = True

    @tornado.gen.coroutine
{'fraction_non_alphanumeric': 0.10586870347094654, 'fraction_numerical': 0.007251281059653872, 'mean_word_length': 2.4630063608972215, 'pattern_counts': {'":': 30, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '905550', 'n_tokens_mistral': 3089, 'n_tokens_neox': 2924, 'n_words': 743}