<commit_before>"""
@file
@brief Extends Jenkins Server from :epkg:`python-jenkins`.

.. todoext::
    :title: handle command unittests line with parameters with jenkins
    :hidden:
    :tag: enhancement
    :cost: 0.1
    :date: 2016-07-25
    :issue: 24
    :release: 1.4

    Example::

        pyquickhelper [UT] {-d_10}

    This adds a job which runs all unit tests with an estimated duration
    below 10 seconds.

.. versionadded:: 1.1
"""

import os
import sys
import socket
import hashlib
import re
import requests
from xml.sax.saxutils import escape
import jenkins
from ..loghelper.flog import noLOG
from ..pycode.windows_scripts import windows_jenkins, windows_jenkins_any
from ..pycode.windows_scripts import windows_jenkins_27_conda, windows_jenkins_27_def
from ..pycode.build_helper import private_script_replacements
from .jenkins_exceptions import JenkinsExtException, JenkinsJobException
from .jenkins_server_template import _config_job, _trigger_up, _trigger_time, _git_repo, _task_batch
from .jenkins_server_template import _trigger_startup, _publishers, _file_creation, _wipe_repo, _artifacts
from .yaml_helper import enumerate_processed_yml
from .jenkins_helper import jenkins_final_postprocessing

_timeout_default = 1200

_default_engine_paths = {
    "windows": {
        "__PY35__": "__PY35__",
        "__PY36__": "__PY36__",
        "__PY35_X64__": "__PY35_X64__",
        "__PY36_X64__": "__PY36_X64__",
        "__PY34__": "__PY34__",
        "__PY34_X64__": "__PY34_X64__",
        "__PY27_X64__": "__PY27_X64__",
    },
}


def _modified_windows_jenkins(requirements_local, requirements_pypi, module="__MODULE__", port="__PORT__", platform=sys.platform):
    return private_script_replacements(
        windows_jenkins, module, (requirements_local,
                                  requirements_pypi), port, raise_exception=False,
        default_engine_paths=_default_engine_paths, platform=platform)


def _modified_windows_jenkins_27(requirements_local, requirements_pypi, module="__MODULE__",
                                 port="__PORT__", anaconda=True, platform=sys.platform):
    return private_script_replacements(
        windows_jenkins_27_conda if anaconda else windows_jenkins_27_def,
        module, (requirements_local,
                 requirements_pypi), port, raise_exception=False,
        default_engine_paths=_default_engine_paths, platform=platform)


def _modified_windows_jenkins_any(requirements_local, requirements_pypi, module="__MODULE__",
                                  port="__PORT__", platform=sys.platform):
    res = private_script_replacements(
        windows_jenkins_any, module, (requirements_local,
                                      requirements_pypi), port, raise_exception=False,
        default_engine_paths=_default_engine_paths, platform=platform)
    return res.replace("virtual_env_suffix=%2", "virtual_env_suffix=___SUFFIX__")


class JenkinsExt(jenkins.Jenkins):

    """
    extensions for the :epkg:`Jenkins` server
    based on module :epkg:`python-jenkins`.

    .. index:: Jenkins, Jenkins extensions

    some useful Jenkins extensions:

    * `Credentials Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Credentials+Plugin>`_
    * `Extrea Column Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Extra+Columns+Plugin>`_
    * `Git Client Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Git+Client+Plugin>`_
    * `GitHub Client Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Github+Plugin>`_
    * `GitLab Client Plugin <https://wiki.jenkins-ci.org/display/JENKINS/GitLab+Plugin>`_
    * `Matrix Project Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Matrix+Project+Plugin>`_
    * `Build Pipeline Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Build+Pipeline+Plugin>`_

    .. versionchanged:: 1.3
        The whole class was changed to defined many different engines.

    .. versionchanged:: 1.4
        Add mail handling. A job can now send a mail at the end of the job execution.
    """

    _config_job = _config_job
    _trigger_up = _trigger_up
    _trigger_time = _trigger_time
    _trigger_startup = _trigger_startup
    _git_repo = _git_repo
    _task_batch = _task_batch
    _publishers = _publishers
    _wipe_repo = _wipe_repo
    _artifacts = _artifacts

    def __init__(self, url, username=None, password=<PASSWORD>, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                 mock=False, engines=None, platform=sys.platform, pypi_port=8067, fLOG=noLOG,
                 mails=None):
        """
        constructor

        @param      url         url of the server
        @param      username    username
        @param      password    password
        @param      timeout     timeout
        @param      mock        True by default, if False, avoid talking to the server
        @param      engines     list of Python engines *{name: path to python.exe}*
        @param      platform    platform of the Jenkins server
        @param      pypi_port   pypi port used for the documentation server
        @param      mails       (str) list of mails to contact in case of a mistaje
        @param      fLOG        logging function

        .. versionchanged:: 1.5
            Parameter *mails* was added.
        """
        jenkins.Jenkins.__init__(
            self, url, username, password, timeout=timeout)
        self._mock = mock
        self.platform = platform
        self.pypi_port = pypi_port
        self.mails = mails
        self.fLOG = fLOG
        if engines is None:
            engines = {"default": os.path.dirname(sys.executable)}
        self.engines = engines
        for k, v in self.engines.items():
            if v.endswith(".exe"):
                raise FileNotFoundError("{}:{} is not a folder".format(k, v))
            if " " in v:
                raise JenkinsJobException(
                    "No space allowed in engine path: " + v)

    @property
    def Engines(self):
        """
        @return the available engines
        """
        return self.engines

    def jenkins_open(self, req, add_crumb=True):
        '''
        Overloads the same method from module jenkins to replace string by bytes

        @param      req             see `jenkins API <https://python-jenkins.readthedocs.org/en/latest/api.html>`_
        @param      add_crumb       see `jenkins API <https://python-jenkins.readthedocs.org/en/latest/api.html>`_
        '''
        if self._mock:
            raise JenkinsExtException("mocking server, cannot be open")

        response = self.jenkins_request(req=req, add_crumb=add_crumb)
        if response is None:
            raise jenkins.EmptyResponseException(
                "Error communicating with server[%s]: "
                "empty response" % self.server)
        return response.content

    def delete_job(self, name):
        '''Delete Jenkins job permanently.

        :param name: Name of Jenkins job, ``str``
        '''
        if self._mock:
            return

        r = self._get_job_folder(name)
        if r is None:
            raise JenkinsExtException('delete[%s] failed (no job)' % (name))
        self.jenkins_open(requests.Request(
            self.server + jenkins.DELETE_JOB % self._get_encoded_params(locals()), b''))
        if self.job_exists(name):
            raise JenkinsExtException('delete[%s] failed' % (name))

    def get_jobs(self):
        """
        Get list of all jobs recursively to the given folder depth,
        see `get_all_jobs <https://python-jenkins.readthedocs.org/en/latest/api.html#jenkins.Jenkins.get_all_jobs>`_.

        @return                     list of jobs, ``[ { str: str} ]``

        .. versionadded:: 1.3
        """
        return jenkins.Jenkins.get_jobs(self)

    def delete_all_jobs(self):
        """
        delete all jobs permanently.
<commit_msg>fix method for python-jenkins 1.1.0<commit_after>"""
@file
@brief Extends Jenkins Server from :epkg:`python-jenkins`.

.. todoext::
    :title: handle command unittests line with parameters with jenkins
    :hidden:
    :tag: enhancement
    :cost: 0.1
    :date: 2016-07-25
    :issue: 24
    :release: 1.4

    Example::

        pyquickhelper [UT] {-d_10}

    This adds a job which runs all unit tests with an estimated duration
    below 10 seconds.

.. versionadded:: 1.1
"""

import os
import sys
import socket
import hashlib
import re
import requests
from xml.sax.saxutils import escape
import jenkins
from ..loghelper.flog import noLOG
from ..pycode.windows_scripts import windows_jenkins, windows_jenkins_any
from ..pycode.windows_scripts import windows_jenkins_27_conda, windows_jenkins_27_def
from ..pycode.build_helper import private_script_replacements
from .jenkins_exceptions import JenkinsExtException, JenkinsJobException
from .jenkins_server_template import _config_job, _trigger_up, _trigger_time, _git_repo, _task_batch
from .jenkins_server_template import _trigger_startup, _publishers, _file_creation, _wipe_repo, _artifacts
from .yaml_helper import enumerate_processed_yml
from .jenkins_helper import jenkins_final_postprocessing

_timeout_default = 1200

_default_engine_paths = {
    "windows": {
        "__PY35__": "__PY35__",
        "__PY36__": "__PY36__",
        "__PY35_X64__": "__PY35_X64__",
        "__PY36_X64__": "__PY36_X64__",
        "__PY34__": "__PY34__",
        "__PY34_X64__": "__PY34_X64__",
        "__PY27_X64__": "__PY27_X64__",
    },
}


def _modified_windows_jenkins(requirements_local, requirements_pypi, module="__MODULE__", port="__PORT__", platform=sys.platform):
    return private_script_replacements(
        windows_jenkins, module, (requirements_local,
                                  requirements_pypi), port, raise_exception=False,
        default_engine_paths=_default_engine_paths, platform=platform)


def _modified_windows_jenkins_27(requirements_local, requirements_pypi, module="__MODULE__",
                                 port="__PORT__", anaconda=True, platform=sys.platform):
    return private_script_replacements(
        windows_jenkins_27_conda if anaconda else windows_jenkins_27_def,
        module, (requirements_local,
                 requirements_pypi), port, raise_exception=False,
        default_engine_paths=_default_engine_paths, platform=platform)


def _modified_windows_jenkins_any(requirements_local, requirements_pypi, module="__MODULE__",
                                  port="__PORT__", platform=sys.platform):
    res = private_script_replacements(
        windows_jenkins_any, module, (requirements_local,
                                      requirements_pypi), port, raise_exception=False,
        default_engine_paths=_default_engine_paths, platform=platform)
    return res.replace("virtual_env_suffix=%2", "virtual_env_suffix=___SUFFIX__")


class JenkinsExt(jenkins.Jenkins):

    """
    extensions for the :epkg:`Jenkins` server
    based on module :epkg:`python-jenkins`.

    .. index:: Jenkins, Jenkins extensions

    some useful Jenkins extensions:

    * `Credentials Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Credentials+Plugin>`_
    * `Extrea Column Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Extra+Columns+Plugin>`_
    * `Git Client Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Git+Client+Plugin>`_
    * `GitHub Client Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Github+Plugin>`_
    * `GitLab Client Plugin <https://wiki.jenkins-ci.org/display/JENKINS/GitLab+Plugin>`_
    * `Matrix Project Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Matrix+Project+Plugin>`_
    * `Build Pipeline Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Build+Pipeline+Plugin>`_

    .. versionchanged:: 1.3
        The whole class was changed to defined many different engines.

    .. versionchanged:: 1.4
        Add mail handling. A job can now send a mail at the end of the job execution.
    """

    _config_job = _config_job
    _trigger_up = _trigger_up
    _trigger_time = _trigger_time
    _trigger_startup = _trigger_startup
    _git_repo = _git_repo
    _task_batch = _task_batch
    _publishers = _publishers
    _wipe_repo = _wipe_repo
    _artifacts = _artifacts

    def __init__(self, url, username=None, password=<PASSWORD>, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                 mock=False, engines=None, platform=sys.platform, pypi_port=8067, fLOG=noLOG,
                 mails=None):
        """
        constructor

        @param      url         url of the server
        @param      username    username
        @param      password    password
        @param      timeout     timeout
        @param      mock        True by default, if False, avoid talking to the server
        @param      engines     list of Python engines *{name: path to python.exe}*
        @param      platform    platform of the Jenkins server
        @param      pypi_port   pypi port used for the documentation server
        @param      mails       (str) list of mails to contact in case of a mistaje
        @param      fLOG        logging function

        .. versionchanged:: 1.5
            Parameter *mails* was added.
        """
        jenkins.Jenkins.__init__(
            self, url, username, password, timeout=timeout)
        self._mock = mock
        self.platform = platform
        self.pypi_port = pypi_port
        self.mails = mails
        self.fLOG = fLOG
        if engines is None:
            engines = {"default": os.path.dirname(sys.executable)}
        self.engines = engines
        for k, v in self.engines.items():
            if v.endswith(".exe"):
                raise FileNotFoundError("{}:{} is not a folder".format(k, v))
            if " " in v:
                raise JenkinsJobException(
                    "No space allowed in engine path: " + v)

    @property
    def Engines(self):
        """
        @return the available engines
        """
        return self.engines

    def jenkins_open(self, req, add_crumb=True):
        '''
        Overloads the same method from module jenkins to replace string by bytes

        @param      req             see `jenkins API <https://python-jenkins.readthedocs.org/en/latest/api.html>`_
        @param      add_crumb       see `jenkins API <https://python-jenkins.readthedocs.org/en/latest/api.html>`_
        '''
        if self._mock:
            raise JenkinsExtException("mocking server, cannot be open")

        response = self.jenkins_request(req=req, add_crumb=add_crumb)
        if response is None:
            raise jenkins.EmptyResponseException(
                "Error communicating with server[%s]: "
                "empty response" % self.server)
        return response.content

    def delete_job(self, name):
        '''Delete Jenkins job permanently.

        :param name: Name of Jenkins job, ``str``
        '''
        if self._mock:
            return
        r = self._get_job_folder(name)
        if r is None:
            raise JenkinsExtException('delete[%s] failed (no job)' % (name))

        folder_url, short_name = self._get_job_folder(name)
        self.jenkins_open(requests.Request(
            'POST', self._build_url(jenkins.DELETE_JOB, locals())
        ))
        if self.job_exists(name):
            raise JenkinsException('delete[%s] failed' % (name))

    def get_jobs(self):
        """
        Get list of all jobs recursively to the given folder depth,
        see `get_all_jobs <https://python-jenkins.readthedocs.org/en/latest/api.html#jenkins.Jenkins.get_all_jobs>`_.

        @return                     list of jobs, ``[ { str: str} ]``

        .. versionadded:: 1.3
        """
        return jenkins.Jenkins.get_jobs(self)

    def delete_all_jobs(self):
        """
        delete all jobs permanently.

{'fraction_non_alphanumeric': 0.09282217559921165, 'fraction_numerical': 0.011761714031406955, 'mean_word_length': 2.6513463324048283, 'pattern_counts': {'":': 18, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 20, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, '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': '128956', 'n_tokens_mistral': 4881, 'n_tokens_neox': 4603, 'n_words': 1128}