<commit_before>import hashlib
from collections import defaultdict
from time import sleep

from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy import or_
from nameparser import HumanName

from app import db
from models.contribution import Contribution
from models.github_api import GithubRateLimitException
from github_api import get_profile
from util import dict_from_dir


class Person(db.Model):
    __tablename__ = 'person'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.Text)
    name = db.Column(db.Text)
    other_names = db.Column(JSONB)
    github_login = db.Column(db.Text)
    github_about = db.deferred(db.Column(JSONB))
    bucket = db.Column(JSONB)
    impact = db.Column(db.Float)
    parsed_name = db.Column(JSONB)
    is_academic = db.Column(db.Boolean)

    type = db.Column(db.Text)


    def __repr__(self):
        return u'<Person "{name}" ({id})>'.format(
            name=self.name,
            id=self.id
        )

    contributions = db.relationship(
        'Contribution',
        # lazy='select',
        cascade="all, delete-orphan",
        backref="person"
    )


    def set_is_academic(self):
        # set this using the sql in the sql folder!  set_person_is_academic.sql
        # this is just a placeholder to remind us to run it :)
        pass


    def to_dict(self, full=True):
        ret = {
            "id": self.id, 
            "name": self.display_name, 
            "github_login": self.github_login, 
            "icon": self.icon, 
            "icon_small": self.icon_small, 
            "is_academic": self.is_academic, 
            "impact": self.impact, 
            "id": self.id
        }
        if full:
            ret["person_packages"] = [p.to_dict() for p in self.get_person_packages()]
            ret["num_packages"] = len(ret["person_packages"])
        return ret


    @property
    def as_snippet(self):

        ret = self.as_package_snippet

        person_packages = self.get_person_packages()
        ret["num_packages"] = len(person_packages)
        ret["num_packages_r"] = len([pp for pp in person_packages if pp.package.language=='r'])
        ret["num_packages_python"] = len([pp for pp in person_packages if pp.package.language=='python'])
        ret["person_packages"] = [p.as_person_snippet for p in person_packages[0:5]]
        return ret


    @property
    def as_package_snippet(self):
        ret = {
            "id": self.id, 
            "name": self.display_name, 
            "github_login": self.github_login, 
            "icon": self.icon, 
            "icon_small": self.icon_small, 
            "is_academic": self.is_academic, 
            "impact": self.impact, 
            "id": self.id
        }
        return ret


    def set_github_about(self):
        if self.github_login is None:
            return None

        self.github_about = get_profile(self.github_login)
        try:
            if not self.name:
                self.name = self.github_about["name"]

            if not self.email :
                self.email = self.github_about["email"]
        except KeyError:

            # our github_about is an error object,
            # it's got no info about the person in it.
            return False


    def set_impact(self):
        self.impact = sum([pp.person_project_impact for pp in self.get_person_packages()])
        return self.impact

    def set_parsed_name(self):
        if not self.name:
            self.parsed_name = None
            return

        name = HumanName(self.name)
        self.parsed_name = name.as_dict()

    def _make_gravatar_url(self, size):
        if self.email is not None:
            hash = hashlib.md5(self.email).hexdigest()
        else:
            hash = hashlib.md5("<EMAIL>").hexdigest()

        url = "http://www.gravatar.com/avatar/{hash}.jpg?s={size}&d=mm".format(
            hash=hash,
            size=size
        )
        return url

    @property
    def icon(self):
        return self._make_gravatar_url(160)

    @property
    def icon_small(self):
        return self._make_gravatar_url(30)

    def has_role_on_project(self, role, package_id):
        for c in self.contributions:
            if c.role == role and c.package_id == package_id:
                return True
        return False

    def num_commits_on_project(self, package_id):
        for c in self.contributions:
            if c.role == "github_contributor" and c.package_id == package_id:
                return c.quantity
        return False


    @property
    def display_name(self):
        if self.name:
            return self.name
        elif self.github_login:
            return self.github_login
        elif self.email:
            return self.email.split("@")[0]
        else:
            return "name unknown"

    # could be a property, but kinda slow, so better as explicity method methinks
    def get_person_packages(self):<commit_msg>add single_name<commit_after>import hashlib
from collections import defaultdict
from time import sleep

from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy import or_
from nameparser import HumanName

from app import db
from models.contribution import Contribution
from models.github_api import GithubRateLimitException
from github_api import get_profile
from util import dict_from_dir


class Person(db.Model):
    __tablename__ = 'person'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.Text)
    name = db.Column(db.Text)
    other_names = db.Column(JSONB)
    github_login = db.Column(db.Text)
    github_about = db.deferred(db.Column(JSONB))
    bucket = db.Column(JSONB)
    impact = db.Column(db.Float)
    parsed_name = db.Column(JSONB)
    is_academic = db.Column(db.Boolean)

    type = db.Column(db.Text)


    def __repr__(self):
        return u'<Person "{name}" ({id})>'.format(
            name=self.name,
            id=self.id
        )

    contributions = db.relationship(
        'Contribution',
        # lazy='select',
        cascade="all, delete-orphan",
        backref="person"
    )


    def set_is_academic(self):
        # set this using the sql in the sql folder!  set_person_is_academic.sql
        # this is just a placeholder to remind us to run it :)
        pass


    def to_dict(self, full=True):
        ret = {
            "id": self.id, 
            "name": self.display_name, 
            "single_name": self.single_name, 
            "github_login": self.github_login, 
            "icon": self.icon, 
            "icon_small": self.icon_small, 
            "is_academic": self.is_academic, 
            "impact": self.impact, 
            "id": self.id
        }
        if full:
            ret["person_packages"] = [p.to_dict() for p in self.get_person_packages()]
            ret["num_packages"] = len(ret["person_packages"])
        return ret


    @property
    def as_snippet(self):

        ret = self.as_package_snippet

        person_packages = self.get_person_packages()
        ret["num_packages"] = len(person_packages)
        ret["num_packages_r"] = len([pp for pp in person_packages if pp.package.language=='r'])
        ret["num_packages_python"] = len([pp for pp in person_packages if pp.package.language=='python'])
        ret["person_packages"] = [p.as_person_snippet for p in person_packages[0:5]]
        return ret


    @property
    def as_package_snippet(self):
        ret = {
            "id": self.id, 
            "name": self.display_name,
            "single_name": self.single_name,              
            "github_login": self.github_login, 
            "icon": self.icon, 
            "icon_small": self.icon_small, 
            "is_academic": self.is_academic, 
            "impact": self.impact, 
            "id": self.id
        }
        return ret


    def set_github_about(self):
        if self.github_login is None:
            return None

        self.github_about = get_profile(self.github_login)
        try:
            if not self.name:
                self.name = self.github_about["name"]

            if not self.email :
                self.email = self.github_about["email"]
        except KeyError:

            # our github_about is an error object,
            # it's got no info about the person in it.
            return False


    def set_impact(self):
        self.impact = sum([pp.person_project_impact for pp in self.get_person_packages()])
        return self.impact

    def set_parsed_name(self):
        if not self.name:
            self.parsed_name = None
            return

        name = HumanName(self.name)
        self.parsed_name = name.as_dict()

    def _make_gravatar_url(self, size):
        if self.email is not None:
            hash = hashlib.md5(self.email).hexdigest()
        else:
            hash = hashlib.md5("<EMAIL>").hexdigest()

        url = "http://www.gravatar.com/avatar/{hash}.jpg?s={size}&d=mm".format(
            hash=hash,
            size=size
        )
        return url

    @property
    def icon(self):
        return self._make_gravatar_url(160)

    @property
    def icon_small(self):
        return self._make_gravatar_url(30)

    def has_role_on_project(self, role, package_id):
        for c in self.contributions:
            if c.role == role and c.package_id == package_id:
                return True
        return False

    def num_commits_on_project(self, package_id):
        for c in self.contributions:
            if c.role == "github_contributor" and c.package_id == package_id:
                return c.quantity
        return False

    @property
    def single_name(self):
        if self.parsed_name and self.parsed_name["last"]:
            return self.parsed_name["last"]
        return self.display_name

    @property
    def display_name(self):
        if self.name:
            return self.name
        elif self.github_login:
            return self.github_login
        elif self.email:
            return self.email.split("@")[0]
        else:
            return "name unknown"

    # could be a property, but kinda slow, so better as explicity method methinks
    def get_person_packages(self):
{'fraction_non_alphanumeric': 0.09880062917813606, 'fraction_numerical': 0.001966181675186787, 'mean_word_length': 2.091157702825889, 'pattern_counts': {'":': 34, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '757980', 'n_tokens_mistral': 3153, 'n_tokens_neox': 3003, 'n_words': 725}