# Path: pypasta/MAINTAINERS.py
# def load_maintainers(config, versions):
#     def __load_maintainers(ret, config, versions):
#         if ret is None:
#             ret = dict()
# 
#         # Only load what's not yet cached
#         versions -= ret.keys()
# 
#         if len(versions) == 0:
#             return ret, False
# 
#         global _repo
#         _repo = config.repo
#         p = Pool(processes=cpu_count())
#         function = partial(_load_maintainer,
#                            d_cluster = config.d_maintainers_cluster,
#                            project_name = config.project_name)
# 
#         for tag, maintainers in tqdm(p.imap_unordered(function, versions),
#                                      total=len(versions), desc='MAINTAINERS'):
#             ret[tag] = maintainers
# 
#         p.close()
#         p.join()
#         _repo = None
# 
#         return ret, True
# 
#     log.info('Loading/Updating MAINTAINERS...')
#     maintainers_version = load_pkl_and_update(config.f_maintainers_pkl,
#                                               __load_maintainers, config,
#                                               versions)
#     return maintainers_version
# 
# Path: pypasta/Util.py
# def get_first_upstream(repo, cluster, commit):
#     upstream = cluster.get_upstream(commit)
#     if not upstream:
#         return None
#     return min(upstream, key=lambda x: repo[x].committer.date)
# 
# def load_pkl_and_update(filename, update_command, *params):
#     ret = None
#     if os.path.isfile(filename):
#         ret = pickle.load(open(filename, 'rb'))
# 
#     if len(params) == 0:
#         ret, changed = update_command(ret)
#     else:
#         ret, changed = update_command(ret, *params)
# 
#     if changed:
#         pickle.dump(ret, open(filename, 'wb'))
# 
#     return ret
# 
# def mail_parse_date(date_str, assume_epoch=False):
#     try:
#         date = email.utils.parsedate_to_datetime(date_str)
#     except Exception:
#         date = None
# 
#     if not date:
#         try:
#             date = dateparser.parse(date_str)
#         except Exception:
#             date = None
# 
#     if not date:
#         date = datetime.datetime.utcfromtimestamp(0)
# 
#     if date.tzinfo is None:
#         date = date.replace(tzinfo=datetime.timezone.utc)
# 
#     return date


import csv
import email
import re
from anytree import LevelOrderIter
from enum import Enum
from logging import getLogger
from multiprocessing import Pool, cpu_count
from .MAINTAINERS import load_maintainers
from .Util import get_first_upstream, load_pkl_and_update, mail_parse_date
    from .BuildrootMailCharacteristics import BuildrootMailCharacteristics
    from .DPDKMailCharacteristics import DPDKMailCharacteristics
    from .GitMailCharacteristics import GitMailCharacteristics
    from .JailhouseMailCharacteristics import JailhouseMailCharacteristics
    from .LinuxMailCharacteristics import LinuxMailCharacteristics
    from .OpenembeddedMailCharacteristics import OpenembeddedMailCharacteristics
    from .QemuMailCharacteristics import QemuMailCharacteristics
    from .UBootMailCharacteristics import UBootMailCharacteristics
    from .XenMailCharacteristics import XenMailCharacteristics
    _characteristics_classes = {
        'buildroot': BuildrootMailCharacteristics,
        'dpdk': DPDKMailCharacteristics,
        'git': GitMailCharacteristics,
        'jailhouse': JailhouseMailCharacteristics,
        'linux': LinuxMailCharacteristics,
        'openembedded': OpenembeddedMailCharacteristics,
        'qemu': QemuMailCharacteristics,
        'u-boot': UBootMailCharacteristics,
        'xen': XenMailCharacteristics,
    }

    if config.project_name not in _characteristics_classes:
        raise NotImplementedError('Missing code for project %s' %
                                  config.project_name)

    # Characteristics need thread information. Ensure it's loaded.
    repo = config.repo
    repo.mbox.load_threads()

    if not message_ids:
        message_ids = repo.mbox.get_ids(config.mbox_time_window,
                                        allow_invalid=True)

    maintainers_version = None
    if _characteristics_classes[config.project_name].HAS_MAINTAINERS:
        # We can safely limit to patches only, as only patches will be used for
        # the maintainers analysis.
        patches = message_ids - repo.mbox.invalid
        tags = {repo.patch_get_version(repo[x]) for x in patches}

# Path: pypasta/MAINTAINERS.py
# def load_maintainers(config, versions):
#     def __load_maintainers(ret, config, versions):
#         if ret is None:
#             ret = dict()
# 
#         # Only load what's not yet cached
#         versions -= ret.keys()
# 
#         if len(versions) == 0:
#             return ret, False
# 
#         global _repo
#         _repo = config.repo
#         p = Pool(processes=cpu_count())
#         function = partial(_load_maintainer,
#                            d_cluster = config.d_maintainers_cluster,
#                            project_name = config.project_name)
# 
#         for tag, maintainers in tqdm(p.imap_unordered(function, versions),
#                                      total=len(versions), desc='MAINTAINERS'):
#             ret[tag] = maintainers
# 
#         p.close()
#         p.join()
#         _repo = None
# 
#         return ret, True
# 
#     log.info('Loading/Updating MAINTAINERS...')
#     maintainers_version = load_pkl_and_update(config.f_maintainers_pkl,
#                                               __load_maintainers, config,
#                                               versions)
#     return maintainers_version
# 
# Path: pypasta/Util.py
# def get_first_upstream(repo, cluster, commit):
#     upstream = cluster.get_upstream(commit)
#     if not upstream:
#         return None
#     return min(upstream, key=lambda x: repo[x].committer.date)
# 
# def load_pkl_and_update(filename, update_command, *params):
#     ret = None
#     if os.path.isfile(filename):
#         ret = pickle.load(open(filename, 'rb'))
# 
#     if len(params) == 0:
#         ret, changed = update_command(ret)
#     else:
#         ret, changed = update_command(ret, *params)
# 
#     if changed:
#         pickle.dump(ret, open(filename, 'wb'))
# 
#     return ret
# 
# def mail_parse_date(date_str, assume_epoch=False):
#     try:
#         date = email.utils.parsedate_to_datetime(date_str)
#     except Exception:
#         date = None
# 
#     if not date:
#         try:
#             date = dateparser.parse(date_str)
#         except Exception:
#             date = None
# 
#     if not date:
#         date = datetime.datetime.utcfromtimestamp(0)
# 
#     if date.tzinfo is None:
#         date = date.replace(tzinfo=datetime.timezone.utc)
# 
#     return date


# Path: pypasta/MailCharacteristics.py
import csv
import email
import re
from anytree import LevelOrderIter
from enum import Enum
from logging import getLogger
from multiprocessing import Pool, cpu_count
from .MAINTAINERS import load_maintainers
from .Util import get_first_upstream, load_pkl_and_update, mail_parse_date
    from .BuildrootMailCharacteristics import BuildrootMailCharacteristics
    from .DPDKMailCharacteristics import DPDKMailCharacteristics
    from .GitMailCharacteristics import GitMailCharacteristics
    from .JailhouseMailCharacteristics import JailhouseMailCharacteristics
    from .LinuxMailCharacteristics import LinuxMailCharacteristics
    from .OpenembeddedMailCharacteristics import OpenembeddedMailCharacteristics
    from .QemuMailCharacteristics import QemuMailCharacteristics
    from .UBootMailCharacteristics import UBootMailCharacteristics
    from .XenMailCharacteristics import XenMailCharacteristics

    _characteristics_classes = {
        'buildroot': BuildrootMailCharacteristics,
        'dpdk': DPDKMailCharacteristics,
        'git': GitMailCharacteristics,
        'jailhouse': JailhouseMailCharacteristics,
        'linux': LinuxMailCharacteristics,
        'openembedded': OpenembeddedMailCharacteristics,
        'qemu': QemuMailCharacteristics,
        'u-boot': UBootMailCharacteristics,
        'xen': XenMailCharacteristics,
    }

    if config.project_name not in _characteristics_classes:
        raise NotImplementedError('Missing code for project %s' %
                                  config.project_name)

    # Characteristics need thread information. Ensure it's loaded.
    repo = config.repo
    repo.mbox.load_threads()

    if not message_ids:
        message_ids = repo.mbox.get_ids(config.mbox_time_window,
                                        allow_invalid=True)

    maintainers_version = None
    if _characteristics_classes[config.project_name].HAS_MAINTAINERS:
        # We can safely limit to patches only, as only patches will be used for
        # the maintainers analysis.
        patches = message_ids - repo.mbox.invalid
        tags = {repo.patch_get_version(repo[x]) for x in patches}

        maintainers_version = load_maintainers(config, tags)