<commit_before>"""
.. module: lemur.plugins.lemur_verisign.plugin
    :platform: Unix
    :synopsis: This module is responsible for communicating with the VeriSign VICE 2.0 API.
    :copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
    :license: Apache, see LICENSE for more details.

.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import arrow
import requests
import xmltodict
from cryptography import x509
from flask import current_app

from lemur.common.utils import get_psuedo_random_string
from lemur.extensions import metrics, sentry
from lemur.plugins import lemur_verisign as verisign
from lemur.plugins.bases import IssuerPlugin, SourcePlugin

# https://support.venafi.com/entries/66445046-Info-VeriSign-Error-Codes
VERISIGN_ERRORS = {
    "0x30c5": "Domain Mismatch when enrolling for an SSL certificate, a domain in your request has not been added to verisign",
    "0x3a10": "Invalid X509 certificate format.: an unsupported certificate format was submitted",
    "0x4002": "Internal QM Error. : Internal Database connection error.",
    "0x3301": "Bad transaction id or parent cert not renewable.: User try to renew a certificate that is not yet ready for renew or the transaction id is wrong",
    "0x3069": "Challenge phrase mismatch: The challenge phrase submitted does not match the original one",
    "0x3111": "Unsupported Product: User submitted a wrong product or requested cipher is not supported",
    "0x30e8": "CN or org does not match the original one.: the submitted CSR contains a common name or org that does not match the original one",
    "0x1005": "Duplicate certificate: a certificate with the same common name exists already",
    "0x0194": "Incorrect Signature Algorithm: The requested signature algorithm is not supported for the key type. i.e. an ECDSA is submitted for an RSA key",
    "0x6000": "parameter missing or incorrect: This is a general error code for missing or incorrect parameters. The reason will be in the response message.  i.e. 'CSR is missing, 'Unsupported serverType' when no supported serverType could be found., 'invalid transaction id'",
    "0x3063": "Certificate not allowed: trying to issue a certificate that is not configured for the account",
    "0x23df": "No MDS Data Returned: internal connection lost or server not responding. this should be rare",
    "0x3004": "Invalid Account: The users mpki account associated with the certificate is not valid or not yet active",
    "0x4101": "Internal Error: internal server error, user should try again later. (Also check that State is spelled out",
    "0x3101": "Missing admin role: Your account does not have the admin role required to access the webservice API",
    "0x3085": "Account does not have webservice feature.: Your account does not the the webservice role required to access the webservice API",
    "0x9511": "Corrupted CSR : the submitted CSR was mal-formed",
    "0xa001": "Public key format does not match.: The public key format does not match the original cert at certificate renewal or replacement. E.g. if you try to renew or replace an RSA cert with a DSA or ECC key based CSR",
    "0x0143": "Certificate End Date Error: You are trying to replace a certificate with validity end date exceeding the original cert. or the certificate end date is not valid",
    "0x482d": "SHA1 validity check error: What error code do we get when we submit the SHA1 SSL requests with the validity more than 12/31/2016?",
    "0x482e": "What error code do we get when we cannot complete the re-authentication for domains with a newly-approved gTLD 30 days after the gTLD approval",
    "0x4824": "Per CA/B Forum baseline requirements, non-FQDN certs cannot exceed 11/1/2015. Examples: hostname, foo.cba (.cba is a pending gTLD)",
    "eE0x48": "Currently the maximum cert validity is 4-years",
    "0x4826": "OU misleading. See comments",
    "0x4827": "Org re-auth past due. EV org has to go through re-authentication every 13 months; OV org has to go through re-authentication every 39 months",
    "0x482a": "Domain re-auth past due. EV domain has to go through re-authentication every 13 months; OV domain has to go through re-authentication every 39 months.",
    "0x482b": "No org address was set to default, should not happen",
    "0x482c": "signature algorithm does not match intended key type in the CSR (e.g. CSR has an ECC key, but the signature algorithm is sha1WithRSAEncryption)",
    "0x600E": "only supports ECC keys with the named curve NIST P-256, aka secp256r1 or prime256v1, other ECC key sizes will get this error ",
    "0x6013": "only supports DSA keys with (2048, 256) as the bit lengths of the prime parameter pair (p, q), other DSA key sizes will get this error",
    "0x600d": "RSA key size < 2A048",
    "0x4828": "Verisign certificates can be at most two years in length",
    "0x3043": "Certificates must have a validity of at least 1 day",
    "0x950b": "CSR: Invalid State",
    "0x3105": "Organization Name Not Matched",
    "0x300a": "Domain/SubjectAltName Mismatched -- make sure that the SANs have the proper domain suffix",
    "0x950e": "Invalid Common Name -- make sure the CN has a proper domain suffix",
    "0xa00e": "Pending. (Insufficient number of tokens.)",
    "0x8134": "Pending. (Domain failed CAA validation.)",
}

def log_status_code(r, *args, **kwargs):
    """
    Is a request hook that logs all status codes to the verisign api.

    :param r:
    :param args:
    :param kwargs:
    :return:
    """
    metrics.send("symantec_status_code_{}".format(r.status_code), "counter", 1)

def get_additional_names(options):
    """
    Return a list of strings to be added to a SAN certificates.

    :param options:
    :return:
    """
    names = []
    # add SANs if present
    if options.get("extensions"):
        for san in options["extensions"]["sub_alt_names"]:
            if isinstance(san, x509.DNSName):
                names.append(san.value)
    return names

def process_options(options):
    """
    Processes and maps the incoming issuer options to fields/options that
    verisign understands

    :param options:
    :return: dict or valid verisign options
    """
    # if there is a config variable with VERISIGN_PRODUCT_<upper(authority.name)> take the value as Cert product-type
    # else default to "Server", to be compatoible with former versions
    authority = options.get("authority").name.upper()
    product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority), "Server")
    data = {
        "challenge": get_psuedo_random_string(),
        "serverType": "Apache",
        "certProductType": product_type,
        "firstName": current_app.config.get("VERISIGN_FIRST_NAME"),
        "lastName": current_app.config.get("VERISIGN_LAST_NAME"),
        "signatureAlgorithm": "sha256WithRSAEncryption",
        "email": current_app.config.get("VERISIGN_EMAIL"),
        "ctLogOption": current_app.config.get("VERISIGN_CS_LOG_OPTION", "public"),
    }

    data["subject_alt_names"] = ",".join(get_additional_names(options))

    if options.get("validity_end"):
        # VeriSign (Symantec) only accepts strictly smaller than 2 year end date
        if options.get("validity_end") < arrow.utcnow().shift(years=2, days=-1):
            period = get_default_issuance(options)
            data["specificEndDate"] = options["validity_end"].format("MM/DD/YYYY")
            data["validityPeriod"] = period
        else:
            # allowing Symantec website setting the end date, given the validity period
            data["validityPeriod"] = str(get_default_issuance(options))
            options.pop("validity_end", None)

    elif options.get("validity_years"):
        if options["validity_years"] in [1, 2]:
            data["validityPeriod"] = str(options["validity_years"]) + "Y"
        else:
            raise Exception(
                "Verisign issued certificates cannot exceed two years in validity"
            )

    return data

def get_default_issuance(options):
    """
    Gets the default time range for certificates

    :param options:
    :return:
    """
    now = arrow.utcnow()

    if options["validity_end"] < now.shift(years=+1):
        validity_period = "1Y"
    elif options["validity_end"] < now.shift(years=+2):
        validity_period = "2Y"
    else:
        raise Exception(
            "Verisign issued certificates cannot exceed two years in validity"
        )

    return validity_period

def handle_response(content):
    """
    Helper function for parsing responses from the Verisign API.
    :param content:
    :return: :raise Exception:
    """
    d = xmltodict.parse(content)
    global VERISIGN_ERRORS
    if d.get("Error"):
        status_code = d["Error"]["StatusCode"]
    elif d.get("Response"):
        status_code = d["Response"]["StatusCode"]
    if status_code in VERISIGN_ERRORS.keys():
        raise Exception(VERISIGN_ERRORS[status_code])
    return d

class VerisignIssuerPlugin(IssuerPlugin):
    title = "Verisign"
    slug = "verisign-issuer"
    description = "Enables the creation of certificates by the VICE2.0 verisign API."
    version = verisign.VERSION

    author = "Kevin Glisson"
    author_url = "https://github.com/netflix/lemur.git"

    def __init__(self, *args, **kwargs):
        self.session = requests.Session()
        self.session.cert = current_app.config.get("VERISIGN_PEM_PATH")
        self.session.hooks = dict(response=log_status_code)
        super(VerisignIssuerPlugin, self).__init__(*args, **kwargs)

    def create_certificate(self, csr, issuer_options):
        """
        Creates a Verisign certificate.

        :param csr:
        :param issuer_options:
        :return: :raise Exception:
        """
        url = current_app.config.get("VERISIGN_URL") + "/rest/services/enroll"

        data = process_options(issuer_options)
        data["csr"] = csr

        current_app.logger.info(
            "Requesting a new verisign certificate: {0}".format(data)
        )

        response = self.session.post(url, data=data)
        try:
            response_dict = handle_response(response.content)
        except KeyError:
            metrics.send(
                "verisign_create_certificate_error",
                "counter",
                1,
                metric_tags={"common_name": issuer_options.get("common_name", "")},
            )
            sentry.captureException(
                extra={"common_name": issuer_options.get("common_name", "")}
            )
            raise Exception(f"Error with Verisign: {response.content}")
        # DONE: TODO add external id
        authority = issuer_options.get("authority").name.upper()
        cert = response_dict['Response']['Certificate']
        external_id = response_dict['Response']['Transaction_ID']
        chain = current_app.config.get("VERISIGN_INTERMEDIATE_{0}".format(authority), current_app.config.get("VERISIGN_INTERMEDIATE"))
        return cert, chain, external_id

    @staticmethod
    def create_authority(options):
        """
        Creates an authority, this authority is then used by Lemur to allow a user
        to specify which Certificate Authority they want to sign their certificate.

        :param options:
        :return:
        """
        role = {"username": "", "password": "", "name": "verisign"}
        return current_app.config.get("VERISIGN_ROOT"), "", [role]

    def get_available_units(self):
        """
        Uses the Verisign to fetch the number of available units left. This can be used to get tabs
        on the number of certificates that can be issued.

        :return:
        """
        url = current_app.config.get("VERISIGN_URL") + "/rest/services/getTokens"
        response = self.session.post(
            url, headers={"content-type": "application/x-www-form-urlencoded"}
        )
        return handle_response(response.content)["Response"]["Order"]

    def clear_pending_certificates(self):
        """
        Uses Verisign to clear the pending certificates awaiting approval.

        :return:
        """
        url = current_app.config.get("VERISIGN_URL") + "/reportingws"

        end = arrow.now()
        start = end.shift(days=-7)

        data = {
            "reportType": "detail",
            "certProductType": "Server",
            "certStatus": "Pending",
            "startDate": start.format("MM/DD/YYYY"),
            "endDate": end.format("MM/DD/YYYY"),
        }
        response = self.session.post(url, data=data)

        url = current_app.config.get("VERISIGN_URL") + "/rest/services/reject"
        for order_id in response.json()["orderNumber"]:
            response = self.session.get(url, params={"transaction_id": order_id})

            if response.status_code == 200:
                print("Rejecting certificate. TransactionId: {}".format(order_id))

class VerisignSourcePlugin(SourcePlugin):
    title = "Verisign"
    slug = "verisign-source"
    description = (
        "Allows for the polling of issued certificates from the VICE2.0 verisign API."
    )
    version = verisign.VERSION

    author = "Kevin Glisson"
    author_url = "https://github.com/netflix/lemur.git"

    def __init__(self, *args, **kwargs):
        self.session = requests.Session()
        self.session.cert = current_app.config.get("VERISIGN_PEM_PATH")
        super(VerisignSourcePlugin, self).__init__(*args, **kwargs)

    def get_certificates(self):
        url = current_app.config.get("VERISIGN_URL") + "/reportingws"
        end = arrow.now()
        start = end.shift(years=-5)
        data = {
            "reportType": "detail",
            "startDate": start.format("MM/DD/YYYY"),
            "endDate": end.format("MM/DD/YYYY"),
            "structuredRecord": "Y",
            "certStatus": "Valid",
        }
        current_app.logger.debug(data)
        response = self.session.post(url, data=data)
<commit_msg>Update plugin.py<commit_after>"""
.. module: lemur.plugins.lemur_verisign.plugin
    :platform: Unix
    :synopsis: This module is responsible for communicating with the VeriSign VICE 2.0 API.
    :copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
    :license: Apache, see LICENSE for more details.

.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
import arrow
import requests
import xmltodict
from cryptography import x509
from flask import current_app

from lemur.common.utils import get_psuedo_random_string
from lemur.extensions import metrics, sentry
from lemur.plugins import lemur_verisign as verisign
from lemur.plugins.bases import IssuerPlugin, SourcePlugin

# https://support.venafi.com/entries/66445046-Info-VeriSign-Error-Codes
VERISIGN_ERRORS = {
    "0x30c5": "Domain Mismatch when enrolling for an SSL certificate, a domain in your request has not been added to verisign",
    "0x3a10": "Invalid X509 certificate format.: an unsupported certificate format was submitted",
    "0x4002": "Internal QM Error. : Internal Database connection error.",
    "0x3301": "Bad transaction id or parent cert not renewable.: User try to renew a certificate that is not yet ready for renew or the transaction id is wrong",
    "0x3069": "Challenge phrase mismatch: The challenge phrase submitted does not match the original one",
    "0x3111": "Unsupported Product: User submitted a wrong product or requested cipher is not supported",
    "0x30e8": "CN or org does not match the original one.: the submitted CSR contains a common name or org that does not match the original one",
    "0x1005": "Duplicate certificate: a certificate with the same common name exists already",
    "0x0194": "Incorrect Signature Algorithm: The requested signature algorithm is not supported for the key type. i.e. an ECDSA is submitted for an RSA key",
    "0x6000": "parameter missing or incorrect: This is a general error code for missing or incorrect parameters. The reason will be in the response message.  i.e. 'CSR is missing, 'Unsupported serverType' when no supported serverType could be found., 'invalid transaction id'",
    "0x3063": "Certificate not allowed: trying to issue a certificate that is not configured for the account",
    "0x23df": "No MDS Data Returned: internal connection lost or server not responding. this should be rare",
    "0x3004": "Invalid Account: The users mpki account associated with the certificate is not valid or not yet active",
    "0x4101": "Internal Error: internal server error, user should try again later. (Also check that State is spelled out",
    "0x3101": "Missing admin role: Your account does not have the admin role required to access the webservice API",
    "0x3085": "Account does not have webservice feature.: Your account does not the the webservice role required to access the webservice API",
    "0x9511": "Corrupted CSR : the submitted CSR was mal-formed",
    "0xa001": "Public key format does not match.: The public key format does not match the original cert at certificate renewal or replacement. E.g. if you try to renew or replace an RSA cert with a DSA or ECC key based CSR",
    "0x0143": "Certificate End Date Error: You are trying to replace a certificate with validity end date exceeding the original cert. or the certificate end date is not valid",
    "0x482d": "SHA1 validity check error: What error code do we get when we submit the SHA1 SSL requests with the validity more than 12/31/2016?",
    "0x482e": "What error code do we get when we cannot complete the re-authentication for domains with a newly-approved gTLD 30 days after the gTLD approval",
    "0x4824": "Per CA/B Forum baseline requirements, non-FQDN certs cannot exceed 11/1/2015. Examples: hostname, foo.cba (.cba is a pending gTLD)",
    "eE0x48": "Currently the maximum cert validity is 4-years",
    "0x4826": "OU misleading. See comments",
    "0x4827": "Org re-auth past due. EV org has to go through re-authentication every 13 months; OV org has to go through re-authentication every 39 months",
    "0x482a": "Domain re-auth past due. EV domain has to go through re-authentication every 13 months; OV domain has to go through re-authentication every 39 months.",
    "0x482b": "No org address was set to default, should not happen",
    "0x482c": "signature algorithm does not match intended key type in the CSR (e.g. CSR has an ECC key, but the signature algorithm is sha1WithRSAEncryption)",
    "0x600E": "only supports ECC keys with the named curve NIST P-256, aka secp256r1 or prime256v1, other ECC key sizes will get this error ",
    "0x6013": "only supports DSA keys with (2048, 256) as the bit lengths of the prime parameter pair (p, q), other DSA key sizes will get this error",
    "0x600d": "RSA key size < 2A048",
    "0x4828": "Verisign certificates can be at most two years in length",
    "0x3043": "Certificates must have a validity of at least 1 day",
    "0x950b": "CSR: Invalid State",
    "0x3105": "Organization Name Not Matched",
    "0x300a": "Domain/SubjectAltName Mismatched -- make sure that the SANs have the proper domain suffix",
    "0x950e": "Invalid Common Name -- make sure the CN has a proper domain suffix",
    "0xa00e": "Pending. (Insufficient number of tokens.)",
    "0x8134": "Pending. (Domain failed CAA validation.)",
}

def log_status_code(r, *args, **kwargs):
    """
    Is a request hook that logs all status codes to the verisign api.

    :param r:
    :param args:
    :param kwargs:
    :return:
    """
    metrics.send("symantec_status_code_{}".format(r.status_code), "counter", 1)

def get_additional_names(options):
    """
    Return a list of strings to be added to a SAN certificates.

    :param options:
    :return:
    """
    names = []
    # add SANs if present
    if options.get("extensions"):
        for san in options["extensions"]["sub_alt_names"]:
            if isinstance(san, x509.DNSName):
                names.append(san.value)
    return names

def process_options(options):
    """
    Processes and maps the incoming issuer options to fields/options that
    verisign understands

    :param options:
    :return: dict or valid verisign options
    """
    # if there is a config variable with VERISIGN_PRODUCT_<upper(authority.name)> take the value as Cert product-type
    # else default to "Server", to be compatoible with former versions
    authority = options.get("authority").name.upper()
    product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority), "Server")
    data = {
        "challenge": get_psuedo_random_string(),
        "serverType": "Apache",
        "certProductType": product_type,
        "firstName": current_app.config.get("VERISIGN_FIRST_NAME"),
        "lastName": current_app.config.get("VERISIGN_LAST_NAME"),
        "signatureAlgorithm": "sha256WithRSAEncryption",
        "email": current_app.config.get("VERISIGN_EMAIL"),
        "ctLogOption": current_app.config.get("VERISIGN_CS_LOG_OPTION", "public"),
    }

    data["subject_alt_names"] = ",".join(get_additional_names(options))

    if options.get("validity_end"):
        # VeriSign (Symantec) only accepts strictly smaller than 2 year end date
        if options.get("validity_end") < arrow.utcnow().shift(years=2, days=-1):
            period = get_default_issuance(options)
            data["specificEndDate"] = options["validity_end"].format("MM/DD/YYYY")
            data["validityPeriod"] = period
        else:
            # allowing Symantec website setting the end date, given the validity period
            data["validityPeriod"] = str(get_default_issuance(options))
            options.pop("validity_end", None)

    elif options.get("validity_years"):
        if options["validity_years"] in [1, 2]:
            data["validityPeriod"] = str(options["validity_years"]) + "Y"
        else:
            raise Exception(
                "Verisign issued certificates cannot exceed two years in validity"
            )

    return data

def get_default_issuance(options):
    """
    Gets the default time range for certificates

    :param options:
    :return:
    """
    now = arrow.utcnow()

    if options["validity_end"] < now.shift(years=+1):
        validity_period = "1Y"
    elif options["validity_end"] < now.shift(years=+2):
        validity_period = "2Y"
    else:
        raise Exception(
            "Verisign issued certificates cannot exceed two years in validity"
        )

    return validity_period

def handle_response(content):
    """
    Helper function for parsing responses from the Verisign API.
    :param content:
    :return: :raise Exception:
    """
    d = xmltodict.parse(content)
    global VERISIGN_ERRORS
    if d.get("Error"):
        status_code = d["Error"]["StatusCode"]
    elif d.get("Response"):
        status_code = d["Response"]["StatusCode"]
    if status_code in VERISIGN_ERRORS.keys():
        raise Exception(VERISIGN_ERRORS[status_code])
    return d

class VerisignIssuerPlugin(IssuerPlugin):
    title = "Verisign"
    slug = "verisign-issuer"
    description = "Enables the creation of certificates by the VICE2.0 verisign API."
    version = verisign.VERSION

    author = "Kevin Glisson"
    author_url = "https://github.com/netflix/lemur.git"

    def __init__(self, *args, **kwargs):
        self.session = requests.Session()
        self.session.cert = current_app.config.get("VERISIGN_PEM_PATH")
        self.session.hooks = dict(response=log_status_code)
        super(VerisignIssuerPlugin, self).__init__(*args, **kwargs)

    def create_certificate(self, csr, issuer_options):
        """
        Creates a Verisign certificate.

        :param csr:
        :param issuer_options:
        :return: :raise Exception:
        """
        url = current_app.config.get("VERISIGN_URL") + "/rest/services/enroll"

        data = process_options(issuer_options)
        data["csr"] = csr

        current_app.logger.info(
            "Requesting a new verisign certificate: {0}".format(data)
        )

        response = self.session.post(url, data=data)
        try:
            response_dict = handle_response(response.content)
        except KeyError:
            metrics.send(
                "verisign_create_certificate_error",
                "counter",
                1,
                metric_tags={"common_name": issuer_options.get("common_name", "")},
            )
            sentry.captureException(
                extra={"common_name": issuer_options.get("common_name", "")}
            )
            raise Exception(f"Error with Verisign: {response.content}")
        authority = issuer_options.get("authority").name.upper()
        cert = response_dict['Response']['Certificate']
        external_id = None
        if 'Transaction_ID' in response_dict['Response'].keys():
            external_id = response_dict['Response']['Transaction_ID']
        chain = current_app.config.get("VERISIGN_INTERMEDIATE_{0}".format(authority), current_app.config.get("VERISIGN_INTERMEDIATE"))
        return cert, chain, external_id

    @staticmethod
    def create_authority(options):
        """
        Creates an authority, this authority is then used by Lemur to allow a user
        to specify which Certificate Authority they want to sign their certificate.

        :param options:
        :return:
        """
        role = {"username": "", "password": "", "name": "verisign"}
        return current_app.config.get("VERISIGN_ROOT"), "", [role]

    def get_available_units(self):
        """
        Uses the Verisign to fetch the number of available units left. This can be used to get tabs
        on the number of certificates that can be issued.

        :return:
        """
        url = current_app.config.get("VERISIGN_URL") + "/rest/services/getTokens"
        response = self.session.post(
            url, headers={"content-type": "application/x-www-form-urlencoded"}
        )
        return handle_response(response.content)["Response"]["Order"]

    def clear_pending_certificates(self):
        """
        Uses Verisign to clear the pending certificates awaiting approval.

        :return:
        """
        url = current_app.config.get("VERISIGN_URL") + "/reportingws"

        end = arrow.now()
        start = end.shift(days=-7)

        data = {
            "reportType": "detail",
            "certProductType": "Server",
            "certStatus": "Pending",
            "startDate": start.format("MM/DD/YYYY"),
            "endDate": end.format("MM/DD/YYYY"),
        }
        response = self.session.post(url, data=data)

        url = current_app.config.get("VERISIGN_URL") + "/rest/services/reject"
        for order_id in response.json()["orderNumber"]:
            response = self.session.get(url, params={"transaction_id": order_id})

            if response.status_code == 200:
                print("Rejecting certificate. TransactionId: {}".format(order_id))

class VerisignSourcePlugin(SourcePlugin):
    title = "Verisign"
    slug = "verisign-source"
    description = (
        "Allows for the polling of issued certificates from the VICE2.0 verisign API."
    )
    version = verisign.VERSION

    author = "Kevin Glisson"
    author_url = "https://github.com/netflix/lemur.git"

    def __init__(self, *args, **kwargs):
        self.session = requests.Session()
        self.session.cert = current_app.config.get("VERISIGN_PEM_PATH")
        super(VerisignSourcePlugin, self).__init__(*args, **kwargs)

    def get_certificates(self):
        url = current_app.config.get("VERISIGN_URL") + "/reportingws"
        end = arrow.now()
        start = end.shift(years=-5)
        data = {
            "reportType": "detail",
            "startDate": start.format("MM/DD/YYYY"),
            "endDate": end.format("MM/DD/YYYY"),
            "structuredRecord": "Y",
            "certStatus": "Valid",
        }
        current_app.logger.debug(data)
        response = self.session.post(url, data=data)
<|endoftext|>


<commit_before>import json
import logging

from channels.handler import AsgiHandler
from leonardo.utils import render_region
from leonardo_channels import Group
from leonardo_channels.auth import (channel_session_user,
                                    channel_session_user_from_http)

LOG = logging.getLogger(__name__)

@channel_session_user_from_http
def ws_add(message):
    # Add them to the right group
    Group("widgets.content").add(message.reply_channel)

@channel_session_user
def ws_disconnect(message):
    Group("widgets.content").discard(message.reply_channel)

def widget_update(message, **kwargs):
    """render widget or widget region and send them to the clients

    this method handles post_save signals

    if instance was created render whole region

    """

    widget = message.content['widget']

    message.content["frontend_editing"] = True

    message.content["method"] = "GET"

    request = AsgiHandler.request_class(message)

    request.frontend_editing = True

    if not hasattr(request, '_feincms_extra_context'):
        request._feincms_extra_context = {}

    from leonardo.module.web.processors.config import ContextConfig

    # call processors
    for fn in reversed(list(widget.parent.request_processors.values())):
        fn(widget.parent, request)

    request.LEONARDO_CONFIG = ContextConfig(request)

    if "created" in message.content or "deleted" in message.content:
        # this widget was created
        # its simplier to render whole region instead update
        msg = {
            'region': widget.region,
            'content': render_region(widget, request),
        }

    else:

        msg = {
            'id': widget.fe_identifier,
            'content': widget.render_content({'request': request}),
        }

    Group("widgets.content").send({'text': json.dumps(msg)})
<commit_msg>Save users and send private message with rendered widgets.<commit_after>import json
import logging

from channels.handler import AsgiHandler
from leonardo.utils import render_region
from leonardo_channels import Group
from leonardo_channels.auth import (channel_session_user,
                                    channel_session_user_from_http)
from django.core.cache import caches
from copy import deepcopy

LOG = logging.getLogger(__name__)

cache = caches['default']

CACHE_KEY = 'widget.content.users'
CACHE_COUNT_KEY = CACHE_KEY + '.count'

@channel_session_user_from_http
def ws_add(message):
    """
    Save all users which is connected to the cache

    users are used to rendering widgets with permissions
    """

    user = cache.get('%s.%s' % (CACHE_KEY, message.user.id))

    if not user:

        cache.set('%s.%s' %
                  (CACHE_KEY, message.user.id),
                  deepcopy(message.user), None)

        # incerement count because standard cache does not support wildcard
        count = cache.get(CACHE_COUNT_KEY)

        if not count:
            count = 1
        else:
            count += 1

        cache.set('widget.content.users.count', count, None)

    Group("widgets.content-%s" %
          message.user.username).add(message.reply_channel)

@channel_session_user
def ws_disconnect(message):

    cache.delete('widget.content.users.%s' % message.user.id)

    count = cache.get(CACHE_COUNT_KEY)

    if count:
        count -= 1
        cache.set(CACHE_KEY, count, None)

    Group("widgets.content-%s" %
          message.user.username).discard(message.reply_channel)

def widget_update(message, **kwargs):
    """render widget or widget region and send them to the clients

    this method handles post_save signals

    if instance was created render whole region

    """

    def get_request_from_message(msg):
        """returns inicialized request
        """

        request = AsgiHandler.request_class(message)

        request.frontend_editing = True

        if not hasattr(request, '_feincms_extra_context'):
            request._feincms_extra_context = {}

        from leonardo.module.web.processors.config import ContextConfig

        # call processors
        for fn in reversed(list(widget.parent.request_processors.values())):
            fn(widget.parent, request)

        request.LEONARDO_CONFIG = ContextConfig(request)

        return request

    widget = message.content['widget']

    message.content["frontend_editing"] = True
    message.content["method"] = "GET"

    request = get_request_from_message(message)

    count = cache.get(CACHE_COUNT_KEY)

    # now we render widgets for all users
    for i in range(0, count):

        user = cache.get('%s.%s' % (CACHE_KEY, i))

        if user:

            request.user = user

            if "created" in message.content or "deleted" in message.content:
                # this widget was created
                # its simplier to render whole region instead update
                msg = {
                    'region': widget.region,
                    'content': render_region(widget, request),
                }

            else:

                msg = {
                    'id': widget.fe_identifier,
                    'content': widget.render_content({'request': request}),
                }

            Group("widgets.content-%s" %
                  user.username).send({'text': json.dumps(msg)})
<|endoftext|>


<commit_before>from typing import List

from sklearn.metrics.pairwise import cosine_similarity

from lexos.helpers.error_messages import NON_NEGATIVE_INDEX_MESSAGE, \
     EMPTY_LIST_MESSAGE

import numpy as np

def similarity_maker(final_matrix: np.ndarray, comp_file_index: int,
                     temp_labels: np.ndarray) -> (np.ndarray, np.ndarray):
    """this function generate the result of cos-similarity between files

    :param final_matrix: the count matrix that filemanager.GetMatirx returned
    :param comp_file_index: the index of the comparison file
                            (the file that compares with others)
    :param temp_labels: the Temporary Labels that user inputs
    :return: docs_score: a parallel list with `docs_score`, is a
                         list of the cos-similarity distance
    :return: docs_name: a parallel list with `docs_score`, is a
                        list of the name (temp labels)
    """
    # precondition
    assert comp_file_index >= 0, NON_NEGATIVE_INDEX_MESSAGE
    assert np.size(temp_labels) > 0, EMPTY_LIST_MESSAGE

    dist = 1 - cosine_similarity(final_matrix)

    # get a list of file index in filemanager.files(also is the index in raw
    # matrix) given the file is not comp file
    other_file_indexes = np.asarray([file_index for file_index in range(
        final_matrix.shape[0]) if file_index != comp_file_index])

    # construct a list of score
    docs_np_score = np.asarray([dist[file_index, comp_file_index]
                               for file_index in other_file_indexes])
    docs_np_name = np.asarray([temp_labels[i] for i in range(
        other_file_indexes.size)])

    # sorting the output:
    docs_np = np.column_stack((docs_np_name, docs_np_score))
    # sort by score
    sorted_docs_np = docs_np[docs_np[:, 1].argsort()]

    # extract the list of name and score out from sorted_docs_list
    docs_name = sorted_docs_np[:, 0]
    docs_score = np.round(sorted_docs_np[:, 1].astype(float), decimals=4)

    return docs_score, docs_name  # 0 is name and 1 is score
<commit_msg>add some changes to the comment of similarity.py<commit_after>from typing import List

from sklearn.metrics.pairwise import cosine_similarity

from lexos.helpers.error_messages import NON_NEGATIVE_INDEX_MESSAGE, \
     EMPTY_LIST_MESSAGE

import numpy as np

def similarity_maker(final_matrix: np.ndarray, comp_file_index: int,
                     temp_labels: np.ndarray) -> (np.ndarray, np.ndarray):
    """this function generate the result of cos-similarity between files

    :param final_matrix: the count matrix that filemanager.GetMatirx returned
    :param comp_file_index: the index of the comparison file
                            (the file that compares with others)
    :param temp_labels: the Temporary Labels that user inputs
    :return: docs_score: a parallel list with `docs_score`, is a
                         list of the cos-similarity distance
    :return: docs_name: a parallel list with `docs_score`, is a
                        list of the name (temp labels)
    """
    # precondition
    assert comp_file_index >= 0, NON_NEGATIVE_INDEX_MESSAGE
    assert np.size(temp_labels) > 0, EMPTY_LIST_MESSAGE

    dist = 1 - cosine_similarity(final_matrix)

    # get an array of file index in filemanager.files
    other_file_indexes = np.asarray([file_index for file_index in range(
        final_matrix.shape[0]) if file_index != comp_file_index])

    # construct an array of scores
    docs_np_score = np.asarray([dist[file_index, comp_file_index]
                               for file_index in other_file_indexes])
    # construct an array of names
    docs_np_name = np.asarray([temp_labels[i] for i in range(
        other_file_indexes.size)])

    docs_np = np.column_stack((docs_np_name, docs_np_score))
    # sort by score
    sorted_docs_np = docs_np[docs_np[:, 1].argsort()]

    # extract the array of name and score out from sorted_docs_list
    docs_name = sorted_docs_np[:, 0]
    docs_score = np.round(sorted_docs_np[:, 1].astype(float), decimals=4)

    return docs_score, docs_name  # 0 is name and 1 is score
<|endoftext|>


<commit_before>#!/usr/bin/env python
# coding: utf8
"""Train a convolutional neural network text classifier on the
IMDB dataset, using the TextCategorizer component. The dataset will be loaded
automatically via Thinc's built-in dataset loader. The model is added to
spacy.pipeline, and predictions are available via `doc.cats`. For more details,
see the documentation:
* Training: https://spacy.io/usage/training

Compatible with: spaCy v2.0.0+
"""
from __future__ import unicode_literals, print_function
import plac
import random
from pathlib import Path
import thinc.extra.datasets

import spacy
from spacy.util import minibatch, compounding

@plac.annotations(
    model=("Model name. Defaults to blank 'en' model.", "option", "m", str),
    output_dir=("Optional output directory", "option", "o", Path),
    n_texts=("Number of texts to train from", "option", "t", int),
    n_iter=("Number of training iterations", "option", "n", int),
    init_tok2vec=("Pretrained tok2vec weights", "option", "t2v", Path)
)
def main(model=None, output_dir=None, n_iter=20, n_texts=2000, init_tok2vec=None):
    if output_dir is not None:
        output_dir = Path(output_dir)
        if not output_dir.exists():
            output_dir.mkdir()

    if model is not None:
        nlp = spacy.load(model)  # load existing spaCy model
        print("Loaded model '%s'" % model)
    else:
        nlp = spacy.blank("en")  # create blank Language class
        print("Created blank 'en' model")

    # add the text classifier to the pipeline if it doesn't exist
    # nlp.create_pipe works for built-ins that are registered with spaCy
    if "textcat" not in nlp.pipe_names:
        textcat = nlp.create_pipe(
            "textcat", config={"architecture": "simple_cnn", "exclusive_classes": True}
        )
        nlp.add_pipe(textcat, last=True)
    # otherwise, get it, so we can add labels to it
    else:
        textcat = nlp.get_pipe("textcat")

    # add label to text classifier
    textcat.add_label("POSITIVE")
    textcat.add_label("NEGATIVE")

    # load the IMDB dataset
    print("Loading IMDB data...")
    (train_texts, train_cats), (dev_texts, dev_cats) = load_data(limit=n_texts)
    print(
        "Using {} examples ({} training, {} evaluation)".format(
            n_texts, len(train_texts), len(dev_texts)
        )
    )
    train_data = list(zip(train_texts, [{"cats": cats} for cats in train_cats]))

    # get names of other pipes to disable them during training
    other_pipes = [pipe for pipe in nlp.pipe_names if pipe != "textcat"]
    with nlp.disable_pipes(*other_pipes):  # only train textcat
        optimizer = nlp.begin_training()
        if init_tok2vec is not None:
            with init_tok2vec.open("rb") as file_:
                textcat.model.tok2vec.from_bytes(file_.read())
        print("Training the model...")
        print("{:^5}\t{:^5}\t{:^5}\t{:^5}".format("LOSS", "P", "R", "F"))
        for i in range(n_iter):
            losses = {}
            # batch up the examples using spaCy's minibatch
            batches = minibatch(train_data, size=compounding(4.0, 32.0, 1.001))
            for batch in batches:
                texts, annotations = zip(*batch)
                nlp.update(texts, annotations, sgd=optimizer, drop=0.2, losses=losses)
            with textcat.model.use_params(optimizer.averages):
                # evaluate on the dev data split off in load_data()
                scores = evaluate(nlp.tokenizer, textcat, dev_texts, dev_cats)
            print(
                "{0:.3f}\t{1:.3f}\t{2:.3f}\t{3:.3f}".format(  # print a simple table
                    losses["textcat"],
                    scores["textcat_p"],
                    scores["textcat_r"],
                    scores["textcat_f"],
                )
            )

    # test the trained model
    test_text = "This movie sucked"
    doc = nlp(test_text)
    print(test_text, doc.cats)

    if output_dir is not None:
        with nlp.use_params(optimizer.averages):
            nlp.to_disk(output_dir)
        print("Saved model to", output_dir)

        # test the saved model
        print("Loading from", output_dir)
        nlp2 = spacy.load(output_dir)
        doc2 = nlp2(test_text)
        print(test_text, doc2.cats)

def load_data(limit=0, split=0.8):
    """Load data from the IMDB dataset."""
    # Partition off part of the train data for evaluation
    train_data, _ = thinc.extra.datasets.imdb()
    random.shuffle(train_data)
    train_data = train_data[-limit:]
    texts, labels = zip(*train_data)
    cats = [{"POSITIVE": bool(y), "NEGATIVE": not bool(y)} for y in labels]
    split = int(len(train_data) * split)
    return (texts[:split], cats[:split]), (texts[split:], cats[split:])

def evaluate(tokenizer, textcat, texts, cats):
    docs = (tokenizer(text) for text in texts)
    tp = 0.0  # True positives
    fp = 1e-8  # False positives
    fn = 1e-8  # False negatives
    tn = 0.0  # True negatives
    for i, doc in enumerate(textcat.pipe(docs)):
        gold = cats[i]
        for label, score in doc.cats.items():
            if label not in gold:
                continue
            if label == "NEGATIVE":
                continue
            if score >= 0.5 and gold[label] >= 0.5:
                tp += 1.0
            elif score >= 0.5 and gold[label] < 0.5:
                fp += 1.0
            elif score < 0.5 and gold[label] < 0.5:
                tn += 1
            elif score < 0.5 and gold[label] >= 0.5:
                fn += 1
    precision = tp / (tp + fp)
    recall = tp / (tp + fn)
    if (precision + recall) == 0:
        f_score = 0.0
    else:
        f_score = 2 * (precision * recall) / (precision + recall)
    return {"textcat_p": precision, "textcat_r": recall, "textcat_f": f_score}

if __name__ == "__main__":
    plac.call(main)
<commit_msg>Fix train loop for train_textcat example<commit_after>#!/usr/bin/env python
# coding: utf8
"""Train a convolutional neural network text classifier on the
IMDB dataset, using the TextCategorizer component. The dataset will be loaded
automatically via Thinc's built-in dataset loader. The model is added to
spacy.pipeline, and predictions are available via `doc.cats`. For more details,
see the documentation:
* Training: https://spacy.io/usage/training

Compatible with: spaCy v2.0.0+
"""
from __future__ import unicode_literals, print_function
import plac
import random
from pathlib import Path
import thinc.extra.datasets

import spacy
from spacy.util import minibatch, compounding

@plac.annotations(
    model=("Model name. Defaults to blank 'en' model.", "option", "m", str),
    output_dir=("Optional output directory", "option", "o", Path),
    n_texts=("Number of texts to train from", "option", "t", int),
    n_iter=("Number of training iterations", "option", "n", int),
    init_tok2vec=("Pretrained tok2vec weights", "option", "t2v", Path)
)
def main(model=None, output_dir=None, n_iter=20, n_texts=2000, init_tok2vec=None):
    if output_dir is not None:
        output_dir = Path(output_dir)
        if not output_dir.exists():
            output_dir.mkdir()

    if model is not None:
        nlp = spacy.load(model)  # load existing spaCy model
        print("Loaded model '%s'" % model)
    else:
        nlp = spacy.blank("en")  # create blank Language class
        print("Created blank 'en' model")

    # add the text classifier to the pipeline if it doesn't exist
    # nlp.create_pipe works for built-ins that are registered with spaCy
    if "textcat" not in nlp.pipe_names:
        textcat = nlp.create_pipe(
            "textcat", config={"architecture": "simple_cnn", "exclusive_classes": True}
        )
        nlp.add_pipe(textcat, last=True)
    # otherwise, get it, so we can add labels to it
    else:
        textcat = nlp.get_pipe("textcat")

    # add label to text classifier
    textcat.add_label("POSITIVE")
    textcat.add_label("NEGATIVE")

    # load the IMDB dataset
    print("Loading IMDB data...")
    (train_texts, train_cats), (dev_texts, dev_cats) = load_data(limit=n_texts)
    print(
        "Using {} examples ({} training, {} evaluation)".format(
            n_texts, len(train_texts), len(dev_texts)
        )
    )
    train_data = list(zip(train_texts, [{"cats": cats} for cats in train_cats]))

    # get names of other pipes to disable them during training
    other_pipes = [pipe for pipe in nlp.pipe_names if pipe != "textcat"]
    with nlp.disable_pipes(*other_pipes):  # only train textcat
        optimizer = nlp.begin_training()
        if init_tok2vec is not None:
            with init_tok2vec.open("rb") as file_:
                textcat.model.tok2vec.from_bytes(file_.read())
        print("Training the model...")
        print("{:^5}\t{:^5}\t{:^5}\t{:^5}".format("LOSS", "P", "R", "F"))
        batch_sizes = compounding(4.0, 32.0, 1.001)
        for i in range(n_iter):
            losses = {}
            # batch up the examples using spaCy's minibatch
            random.shuffle(train_data)
            batches = minibatch(train_data, size=batch_sizes)
            for batch in batches:
                texts, annotations = zip(*batch)
                nlp.update(texts, annotations, sgd=optimizer, drop=0.2, losses=losses)
            with textcat.model.use_params(optimizer.averages):
                # evaluate on the dev data split off in load_data()
                scores = evaluate(nlp.tokenizer, textcat, dev_texts, dev_cats)
            print(
                "{0:.3f}\t{1:.3f}\t{2:.3f}\t{3:.3f}".format(  # print a simple table
                    losses["textcat"],
                    scores["textcat_p"],
                    scores["textcat_r"],
                    scores["textcat_f"],
                )
            )

    # test the trained model
    test_text = "This movie sucked"
    doc = nlp(test_text)
    print(test_text, doc.cats)

    if output_dir is not None:
        with nlp.use_params(optimizer.averages):
            nlp.to_disk(output_dir)
        print("Saved model to", output_dir)

        # test the saved model
        print("Loading from", output_dir)
        nlp2 = spacy.load(output_dir)
        doc2 = nlp2(test_text)
        print(test_text, doc2.cats)

def load_data(limit=0, split=0.8):
    """Load data from the IMDB dataset."""
    # Partition off part of the train data for evaluation
    train_data, _ = thinc.extra.datasets.imdb()
    random.shuffle(train_data)
    train_data = train_data[-limit:]
    texts, labels = zip(*train_data)
    cats = [{"POSITIVE": bool(y), "NEGATIVE": not bool(y)} for y in labels]
    split = int(len(train_data) * split)
    return (texts[:split], cats[:split]), (texts[split:], cats[split:])

def evaluate(tokenizer, textcat, texts, cats):
    docs = (tokenizer(text) for text in texts)
    tp = 0.0  # True positives
    fp = 1e-8  # False positives
    fn = 1e-8  # False negatives
    tn = 0.0  # True negatives
    for i, doc in enumerate(textcat.pipe(docs)):
        gold = cats[i]
        for label, score in doc.cats.items():
            if label not in gold:
                continue
            if label == "NEGATIVE":
                continue
            if score >= 0.5 and gold[label] >= 0.5:
                tp += 1.0
            elif score >= 0.5 and gold[label] < 0.5:
                fp += 1.0
            elif score < 0.5 and gold[label] < 0.5:
                tn += 1
            elif score < 0.5 and gold[label] >= 0.5:
                fn += 1
    precision = tp / (tp + fp)
    recall = tp / (tp + fn)
    if (precision + recall) == 0:
        f_score = 0.0
    else:
        f_score = 2 * (precision * recall) / (precision + recall)
    return {"textcat_p": precision, "textcat_r": recall, "textcat_f": f_score}

if __name__ == "__main__":
    plac.call(main)
<|endoftext|>


<commit_before>import os
import sys
import stat
import shutil
import string
import getpass
from collections import OrderedDict
from distutils.dir_util import copy_tree

from wa import ComplexCommand, SubCommand, pluginloader, settings
from wa.framework.target.descriptor import list_target_descriptions
from wa.framework.exception import ConfigError, CommandError
from wa.utils.misc import (ensure_directory_exists as _d, capitalize,
                           ensure_file_directory_exists as _f)
from wa.utils.serializer import yaml

from devlib.utils.types import identifier

TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), 'templates')

class CreateAgendaSubcommand(SubCommand):

    name = 'agenda'
    description = """
    Create an agenda with the specified extensions enabled. And parameters set
    to their default values.
    """

    def initialize(self, context):
        self.parser.add_argument('plugins', nargs='+',
                                 help='Plugins to be added to the agendas')
        self.parser.add_argument('-i', '--iterations', type=int, default=1,
                                 help='Sets the number of iterations for all workloads')
        self.parser.add_argument('-o', '--output', metavar='FILE',
                                 help='Output file. If not specfied, STDOUT will be used instead.')

    def execute(self, state, args):
        agenda = OrderedDict()
        agenda['config'] = OrderedDict(augmentations=[], iterations=args.iterations)
        agenda['workloads'] = []
        target_desc = None

        targets = {td.name: td for td in list_target_descriptions()}

        for name in args.plugins:
            if name in targets:
                if target_desc is not None:
                    raise ConfigError('Specifying multiple devices: {} and {}'.format(target_desc.name, name))
                target_desc = targets[name]
                agenda['config']['device'] = name
                agenda['config']['device_config'] = target_desc.get_default_config()
                continue

            extcls = pluginloader.get_plugin_class(name)
            config = pluginloader.get_default_config(name)

            if extcls.kind == 'workload':
                entry = OrderedDict()
                entry['name'] = extcls.name
                if name != extcls.name:
                    entry['label'] = name
                entry['params'] = config
                agenda['workloads'].append(entry)
            else:
                if extcls.kind == 'instrument':
                    agenda['config']['augmentations'].append(name)
                if extcls.kind == 'output_processor':
                    agenda['config']['augmentations'].append(name)
                agenda['config'][name] = config

        if args.output:
            wfh = open(args.output, 'w')
        else:
            wfh = sys.stdout
        yaml.dump(agenda, wfh, indent=4, default_flow_style=False)
        if args.output:
            wfh.close()

class CreateWorkloadSubcommand(SubCommand):

    name = 'workload'
    description = '''Create a new workload. By default, a basic workload template will be
                     used but you can specify the `KIND` to choose a different template.'''

    def initialize(self, context):
        self.parser.add_argument('name', metavar='NAME',
                                 help='Name of the workload to be created')
        self.parser.add_argument('-p', '--path', metavar='PATH', default=None,
                                 help='The location at which the workload will be created. If not specified, ' +
                                      'this defaults to "~/.workload_automation/plugins".')
        self.parser.add_argument('-f', '--force', action='store_true',
                                 help='Create the new workload even if a workload with the specified ' +
                                      'name already exists.')
        self.parser.add_argument('-k', '--kind', metavar='KIND', default='basic', choices=create_funcs.keys(),
                                 help='The type of workload to be created. The available options ' +
                                      'are: {}'.format(', '.join(create_funcs.keys())))

    def execute(self, state, args):  # pylint: disable=R0201
        where = args.path or 'local'
        check_name = not args.force

        try:
            create_workload(args.name, args.kind, where, check_name)
        except CommandError as e:
            self.logger.error('ERROR: {}'.format(e))

class CreatePackageSubcommand(SubCommand):

    name = 'package'
    description = '''Create a new empty Python package for WA extensions. On installation,
                     this package will "advertise" itself to WA so that Plugins within it will
                     be loaded by WA when it runs.'''

    def initialize(self, context):
        self.parser.add_argument('name', metavar='NAME',
                                 help='Name of the package to be created')
        self.parser.add_argument('-p', '--path', metavar='PATH', default=None,
                                 help='The location at which the new package will be created. If not specified, ' +
                                      'current working directory will be used.')
        self.parser.add_argument('-f', '--force', action='store_true',
                                 help='Create the new package even if a file or directory with the same name '
                                      'already exists at the specified location.')

    def execute(self, state, args):  # pylint: disable=R0201
        package_dir = args.path or os.path.abspath('.')
        template_path = os.path.join(TEMPLATES_DIR, 'setup.template')
        self.create_extensions_package(package_dir, args.name, template_path, args.force)

    def create_extensions_package(self, location, name, setup_template_path, overwrite=False):
        package_path = os.path.join(location, name)
        if os.path.exists(package_path):
            if overwrite:
                self.logger.info('overwriting existing "{}"'.format(package_path))
                shutil.rmtree(package_path)
            else:
                raise CommandError('Location "{}" already exists.'.format(package_path))
        actual_package_path = os.path.join(package_path, name)
        os.makedirs(actual_package_path)
        setup_text = render_template(setup_template_path, {'package_name': name, 'user': getpass.getuser()})
        with open(os.path.join(package_path, 'setup.py'), 'w') as wfh:
            wfh.write(setup_text)
        touch(os.path.join(actual_package_path, '__init__.py'))

class CreateCommand(ComplexCommand):

    name = 'create'
    description = '''
    Used to create various WA-related objects (see positional arguments list
    for what objects may be created).\n\nUse "wa create <object> -h" for
    object-specific arguments.
    '''
    subcmd_classes = [
        CreateWorkloadSubcommand,
        CreateAgendaSubcommand,
        CreatePackageSubcommand,
    ]

def create_workload(name, kind='basic', where='local', check_name=True, **kwargs):

    if check_name:
        if name in [wl.name for wl in pluginloader.list_plugins('workload')]:
            raise CommandError('Workload with name "{}" already exists.'.format(name))

    class_name = get_class_name(name)
    if where == 'local':
        workload_dir = _d(os.path.join(settings.plugins_directory, name))
    else:
        workload_dir = _d(os.path.join(where, name))

    try:
        # Note: `create_funcs` mapping is listed below
        create_funcs[kind](workload_dir, name, kind, class_name, **kwargs)
    except KeyError:
        raise CommandError('Unknown workload type: {}'.format(kind))

    print 'Workload created in {}'.format(workload_dir)

def create_template_workload(path, name, kind, class_name):
    source_file = os.path.join(path, '__init__.py')
    with open(source_file, 'w') as wfh:
        wfh.write(render_template('{}_workload'.format(kind), {'name': name, 'class_name': class_name}))

def create_uiautomator_template_workload(path, name, kind, class_name):
    uiauto_path = os.path.join(path, 'uiauto')
    create_uiauto_project(uiauto_path, name)
    create_template_workload(path, name, kind, class_name)

def create_uiauto_project(path, name):
    package_name = 'com.arm.wa.uiauto.' + name.lower()

    copy_tree(os.path.join(TEMPLATES_DIR, 'uiauto', 'uiauto_workload_template'), path)

    manifest_path = os.path.join(path, 'app', 'src', 'main')
    mainifest = os.path.join(_d(manifest_path), 'AndroidManifest.xml')
    with open(mainifest, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'uiauto_AndroidManifest.xml'),
                                  {'package_name': package_name}))

    build_gradle_path = os.path.join(path, 'app')
    build_gradle = os.path.join(_d(build_gradle_path), 'build.gradle')
    with open(build_gradle, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'uiauto_build.gradle'),
                                  {'package_name': package_name}))

    build_script = os.path.join(path, 'build.sh')
    with open(build_script, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'uiauto_build_script'),
                                  {'package_name': package_name}))
    os.chmod(build_script, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)

    source_file = _f(os.path.join(path, 'app', 'src', 'main', 'java',
                                  os.sep.join(package_name.split('.')[:-1]),
                                  'UiAutomation.java'))
    with open(source_file, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'UiAutomation.java'),
                                  {'name': name, 'package_name': package_name}))

# Mapping of workload types to their corresponding creation method
create_funcs = {
    'basic' : create_template_workload,
    'apk' : create_template_workload,
    'revent' : create_template_workload,
    'apk_revent' : create_template_workload,
    'uiauto' : create_uiautomator_template_workload,
    'apk_uiauto' : create_uiautomator_template_workload,
}

# Utility functions
def render_template(name, params):
    filepath = os.path.join(TEMPLATES_DIR, name)
    with open(filepath) as fh:
        text = fh.read()
        template = string.Template(text)
        return template.substitute(params)

def get_class_name(name, postfix=''):
    name = identifier(name)
    return ''.join(map(capitalize, name.split('_'))) + postfix

def touch(path):
    with open(path, 'w') as _:
        pass
<commit_msg>commands/create: Rename workload type names<commit_after>import os
import sys
import stat
import shutil
import string
import getpass
from collections import OrderedDict
from distutils.dir_util import copy_tree

from wa import ComplexCommand, SubCommand, pluginloader, settings
from wa.framework.target.descriptor import list_target_descriptions
from wa.framework.exception import ConfigError, CommandError
from wa.utils.misc import (ensure_directory_exists as _d, capitalize,
                           ensure_file_directory_exists as _f)
from wa.utils.serializer import yaml

from devlib.utils.types import identifier

TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), 'templates')

class CreateAgendaSubcommand(SubCommand):

    name = 'agenda'
    description = """
    Create an agenda with the specified extensions enabled. And parameters set
    to their default values.
    """

    def initialize(self, context):
        self.parser.add_argument('plugins', nargs='+',
                                 help='Plugins to be added to the agendas')
        self.parser.add_argument('-i', '--iterations', type=int, default=1,
                                 help='Sets the number of iterations for all workloads')
        self.parser.add_argument('-o', '--output', metavar='FILE',
                                 help='Output file. If not specfied, STDOUT will be used instead.')

    def execute(self, state, args):
        agenda = OrderedDict()
        agenda['config'] = OrderedDict(augmentations=[], iterations=args.iterations)
        agenda['workloads'] = []
        target_desc = None

        targets = {td.name: td for td in list_target_descriptions()}

        for name in args.plugins:
            if name in targets:
                if target_desc is not None:
                    raise ConfigError('Specifying multiple devices: {} and {}'.format(target_desc.name, name))
                target_desc = targets[name]
                agenda['config']['device'] = name
                agenda['config']['device_config'] = target_desc.get_default_config()
                continue

            extcls = pluginloader.get_plugin_class(name)
            config = pluginloader.get_default_config(name)

            if extcls.kind == 'workload':
                entry = OrderedDict()
                entry['name'] = extcls.name
                if name != extcls.name:
                    entry['label'] = name
                entry['params'] = config
                agenda['workloads'].append(entry)
            else:
                if extcls.kind == 'instrument':
                    agenda['config']['augmentations'].append(name)
                if extcls.kind == 'output_processor':
                    agenda['config']['augmentations'].append(name)
                agenda['config'][name] = config

        if args.output:
            wfh = open(args.output, 'w')
        else:
            wfh = sys.stdout
        yaml.dump(agenda, wfh, indent=4, default_flow_style=False)
        if args.output:
            wfh.close()

class CreateWorkloadSubcommand(SubCommand):

    name = 'workload'
    description = '''Create a new workload. By default, a basic workload template will be
                     used but you can specify the `KIND` to choose a different template.'''

    def initialize(self, context):
        self.parser.add_argument('name', metavar='NAME',
                                 help='Name of the workload to be created')
        self.parser.add_argument('-p', '--path', metavar='PATH', default=None,
                                 help='The location at which the workload will be created. If not specified, ' +
                                      'this defaults to "~/.workload_automation/plugins".')
        self.parser.add_argument('-f', '--force', action='store_true',
                                 help='Create the new workload even if a workload with the specified ' +
                                      'name already exists.')
        self.parser.add_argument('-k', '--kind', metavar='KIND', default='basic', choices=create_funcs.keys(),
                                 help='The type of workload to be created. The available options ' +
                                      'are: {}'.format(', '.join(create_funcs.keys())))

    def execute(self, state, args):  # pylint: disable=R0201
        where = args.path or 'local'
        check_name = not args.force

        try:
            create_workload(args.name, args.kind, where, check_name)
        except CommandError as e:
            self.logger.error('ERROR: {}'.format(e))

class CreatePackageSubcommand(SubCommand):

    name = 'package'
    description = '''Create a new empty Python package for WA extensions. On installation,
                     this package will "advertise" itself to WA so that Plugins within it will
                     be loaded by WA when it runs.'''

    def initialize(self, context):
        self.parser.add_argument('name', metavar='NAME',
                                 help='Name of the package to be created')
        self.parser.add_argument('-p', '--path', metavar='PATH', default=None,
                                 help='The location at which the new package will be created. If not specified, ' +
                                      'current working directory will be used.')
        self.parser.add_argument('-f', '--force', action='store_true',
                                 help='Create the new package even if a file or directory with the same name '
                                      'already exists at the specified location.')

    def execute(self, state, args):  # pylint: disable=R0201
        package_dir = args.path or os.path.abspath('.')
        template_path = os.path.join(TEMPLATES_DIR, 'setup.template')
        self.create_extensions_package(package_dir, args.name, template_path, args.force)

    def create_extensions_package(self, location, name, setup_template_path, overwrite=False):
        package_path = os.path.join(location, name)
        if os.path.exists(package_path):
            if overwrite:
                self.logger.info('overwriting existing "{}"'.format(package_path))
                shutil.rmtree(package_path)
            else:
                raise CommandError('Location "{}" already exists.'.format(package_path))
        actual_package_path = os.path.join(package_path, name)
        os.makedirs(actual_package_path)
        setup_text = render_template(setup_template_path, {'package_name': name, 'user': getpass.getuser()})
        with open(os.path.join(package_path, 'setup.py'), 'w') as wfh:
            wfh.write(setup_text)
        touch(os.path.join(actual_package_path, '__init__.py'))

class CreateCommand(ComplexCommand):

    name = 'create'
    description = '''
    Used to create various WA-related objects (see positional arguments list
    for what objects may be created).\n\nUse "wa create <object> -h" for
    object-specific arguments.
    '''
    subcmd_classes = [
        CreateWorkloadSubcommand,
        CreateAgendaSubcommand,
        CreatePackageSubcommand,
    ]

def create_workload(name, kind='basic', where='local', check_name=True, **kwargs):

    if check_name:
        if name in [wl.name for wl in pluginloader.list_plugins('workload')]:
            raise CommandError('Workload with name "{}" already exists.'.format(name))

    class_name = get_class_name(name)
    if where == 'local':
        workload_dir = _d(os.path.join(settings.plugins_directory, name))
    else:
        workload_dir = _d(os.path.join(where, name))

    try:
        # Note: `create_funcs` mapping is listed below
        create_funcs[kind](workload_dir, name, kind, class_name, **kwargs)
    except KeyError:
        raise CommandError('Unknown workload type: {}'.format(kind))

    print 'Workload created in {}'.format(workload_dir)

def create_template_workload(path, name, kind, class_name):
    source_file = os.path.join(path, '__init__.py')
    with open(source_file, 'w') as wfh:
        wfh.write(render_template('{}_workload'.format(kind), {'name': name, 'class_name': class_name}))

def create_uiautomator_template_workload(path, name, kind, class_name):
    uiauto_path = os.path.join(path, 'uiauto')
    create_uiauto_project(uiauto_path, name)
    create_template_workload(path, name, kind, class_name)

def create_uiauto_project(path, name):
    package_name = 'com.arm.wa.uiauto.' + name.lower()

    copy_tree(os.path.join(TEMPLATES_DIR, 'uiauto', 'uiauto_workload_template'), path)

    manifest_path = os.path.join(path, 'app', 'src', 'main')
    mainifest = os.path.join(_d(manifest_path), 'AndroidManifest.xml')
    with open(mainifest, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'uiauto_AndroidManifest.xml'),
                                  {'package_name': package_name}))

    build_gradle_path = os.path.join(path, 'app')
    build_gradle = os.path.join(_d(build_gradle_path), 'build.gradle')
    with open(build_gradle, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'uiauto_build.gradle'),
                                  {'package_name': package_name}))

    build_script = os.path.join(path, 'build.sh')
    with open(build_script, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'uiauto_build_script'),
                                  {'package_name': package_name}))
    os.chmod(build_script, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)

    source_file = _f(os.path.join(path, 'app', 'src', 'main', 'java',
                                  os.sep.join(package_name.split('.')[:-1]),
                                  'UiAutomation.java'))
    with open(source_file, 'w') as wfh:
        wfh.write(render_template(os.path.join('uiauto', 'UiAutomation.java'),
                                  {'name': name, 'package_name': package_name}))

# Mapping of workload types to their corresponding creation method
create_funcs = {
    'basic' : create_template_workload,
    'apk' : create_template_workload,
    'revent' : create_template_workload,
    'apkrevent' : create_template_workload,
    'uiauto' : create_uiautomator_template_workload,
    'apkuiauto' : create_uiautomator_template_workload,
}

# Utility functions
def render_template(name, params):
    filepath = os.path.join(TEMPLATES_DIR, name)
    with open(filepath) as fh:
        text = fh.read()
        template = string.Template(text)
        return template.substitute(params)

def get_class_name(name, postfix=''):
    name = identifier(name)
    return ''.join(map(capitalize, name.split('_'))) + postfix

def touch(path):
    with open(path, 'w') as _:
        pass
<|endoftext|>


<commit_before>from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.utils.encoding import python_2_unicode_compatible

from libravatar import libravatar_url
try:
    from urllib2 import urlparse
except ImportError:
    from urllib import parse as urlparse

from wafer.kv.models import KeyValue
from wafer.talks.models import ACCEPTED, PENDING

@python_2_unicode_compatible
class UserProfile(models.Model):

    class Meta:
         ordering = ['id']

    user = models.OneToOneField(User)
    kv = models.ManyToManyField(KeyValue)
    contact_number = models.CharField(max_length=16, null=True, blank=True)
    bio = models.TextField(null=True, blank=True)

    homepage = models.CharField(max_length=256, null=True, blank=True)
    # We should probably do social auth instead
    # And care about other code hosting sites...
    twitter_handle = models.CharField(max_length=15, null=True, blank=True)
    github_username = models.CharField(max_length=32, null=True, blank=True)

    def __str__(self):
        return u'%s' % self.user

    def accepted_talks(self):
        return self.user.talks.filter(status=ACCEPTED)

    def pending_talks(self):
        return self.user.talks.filter(status=PENDING)

    def avatar_url(self, size=96, https=True, default='mm'):
        if not self.user.email:
            return None
        return libravatar_url(self.user.email, size=size, https=https,
                              default=default)

    def homepage_url(self):
        """Try ensure we prepend http: to the url if there's nothing there

           This is to ensure we're not generating relative links in the
           user templates."""
        if not self.homepage:
            return self.homepage
        parsed = urlparse.urlparse(self.homepage)
        if parsed.scheme:
            return self.homepage
        # Vague sanity check
        abs_url = ''.join(['http://', self.homepage])
        if urlparse.urlparse(abs_url).scheme == 'http':
            return abs_url
        return self.homepage

    def display_name(self):
        return self.user.get_full_name() or self.user.username

    def is_registered(self):
        from wafer.users.forms import get_registration_form_class

        if settings.WAFER_REGISTRATION_MODE == 'ticket':
            return self.user.ticket.exists()
        elif settings.WAFER_REGISTRATION_MODE == 'form':
            form = get_registration_form_class()
            return form.is_registered(self.kv)
        raise NotImplemented('Invalid WAFER_REGISTRATION_MODE: %s'
                             % settings.WAFER_REGISTRATION_MODE)

def create_user_profile(sender, instance, created, raw=False, **kwargs):
    if raw:
        return
    if created:
        UserProfile.objects.create(user=instance)

post_save.connect(create_user_profile, sender=User)
<commit_msg>Tag is_registered as boolean, for admin<commit_after>from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.utils.encoding import python_2_unicode_compatible

from libravatar import libravatar_url
try:
    from urllib2 import urlparse
except ImportError:
    from urllib import parse as urlparse

from wafer.kv.models import KeyValue
from wafer.talks.models import ACCEPTED, PENDING

@python_2_unicode_compatible
class UserProfile(models.Model):

    class Meta:
         ordering = ['id']

    user = models.OneToOneField(User)
    kv = models.ManyToManyField(KeyValue)
    contact_number = models.CharField(max_length=16, null=True, blank=True)
    bio = models.TextField(null=True, blank=True)

    homepage = models.CharField(max_length=256, null=True, blank=True)
    # We should probably do social auth instead
    # And care about other code hosting sites...
    twitter_handle = models.CharField(max_length=15, null=True, blank=True)
    github_username = models.CharField(max_length=32, null=True, blank=True)

    def __str__(self):
        return u'%s' % self.user

    def accepted_talks(self):
        return self.user.talks.filter(status=ACCEPTED)

    def pending_talks(self):
        return self.user.talks.filter(status=PENDING)

    def avatar_url(self, size=96, https=True, default='mm'):
        if not self.user.email:
            return None
        return libravatar_url(self.user.email, size=size, https=https,
                              default=default)

    def homepage_url(self):
        """Try ensure we prepend http: to the url if there's nothing there

           This is to ensure we're not generating relative links in the
           user templates."""
        if not self.homepage:
            return self.homepage
        parsed = urlparse.urlparse(self.homepage)
        if parsed.scheme:
            return self.homepage
        # Vague sanity check
        abs_url = ''.join(['http://', self.homepage])
        if urlparse.urlparse(abs_url).scheme == 'http':
            return abs_url
        return self.homepage

    def display_name(self):
        return self.user.get_full_name() or self.user.username

    def is_registered(self):
        from wafer.users.forms import get_registration_form_class

        if settings.WAFER_REGISTRATION_MODE == 'ticket':
            return self.user.ticket.exists()
        elif settings.WAFER_REGISTRATION_MODE == 'form':
            form = get_registration_form_class()
            return form.is_registered(self.kv)
        raise NotImplemented('Invalid WAFER_REGISTRATION_MODE: %s'
                             % settings.WAFER_REGISTRATION_MODE)

    is_registered.boolean = True

def create_user_profile(sender, instance, created, raw=False, **kwargs):
    if raw:
        return
    if created:
        UserProfile.objects.create(user=instance)

post_save.connect(create_user_profile, sender=User)
<|endoftext|>


<commit_before>import asyncio
from typing import Callable

from wcpan.logger import DEBUG, EXCEPTION

from .task import regular_call, ensure_task, MaybeTask, TerminalTask

class AsyncQueue(object):

    def __init__(self, maximum=None):
        self._max = 1 if maximum is None else maximum
        self._lock = asyncio.Semaphore(self._max)
        self._loop = asyncio.get_event_loop()
        self._reset()

    def start(self):
        if self._runner or self._stopper:
            return
        self._runner = self._loop.create_task(self._process())

    async def stop(self):
        if not self._runner or self._stopper:
            return
        task = TerminalTask()
        self._queue.put_nowait(task)
        self._stopper = asyncio.Event()
        await self._stopper.wait()
        self._runner.cancel()
        self._reset()

    def flush(self, filter_: Callable[['Task'], bool]):
        q = self._get_internal_queue()
        nq = filter(lambda _: not filter_(_), q)
        nq = list(nq)
        DEBUG('wcpan.worker') << 'flush:' << 'before' << len(q) << 'after' << len(nq)
        self._set_internal_queue(nq)

    def post(self, task: MaybeTask):
        task = ensure_task(task)
        self._queue.put_nowait(task)

    async def _process(self):
        while self._runner:
            await self._lock.acquire()
            task = await self._queue.get()
            self._loop.create_task(self._run(task))

    async def _run(self, task):
        try:
            if isinstance(task, TerminalTask):
                return
            else:
                await regular_call(task)
        except Exception as e:
            EXCEPTION('wcpan.worker', e) << 'uncaught exception'
        finally:
            self._queue.task_done()
            self._lock.release()
            if self._stopper:
                self._stopper.set()

    def _reset(self):
        self._queue = asyncio.PriorityQueue()
        self._stopper = None
        self._runner = None

    def _get_internal_queue(self):
        return self._queue._queue

    def _set_internal_queue(self, nq):
        self._queue._queue = nq
<commit_msg>reimplement<commit_after>import asyncio
import contextlib as cl
from typing import Callable

from wcpan.logger import DEBUG, EXCEPTION

from .task import regular_call, ensure_task, MaybeTask, TerminalTask

class AsyncQueue(object):

    def __init__(self, maximum=None):
        self._max = 1 if maximum is None else maximum
        self._reset()

    def start(self):
        if self._consumer_list:
            return

        loop = asyncio.get_event_loop()
        self._consumer_list = [
            loop.create_task(self._consume())
                for _ in range(self._max)
        ]

    async def stop(self):
        if not self._consumer_list or self._waiting_finish:
            return

        # if all consumers are busy, we need to flush all pending tasks
        self._set_internal_queue([])

        # if some consumers are busy, we need to wait for running tasks
        if not self.idle:
            self._waiting_finish = asyncio.Event()
            await self._waiting_finish.wait()

        # cancel all consumers
        for consumer in self._consumer_list:
            consumer.cancel()

        self._reset()

    def flush(self, filter_: Callable[['Task'], bool]):
        q = self._get_internal_queue()
        nq = filter(lambda _: not filter_(_), q)
        nq = list(nq)
        DEBUG('wcpan.worker') << 'flush:' << 'before' << len(q) << 'after' << len(nq)
        self._set_internal_queue(nq)

    def post(self, task: MaybeTask):
        if self._waiting_finish:
            return

        self.start()

        task = ensure_task(task)
        self._queue.put_nowait(task)

    @property
    def idle(self):
        return self._active_count == 0

    async def _consume(self):
        while True:
            async with self._pop_task() as task, \
                       self._active_guard():
                try:
                    await regular_call(task)
                except Exception as e:
                    EXCEPTION('wcpan.worker', e) << 'uncaught exception'

            if self._waiting_finish and self.idle:
                self._waiting_finish.set()

    def _reset(self):
        self._queue = asyncio.PriorityQueue()
        self._active_count = 0
        self._waiting_finish = None
        self._consumer_list = None

    def _get_internal_queue(self):
        return self._queue._queue

    def _set_internal_queue(self, nq):
        self._queue._queue = nq

    @cl.asynccontextmanager
    async def _active_guard(self):
        self._active_count += 1
        try:
            yield
        finally:
            self._active_count -= 1

    @cl.asynccontextmanager
    async def _pop_task(self):
        task = await self._queue.get()
        try:
            yield task
        finally:
            self._queue.task_done()
<|endoftext|>


<commit_before>class Tile:
    def __init__(self):
        self.challenge_completed = False

    def describe(self):
        print("The rocks led you to a path uphill.\n*Poof* A purrr-sian cat "
              "appears in front of you!\n")
        if not self.challenge_completed:
            self.challenge()
        else:
            print("The rocks led you to a path uphill.\nIt's a little bit "
                  "windy and a flock of seagulls is flying above you.")

    def challenge(self):
        print("Meow good traveler! I have a question for you.\n"
              "You might as well go away and ignore me and my awesome fur but "
              "if you pass my little test I will reward you!\n\n"
              "Here is a series of numbers.\n"
              "What is the next number in the sequence?\n"
              "1\n"
              "11\n"
              "21\n"
              "1211\n"
              "111221\n"
              "312211\n"
              "13112221\n")
    #The next number in the sequence is 1113213211, because the rule for
    #creating the next number is to simply describe the previous number.
    #The first number is 1, or 1 (one) 1, so you get 11.
    #To describe 11, you have two 1's, or 21. Now you have one 2 and one 1,
    #so the next number is 1211. The solution is to simply continue describing
    #the previous number using only numbers.\n")

    def action(self, player, do):
        if not self.challenge_completed:
            pass
        else:
            print("I don't understand...")

    def leave(self, player, direction):
        if direction == "s":
            print("Oh dear Basdet! You can't go north, you will fall of the "
                   "cliff!\n")
            return False
        elif direction == "n":
            print("It looks like the side of a wooden hut.\n"
                  "Since you don't walk though walls (yet) you can't go there.")
            return False
        else:
            return True
<commit_msg>added action and an kitten item :3 (n1w4)<commit_after>from weatbag.utils import transfer

class Tile:
    def __init__(self):
        self.contents = {'neko': 1}
        self.challenge_completed = False
        self.sequence = "1113213211"

    def describe(self):
        if not self.challenge_completed:
            print("The rocks led you to a path uphill.\n*Poof* A purrr-sian "
                  "cat appears in front of you!\n")
            self.challenge()
        else:
            print("The rocks led you to a path uphill.\nIt's a little bit "
                  "windy and a flock of seagulls is flying above you.")

    def challenge(self):
        print("Meow good traveler! I have a question for you.\n"
              "You might as well go away and ignore me and my awesome fur but "
              "if you pass my little test I will reward you!\n\n"
              "Here is a series of numbers.\n"
              "What is the next number in the sequence?\n"
              "1\n"
              "11\n"
              "21\n"
              "1211\n"
              "111221\n"
              "312211\n"
              "13112221\n")
        #The next number in the sequence is 1113213211, because the rule for
        #creating the next number is to simply describe the previous number.
        #The first number is 1, or 1 (one) 1, so you get 11.
        #To describe 11, you have two 1's, or 21. Now you have one 2 and one 1,
        #so the next number is 1211. The solution is to simply continue
        #describing the previous number using only numbers.\n")

    def action(self, player, do):
        if not self.challenge_completed:
            if do[0] == self.sequence:
                print("You are a clever human, aren't you?!\n\n"
                      "Here is my offering. I give you this maneki-neko.\n"
                      "If you ever come across a wall of kittens and you want "
                      "them to let you pass you will use this lucky charm!")
                #https://en.wikipedia.org/wiki/Maneki-neko
                self.contents['neko'] -= 1
                player.give('neko')
                self.challenge_completed = True
        else:
            print("I don't understand...")

    def leave(self, player, direction):
        if direction == "s":
            print("Oh dear Basdet! You can't go north, you will fall of the "
                   "cliff!\n")
            return False
        elif direction == "n":
            print("It looks like the side of a wooden hut.\n"
                  "Since you don't walk though walls (yet) "
                  "you can't go there.\n")
            return False
        else:
            return True
<|endoftext|>


<commit_before>from weatbag import words
from weatbag.utils import transfer

class Tile:
    def __init__(self):
        self.give_catfood = False

    def describe(self):
        if not self.give_catfood:
            print("You are at the beach and there is an old man with an enormous "
              "beard that is geting his little boat ready to go across to the "
              "mainland.\nNaturally you are asking him to take you in his "
              "boat.\nThe old man replies:\nOf course I can take you with me my "
              "dear adventurer but first I need some catfood so I can feed the "
              "kitties across in the mainland!")
        else:
            print("There's the old fisherman with his boat, he can take you "
                  "across if you like!")

    def action(self, player, do):
        if (do[0] in words.use or do[0] in words.give) and do[1]=='catfood':
            if player.has('catfood'):
                player.take('catfood')
                print("Great! Now we can sail!")
                self.give_catfood = True
            else:
                print("No boat for you if you don't give me tha catfood!")
                self.give_catfood = False
        else:
            print("Sorry, I don't understand.")

    def leave(self, player, direction):
        if direction=='e':
            print("You can't swim, remember the electric eels? You need a "
                  "transport to go across.")
            return False
        return True

test_items = ['catfood']
<commit_msg>small changes / added a comment<commit_after>from weatbag import words
from weatbag.utils import transfer

class Tile:
    def __init__(self):
        self.give_catfood = False

    def describe(self):
        if not self.give_catfood:
            print("You are at the beach and there is an old man with an "
                  "enormous beard that is getting his little boat ready to go "
                  "across to the mainland.\nNaturally, you are asking him to "
                  "take you in his boat.\nThe old man replies:\nOf course I "
                  "can take you with me, my dear adventurer but first I need "
                  "some catfood so I can feed the kitties across in the "
                  "mainland!")
        else:
            print("There's the old fisherman with his boat, he can take you "
                  "across if you like!")

    def action(self, player, do):
        if (do[0] in words.use or do[0] in words.give) and do[1]=='catfood':

#   I only added words.use because player might
#   use the word "use" insteed of "give".

            if player.has('catfood'):
                player.take('catfood')
                print("Great! Now we can sail!")
                self.give_catfood = True
            else:
                print("No boat for you if you don't give me tha catfood!")
                self.give_catfood = False
        else:
            print("Sorry, I don't understand.")

    def leave(self, player, direction):
        if direction=='e'and not self.give_catfood:
            print("You can't swim, remember the electric eels? You need a "
                  "transport to go across.")
            return False
        return True

test_items = ['catfood']
<|endoftext|>


<commit_before># test plugin
from bot.pluginDespatch import Plugin
import re
import datetime
import logging
import pyowm
from logos.roomlib import get_global_option

from django.conf import settings
logger = logging.getLogger(__name__)
logging.config.dictConfig(settings.LOGGING)

class WeatherPlugin(Plugin):
    plugin = ("weather", "Weather Plugin")
    def __init__(self, *args, **kwargs):
        Plugin.__init__(self, *args, **kwargs)

        self.commands = (\
         (r'w (?P<arg>\S.*)$', self.weather, "Weather query"),
        )

    def privmsg(self, user, channel, message):
        pass

    def weather(self, regex, chan, nick, **kwargs):
        api_key = get_global_option('weather_api_key')
        owm = pyowm.OWM(api_key)
        arg = regex.group('arg')
        observation = owm.weather_at_place(arg)
        w = observation.get_weather()
        tm = w.get_reference_time()
        tm_str = datetime.datetime.fromtimestamp(tm).strftime("%b %d %Y %H:%M")
        str1 = "Reference time : " + tm_str
        str1 += " Status : {}. ".format(w.get_status())
        str1 += " Wind: speed {speed} ".format(**w.get_wind())
        str1 += " Humidity: {} ".format(w.get_humidity())
        celsius = w.get_temperature('celsius')['temp']
        fahrenheit = w.get_temperature('fahrenheit')['temp']
        str1 += " Temperature {} F  {} C".format(fahrenheit, celsius)
#        str1 += " Temperature (F): {temp_min}, {temp}, {temp_max}.".format(**w.get_temperature())
#        str1 += " Temperature (C): {temp_min}, {temp}, {temp_max}.".format(**w.get_temperature('celsius'))
        self.say(chan, str1)

<commit_msg>Add location name to weather report<commit_after># test plugin
from bot.pluginDespatch import Plugin
import re
import datetime
import logging
import pyowm
from logos.roomlib import get_global_option

from django.conf import settings
logger = logging.getLogger(__name__)
logging.config.dictConfig(settings.LOGGING)

class WeatherPlugin(Plugin):
    plugin = ("weather", "Weather Plugin")
    def __init__(self, *args, **kwargs):
        Plugin.__init__(self, *args, **kwargs)

        self.commands = (\
         (r'w (?P<arg>\S.*)$', self.weather, "Weather query"),
        )

    def privmsg(self, user, channel, message):
        pass

    def weather(self, regex, chan, nick, **kwargs):
        api_key = get_global_option('weather_api_key')
        owm = pyowm.OWM(api_key)
        arg = regex.group('arg')
        observation = owm.weather_at_place(arg)
        location = observation.get_location().get_name()
        w = observation.get_weather()
        tm = w.get_reference_time()
        tm_str = datetime.datetime.fromtimestamp(tm).strftime("%b %d %Y %H:%M UTC")
        str1 = "Reference time : {}.  ".format(tm_str)
        str1 += " Location : {}. ".format(location)
        str1 += " Status : {}. ".format(w.get_status())
        str1 += " Wind speed : {speed} ".format(**w.get_wind())
        str1 += " Humidity: {} ".format(w.get_humidity())
        celsius = w.get_temperature('celsius')['temp']
        fahrenheit = w.get_temperature('fahrenheit')['temp']
        str1 += " Temperature {} F  {} C".format(fahrenheit, celsius)
#        str1 += " Temperature (F): {temp_min}, {temp}, {temp_max}.".format(**w.get_temperature())
#        str1 += " Temperature (C): {temp_min}, {temp}, {temp_max}.".format(**w.get_temperature('celsius'))
        self.say(chan, str1)

<|endoftext|>